From 01482ad607ea3426c1ec368265aa5e562ca0ed97 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Thu, 18 Jun 2026 08:28:26 +0000 Subject: [PATCH 001/707] 8386247: G1: Cleanup naming and type use of G1CollectionSet class members and methods Reviewed-by: stefank, ayang, iwalulya --- src/hotspot/share/gc/g1/g1CollectionSet.cpp | 117 +++++++++--------- src/hotspot/share/gc/g1/g1CollectionSet.hpp | 62 +++++----- .../share/gc/g1/g1CollectionSet.inline.hpp | 14 +-- .../share/gc/g1/g1ParScanThreadState.cpp | 6 +- src/hotspot/share/gc/g1/g1Policy.cpp | 14 +-- src/hotspot/share/gc/g1/g1RemSet.cpp | 8 +- src/hotspot/share/gc/g1/g1YoungCollector.cpp | 2 +- .../g1/g1YoungGCAllocationFailureInjector.cpp | 2 +- .../gc/g1/g1YoungGCPostEvacuateTasks.cpp | 6 +- 9 files changed, 115 insertions(+), 116 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.cpp b/src/hotspot/share/gc/g1/g1CollectionSet.cpp index 7329e679519..14b5e321585 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.cpp @@ -37,13 +37,13 @@ #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" -uint G1CollectionSet::groups_cur_length() const { +uint G1CollectionSet::num_groups() const { assert(_inc_build_state == CSetBuildType::Inactive, "must be"); return _groups.length(); } -uint G1CollectionSet::groups_increment_length() const { - return groups_cur_length() - _groups_inc_part_start; +uint G1CollectionSet::num_groups_in_increment() const { + return num_groups() - _groups_inc_part_start; } G1CollectorState* G1CollectionSet::collector_state() const { @@ -59,12 +59,12 @@ G1CollectionSet::G1CollectionSet(G1CollectedHeap* g1h, G1Policy* policy) : _policy(policy), _candidates(), _regions(nullptr), - _regions_max_length(0), - _regions_cur_length(0), + _max_num_regions(0), + _num_regions(0), _groups(), - _eden_region_length(0), - _survivor_region_length(0), - _initial_old_region_length(0), + _num_eden_regions(0), + _num_survivor_regions(0), + _num_initial_old_regions(0), _optional_groups(), DEBUG_ONLY(_inc_build_state(CSetBuildType::Inactive) COMMA) _regions_inc_part_start(0), @@ -76,27 +76,27 @@ G1CollectionSet::~G1CollectionSet() { abandon_all_candidates(); } -void G1CollectionSet::init_region_lengths(uint eden_cset_region_length, - uint survivor_cset_region_length) { +void G1CollectionSet::prepare_for_collection(uint num_eden_cset_regions, + uint num_survivor_cset_regions) { assert_at_safepoint_on_vm_thread(); - _eden_region_length = eden_cset_region_length; - _survivor_region_length = survivor_cset_region_length; + _num_eden_regions = num_eden_cset_regions; + _num_survivor_regions = num_survivor_cset_regions; - assert((size_t)young_region_length() == _regions_cur_length, - "Young region length %u should match collection set length %u", young_region_length(), _regions_cur_length); + assert(num_young_regions() == num_regions(), + "Young region amount %u should match collection set region amount %u", num_young_regions(), num_regions()); - _initial_old_region_length = 0; + _num_initial_old_regions = 0; assert(_optional_groups.length() == 0, "Should not have any optional groups yet"); _optional_groups.clear(); } -void G1CollectionSet::initialize(uint max_region_length) { +void G1CollectionSet::initialize(uint max_num_regions) { guarantee(_regions == nullptr, "Must only initialize once."); - _regions_max_length = max_region_length; - _regions = NEW_C_HEAP_ARRAY(uint, max_region_length, mtGC); + _max_num_regions = max_num_regions; + _regions = NEW_C_HEAP_ARRAY(uint, max_num_regions, mtGC); - _candidates.initialize(max_region_length); + _candidates.initialize(max_num_regions); } void G1CollectionSet::abandon() { @@ -109,7 +109,7 @@ void G1CollectionSet::abandon() { void G1CollectionSet::abandon_all_candidates() { _candidates.clear(); - _initial_old_region_length = 0; + _num_initial_old_regions = 0; } void G1CollectionSet::prepare_for_scan () { @@ -128,17 +128,18 @@ void G1CollectionSet::add_old_region(G1HeapRegion* hr) { _g1h->register_old_collection_set_region_with_region_attr(hr); - assert(_regions_cur_length < _regions_max_length, "Collection set now larger than maximum size."); - _regions[_regions_cur_length++] = hr->hrm_index(); - _initial_old_region_length++; + assert(num_regions() < _max_num_regions, "Collection set now larger than maximum size."); + _regions[_num_regions++] = hr->hrm_index(); + _num_initial_old_regions++; _g1h->old_set_remove(hr); } void G1CollectionSet::start() { - assert(_regions_cur_length == 0, "Collection set must be empty before starting a new collection set."); - assert(groups_cur_length() == 0, "Collection set groups must be empty before starting a new collection set."); - assert(_optional_groups.length() == 0, "Collection set optional gorups must be empty before starting a new collection set."); + assert(num_regions() == 0, "Collection set must be empty before starting a new collection set."); + assert(num_groups() == 0, "Collection set groups must be empty before starting a new collection set."); + assert(_optional_groups.length() == 0, + "Collection set optional groups must be empty before starting a new collection set."); continue_incremental_building(); @@ -149,8 +150,8 @@ void G1CollectionSet::start() { void G1CollectionSet::continue_incremental_building() { assert(_inc_build_state == CSetBuildType::Inactive, "Precondition"); - _regions_inc_part_start = _regions_cur_length; - _groups_inc_part_start = groups_cur_length(); + _regions_inc_part_start = num_regions(); + _groups_inc_part_start = num_groups(); DEBUG_ONLY(_inc_build_state = CSetBuildType::Active;) } @@ -161,13 +162,13 @@ void G1CollectionSet::stop_incremental_building() { void G1CollectionSet::clear() { assert_at_safepoint_on_vm_thread(); - _regions_cur_length = 0; + _num_regions = 0; _groups.clear(); assert(_optional_groups.length() == 0, "must be"); } void G1CollectionSet::iterate(G1HeapRegionClosure* cl) const { - size_t len = _regions_cur_length; + uint len = _num_regions; OrderAccess::loadload(); for (uint i = 0; i < len; i++) { @@ -182,7 +183,7 @@ void G1CollectionSet::iterate(G1HeapRegionClosure* cl) const { void G1CollectionSet::par_iterate(G1HeapRegionClosure* cl, G1HeapRegionClaimer* hr_claimer, uint worker_id) const { - iterate_part_from(cl, hr_claimer, 0, cur_length(), worker_id); + iterate_part_from(cl, hr_claimer, 0, num_regions(), worker_id); } void G1CollectionSet::iterate_optional(G1HeapRegionClosure* cl) const { @@ -197,13 +198,13 @@ void G1CollectionSet::iterate_optional(G1HeapRegionClosure* cl) const { void G1CollectionSet::iterate_incremental_part_from(G1HeapRegionClosure* cl, G1HeapRegionClaimer* hr_claimer, uint worker_id) const { - iterate_part_from(cl, hr_claimer, _regions_inc_part_start, regions_cur_length(), worker_id); + iterate_part_from(cl, hr_claimer, _regions_inc_part_start, num_regions_in_increment(), worker_id); } void G1CollectionSet::iterate_part_from(G1HeapRegionClosure* cl, G1HeapRegionClaimer* hr_claimer, - size_t offset, - size_t length, + uint offset, + uint length, uint worker_id) const { _g1h->par_iterate_regions_array(cl, hr_claimer, @@ -223,17 +224,17 @@ void G1CollectionSet::add_young_region_common(G1HeapRegion* hr) { // Synchronize with the region attribute table. _g1h->register_young_region_with_region_attr(hr); + uint index = num_regions(); // We use UINT_MAX as "invalid" marker in verification. - assert(_regions_cur_length < (UINT_MAX - 1), - "Collection set is too large with %u entries", _regions_cur_length); - hr->set_young_index_in_cset(_regions_cur_length + 1); + assert(index < (UINT_MAX - 1), "Collection set is too large with %u entries", index); + hr->set_young_index_in_cset(index + 1); - assert(_regions_cur_length < _regions_max_length, "Collection set larger than maximum allowed."); - _regions[_regions_cur_length] = hr->hrm_index(); + assert(index < _max_num_regions, "Collection set larger than maximum allowed."); + _regions[index] = hr->hrm_index(); // Concurrent readers must observe the store of the value in the array before an - // update to the length field. + // update to the _num_regions field. OrderAccess::storestore(); - _regions_cur_length++; + _num_regions++; } void G1CollectionSet::add_survivor_regions(G1HeapRegion* hr) { @@ -335,9 +336,9 @@ double G1CollectionSet::finalize_young_part(double target_pause_time_ms, G1Survi // pause are appended to the RHS of the young list, i.e. // [Newly Young Regions ++ Survivors from last pause]. - uint eden_region_length = _g1h->eden_regions_count(); - uint survivor_region_length = survivors->length(); - init_region_lengths(eden_region_length, survivor_region_length); + uint num_eden_regions = _g1h->eden_regions_count(); + uint num_survivor_regions = survivors->length(); + prepare_for_collection(num_eden_regions, num_survivor_regions); verify_young_cset_indices(); @@ -345,13 +346,13 @@ double G1CollectionSet::finalize_young_part(double target_pause_time_ms, G1Survi double predicted_base_time_ms = _policy->predict_base_time_ms(pending_cards, card_rs_length); // Base time already includes the whole remembered set related time, so do not add that here // again. - double predicted_eden_time = _policy->predict_young_region_other_time_ms(eden_region_length) + - _policy->predict_eden_copy_time_ms(eden_region_length); + double predicted_eden_time = _policy->predict_young_region_other_time_ms(num_eden_regions) + + _policy->predict_eden_copy_time_ms(num_eden_regions); double remaining_time_ms = MAX2(target_pause_time_ms - (predicted_base_time_ms + predicted_eden_time), 0.0); log_trace(gc, ergo, cset)("Added young regions to CSet. Eden: %u regions, Survivors: %u regions, " "predicted eden time: %1.2fms, predicted base time: %1.2fms, target pause time: %1.2fms, remaining time: %1.2fms", - eden_region_length, survivor_region_length, + num_eden_regions, num_survivor_regions, predicted_eden_time, predicted_base_time_ms, target_pause_time_ms, remaining_time_ms); // Clear the fields that point to the survivor list - they are all young now. @@ -669,8 +670,8 @@ double G1CollectionSet::select_candidates_from_optional_groups(double time_remai } uint G1CollectionSet::select_optional_groups(double time_remaining_ms) { - uint optional_regions_count = num_optional_regions(); - assert(optional_regions_count > 0, + uint total_optional_regions = num_optional_regions(); + assert(total_optional_regions > 0, "Should only be called when there are optional regions"); uint num_regions_selected = 0; @@ -678,7 +679,7 @@ uint G1CollectionSet::select_optional_groups(double time_remaining_ms) { double total_prediction_ms = select_candidates_from_optional_groups(time_remaining_ms, num_regions_selected); log_debug(gc, ergo, cset)("Prepared %u regions out of %u for optional evacuation. Total predicted time: %.3fms", - num_regions_selected, optional_regions_count, total_prediction_ms); + num_regions_selected, total_optional_regions, total_prediction_ms); return num_regions_selected; } @@ -754,13 +755,12 @@ void G1CollectionSet::abandon_optional_collection_set(G1ParScanThreadStateSet* p #ifdef ASSERT class G1VerifyYoungCSetIndicesClosure : public G1HeapRegionClosure { -private: - size_t _young_length; + uint _num_young_regions; uint* _heap_region_indices; public: - G1VerifyYoungCSetIndicesClosure(size_t young_length) : G1HeapRegionClosure(), _young_length(young_length) { - _heap_region_indices = NEW_C_HEAP_ARRAY(uint, young_length + 1, mtGC); - for (size_t i = 0; i < young_length + 1; i++) { + G1VerifyYoungCSetIndicesClosure(uint num_young_regions) : G1HeapRegionClosure(), _num_young_regions(num_young_regions) { + _heap_region_indices = NEW_C_HEAP_ARRAY(uint, num_young_regions + 1, mtGC); + for (uint i = 0; i < num_young_regions + 1; i++) { _heap_region_indices[i] = UINT_MAX; } } @@ -771,8 +771,9 @@ class G1VerifyYoungCSetIndicesClosure : public G1HeapRegionClosure { virtual bool do_heap_region(G1HeapRegion* r) { const uint idx = r->young_index_in_cset(); - assert(idx > 0, "Young index must be set for all regions in the incremental collection set but is not for region %u.", r->hrm_index()); - assert(idx <= _young_length, "Young cset index %u too large for region %u", idx, r->hrm_index()); + assert(r->is_young(), "must be, but region %u is not", r->hrm_index()); + assert(idx > 0, "Young index must be set for all regions in the collection set but is not for region %u.", r->hrm_index()); + assert(idx <= _num_young_regions, "Young cset index %u too large for region %u", idx, r->hrm_index()); assert(_heap_region_indices[idx] == UINT_MAX, "Index %d used by multiple regions, first use by region %u, second by region %u", @@ -787,7 +788,7 @@ class G1VerifyYoungCSetIndicesClosure : public G1HeapRegionClosure { void G1CollectionSet::verify_young_cset_indices() const { assert_at_safepoint_on_vm_thread(); - G1VerifyYoungCSetIndicesClosure cl(_regions_cur_length); + G1VerifyYoungCSetIndicesClosure cl(num_regions()); iterate(&cl); } #endif diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.hpp b/src/hotspot/share/gc/g1/g1CollectionSet.hpp index df0228c4956..5fa9868f2b2 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -139,7 +139,7 @@ class G1CollectionSet { // The actual collection set as a set of region indices. // - // All regions in _regions below _regions_cur_length are assumed to be part of the + // All regions in _regions below _num_regions are assumed to be part of the // collection set. // We assume that at any time there is at most only one writer and (one or more) // concurrent readers. This means synchronization using storestore and loadload @@ -147,18 +147,18 @@ class G1CollectionSet { // // This corresponds to the regions referenced by the candidate groups further below. uint* _regions; - uint _regions_max_length; + uint _max_num_regions; - volatile uint _regions_cur_length; + volatile uint _num_regions; // Old gen groups selected for evacuation. G1CSetCandidateGroupList _groups; - uint groups_cur_length() const; + uint num_groups() const; - uint _eden_region_length; - uint _survivor_region_length; - uint _initial_old_region_length; + uint _num_eden_regions; + uint _num_survivor_regions; + uint _num_initial_old_regions; // When doing mixed collections we can add old regions to the collection set, which // will be collected only if there is enough time. We call these optional (old) @@ -174,7 +174,7 @@ class G1CollectionSet { CSetBuildType _inc_build_state; #endif // Index into the _regions indicating the start of the current collection set increment. - size_t _regions_inc_part_start; + uint _regions_inc_part_start; // Index into the _groups indicating the start of the current collection set increment. uint _groups_inc_part_start; @@ -188,6 +188,9 @@ class G1CollectionSet { // Add the given old region to the current collection set. void add_old_region(G1HeapRegion* hr); + void prepare_for_collection(uint num_eden_cset_regions, + uint num_survivor_cset_regions); + void prepare_optional_group(G1CSetCandidateGroup* gr, uint cur_index); void add_group_to_collection_set(G1CSetCandidateGroup* gr); @@ -201,7 +204,7 @@ class G1CollectionSet { // Select groups for evacuation from the optional candidates given the remaining time // and return the number of actually selected regions. uint select_optional_groups(double time_remaining_ms); - double select_candidates_from_optional_groups(double time_remaining_ms, uint& num_groups_selected); + double select_candidates_from_optional_groups(double time_remaining_ms, uint& num_regions_selected); // Finalize the young part of the initial collection set. Relabel survivor regions // as Eden and calculate a prediction on how long the evacuation of all young regions @@ -217,8 +220,8 @@ class G1CollectionSet { // to allow for more efficient parallel iteration. void iterate_part_from(G1HeapRegionClosure* cl, G1HeapRegionClaimer* hr_claimer, - size_t offset, - size_t length, + uint offset, + uint length, uint worker_id) const; // Adds the given group to the optional groups list (_optional_groups) @@ -232,8 +235,8 @@ class G1CollectionSet { G1CollectionSet(G1CollectedHeap* g1h, G1Policy* policy); ~G1CollectionSet(); - // Initializes the collection set giving the maximum possible length of the collection set. - void initialize(uint max_region_length); + // Initializes the collection set giving the maximum possible number of regions in the collection set. + void initialize(uint max_num_regions); // Drop the collection set and collection set candidates. void abandon(); @@ -245,26 +248,23 @@ class G1CollectionSet { void prepare_for_scan(); - void init_region_lengths(uint eden_cset_region_length, - uint survivor_cset_region_length); - - // Total length of the initial collection set in regions. - uint initial_region_length() const { return young_region_length() + - initial_old_region_length(); } - uint young_region_length() const { return eden_region_length() + - survivor_region_length(); } + // Total number of regions in the initial collection set. + uint num_initial_regions() const { return num_young_regions() + + num_initial_old_regions(); } + uint num_young_regions() const { return num_eden_regions() + + num_survivor_regions(); } - uint eden_region_length() const { return _eden_region_length; } - uint survivor_region_length() const { return _survivor_region_length; } - uint initial_old_region_length() const { return _initial_old_region_length; } + uint num_eden_regions() const { return _num_eden_regions; } + uint num_survivor_regions() const { return _num_survivor_regions; } + uint num_initial_old_regions() const { return _num_initial_old_regions; } uint num_optional_regions() const { return _optional_groups.num_regions(); } - bool only_contains_young_regions() const { return (initial_old_region_length() + num_optional_regions()) == 0; } + bool only_contains_young_regions() const { return (num_initial_old_regions() + num_optional_regions()) == 0; } template inline void merge_cardsets_for_collection_groups(CardOrRangeVisitor& cl, uint worker_id, uint num_workers); - uint groups_increment_length() const; + uint num_groups_in_increment() const; // Reset the contents of the collection set. void clear(); @@ -282,10 +282,10 @@ class G1CollectionSet { // from a starting position determined by the given worker id. void iterate_incremental_part_from(G1HeapRegionClosure* cl, G1HeapRegionClaimer* hr_claimer, uint worker_id) const; - // Returns the length of the current increment in number of regions. - size_t regions_cur_length() const { return _regions_cur_length - _regions_inc_part_start; } - // Returns the length of the whole current collection set in number of regions - size_t cur_length() const { return _regions_cur_length; } + // Returns the number of regions in the current collection set increment. + uint num_regions_in_increment() const { return num_regions() - _regions_inc_part_start; } + // Returns the total number of regions in the current collection set. + uint num_regions() const { return _num_regions; } // Iterate over the entire collection set (all increments calculated so far), applying // the given G1HeapRegionClosure on all of the regions. diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.inline.hpp b/src/hotspot/share/gc/g1/g1CollectionSet.inline.hpp index b0321588d18..f9ec42b9432 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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,24 +31,22 @@ template inline void G1CollectionSet::merge_cardsets_for_collection_groups(CardOrRangeVisitor& cl, uint worker_id, uint num_workers) { - uint offset = _groups_inc_part_start; + uint offset = _groups_inc_part_start; if (offset == 0) { G1HeapRegionRemSet::iterate_for_merge(_g1h->young_regions_cset_group()->card_set(), cl); } - uint next_increment_length = groups_increment_length(); - if (next_increment_length == 0) { + const uint next_group_increment = num_groups_in_increment(); + if (next_group_increment == 0) { return; } - uint start_pos = (worker_id * next_increment_length) / num_workers; + uint start_pos = (worker_id * next_group_increment) / num_workers; uint cur_pos = start_pos; - uint count = 0; do { G1HeapRegionRemSet::iterate_for_merge(_groups.at(offset + cur_pos)->card_set(), cl); cur_pos++; - count++; - if (cur_pos == next_increment_length) { + if (cur_pos == next_group_increment) { cur_pos = 0; } } while (cur_pos != start_pos); diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp index 45e1b25cb95..5a66f64090a 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp @@ -76,7 +76,7 @@ G1ParScanThreadState::G1ParScanThreadState(G1CollectedHeap* g1h, _trim_ticks(), _surviving_young_words_base(nullptr), _surviving_young_words(nullptr), - _surviving_words_length(collection_set->young_region_length() + 1), + _surviving_words_length(collection_set->num_young_regions() + 1), _old_gen_is_full(false), _partial_array_splitter(g1h->partial_array_state_manager(), num_workers), _string_dedup_requests(), @@ -717,7 +717,7 @@ G1ParScanThreadStateSet::G1ParScanThreadStateSet(G1CollectedHeap* g1h, _g1h(g1h), _collection_set(collection_set), _states(NEW_C_HEAP_ARRAY(G1ParScanThreadState*, num_workers, mtGC)), - _surviving_young_words_total(NEW_C_HEAP_ARRAY(size_t, collection_set->young_region_length() + 1, mtGC)), + _surviving_young_words_total(NEW_C_HEAP_ARRAY(size_t, collection_set->num_young_regions() + 1, mtGC)), _num_workers(num_workers), _flushed(false), _evac_failure_regions(evac_failure_regions) @@ -725,7 +725,7 @@ G1ParScanThreadStateSet::G1ParScanThreadStateSet(G1CollectedHeap* g1h, for (uint i = 0; i < num_workers; ++i) { _states[i] = nullptr; } - memset(_surviving_young_words_total, 0, (collection_set->young_region_length() + 1) * sizeof(size_t)); + memset(_surviving_young_words_total, 0, (collection_set->num_young_regions() + 1) * sizeof(size_t)); } G1ParScanThreadStateSet::~G1ParScanThreadStateSet() { diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 35211938065..04afd262dd4 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -849,13 +849,13 @@ G1CollectorState G1Policy::record_young_collection_end(bool concurrent_operation if (update_stats) { // We maintain the invariant that all objects allocated by mutator // threads will be allocated out of eden regions. So, we can use - // the eden region number allocated since the previous GC to - // calculate the application's allocate rate. The only exception + // the number of eden regions allocated since the previous GC to + // calculate the application's allocation rate. The only exception // to that is humongous objects that are allocated separately. But // given that humongous object allocations do not really affect // either the pause's duration nor when the next pause will take // place we can safely ignore them here. - uint regions_allocated = _collection_set->eden_region_length(); + uint regions_allocated = _collection_set->num_eden_regions(); double alloc_rate_ms = (double) regions_allocated / app_time_ms; _analytics->report_alloc_rate_ms(alloc_rate_ms); @@ -919,14 +919,14 @@ G1CollectorState G1Policy::record_young_collection_end(bool concurrent_operation _analytics->report_cost_per_byte_ms(cost_per_byte_ms, is_young_only_pause); } - if (_collection_set->young_region_length() > 0) { + if (_collection_set->num_young_regions() > 0) { _analytics->report_young_other_cost_per_region_ms(young_other_time_ms() / - _collection_set->young_region_length()); + _collection_set->num_young_regions()); } - if (_collection_set->initial_old_region_length() > 0) { + if (_collection_set->num_initial_old_regions() > 0) { _analytics->report_non_young_other_cost_per_region_ms(non_young_other_time_ms() / - _collection_set->initial_old_region_length()); + _collection_set->num_initial_old_regions()); } _analytics->report_constant_other_time_ms(constant_other_time_ms(pause_time_ms)); diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp index be18a3065e9..608f12e3859 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.cpp +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp @@ -1232,14 +1232,14 @@ void G1RemSet::merge_heap_roots(bool initial_evacuation) { { WorkerThreads* workers = g1h->workers(); - size_t const increment_length = g1h->collection_set()->groups_increment_length(); + uint const num_groups_in_increment = g1h->collection_set()->num_groups_in_increment(); uint const num_workers = initial_evacuation ? workers->active_workers() : - MIN2(workers->active_workers(), (uint)increment_length); + MIN2(workers->active_workers(), num_groups_in_increment); G1MergeHeapRootsTask cl(_scan_state, num_workers, initial_evacuation); - log_debug(gc, ergo)("Running %s using %u workers for %zu regions", - cl.name(), num_workers, increment_length); + log_debug(gc, ergo)("Running %s using %u workers for %u groups", + cl.name(), num_workers, num_groups_in_increment); workers->run_task(&cl, num_workers); } diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.cpp b/src/hotspot/share/gc/g1/g1YoungCollector.cpp index 810b54ec587..edfe97d04d6 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.cpp @@ -260,7 +260,7 @@ void G1YoungCollector::calculate_collection_set(G1EvacInfo* evacuation_info, dou allocator()->release_mutator_alloc_regions(); collection_set()->finalize_initial_collection_set(target_pause_time_ms, survivor_regions()); - evacuation_info->set_collection_set_regions(collection_set()->initial_region_length() + + evacuation_info->set_collection_set_regions(collection_set()->num_initial_regions() + collection_set()->num_optional_regions()); concurrent_mark()->verify_no_collection_set_oops(); diff --git a/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp b/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp index 2b33a85da29..0d922c0a3ed 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp @@ -56,7 +56,7 @@ G1YoungGCAllocationFailureInjector::G1YoungGCAllocationFailureInjector() void G1YoungGCAllocationFailureInjector::select_allocation_failure_regions() { G1CollectedHeap* g1h = G1CollectedHeap::heap(); _allocation_failure_regions.reinitialize(g1h->max_num_regions()); - SelectAllocationFailureRegionClosure closure(_allocation_failure_regions, g1h->collection_set()->cur_length()); + SelectAllocationFailureRegionClosure closure(_allocation_failure_regions, g1h->collection_set()->num_regions()); g1h->collection_set_iterate_all(&closure); } diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp index bf4a6cca81d..cc9c4b10202 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp @@ -649,9 +649,9 @@ class FreeCSetClosure : public G1HeapRegionClosure { void assert_tracks_surviving_words(G1HeapRegion* r) { assert(r->young_index_in_cset() != 0 && - (uint)r->young_index_in_cset() <= _g1h->collection_set()->young_region_length(), + (uint)r->young_index_in_cset() <= _g1h->collection_set()->num_young_regions(), "Young index %u is wrong for region %u of type %s with %u young regions", - r->young_index_in_cset(), r->hrm_index(), r->get_type_str(), _g1h->collection_set()->young_region_length()); + r->young_index_in_cset(), r->hrm_index(), r->get_type_str(), _g1h->collection_set()->num_young_regions()); } void handle_evacuated_region(G1HeapRegion* r) { @@ -810,7 +810,7 @@ class G1PostEvacuateCollectionSetCleanupTask2::FreeCollectionSetTask : public G1 p->record_serial_free_cset_time_ms((Ticks::now() - serial_time).seconds() * 1000.0); } - double worker_cost() const override { return G1CollectedHeap::heap()->collection_set()->initial_region_length(); } + double worker_cost() const override { return G1CollectedHeap::heap()->collection_set()->num_initial_regions(); } void set_max_workers(uint max_workers) override { _active_workers = max_workers; From 1a395aac6f1bfc385d8ad0399467d1df6cf027a9 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Thu, 18 Jun 2026 09:53:31 +0000 Subject: [PATCH 002/707] 8386762: C2: Allow inlining cold methods Reviewed-by: chagedorn, qamai, vlivanov --- .../share/compiler/compilerDefinitions.cpp | 4 ++ src/hotspot/share/opto/bytecodeInfo.cpp | 56 ++++++++++--------- src/hotspot/share/opto/c2_globals.hpp | 5 ++ 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/src/hotspot/share/compiler/compilerDefinitions.cpp b/src/hotspot/share/compiler/compilerDefinitions.cpp index 69c9bc585f7..5bb96f8d031 100644 --- a/src/hotspot/share/compiler/compilerDefinitions.cpp +++ b/src/hotspot/share/compiler/compilerDefinitions.cpp @@ -472,6 +472,10 @@ void CompilerConfig::ergo_initialize() { } #ifdef COMPILER2 + // Xcomp has no reasonable profiling information, enable inlining cold methods + if (Arguments::is_compiler_only() && FLAG_IS_DEFAULT(InlineColdMethods)) { + FLAG_SET_DEFAULT(InlineColdMethods, true); + } if (!EliminateLocks) { EliminateNestedLocks = false; } diff --git a/src/hotspot/share/opto/bytecodeInfo.cpp b/src/hotspot/share/opto/bytecodeInfo.cpp index 330a8688110..1baf76f00cd 100644 --- a/src/hotspot/share/opto/bytecodeInfo.cpp +++ b/src/hotspot/share/opto/bytecodeInfo.cpp @@ -295,32 +295,34 @@ bool InlineTree::should_not_inline(ciMethod* callee_method, ciMethod* caller_met return false; } - // don't use counts with -Xcomp - if (UseInterpreter) { - if (!callee_method->has_compiled_code() && - !callee_method->was_executed_more_than(0)) { - set_msg("never executed"); - return true; - } + // accept cold methods in CTW or -Xcomp + if (InlineColdMethods) { + return false; + } - if (is_init_with_ea(callee_method, caller_method, C)) { - // Escape Analysis: inline all executed constructors - return false; - } + if (!callee_method->has_compiled_code() && + !callee_method->was_executed_more_than(0)) { + set_msg("never executed"); + return true; + } - if (MinInlineFrequencyRatio > 0) { - int call_site_count = caller_method->scale_count(profile.count()); - int invoke_count = caller_method->interpreter_invocation_count(); - assert(invoke_count != 0, "require invocation count greater than zero"); - double freq = (double)call_site_count / (double)invoke_count; - // avoid division by 0, set divisor to at least 1 - int cp_min_inv = MAX2(1, CompilationPolicy::min_invocations()); - double min_freq = MAX2(MinInlineFrequencyRatio, 1.0 / cp_min_inv); - - if (freq < min_freq) { - set_msg("low call site frequency"); - return true; - } + if (is_init_with_ea(callee_method, caller_method, C)) { + // Escape Analysis: inline all executed constructors + return false; + } + + if (MinInlineFrequencyRatio > 0) { + int call_site_count = caller_method->scale_count(profile.count()); + int invoke_count = caller_method->interpreter_invocation_count(); + assert(invoke_count != 0, "require invocation count greater than zero"); + double freq = (double)call_site_count / (double)invoke_count; + // avoid division by 0, set divisor to at least 1 + int cp_min_inv = MAX2(1, CompilationPolicy::min_invocations()); + double min_freq = MAX2(MinInlineFrequencyRatio, 1.0 / cp_min_inv); + + if (freq < min_freq) { + set_msg("low call site frequency"); + return true; } } @@ -328,8 +330,8 @@ bool InlineTree::should_not_inline(ciMethod* callee_method, ciMethod* caller_met } bool InlineTree::is_not_reached(ciMethod* callee_method, ciMethod* caller_method, int caller_bci, ciCallProfile& profile) { - if (!UseInterpreter) { - return false; // -Xcomp + if (InlineColdMethods) { + return false; // CTW or -Xcomp } if (profile.count() > 0) { return false; // reachable according to profile @@ -405,7 +407,7 @@ bool InlineTree::try_to_inline(ciMethod* callee_method, ciMethod* caller_method, } } - if (!UseInterpreter && + if (InlineColdMethods && is_init_with_ea(callee_method, caller_method, C)) { // Escape Analysis stress testing when running Xcomp: // inline constructors even if they are not reached. diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index dc2a08a3da5..cbe149fd01a 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -789,6 +789,11 @@ "high tier compiler") \ range(0, max_jint) \ \ + product(bool, InlineColdMethods, false, DIAGNOSTIC, \ + "Inline cold methods that would otherwise be rejected due to " \ + "cold profile counters. Useful for compiler testing to expose " \ + "more code to compilers.") \ + \ product(bool, IncrementalInline, true, \ "do post parse inlining") \ \ From 7644d4afd437b64674a86012ab65dd0c9861cafe Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Thu, 18 Jun 2026 10:34:14 +0000 Subject: [PATCH 003/707] 8386085: Livelock in AbstractQueuedSyncronizer.cleanQueue() when multiple threads do tryAcquire() with a short timeout and no permits available Reviewed-by: dl, alanb --- .../locks/AbstractQueuedLongSynchronizer.java | 3 +- .../locks/AbstractQueuedSynchronizer.java | 3 +- .../util/concurrent/locks/StampedLock.java | 3 +- .../util/concurrent/tck/SemaphoreTest.java | 39 +++++++++++++++++++ .../util/concurrent/tck/StampedLockTest.java | 37 +++++++++++++++++- 5 files changed, 81 insertions(+), 4 deletions(-) diff --git a/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java b/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java index ba81123fc35..2711f047d4f 100644 --- a/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java +++ b/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java @@ -452,7 +452,8 @@ private void cleanQueue() { if (q.status < 0) { // cancelled if ((s == null ? casTail(q, p) : s.casPrev(q, p)) && q.prev == p) { - p.casNext(q, s); // OK if fails + if (s != null) + p.casNext(q, s); // OK if fails if (p.prev == null) signalNext(p); } diff --git a/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java b/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java index c0779545083..526b459a87f 100644 --- a/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java +++ b/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java @@ -832,7 +832,8 @@ private void cleanQueue() { if (q.status < 0) { // cancelled if ((s == null ? casTail(q, p) : s.casPrev(q, p)) && q.prev == p) { - p.casNext(q, s); // OK if fails + if (s != null) + p.casNext(q, s); // OK if fails if (p.prev == null) signalNext(p); } diff --git a/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java b/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java index 3fbfad875d6..3dfd4793cd4 100644 --- a/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java +++ b/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java @@ -1450,7 +1450,8 @@ private void cleanQueue() { if (q.status < 0) { // cancelled if ((s == null ? casTail(q, p) : s.casPrev(q, p)) && q.prev == p) { - p.casNext(q, s); // OK if fails + if (s != null) + p.casNext(q, s); // OK if fails if (p.prev == null) signalNext(p); } diff --git a/test/jdk/java/util/concurrent/tck/SemaphoreTest.java b/test/jdk/java/util/concurrent/tck/SemaphoreTest.java index 5d45dc21a8e..38ba54645b9 100644 --- a/test/jdk/java/util/concurrent/tck/SemaphoreTest.java +++ b/test/jdk/java/util/concurrent/tck/SemaphoreTest.java @@ -33,11 +33,15 @@ * Pat Fisher, Mike Judd. */ +import static java.util.concurrent.TimeUnit.MICROSECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS; import java.util.Collection; +import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicBoolean; import junit.framework.Test; import junit.framework.TestSuite; @@ -672,4 +676,39 @@ public void testToString(boolean fair) { assertTrue(s.toString().contains("Permits = -2")); } + /** + * Test scenario for JDK-8386085 + * When investigating failures of this test, it is important to know that AbstractQueuedSynchronizer, + * AbstractQueuedLongSynchronizer, and StampedLock all share similar code which was fixed for JDK-8386085 + */ + public void testShortTimeoutAcquisition() throws InterruptedException { + final int width = Runtime.getRuntime().availableProcessors(); + try (var pool = Executors.newFixedThreadPool(width)) { + // Setup + final AtomicBoolean done = new AtomicBoolean(false); + final CountDownLatch waitingToRun = new CountDownLatch(width); + final Semaphore s = new Semaphore(0); + final Callable c = () -> { + waitingToRun.countDown(); + do { + s.tryAcquire(1, MICROSECONDS); // acquisition storm + } while (!done.get()); + return null; + }; + + // Task creation + for(int i = 0; i < width; ++i) + pool.submit(c); + + waitingToRun.await(); // Wait for all tasks to start + Thread.sleep(3000); // Wait a while for acquisitions + s.release(width); // Hand out permits + Thread.sleep(1000); // Wait a while for permit acquisitions + + final int permitsAvailable = s.availablePermits(); + done.set(true); // Ensure that tasks can exit + assertTrue(permitsAvailable < width); // Some permits should've been taken + } + } + } diff --git a/test/jdk/java/util/concurrent/tck/StampedLockTest.java b/test/jdk/java/util/concurrent/tck/StampedLockTest.java index 8fe8aaca569..45500fa581e 100644 --- a/test/jdk/java/util/concurrent/tck/StampedLockTest.java +++ b/test/jdk/java/util/concurrent/tck/StampedLockTest.java @@ -33,8 +33,8 @@ */ import static java.util.concurrent.TimeUnit.DAYS; +import static java.util.concurrent.TimeUnit.MICROSECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS; - import static java.util.concurrent.locks.StampedLock.isLockStamp; import static java.util.concurrent.locks.StampedLock.isOptimisticReadStamp; import static java.util.concurrent.locks.StampedLock.isReadLockStamp; @@ -45,6 +45,7 @@ import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; @@ -1527,4 +1528,38 @@ public void testConcurrentAccess() throws Exception { checkTimedGet(future, null); } + /** + * test scenario for JDK-8386085 + */ + public void testShortTimeoutAcquisition() throws InterruptedException { + final int width = Runtime.getRuntime().availableProcessors(); + final StampedLock s = new StampedLock(); + try (var pool = Executors.newFixedThreadPool(width)) { + // Setup + final AtomicBoolean done = new AtomicBoolean(false); + final CountDownLatch waitingToRun = new CountDownLatch(width); + final long stamp = s.writeLock(); + assertTrue(s.validate(stamp)); + final Callable c = () -> { + waitingToRun.countDown(); + do { + long lock = s.tryWriteLock(1, MICROSECONDS); // acquisition storm + if (s.validate(lock)) + s.unlockWrite(lock); + } while (!done.get()); + return null; + }; + + // Task creation + for(int i = 0; i < width; ++i) + pool.submit(c); + + waitingToRun.await(); // Wait for all tasks to start + Thread.sleep(3000); // Wait a while for acquisitions + s.unlockWrite(stamp); // Hand out permits + Thread.sleep(1000); // Wait a while for permit acquisitions + done.set(true); // Ensure that tasks can exit + } + assertTrue(s.validate(s.writeLockInterruptibly())); // Should succeed + } } From 39d2d165d2ce5467d02374add9f5803e4af57309 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Thu, 18 Jun 2026 13:39:16 +0000 Subject: [PATCH 004/707] 8154193: Move jdk.naming.rmi module to platform class loader Reviewed-by: alanb, lancea --- make/conf/module-loader-map.conf | 4 +- .../InitialContextFactoryClassLoaderTest.java | 77 +++++++++++++++++++ 2 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 test/jdk/com/sun/jndi/rmi/InitialContextFactoryClassLoaderTest.java diff --git a/make/conf/module-loader-map.conf b/make/conf/module-loader-map.conf index 35b9345ed8f..2d528c4b8ed 100644 --- a/make/conf/module-loader-map.conf +++ b/make/conf/module-loader-map.conf @@ -28,7 +28,7 @@ # PLATFORM_MODULES are modules defined by the platform loader # # All other modules not declared below are defined by the application loader -# and are not included in JRE. +# and are not included in the JDK runtime image. BOOT_MODULES= \ java.base \ @@ -52,7 +52,6 @@ BOOT_MODULES= \ jdk.nio.mapmode \ jdk.sctp \ jdk.unsupported \ - jdk.naming.rmi \ # # Modules that directly or indirectly requiring upgradeable modules @@ -78,6 +77,7 @@ PLATFORM_MODULES= \ jdk.httpserver \ jdk.localedata \ jdk.naming.dns \ + jdk.naming.rmi \ jdk.security.auth \ jdk.security.jgss \ jdk.xml.dom \ diff --git a/test/jdk/com/sun/jndi/rmi/InitialContextFactoryClassLoaderTest.java b/test/jdk/com/sun/jndi/rmi/InitialContextFactoryClassLoaderTest.java new file mode 100644 index 00000000000..386409a9f48 --- /dev/null +++ b/test/jdk/com/sun/jndi/rmi/InitialContextFactoryClassLoaderTest.java @@ -0,0 +1,77 @@ +/* + * 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. + */ + +import java.util.List; +import java.util.ServiceLoader; +import java.util.ServiceLoader.Provider; + +import javax.naming.spi.InitialContextFactory; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/* + * @test + * @bug 8154193 + * @summary Verify that the com.sun.jndi.rmi.registry.RegistryContextFactory Service + * provided by the jdk.naming.rmi module isn't loaded by the boot loader + * @run junit ${test.main.class} + */ +class InitialContextFactoryClassLoaderTest { + + private static final String RMI_INITIAL_CTX_FACTORY_SERVICE = + "com.sun.jndi.rmi.registry.RegistryContextFactory"; + + /* + * Verifies that the javax.naming.spi.InitialContextFactory service provided by the + * jdk.naming.rmi module isn't loaded through the boot loader + */ + @Test + void testClassLoader() throws Exception { + final ServiceLoader serviceLoader = + ServiceLoader.load(InitialContextFactory.class, null); + final List> serviceTypes = serviceLoader + .stream() + .map(Provider::get) + .map(InitialContextFactory::getClass) + .toList(); + System.err.println("Found InitialContextFactory services: " + serviceTypes); + Class rmiInitialCtxService = null; + for (Class klass : serviceTypes) { + if (klass.getName().equals(RMI_INITIAL_CTX_FACTORY_SERVICE)) { + rmiInitialCtxService = klass; + break; // found the relevant service + } + } + // verify that the RMI InitialContextFactory service was found by the ServiceLoader + assertNotNull(rmiInitialCtxService, RMI_INITIAL_CTX_FACTORY_SERVICE + + " was not found by ServiceLoader"); + // now verify its module and the classloader + assertEquals("jdk.naming.rmi", rmiInitialCtxService.getModule().getName(), + "unexpected module for " + RMI_INITIAL_CTX_FACTORY_SERVICE + " class"); + // we don't expect the service to be loaded by boot loader + assertNotNull(rmiInitialCtxService.getClassLoader(), RMI_INITIAL_CTX_FACTORY_SERVICE + + " was unexpectedly loaded by boot loader"); + } +} From 0bf930d9f93508aad0f24008c8496882d1c08b30 Mon Sep 17 00:00:00 2001 From: Patrick Fontanilla Date: Thu, 18 Jun 2026 16:06:59 +0000 Subject: [PATCH 005/707] 8386604: GenShen: _do_old_gc_bootstrap could become stuck after a full GC Reviewed-by: wkemper, kdnilsen --- .../share/gc/shenandoah/shenandoahGenerationalControlThread.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp index e456366be5d..ac1feacd74d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -608,6 +608,7 @@ void ShenandoahGenerationalControlThread::service_stw_full_cycle(GCCause::Cause ShenandoahFullGC gc; gc.collect(cause); _degen_point = ShenandoahGC::_degenerated_unset; + _do_old_gc_bootstrap = false; } void ShenandoahGenerationalControlThread::service_stw_degenerated_cycle(const ShenandoahGCRequest& request) { From 33a49d1119826f541d78bb702e16790b8c2664eb Mon Sep 17 00:00:00 2001 From: Weijun Wang Date: Thu, 18 Jun 2026 16:07:48 +0000 Subject: [PATCH 006/707] 8386681: Remove RawKeySpec Reviewed-by: ascarpino --- .../classes/sun/security/provider/HSS.java | 26 +++-- .../security/provider/NamedKeyFactory.java | 27 +---- .../classes/sun/security/ssl/Hybrid.java | 109 ++++++++++-------- .../sun/security/ssl/KAKeyDerivation.java | 20 +++- .../sun/security/ssl/KEMKeyExchange.java | 17 ++- .../classes/sun/security/util/KeyUtil.java | 62 ++++++++++ .../classes/sun/security/util/RawKeySpec.java | 52 --------- .../sun/security/provider/acvp/LMS_Test.java | 8 +- .../sun/security/provider/hss/TestHSS.java | 4 +- .../provider/named/NamedKeyFactoryTest.java | 23 ++-- .../bench/javax/crypto/full/HSSBench.java | 22 +++- 11 files changed, 203 insertions(+), 167 deletions(-) delete mode 100644 src/java.base/share/classes/sun/security/util/RawKeySpec.java diff --git a/src/java.base/share/classes/sun/security/provider/HSS.java b/src/java.base/share/classes/sun/security/provider/HSS.java index 50afba7cab8..84b3ff2c33b 100644 --- a/src/java.base/share/classes/sun/security/provider/HSS.java +++ b/src/java.base/share/classes/sun/security/provider/HSS.java @@ -827,12 +827,6 @@ protected PublicKey engineGeneratePublic(KeySpec keySpec) } catch (InvalidKeyException e) { throw new InvalidKeySpecException(e); } - } else if (keySpec instanceof RawKeySpec rawSpec) { - try { - return new HSSPublicKey(rawSpec.getKeyArr(), false); - } catch (InvalidKeyException e) { - throw new InvalidKeySpecException(e); - } } throw new InvalidKeySpecException("Unrecognized KeySpec"); } @@ -866,17 +860,27 @@ protected Key engineTranslateKey(Key key) throws InvalidKeyException { if (key == null) { throw new InvalidKeyException("key cannot be null"); } + if (!(key instanceof PublicKey)) { + throw new InvalidKeyException("Only support public key"); + } PublicKey pKey; try { // Check if key originates from this factory if (key instanceof HSSPublicKey) { return key; } - // Convert key to spec - X509EncodedKeySpec x509EncodedKeySpec - = engineGetKeySpec(key, X509EncodedKeySpec.class); - // Create key from spec, and return it - pKey = engineGeneratePublic(x509EncodedKeySpec); + String format = key.getFormat(); + if ("X.509".equalsIgnoreCase(format)) { + // Convert key to spec + X509EncodedKeySpec x509EncodedKeySpec + = engineGetKeySpec(key, X509EncodedKeySpec.class); + // Create key from spec, and return it + pKey = engineGeneratePublic(x509EncodedKeySpec); + } else if ("RAW".equalsIgnoreCase(format)) { + pKey = new HSSPublicKey(key.getEncoded(), false); + } else { + throw new InvalidKeyException("Unknown format " + format); + } } catch (InvalidKeySpecException e) { throw new InvalidKeyException(e); } diff --git a/src/java.base/share/classes/sun/security/provider/NamedKeyFactory.java b/src/java.base/share/classes/sun/security/provider/NamedKeyFactory.java index 9099f1446ff..761d97aeb85 100644 --- a/src/java.base/share/classes/sun/security/provider/NamedKeyFactory.java +++ b/src/java.base/share/classes/sun/security/provider/NamedKeyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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,7 +26,6 @@ package sun.security.provider; import sun.security.pkcs.NamedPKCS8Key; -import sun.security.util.RawKeySpec; import sun.security.x509.NamedX509Key; import java.security.AsymmetricKey; @@ -53,7 +52,6 @@ /// 2. It writes to a RAW [EncodedKeySpec] if `getKeySpec(key, EncodedKeySpec.class)` /// is called. The format of the output is "RAW" and the algorithm is /// intentionally left unspecified. -/// 3. It reads from and writes to the internal type [RawKeySpec]. /// /// When reading from a RAW format, it needs enough info to derive the /// parameter set name. @@ -98,13 +96,6 @@ protected PublicKey engineGeneratePublic(KeySpec keySpec) throw new InvalidKeySpecException(e); } } - case RawKeySpec rks -> { - if (pnames.length == 1) { - yield new NamedX509Key(fname, pnames[0], rks.getKeyArr()); - } else { - throw new InvalidKeySpecException("Parameter set name unavailable"); - } - } case EncodedKeySpec espec when espec.getFormat().equalsIgnoreCase("RAW") -> { if (pnames.length == 1) { yield new NamedX509Key(fname, pnames[0], espec.getEncoded()); @@ -134,18 +125,6 @@ protected PrivateKey engineGeneratePrivate(KeySpec keySpec) Arrays.fill(bytes, (byte) 0); } } - case RawKeySpec rks -> { - if (pnames.length == 1) { - var raw = rks.getKeyArr(); - try { - yield fromRaw(pnames[0], raw); - } catch (InvalidKeyException e) { - throw new InvalidKeySpecException("Invalid key input", e); - } - } else { - throw new InvalidKeySpecException("Parameter set name unavailable"); - } - } case EncodedKeySpec espec when espec.getFormat().equalsIgnoreCase("RAW") -> { if (pnames.length == 1) { var raw = espec.getEncoded(); @@ -212,8 +191,6 @@ protected T engineGetKeySpec(Key key, Class keySpec) if (keySpec == PKCS8EncodedKeySpec.class) { return keySpec.cast( new PKCS8EncodedKeySpec(bytes = key.getEncoded())); - } else if (keySpec == RawKeySpec.class) { - return keySpec.cast(new RawKeySpec(nk.getRawBytes())); } else if (keySpec.isAssignableFrom(EncodedKeySpec.class)) { return keySpec.cast( new RawEncodedKeySpec(nk.getRawBytes())); @@ -229,8 +206,6 @@ protected T engineGetKeySpec(Key key, Class keySpec) if (keySpec == X509EncodedKeySpec.class && key.getFormat().equalsIgnoreCase("X.509")) { return keySpec.cast(new X509EncodedKeySpec(key.getEncoded())); - } else if (keySpec == RawKeySpec.class) { - return keySpec.cast(new RawKeySpec(nk.getRawBytes())); } else if (keySpec.isAssignableFrom(EncodedKeySpec.class)) { return keySpec.cast(new RawEncodedKeySpec(nk.getRawBytes())); } else { diff --git a/src/java.base/share/classes/sun/security/ssl/Hybrid.java b/src/java.base/share/classes/sun/security/ssl/Hybrid.java index 43634ce2f34..2d00973318a 100644 --- a/src/java.base/share/classes/sun/security/ssl/Hybrid.java +++ b/src/java.base/share/classes/sun/security/ssl/Hybrid.java @@ -28,7 +28,7 @@ import sun.security.util.ArrayUtil; import sun.security.util.CurveDB; import sun.security.util.ECUtil; -import sun.security.util.RawKeySpec; +import sun.security.util.KeyUtil; import sun.security.x509.X509Key; import javax.crypto.DecapsulateException; @@ -171,21 +171,49 @@ public KeyFactoryImpl(String left, String right) @Override protected PublicKey engineGeneratePublic(KeySpec keySpec) throws InvalidKeySpecException { - if (keySpec == null) { - throw new InvalidKeySpecException("keySpec must not be null"); + throw new InvalidKeySpecException("Not supported"); + } + + @Override + protected PrivateKey engineGeneratePrivate(KeySpec keySpec) throws + InvalidKeySpecException { + throw new InvalidKeySpecException("Not supported"); + } + + @Override + protected T engineGetKeySpec(Key key, + Class keySpec) throws InvalidKeySpecException { + throw new InvalidKeySpecException("Not supported"); + } + + private static int leftPublicLength(String name) { + return switch (name.toLowerCase(Locale.ROOT)) { + case "secp256r1" -> 65; + case "secp384r1" -> 97; + case "ml-kem-768" -> 1184; + default -> throw new IllegalArgumentException( + "Unknown named group: " + name); + }; + } + + @Override + protected Key engineTranslateKey(Key inKey) throws InvalidKeyException { + if (inKey == null) { + throw new InvalidKeyException("key must not be null"); } - if (keySpec instanceof RawKeySpec rks) { - byte[] key = rks.getKeyArr(); + if (inKey instanceof PublicKey + && "RAW".equalsIgnoreCase(inKey.getFormat())) { + byte[] key = inKey.getEncoded(); if (key == null) { - throw new InvalidKeySpecException( - "RawkeySpec contains null key data"); + throw new InvalidKeyException( + "Key contains null key data"); } if (key.length <= leftlen) { - throw new InvalidKeySpecException( + throw new InvalidKeyException( "Hybrid key length " + key.length + - " is too short and its left key length is " + - leftlen); + " is too short and its left key length is " + + leftlen); } byte[] leftKeyBytes = Arrays.copyOfRange(key, 0, leftlen); @@ -198,11 +226,18 @@ protected PublicKey engineGeneratePublic(KeySpec keySpec) var curve = CurveDB.lookup(leftname); var ecSpec = new ECPublicKeySpec( ECUtil.decodePoint(leftKeyBytes, - curve.getCurve()), curve); + curve.getCurve()), curve); leftKey = left.generatePublic(ecSpec); } else if (leftname.startsWith("ML-KEM")) { - leftKey = left.generatePublic(new RawKeySpec( - leftKeyBytes)); + try { + leftKey = (PublicKey) left.translateKey(KeyUtil + .newRawPublicKey(leftname, leftKeyBytes)); + } catch (InvalidKeyException e) { + // Fallback to X.509 encoding if ML-KEM impl + // does not support translating from RAW + leftKey = left.generatePublic(new X509EncodedKeySpec( + KeyUtil.rawToX509(leftname, leftKeyBytes))); + } } else { throw new InvalidKeySpecException("Unsupported left" + " algorithm" + leftname); @@ -215,8 +250,15 @@ protected PublicKey engineGeneratePublic(KeySpec keySpec) new BigInteger(1, rightKeyBytes)); rightKey = right.generatePublic(xecSpec); } else if (rightname.startsWith("ML-KEM")) { - rightKey = right.generatePublic(new RawKeySpec( - rightKeyBytes)); + try { + rightKey = (PublicKey) right.translateKey(KeyUtil + .newRawPublicKey(rightname, rightKeyBytes)); + } catch (InvalidKeyException e) { + // Fallback to X.509 encoding if ML-KEM impl + // does not support translating from RAW + rightKey = right.generatePublic(new X509EncodedKeySpec( + KeyUtil.rawToX509(rightname, rightKeyBytes))); + } } else { throw new InvalidKeySpecException("Unsupported right" + " algorithm: " + rightname); @@ -224,41 +266,14 @@ protected PublicKey engineGeneratePublic(KeySpec keySpec) return new PublicKeyImpl("Hybrid", leftKey, rightKey); } catch (Exception e) { - throw new InvalidKeySpecException("Failed to decode " + + throw new InvalidKeyException("Failed to decode " + "hybrid key", e); } + } else { + throw new InvalidKeyException("Unknown key " + + inKey.getClass().getName() + " in " + + inKey.getFormat()); } - - throw new InvalidKeySpecException( - "KeySpec type:" + - keySpec.getClass().getName() + " not supported"); - } - - private static int leftPublicLength(String name) { - return switch (name.toLowerCase(Locale.ROOT)) { - case "secp256r1" -> 65; - case "secp384r1" -> 97; - case "ml-kem-768" -> 1184; - default -> throw new IllegalArgumentException( - "Unknown named group: " + name); - }; - } - - @Override - protected PrivateKey engineGeneratePrivate(KeySpec keySpec) throws - InvalidKeySpecException { - throw new UnsupportedOperationException(); - } - - @Override - protected T engineGetKeySpec(Key key, - Class keySpec) throws InvalidKeySpecException { - throw new UnsupportedOperationException(); - } - - @Override - protected Key engineTranslateKey(Key key) throws InvalidKeyException { - throw new UnsupportedOperationException(); } } diff --git a/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java index 0ca197160a9..95b021b0f6e 100644 --- a/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java +++ b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java @@ -24,8 +24,6 @@ */ package sun.security.ssl; -import sun.security.util.RawKeySpec; - import javax.crypto.DecapsulateException; import javax.crypto.KDF; import javax.crypto.KEM; @@ -42,6 +40,8 @@ import java.security.Provider; import java.security.PublicKey; import java.security.SecureRandom; +import java.security.spec.X509EncodedKeySpec; + import sun.security.util.KeyUtil; /** @@ -185,7 +185,21 @@ KEM.Encapsulated encapsulate(String algorithm, SecureRandom random) KeyFactory kf = (provider != null) ? KeyFactory.getInstance(algorithmName, provider) : KeyFactory.getInstance(algorithmName); - var pk = kf.generatePublic(new RawKeySpec(keyshare)); + PublicKey pk; + try { + pk = (PublicKey) kf.translateKey( + KeyUtil.newRawPublicKey(algorithmName, keyshare)); + } catch (InvalidKeyException e) { + // Fallback to X.509 encoding if ML-KEM impl + // does not support translating from RAW + try { + pk = kf.generatePublic(new X509EncodedKeySpec( + KeyUtil.rawToX509(algorithmName, keyshare))); + } catch (GeneralSecurityException e2) { + e2.addSuppressed(e); + throw new InvalidKeyException(e2); + } + } KEM kem = (provider != null) ? KEM.getInstance(algorithmName, provider) : diff --git a/src/java.base/share/classes/sun/security/ssl/KEMKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/KEMKeyExchange.java index fb8de6cb104..2ce3c57e412 100644 --- a/src/java.base/share/classes/sun/security/ssl/KEMKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/KEMKeyExchange.java @@ -37,6 +37,7 @@ import javax.crypto.SecretKey; import sun.security.ssl.NamedGroup.NamedGroupSpec; +import sun.security.util.KeyUtil; import sun.security.x509.X509Key; /** @@ -140,10 +141,20 @@ static final class KEMReceiverPossession extends KEMPossession { public byte[] encode() { if (publicKey instanceof X509Key xk) { return xk.getKeyAsBytes(); - } else if (publicKey instanceof Hybrid.PublicKeyImpl hk) { - return hk.getEncoded(); + } else { + String format = publicKey.getFormat(); + if ("RAW".equalsIgnoreCase(format)) { + return publicKey.getEncoded(); + } else if ("X.509".equalsIgnoreCase(format)) { + try { + return KeyUtil.x509ToRaw(publicKey.getEncoded()); + } catch (IOException e) { + throw new ProviderException("Invalid X.509 format"); + } + } else { + throw new ProviderException("Unknown format " + format); + } } - throw new ProviderException("Unsupported key type: " + publicKey); } // Package-private diff --git a/src/java.base/share/classes/sun/security/util/KeyUtil.java b/src/java.base/share/classes/sun/security/util/KeyUtil.java index 5a14deb70a4..0c0bc134353 100644 --- a/src/java.base/share/classes/sun/security/util/KeyUtil.java +++ b/src/java.base/share/classes/sun/security/util/KeyUtil.java @@ -46,6 +46,7 @@ import sun.security.jca.JCAUtil; import sun.security.pkcs.PKCS8Key; import sun.security.x509.AlgorithmId; +import sun.security.x509.X509Key; /** * A utility class to get key length, validate keys, etc. @@ -589,5 +590,66 @@ public static T clear(byte[] encoding, Function op) { } } } + + public static PublicKey newRawPublicKey(String algorithm, byte[] key) { + return newRawPublicKey(algorithm, null, key); + } + + public static PublicKey newRawPublicKey(String algorithm, + AlgorithmParameterSpec params, byte[] key) { + return new RawPublicKey(algorithm, params, key); + } + + private record RawPublicKey(String algorithm, AlgorithmParameterSpec params, + byte[] data) implements PublicKey { + + RawPublicKey { + data = data.clone(); + } + + @Override + public String getAlgorithm() { + return algorithm; + } + + @Override + public String getFormat() { + return "RAW"; + } + + @Override + public byte[] getEncoded() { + return data.clone(); + } + + @Override + public AlgorithmParameterSpec getParams() { + return params; + } + } + + // Convert RAW encoding to X.509 encoding of a public key. + // The AlgorithmId will be a single OID from `pname`, so this + // cannot be used by EC or RSASSA-PSS. + static public byte[] rawToX509(String pname, byte[] bytes) + throws NoSuchAlgorithmException { + return new X509Key(AlgorithmId.get(pname), + new BitArray(bytes.length * 8, bytes)).getEncoded(); + } + + // Convert X.509 encoding to RAW encoding of a public key. + // AlgorithmId is ignored. No check for trailing data after key. + static public byte[] x509ToRaw(byte[] bytes) throws IOException { + DerValue in = new DerValue(bytes); + if (in.tag != DerValue.tag_Sequence) { + throw new IOException("corrupt subject key"); + } + AlgorithmId.parse(in.data.getDerValue()); + BitArray keyMaterial = in.data.getUnalignedBitString(); + if (keyMaterial.length() % 8 != 0) { + throw new IOException("Unaligned bits in public key"); + } + return keyMaterial.toByteArray(); + } } diff --git a/src/java.base/share/classes/sun/security/util/RawKeySpec.java b/src/java.base/share/classes/sun/security/util/RawKeySpec.java deleted file mode 100644 index 8e811573453..00000000000 --- a/src/java.base/share/classes/sun/security/util/RawKeySpec.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.security.util; - -import java.security.spec.KeySpec; - -/** - * This is a KeySpec that is used to specify a key by its byte array implementation. - * It is intended to be used in testing algorithms where the algorithm specification - * describes the key in this form. - */ -public class RawKeySpec implements KeySpec { - private final byte[] keyArr; - /** - * The sole constructor. - * @param key contains the key as a byte array - */ - public RawKeySpec(byte[] key) { - keyArr = key.clone(); - } - - /** - * Getter function. - * @return a copy of the key bits - */ - public byte[] getKeyArr() { - return keyArr.clone(); - } -} diff --git a/test/jdk/sun/security/provider/acvp/LMS_Test.java b/test/jdk/sun/security/provider/acvp/LMS_Test.java index 692e398359f..c8858ff8d14 100644 --- a/test/jdk/sun/security/provider/acvp/LMS_Test.java +++ b/test/jdk/sun/security/provider/acvp/LMS_Test.java @@ -22,7 +22,7 @@ */ import jdk.test.lib.Asserts; import jdk.test.lib.json.JSONValue; -import sun.security.util.RawKeySpec; +import sun.security.util.KeyUtil; import java.security.InvalidKeyException; import java.security.KeyFactory; @@ -62,11 +62,11 @@ static void sigVerTest(JSONValue kat, Provider p) throws Exception { // Convert to HSS key by prepending height of tree (1) // to the LMS public key. - RawKeySpec rks = new RawKeySpec(toByteArray( - "00000001" + t.get("publicKey").asString())); KeyFactory kf = p == null ? KeyFactory.getInstance("HSS/LMS") : KeyFactory.getInstance("HSS/LMS", p); - PublicKey pk1 = kf.generatePublic(rks); + PublicKey pk1 = (PublicKey) kf.translateKey(KeyUtil + .newRawPublicKey("HSS/LMS", toByteArray( + "00000001" + t.get("publicKey").asString()))); try { s.initVerify(pk1); diff --git a/test/jdk/sun/security/provider/hss/TestHSS.java b/test/jdk/sun/security/provider/hss/TestHSS.java index 48019d4e465..d2d815b2025 100644 --- a/test/jdk/sun/security/provider/hss/TestHSS.java +++ b/test/jdk/sun/security/provider/hss/TestHSS.java @@ -167,9 +167,9 @@ static boolean verifyRawKey(byte[] pk, byte[] sig, byte[] msg) PublicKey pk1; // build public key - RawKeySpec rks = new RawKeySpec(pk); KeyFactory kf = KeyFactory.getInstance(ALG, provider); - pk1 = kf.generatePublic(rks); + pk1 = (PublicKey) kf.translateKey(KeyUtil + .newRawPublicKey(ALG, pk)); var v = Signature.getInstance(ALG); v.initVerify(pk1); diff --git a/test/jdk/sun/security/provider/named/NamedKeyFactoryTest.java b/test/jdk/sun/security/provider/named/NamedKeyFactoryTest.java index e58809fcb69..ff5430e60bc 100644 --- a/test/jdk/sun/security/provider/named/NamedKeyFactoryTest.java +++ b/test/jdk/sun/security/provider/named/NamedKeyFactoryTest.java @@ -36,7 +36,6 @@ import sun.security.pkcs.NamedPKCS8Key; import sun.security.provider.NamedKeyFactory; import sun.security.provider.NamedKeyPairGenerator; -import sun.security.util.RawKeySpec; import sun.security.x509.NamedX509Key; import java.security.*; @@ -119,30 +118,22 @@ public static void main(String[] args) throws Exception { Utils.runAndCheckException(() -> kf5.generatePublic(skSpec), InvalidKeySpecException.class); - // The private RawKeySpec and unnamed RAW EncodedKeySpec - var prk = kf.getKeySpec(pk, RawKeySpec.class); - Asserts.assertEqualsByteArray(prk.getKeyArr(), pk.getRawBytes()); - var prk2 = kf.getKeySpec(pk, EncodedKeySpec.class); - Asserts.assertEquals("RAW", prk2.getFormat()); - Asserts.assertEqualsByteArray(prk.getKeyArr(), prk2.getEncoded()); + // The unnamed RAW EncodedKeySpec + var prk = kf.getKeySpec(pk, EncodedKeySpec.class); + Asserts.assertEquals("RAW", prk.getFormat()); + Asserts.assertEqualsByteArray(pk.getRawBytes(), prk.getEncoded()); Asserts.assertEqualsByteArray(kf2.generatePublic(prk).getEncoded(), pk.getEncoded()); Utils.runAndCheckException(() -> kf.generatePublic(prk), InvalidKeySpecException.class); // no pname - Asserts.assertEqualsByteArray(kf2.generatePublic(prk2).getEncoded(), pk.getEncoded()); - Utils.runAndCheckException(() -> kf.generatePublic(prk2), InvalidKeySpecException.class); // no pname - var srk = kf.getKeySpec(sk, RawKeySpec.class); - Asserts.assertEqualsByteArray(srk.getKeyArr(), sk.getRawBytes()); - var srk2 = kf.getKeySpec(sk, EncodedKeySpec.class); - Asserts.assertEquals("RAW", srk2.getFormat()); - Asserts.assertEqualsByteArray(srk2.getEncoded(), sk.getRawBytes()); + var srk = kf.getKeySpec(sk, EncodedKeySpec.class); + Asserts.assertEquals("RAW", srk.getFormat()); + Asserts.assertEqualsByteArray(srk.getEncoded(), sk.getRawBytes()); checkKey(kf2.generatePrivate(srk), "SHA", "SHA-256"); Asserts.assertEqualsByteArray(kf2.generatePrivate(srk).getEncoded(), sk.getEncoded()); Utils.runAndCheckException(() -> kf.generatePrivate(srk), InvalidKeySpecException.class); // no pname checkKey(kf2.generatePrivate(srk), "SHA", "SHA-256"); - Asserts.assertEqualsByteArray(kf2.generatePrivate(srk2).getEncoded(), sk.getEncoded()); - Utils.runAndCheckException(() -> kf.generatePrivate(srk2), InvalidKeySpecException.class); // no pname var pk1 = new PublicKey() { public String getAlgorithm() { return "SHA"; } diff --git a/test/micro/org/openjdk/bench/javax/crypto/full/HSSBench.java b/test/micro/org/openjdk/bench/javax/crypto/full/HSSBench.java index 13cc9a25113..856667a4e8b 100644 --- a/test/micro/org/openjdk/bench/javax/crypto/full/HSSBench.java +++ b/test/micro/org/openjdk/bench/javax/crypto/full/HSSBench.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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 @@ -34,9 +34,9 @@ import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; -import sun.security.util.RawKeySpec; import java.security.KeyFactory; +import java.security.PublicKey; import java.security.Security; import java.security.Signature; import java.util.HexFormat; @@ -64,7 +64,23 @@ static byte[] decode(String s) { public static Signature getVerifier(byte[] pk) throws Exception { var kf = KeyFactory.getInstance("HSS/LMS", Security.getProvider("SUN")); - var pk1 = kf.generatePublic(new RawKeySpec(pk)); + var pk1 = (PublicKey) kf.translateKey(new PublicKey() { + + @Override + public String getAlgorithm() { + return "HSS/LMS"; + } + + @Override + public String getFormat() { + return "RAW"; + } + + @Override + public byte[] getEncoded() { + return pk.clone(); + } + }); var vv = Signature.getInstance("HSS/LMS"); vv.initVerify(pk1); From 96513c34c6e3e5ca70cbdecbafb3b6409df79cc0 Mon Sep 17 00:00:00 2001 From: Jeremy Wood Date: Thu, 18 Jun 2026 16:12:18 +0000 Subject: [PATCH 007/707] 8381236: VoiceOver Fails to Identify Component After Switching Windows Reviewed-by: kizune, psadhukhan --- .../classes/sun/lwawt/macosx/CAccessible.java | 52 ++++++++- .../8381236/VoiceOverHierarchyChangeTest.java | 103 ++++++++++++++++++ 2 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 test/jdk/javax/accessibility/8381236/VoiceOverHierarchyChangeTest.java diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java index 4315abe6197..b0794da2e29 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java @@ -25,7 +25,12 @@ package sun.lwawt.macosx; +import java.awt.AWTEvent; import java.awt.Component; +import java.awt.Container; +import java.awt.Toolkit; +import java.awt.event.AWTEventListener; +import java.awt.event.ContainerEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Objects; @@ -52,6 +57,16 @@ final class CAccessible extends CFRetainedResource implements Accessible { public static CAccessible getCAccessible(final Accessible a) { + return getCAccessible(a, true); + } + + /** + * @param createIfUndefined if there is not yet a cached CAccessible for + * the given Accessible, then this boolean + * controls whether this method creates a new + * CAccessible or returns null. + */ + private static CAccessible getCAccessible(final Accessible a, final boolean createIfUndefined) { if (a == null) return null; AccessibleContext context = a.getAccessibleContext(); AWTAccessor.AccessibleContextAccessor accessor @@ -60,9 +75,40 @@ public static CAccessible getCAccessible(final Accessible a) { if (cachedCAX != null) { return cachedCAX; } - final CAccessible newCAX = new CAccessible(a); - accessor.setNativeAXResource(context, newCAX); - return newCAX; + if (createIfUndefined) { + final CAccessible newCAX = new CAccessible(a); + accessor.setNativeAXResource(context, newCAX); + return newCAX; + } + return null; + } + + static { + // Call CAccessible.dispose() as objects are removed from the AWT hierarchy. + AWTEventListener componentRemovedListener = new AWTEventListener() { + @Override + public void eventDispatched(AWTEvent event) { + if (event.getID() == ContainerEvent.COMPONENT_REMOVED) { + ContainerEvent containerEvent = (ContainerEvent) event; + disposeRecursively(containerEvent.getChild()); + } + } + + private void disposeRecursively(Component c) { + if (c instanceof Container container) { + for (Component child : container.getComponents()) { + disposeRecursively(child); + } + } + if (c instanceof Accessible ax) { + CAccessible ca = getCAccessible(ax, false); + if (ca != null) { + ca.dispose(); + } + } + } + }; + Toolkit.getDefaultToolkit().addAWTEventListener(componentRemovedListener, ContainerEvent.CONTAINER_EVENT_MASK); } private static native void unregisterFromCocoaAXSystem(long ptr); diff --git a/test/jdk/javax/accessibility/8381236/VoiceOverHierarchyChangeTest.java b/test/jdk/javax/accessibility/8381236/VoiceOverHierarchyChangeTest.java new file mode 100644 index 00000000000..42a0de1c405 --- /dev/null +++ b/test/jdk/javax/accessibility/8381236/VoiceOverHierarchyChangeTest.java @@ -0,0 +1,103 @@ +/* + * 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. + */ + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Rectangle; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; + +/* + * @test + * @key headful + * @bug 8381236 + * @summary manual test for VoiceOver that moves Components across Windows + * @requires os.family == "mac" + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual VoiceOverHierarchyChangeTest + */ + +public class VoiceOverHierarchyChangeTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + INSTRUCTIONS: + 1. Open VoiceOver + 2. Move the mouse over the "Does Nothing" button + 3. Click the "Move To Other Window" button + 4. Move the mouse over the "Does Nothing" button + + Expected behavior: VoiceOver reads "Does Nothing" after steps + 2 and 4. + """; + + PassFailJFrame.builder() + .title("VoiceOverHierarchyChangeTest Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(VoiceOverHierarchyChangeTest::createUI) + .build() + .awaitAndCheck(); + } + public static JFrame createUI() { + JFrame f1 = new JFrame(); + f1.getContentPane().setPreferredSize(new Dimension(300, 100)); + JFrame f2 = new JFrame(); + f2.getContentPane().setPreferredSize(new Dimension(300, 100)); + + JButton hopButton = new JButton("Move To Other Window"); + JButton noopButton = new JButton("Does Nothing"); + JPanel panel = new JPanel(new BorderLayout()); + panel.add(hopButton, BorderLayout.NORTH); + panel.add(noopButton, BorderLayout.SOUTH); + + hopButton.addActionListener(e -> { + if (SwingUtilities.isDescendingFrom(hopButton, f1)) { + f2.getContentPane().add(panel); + } else { + f1.getContentPane().add(panel); + } + f1.repaint(); + f2.repaint(); + }); + + f1.getContentPane().add(panel); + f1.pack(); + f2.pack(); + + f1.addComponentListener(new ComponentAdapter() { + @Override + public void componentMoved(ComponentEvent e) { + Rectangle r = f1.getBounds(); + f2.setLocation(r.x, r.y + r.height); + f2.setVisible(true); + } + }); + + return f1; + } +} From 1ad2db7ad11e5158177b10d5e8edbc0a8547f084 Mon Sep 17 00:00:00 2001 From: Shawn Emery Date: Thu, 18 Jun 2026 17:16:21 +0000 Subject: [PATCH 008/707] 8386473: DESKeySpec and DESedeKeySpec may throw InvalidKeyException instead of ArrayIndexOutOfBoundsException for Integer.MIN_VALUE offset Reviewed-by: mullan, syan --- .../classes/javax/crypto/spec/DESKeySpec.java | 18 ++++----- .../javax/crypto/spec/DESedeKeySpec.java | 12 +++--- .../crypto/spec/DESKeySpec/OffsetKey.java | 37 +++++++++++++++---- 3 files changed, 45 insertions(+), 22 deletions(-) diff --git a/src/java.base/share/classes/javax/crypto/spec/DESKeySpec.java b/src/java.base/share/classes/javax/crypto/spec/DESKeySpec.java index a2afd88c3ba..45511a8bbef 100644 --- a/src/java.base/share/classes/javax/crypto/spec/DESKeySpec.java +++ b/src/java.base/share/classes/javax/crypto/spec/DESKeySpec.java @@ -156,12 +156,12 @@ public DESKeySpec(byte[] key, int offset) throws InvalidKeyException { if (key == null) { throw new NullPointerException("null key"); } - if (key.length - offset < DES_KEY_LEN) { - throw new InvalidKeyException("Wrong key size"); - } if (offset < 0) { throw new ArrayIndexOutOfBoundsException("offset is negative"); } + if (key.length - offset < DES_KEY_LEN) { + throw new InvalidKeyException("Wrong key size"); + } this.key = new byte[DES_KEY_LEN]; System.arraycopy(key, offset, this.key, 0, DES_KEY_LEN); } @@ -198,12 +198,12 @@ public static boolean isParityAdjusted(byte[] key, int offset) if (key == null) { throw new InvalidKeyException("null key"); } - if (key.length - offset < DES_KEY_LEN) { - throw new InvalidKeyException("Wrong key size"); - } if (offset < 0) { throw new ArrayIndexOutOfBoundsException("offset is negative"); } + if (key.length - offset < DES_KEY_LEN) { + throw new InvalidKeyException("Wrong key size"); + } for (int i = 0; i < DES_KEY_LEN; i++) { int k = Integer.bitCount(key[offset++] & 0xff); if ((k & 1) == 0) { @@ -235,12 +235,12 @@ public static boolean isWeak(byte[] key, int offset) if (key == null) { throw new InvalidKeyException("null key"); } - if (key.length - offset < DES_KEY_LEN) { - throw new InvalidKeyException("Wrong key size"); - } if (offset < 0) { throw new ArrayIndexOutOfBoundsException("offset is negative"); } + if (key.length - offset < DES_KEY_LEN) { + throw new InvalidKeyException("Wrong key size"); + } for (int i = 0; i < WEAK_KEYS.length; i++) { boolean found = true; for (int j = 0; j < DES_KEY_LEN; j++) { diff --git a/src/java.base/share/classes/javax/crypto/spec/DESedeKeySpec.java b/src/java.base/share/classes/javax/crypto/spec/DESedeKeySpec.java index fb5a19b4a9b..9d93ea90bc9 100644 --- a/src/java.base/share/classes/javax/crypto/spec/DESedeKeySpec.java +++ b/src/java.base/share/classes/javax/crypto/spec/DESedeKeySpec.java @@ -86,12 +86,12 @@ public DESedeKeySpec(byte[] key, int offset) throws InvalidKeyException { if (key == null) { throw new NullPointerException("null key"); } - if (key.length - offset < DES_EDE_KEY_LEN) { - throw new InvalidKeyException("Wrong key size"); - } if (offset < 0) { throw new ArrayIndexOutOfBoundsException("offset is negative"); } + if (key.length - offset < DES_EDE_KEY_LEN) { + throw new InvalidKeyException("Wrong key size"); + } this.key = new byte[24]; System.arraycopy(key, offset, this.key, 0, DES_EDE_KEY_LEN); } @@ -126,12 +126,12 @@ public static boolean isParityAdjusted(byte[] key, int offset) if (key == null) { throw new InvalidKeyException("null key"); } - if (key.length - offset < DES_EDE_KEY_LEN) { - throw new InvalidKeyException("Wrong key size"); - } if (offset < 0) { throw new ArrayIndexOutOfBoundsException("offset is negative"); } + if (key.length - offset < DES_EDE_KEY_LEN) { + throw new InvalidKeyException("Wrong key size"); + } return DESKeySpec.isParityAdjusted(key, offset) && DESKeySpec.isParityAdjusted(key, offset + 8) && DESKeySpec.isParityAdjusted(key, offset + 16); diff --git a/test/jdk/javax/crypto/spec/DESKeySpec/OffsetKey.java b/test/jdk/javax/crypto/spec/DESKeySpec/OffsetKey.java index c97bb819ba2..f0f4c149895 100644 --- a/test/jdk/javax/crypto/spec/DESKeySpec/OffsetKey.java +++ b/test/jdk/javax/crypto/spec/DESKeySpec/OffsetKey.java @@ -23,11 +23,9 @@ /* * @test - * @bug 8364121 - * @summary DESKeySpec.isWeak should throw aiobe exception if the offset is - * negative. + * @bug 8364121 8386473 + * @summary Test DES[ede]KeySpec for negative and integer overflow offsets */ -import java.security.InvalidKeyException; import javax.crypto.spec.DESedeKeySpec; import javax.crypto.spec.DESKeySpec; @@ -52,21 +50,46 @@ public static void main(String[] args) throws Exception { boolean weak = DESKeySpec.isWeak(strongKey, -1); throw new Exception("expected ArrayIndexOutOfBoundsException"); } catch (ArrayIndexOutOfBoundsException aiobe) {} - try{ + try { boolean parityAdjusted = DESKeySpec.isParityAdjusted(strongKey, -1); throw new Exception("expected ArrayIndexOutOfBoundsException"); } catch (ArrayIndexOutOfBoundsException aiobe) {} + try { + DESKeySpec desKey = new DESKeySpec(strongKey, Integer.MIN_VALUE); + throw new Exception("expected ArrayIndexOutOfBoundsException"); + } catch (ArrayIndexOutOfBoundsException aiobe) {} + try { + boolean weak = DESKeySpec.isWeak(strongKey, Integer.MIN_VALUE); + throw new Exception("expected ArrayIndexOutOfBoundsException"); + } catch (ArrayIndexOutOfBoundsException aiobe) {} + try { + boolean parityAdjusted = DESKeySpec.isParityAdjusted(strongKey, + Integer.MIN_VALUE); + throw new Exception("expected ArrayIndexOutOfBoundsException"); + } catch (ArrayIndexOutOfBoundsException aiobe) {} + // Test triple-DES - try{ + try { DESedeKeySpec desEdeKey = new DESedeKeySpec(strongKey, -1); throw new Exception("expected ArrayIndexOutOfBoundsException"); } catch (ArrayIndexOutOfBoundsException aiobe) {} - try{ + try { boolean parityAdjusted = DESedeKeySpec.isParityAdjusted(strongKey, -1); throw new Exception("expected ArrayIndexOutOfBoundsException"); } catch (ArrayIndexOutOfBoundsException aiobe) {} + + try { + DESedeKeySpec desEdeKey = new DESedeKeySpec(strongKey, + Integer.MIN_VALUE); + throw new Exception("expected ArrayIndexOutOfBoundsException"); + } catch (ArrayIndexOutOfBoundsException aiobe) {} + try { + boolean parityAdjusted = DESedeKeySpec.isParityAdjusted(strongKey, + Integer.MIN_VALUE); + throw new Exception("expected ArrayIndexOutOfBoundsException"); + } catch (ArrayIndexOutOfBoundsException aiobe) {} } } From 37856eccb36c722e37653b23d21646e9a7dadf06 Mon Sep 17 00:00:00 2001 From: Shawn Emery Date: Thu, 18 Jun 2026 17:16:48 +0000 Subject: [PATCH 009/707] 8386466: DESedeKeySpec.isParityAdjusted spec permits 8-byte key but RI throws InvalidKeyException Reviewed-by: mullan --- .../share/classes/javax/crypto/spec/DESedeKeySpec.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/javax/crypto/spec/DESedeKeySpec.java b/src/java.base/share/classes/javax/crypto/spec/DESedeKeySpec.java index 9d93ea90bc9..35cbbf9e462 100644 --- a/src/java.base/share/classes/javax/crypto/spec/DESedeKeySpec.java +++ b/src/java.base/share/classes/javax/crypto/spec/DESedeKeySpec.java @@ -117,7 +117,7 @@ public byte[] getKey() { * * @exception InvalidKeyException if the given key material is * null, or starting at offset inclusive, is - * shorter than 8 bytes. + * shorter than 24 bytes. * @exception ArrayIndexOutOfBoundsException if offset is * negative. */ From d5b3643b56817f96953a87279c0a0370df6dbde4 Mon Sep 17 00:00:00 2001 From: Xiaolong Peng Date: Thu, 18 Jun 2026 18:29:51 +0000 Subject: [PATCH 010/707] 8385592: Shenandoah: Introduce ShenandoahAllocator interface to encapsulate memory allocation 8385596: Shenandoah: Introduce per-partition allocators with FreeSet API boundary Reviewed-by: kdnilsen, wkemper --- .../shenandoah/shenandoahAllocRate.inline.hpp | 4 +- .../gc/shenandoah/shenandoahAllocator.cpp | 69 ++++++ .../gc/shenandoah/shenandoahAllocator.hpp | 63 ++++++ .../share/gc/shenandoah/shenandoahFreeSet.cpp | 200 +++++++++++++++++- .../share/gc/shenandoah/shenandoahFreeSet.hpp | 61 ++++-- .../share/gc/shenandoah/shenandoahHeap.cpp | 36 ++-- .../share/gc/shenandoah/shenandoahHeap.hpp | 5 +- .../gc/shenandoah/shenandoahOldGeneration.cpp | 18 +- .../gc/shenandoah/shenandoahOldGeneration.hpp | 6 +- .../shenandoahPartitionAllocator.cpp | 172 +++++++++++++++ .../shenandoahPartitionAllocator.hpp | 66 ++++++ .../test_shenandoahOldGeneration.cpp | 6 +- 12 files changed, 642 insertions(+), 64 deletions(-) create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahAllocator.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahAllocator.hpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahPartitionAllocator.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahPartitionAllocator.hpp diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAllocRate.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAllocRate.inline.hpp index 4872b288a5b..9ffad0d312c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAllocRate.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAllocRate.inline.hpp @@ -57,7 +57,7 @@ void ShenandoahAllocRate::update_minimum_sample_size(const size_t availab template void ShenandoahAllocRate::allocated(const size_t allocated_bytes) { - size_t unsampled = _allocated_bytes_since_last_sample.add_then_fetch(allocated_bytes); + size_t unsampled = _allocated_bytes_since_last_sample.add_then_fetch(allocated_bytes, memory_order_relaxed); const size_t minimum_sample_size = _minimum_sample_size.load_relaxed(); if (unsampled < minimum_sample_size) { // Not enough to sample yet @@ -120,7 +120,7 @@ void ShenandoahAllocRate::take_sample(jlong now, jlong elapsed, size_t un // We are recording this sample, deduct it from the counter. It may be increased // concurrently by other threads outside the lock, so we still use an atomic access. - _allocated_bytes_since_last_sample.sub_then_fetch(unsampled); + _allocated_bytes_since_last_sample.sub_then_fetch(unsampled, memory_order_relaxed); const double timestamp = static_cast(_last_sample_time) / Clock::elapsed_frequency(); const double rate_seconds = static_cast(unsampled) * Clock::elapsed_frequency() / elapsed; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAllocator.cpp b/src/hotspot/share/gc/shenandoah/shenandoahAllocator.cpp new file mode 100644 index 00000000000..e656e206272 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahAllocator.cpp @@ -0,0 +1,69 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE THIS COPYRIGHT NOTICE 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 "gc/shenandoah/shenandoahAllocator.hpp" +#include "gc/shenandoah/shenandoahAllocRequest.hpp" +#include "gc/shenandoah/shenandoahFreeSet.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahHeapRegion.hpp" + +ShenandoahAllocator::ShenandoahAllocator(ShenandoahFreeSet* free_set) + : _free_set(free_set), + _mutator_alloc(free_set), + _collector_alloc(free_set), + _old_collector_alloc(free_set) {} + +HeapWord* ShenandoahAllocator::allocate(ShenandoahAllocRequest& req, bool& in_new_region) { + if (ShenandoahHeapRegion::requires_humongous(req.size())) { + ShenandoahHeapLocker locker(ShenandoahHeap::heap()->lock(), req.is_mutator_alloc()); + switch (req.type()) { + case ShenandoahAllocRequest::_alloc_shared: + case ShenandoahAllocRequest::_alloc_shared_gc: + in_new_region = true; + return _free_set->allocate_contiguous(req, /* is_humongous = */ true); + case ShenandoahAllocRequest::_alloc_cds: + in_new_region = true; + return _free_set->allocate_contiguous(req, /* is_humongous = */ false); + default: + ShouldNotReachHere(); + in_new_region = false; + return nullptr; + } + } + + // Route to the appropriate per-partition allocator. + if (req.is_mutator_alloc()) { + return _mutator_alloc.allocate(req, in_new_region); + } else if (req.is_old()) { + return _old_collector_alloc.allocate(req, in_new_region); + } else { + return _collector_alloc.allocate(req, in_new_region); + } +} + +void ShenandoahAllocator::release_alloc_regions() { + _mutator_alloc.release_alloc_region(); + _collector_alloc.release_alloc_region(); + _old_collector_alloc.release_alloc_region(); +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAllocator.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAllocator.hpp new file mode 100644 index 00000000000..d2c8abb2faa --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahAllocator.hpp @@ -0,0 +1,63 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE THIS COPYRIGHT NOTICE 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_GC_SHENANDOAH_SHENANDOAHALLOCATOR_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHALLOCATOR_HPP + +#include "gc/shenandoah/shenandoahAllocRequest.hpp" +#include "gc/shenandoah/shenandoahPartitionAllocator.hpp" +#include "memory/allocation.hpp" + +typedef ShenandoahPartitionAllocator ShenandoahMutatorAllocator; +typedef ShenandoahPartitionAllocator ShenandoahCollectorAllocator; +typedef ShenandoahPartitionAllocator ShenandoahOldCollectorAllocator; + +// ShenandoahAllocator is the single entry point for memory allocations. Humongous +// requests are served directly via ShenandoahFreeSet; all other requests are routed +// to the appropriate per-partition allocator (mutator, collector, or old-collector). +// Both paths run under the heap lock. +class ShenandoahAllocator : public CHeapObj { +private: + ShenandoahFreeSet* _free_set; + ShenandoahMutatorAllocator _mutator_alloc; + ShenandoahCollectorAllocator _collector_alloc; + ShenandoahOldCollectorAllocator _old_collector_alloc; + +public: + ShenandoahAllocator(ShenandoahFreeSet* free_set); + + // Allocate memory from heap for a request. Humongous requests are served directly via + // ShenandoahFreeSet; all other requests are routed to the mutator, collector, or + // old-collector partition allocator based on request type. The heap lock is taken + // on both paths (here for humongous, inside the partition allocator otherwise). + // Returns nullptr if the request cannot be satisfied. Sets in_new_region to indicate + // whether the returned address is the first allocation in a freshly acquired region. + HeapWord* allocate(ShenandoahAllocRequest& req, bool& in_new_region); + + // Release the cached alloc region in every partition allocator. Call before the + // free set is rebuilt, since rebuild may reclassify region affiliation/membership. + void release_alloc_regions(); +}; + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHALLOCATOR_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp index 9d2b92a5255..3bbca7de1ff 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp @@ -26,6 +26,7 @@ #include "gc/shared/tlab_globals.hpp" #include "gc/shenandoah/shenandoahAffiliation.hpp" +#include "gc/shenandoah/shenandoahAllocator.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegionSet.hpp" @@ -222,18 +223,18 @@ ShenandoahFreeSetPartitionId ShenandoahFreeSet::prepare_to_promote_in_place(size return p; } -inline bool ShenandoahFreeSet::can_allocate_from(ShenandoahHeapRegion *r) const { +bool ShenandoahFreeSet::can_allocate_from(ShenandoahHeapRegion *r) const { const auto state = r->state(); return ShenandoahHeapRegion::is_empty_state(state) || (ShenandoahHeapRegion::is_trash(state) && !_heap->is_concurrent_weak_root_in_progress()); } -inline bool ShenandoahFreeSet::can_allocate_from(size_t idx) const { +bool ShenandoahFreeSet::can_allocate_from(size_t idx) const { ShenandoahHeapRegion* r = _heap->get_region(idx); return can_allocate_from(r); } -inline size_t ShenandoahFreeSet::alloc_capacity(ShenandoahHeapRegion *r) const { +size_t ShenandoahFreeSet::alloc_capacity(ShenandoahHeapRegion *r) const { if (r->is_trash()) { // This would be recycled on allocation path return ShenandoahHeapRegion::region_size_bytes(); @@ -242,12 +243,12 @@ inline size_t ShenandoahFreeSet::alloc_capacity(ShenandoahHeapRegion *r) const { } } -inline size_t ShenandoahFreeSet::alloc_capacity(size_t idx) const { +size_t ShenandoahFreeSet::alloc_capacity(size_t idx) const { ShenandoahHeapRegion* r = _heap->get_region(idx); return alloc_capacity(r); } -inline bool ShenandoahFreeSet::has_alloc_capacity(ShenandoahHeapRegion *r) const { +bool ShenandoahFreeSet::has_alloc_capacity(ShenandoahHeapRegion *r) const { return alloc_capacity(r) > 0; } @@ -289,7 +290,181 @@ void ShenandoahFreeSet::resize_old_collector_capacity(size_t regions) { // else, old generation is already appropriately sized } -inline idx_t ShenandoahRegionPartitions::leftmost(ShenandoahFreeSetPartitionId which_partition) const { +void ShenandoahFreeSet::notify_allocation(ShenandoahFreeSetPartitionId partition, bool in_new_region, bool boundary_changed) { + switch (partition) { + case ShenandoahFreeSetPartitionId::Mutator: + recompute_total_used(); + if (in_new_region) { + recompute_total_affiliated(); + } + break; + case ShenandoahFreeSetPartitionId::Collector: + recompute_total_used(); + if (in_new_region) { + recompute_total_affiliated(); + } + break; + case ShenandoahFreeSetPartitionId::OldCollector: + recompute_total_used(); + if (in_new_region) { + recompute_total_affiliated(); + } + break; + case ShenandoahFreeSetPartitionId::NotFree: + default: + assert(false, "won't happen"); + } + if (boundary_changed) { + _partitions.assert_bounds(); + } else { + _partitions.assert_bounds_sanity(); + } +} + +void ShenandoahFreeSet::increase_partition_used(ShenandoahFreeSetPartitionId partition, size_t bytes) { + _partitions.increase_used(partition, bytes); +} + +void ShenandoahFreeSet::mark_region_used(ShenandoahFreeSetPartitionId partition) { + _partitions.one_region_is_no_longer_empty(partition); +} + +size_t ShenandoahFreeSet::retire_region(ShenandoahFreeSetPartitionId partition, size_t idx, size_t used_bytes) { + return _partitions.retire_from_partition(partition, idx, used_bytes); +} + +template +ShenandoahHeapRegion* ShenandoahFreeSet::find_region_for_alloc(size_t min_size_words, bool& in_new_region) { + shenandoah_assert_heaplocked(); + // Allocation bias is a Mutator-only heuristic; updating it for collector allocations would + // perturb mutator placement and burn down the shared bias weight. + if constexpr (PARTITION == ShenandoahFreeSetPartitionId::Mutator) { + update_allocation_bias(); + } + + if (_partitions.is_empty(PARTITION)) { + return nullptr; + } + + constexpr ShenandoahAffiliation affiliation = + (PARTITION == ShenandoahFreeSetPartitionId::OldCollector) ? OLD_GENERATION : YOUNG_GENERATION; + + ShenandoahHeapRegion* result = nullptr; + ShenandoahHeapRegion* free_region = nullptr; + size_t min_size_bytes = min_size_words * HeapWordSize; + + auto search = [&](auto& iterator) { + for (idx_t idx = iterator.current(); iterator.has_next(); idx = iterator.next()) { + ShenandoahHeapRegion* r = _heap->get_region(idx); + if (_heap->is_concurrent_weak_root_in_progress() && r->is_trash()) continue; + r->try_recycle_under_lock(); + if (r->is_empty()) { + if (free_region == nullptr) free_region = r; + // Mutator takes the first region with capacity. Collectors prefer an affiliated region + // and only fall back to an empty region (below) to preserve free regions. + if (PARTITION == ShenandoahFreeSetPartitionId::Mutator && alloc_capacity(r) >= min_size_bytes) { + result = r; + return; + } + } else if (r->affiliation() == affiliation) { + if (alloc_capacity(r) >= min_size_bytes) { result = r; return; } + } + } + }; + + if (_partitions.alloc_from_left_bias(PARTITION)) { + ShenandoahLeftRightIterator iterator(&_partitions, PARTITION); + search(iterator); + } else { + ShenandoahRightLeftIterator iterator(&_partitions, PARTITION); + search(iterator); + } + + // For collector partitions: fall back to any free (empty) region if no affiliated region found. + if constexpr (PARTITION != ShenandoahFreeSetPartitionId::Mutator) { + if (result == nullptr && free_region != nullptr) { + result = free_region; + } + } + + if (result == nullptr) { + return nullptr; + } + + // Prepare the region for allocation. + in_new_region = result->is_empty(); + if (in_new_region) { + assert(!result->is_affiliated(), "New region should be unaffiliated"); + result->set_affiliation(affiliation); + if constexpr (PARTITION == ShenandoahFreeSetPartitionId::OldCollector) { + result->end_preemptible_coalesce_and_fill(); + } +#ifdef ASSERT + ShenandoahMarkingContext* const ctx = _heap->marking_context(); + assert(ctx->top_at_mark_start(result) == result->bottom(), "TAMS must equal bottom for new region"); + assert(ctx->is_bitmap_range_within_region_clear(ctx->top_bitmap(result), result->end()), "Bitmap must be clear"); +#endif + } + return result; +} + +// Explicit instantiations for find_region_for_alloc. +template ShenandoahHeapRegion* ShenandoahFreeSet::find_region_for_alloc(size_t, bool&); +template ShenandoahHeapRegion* ShenandoahFreeSet::find_region_for_alloc(size_t, bool&); +template ShenandoahHeapRegion* ShenandoahFreeSet::find_region_for_alloc(size_t, bool&); + +ShenandoahHeapRegion* ShenandoahFreeSet::steal_from_mutator(ShenandoahFreeSetPartitionId target_partition, + ShenandoahAllocRequest& req) { + shenandoah_assert_heaplocked(); + assert(target_partition != ShenandoahFreeSetPartitionId::Mutator, "Cannot steal from self"); + + if (_partitions.get_empty_region_counts(ShenandoahFreeSetPartitionId::Mutator) == 0) { + return nullptr; + } + + ShenandoahRightLeftIterator iterator(&_partitions, ShenandoahFreeSetPartitionId::Mutator, true); + for (idx_t idx = iterator.current(); iterator.has_next(); idx = iterator.next()) { + ShenandoahHeapRegion* r = _heap->get_region(idx); + if (can_allocate_from(r)) { + if (req.is_old()) { + if (!flip_to_old_gc(r)) { + continue; + } + } else { + flip_to_gc(r); + } + log_debug(gc, free)("Flipped region %zu to gc for request: " PTR_FORMAT, idx, p2i(&req)); + + r->try_recycle_under_lock(); + assert(r->is_empty(), "Must be empty"); + ShenandoahAffiliation aff = (target_partition == ShenandoahFreeSetPartitionId::OldCollector) + ? OLD_GENERATION : YOUNG_GENERATION; + r->set_affiliation(aff); + if (r->is_old()) { + r->end_preemptible_coalesce_and_fill(); + } + return r; + } + } + return nullptr; +} + +idx_t ShenandoahRegionPartitions::leftmost(ShenandoahFreeSetPartitionId which_partition) const { assert (which_partition < NumPartitions, "selected free partition must be valid"); idx_t idx = _leftmosts[int(which_partition)]; if (idx >= _max) { @@ -302,7 +477,7 @@ inline idx_t ShenandoahRegionPartitions::leftmost(ShenandoahFreeSetPartitionId w } } -inline idx_t ShenandoahRegionPartitions::rightmost(ShenandoahFreeSetPartitionId which_partition) const { +idx_t ShenandoahRegionPartitions::rightmost(ShenandoahFreeSetPartitionId which_partition) const { assert (which_partition < NumPartitions, "selected free partition must be valid"); idx_t idx = _rightmosts[int(which_partition)]; // Cannot assert that membership[which_partition.is_set(idx) because this helper method may be used @@ -793,12 +968,12 @@ inline bool ShenandoahRegionPartitions::partition_id_matches(idx_t idx, Shenando } #endif -inline bool ShenandoahRegionPartitions::is_empty(ShenandoahFreeSetPartitionId which_partition) const { +bool ShenandoahRegionPartitions::is_empty(ShenandoahFreeSetPartitionId which_partition) const { assert (which_partition < NumPartitions, "selected free partition must be valid"); return (leftmost(which_partition) > rightmost(which_partition)); } -inline idx_t ShenandoahRegionPartitions::find_index_of_next_available_region( +idx_t ShenandoahRegionPartitions::find_index_of_next_available_region( ShenandoahFreeSetPartitionId which_partition, idx_t start_index) const { idx_t rightmost_idx = rightmost(which_partition); idx_t leftmost_idx = leftmost(which_partition); @@ -814,7 +989,7 @@ inline idx_t ShenandoahRegionPartitions::find_index_of_next_available_region( return result; } -inline idx_t ShenandoahRegionPartitions::find_index_of_previous_available_region( +idx_t ShenandoahRegionPartitions::find_index_of_previous_available_region( ShenandoahFreeSetPartitionId which_partition, idx_t last_index) const { idx_t rightmost_idx = rightmost(which_partition); idx_t leftmost_idx = leftmost(which_partition); @@ -1187,7 +1362,7 @@ void ShenandoahRegionPartitions::assert_bounds() { "Mutator humongous waste must match"); } -inline void ShenandoahRegionPartitions::assert_bounds_sanity() { +void ShenandoahRegionPartitions::assert_bounds_sanity() { for (uint8_t i = 0; i < UIntNumPartitions; i++) { ShenandoahFreeSetPartitionId partition = static_cast(i); assert(leftmost(partition) == _max || membership(leftmost(partition)) == partition, "Left most boundry must be sane"); @@ -2487,6 +2662,9 @@ void ShenandoahFreeSet::prepare_to_rebuild(size_t &young_trashed_regions, size_t shenandoah_assert_heaplocked(); assert(rebuild_lock() != nullptr, "sanity"); rebuild_lock()->lock(false); + // Drop cached alloc regions before clearing partition state — partition membership + // is about to change and would invalidate the cached regions. + _heap->allocator()->release_alloc_regions(); // This resets all state information, removing all regions from all sets. clear(); log_debug(gc, free)("Rebuilding FreeSet"); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp index 84f44415684..4d3be955ddc 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp @@ -161,7 +161,7 @@ using idx_t = ShenandoahSimpleBitMap::idx_t; _membership[int(p)].clear_bit(idx); } - inline void one_region_is_no_longer_empty(ShenandoahFreeSetPartitionId partition); + void one_region_is_no_longer_empty(ShenandoahFreeSetPartitionId partition); // Set the Mutator intervals, usage, and capacity according to arguments. Reset the Collector intervals, used, capacity // to represent empty Collector free set. We use this at the end of rebuild_free_set() to avoid the overhead of making @@ -231,11 +231,11 @@ using idx_t = ShenandoahSimpleBitMap::idx_t; const char* partition_membership_name(idx_t idx) const; // Return the index of the next available region >= start_index, or maximum_regions if not found. - inline idx_t find_index_of_next_available_region(ShenandoahFreeSetPartitionId which_partition, + idx_t find_index_of_next_available_region(ShenandoahFreeSetPartitionId which_partition, idx_t start_index) const; // Return the index of the previous available region <= last_index, or -1 if not found. - inline idx_t find_index_of_previous_available_region(ShenandoahFreeSetPartitionId which_partition, + idx_t find_index_of_previous_available_region(ShenandoahFreeSetPartitionId which_partition, idx_t last_index) const; // Return the index of the next available cluster of cluster_size regions >= start_index, or maximum_regions if not found. @@ -279,12 +279,12 @@ using idx_t = ShenandoahSimpleBitMap::idx_t; // leftmost() and leftmost_empty() return _max, rightmost() and rightmost_empty() return 0 // otherwise, expect the following: // 0 <= leftmost <= leftmost_empty <= rightmost_empty <= rightmost < _max - inline idx_t leftmost(ShenandoahFreeSetPartitionId which_partition) const; - inline idx_t rightmost(ShenandoahFreeSetPartitionId which_partition) const; + idx_t leftmost(ShenandoahFreeSetPartitionId which_partition) const; + idx_t rightmost(ShenandoahFreeSetPartitionId which_partition) const; idx_t leftmost_empty(ShenandoahFreeSetPartitionId which_partition); idx_t rightmost_empty(ShenandoahFreeSetPartitionId which_partition); - inline bool is_empty(ShenandoahFreeSetPartitionId which_partition) const; + bool is_empty(ShenandoahFreeSetPartitionId which_partition) const; inline void increase_region_counts(ShenandoahFreeSetPartitionId which_partition, size_t regions); inline void decrease_region_counts(ShenandoahFreeSetPartitionId which_partition, size_t regions); @@ -315,7 +315,7 @@ using idx_t = ShenandoahSimpleBitMap::idx_t; inline void decrease_available(ShenandoahFreeSetPartitionId which_partition, size_t bytes); inline size_t get_available(ShenandoahFreeSetPartitionId which_partition); - inline void increase_used(ShenandoahFreeSetPartitionId which_partition, size_t bytes); + void increase_used(ShenandoahFreeSetPartitionId which_partition, size_t bytes); inline void decrease_used(ShenandoahFreeSetPartitionId which_partition, size_t bytes); inline size_t get_used(ShenandoahFreeSetPartitionId which_partition) { assert (which_partition < NumPartitions, "Partition must be valid"); @@ -408,7 +408,7 @@ using idx_t = ShenandoahSimpleBitMap::idx_t; void assert_bounds() NOT_DEBUG_RETURN; // this checks certain sanity conditions related to the bounds with much less effort than is required to // more rigorously enforce correctness as is done by assert_bounds() - inline void assert_bounds_sanity() NOT_DEBUG_RETURN; + void assert_bounds_sanity() NOT_DEBUG_RETURN; }; // Publicly, ShenandoahFreeSet represents memory that is available to mutator threads. The public capacity(), used(), @@ -436,6 +436,7 @@ using idx_t = ShenandoahSimpleBitMap::idx_t; // during the next GC pass. class ShenandoahFreeSet : public CHeapObj { + using idx_t = ShenandoahSimpleBitMap::idx_t; private: ShenandoahHeap* const _heap; @@ -570,13 +571,6 @@ using idx_t = ShenandoahSimpleBitMap::idx_t; // Precondition: !ShenandoahHeapRegion::requires_humongous(req.size()) HeapWord* allocate_single(ShenandoahAllocRequest& req, bool& in_new_region); - // While holding the heap lock, allocate memory for a humongous object which spans one or more regions that - // were previously empty. Regions that represent humongous objects are entirely dedicated to the humongous - // object. No other objects are packed into these regions. - // - // Precondition: ShenandoahHeapRegion::requires_humongous(req.size()) - HeapWord* allocate_contiguous(ShenandoahAllocRequest& req, bool is_humongous); - bool transfer_one_region_from_mutator_to_old_collector(size_t idx, size_t alloc_capacity); // Change region r from the Mutator partition to the GC's Collector or OldCollector partition. This requires that the @@ -620,10 +614,10 @@ using idx_t = ShenandoahSimpleBitMap::idx_t; // Returns true iff this region is entirely available, either because it is empty() or because it has been found to represent // immediate trash and we'll be able to immediately recycle it. Note that we cannot recycle immediate trash if // concurrent weak root processing is in progress. - inline bool can_allocate_from(ShenandoahHeapRegion *r) const; - inline bool can_allocate_from(size_t idx) const; + bool can_allocate_from(ShenandoahHeapRegion *r) const; + bool can_allocate_from(size_t idx) const; - inline bool has_alloc_capacity(ShenandoahHeapRegion *r) const; + bool has_alloc_capacity(ShenandoahHeapRegion *r) const; void transfer_empty_regions_from_to(ShenandoahFreeSetPartitionId source_partition, ShenandoahFreeSetPartitionId dest_partition, @@ -661,9 +655,36 @@ using idx_t = ShenandoahSimpleBitMap::idx_t; return _partitions.shrink_interval_if_range_modifies_either_boundary(partition, low_idx, high_idx, num_regions); } + // Called by ShenandoahAllocator after a successful allocation to update used/affiliated totals. + // boundary_changed indicates if partition boundaries were modified (retire or new-region), + // triggering a full bounds validation in debug builds. + void notify_allocation(ShenandoahFreeSetPartitionId partition, bool in_new_region, bool boundary_changed); + + // Find a region in the given partition with at least min_size_words of allocatable capacity. + // Handles bias direction, trash recycling, and affiliation setup for new (empty) regions. + // Returns nullptr if no suitable region found. Sets in_new_region if the returned region was empty. + // Caller must hold the heap lock. + template + ShenandoahHeapRegion* find_region_for_alloc(size_t min_size_words, bool& in_new_region); + + // Steal an empty region from the Mutator partition for the given collector partition. + // Flips the region, sets up affiliation, and returns it ready for allocation. + // The returned region is always empty (newly available for allocation). + // Returns nullptr if no region can be stolen. Caller must hold the heap lock. + ShenandoahHeapRegion* steal_from_mutator(ShenandoahFreeSetPartitionId target_partition, + ShenandoahAllocRequest& req); + + // Allocate contiguous regions for humongous objects. Caller must hold heap lock. + HeapWord* allocate_contiguous(ShenandoahAllocRequest& req, bool is_humongous); + + // Partition accounting APIs for allocators. + void increase_partition_used(ShenandoahFreeSetPartitionId partition, size_t bytes); + void mark_region_used(ShenandoahFreeSetPartitionId partition); + size_t retire_region(ShenandoahFreeSetPartitionId partition, size_t idx, size_t used_bytes); + // Public because ShenandoahRegionPartitions assertions require access. - inline size_t alloc_capacity(ShenandoahHeapRegion *r) const; - inline size_t alloc_capacity(size_t idx) const; + size_t alloc_capacity(ShenandoahHeapRegion *r) const; + size_t alloc_capacity(size_t idx) const; // Return bytes used by old inline size_t old_used() { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index c0df4bbe10c..e60db88974a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -42,6 +42,7 @@ #include "gc/shenandoah/mode/shenandoahGenerationalMode.hpp" #include "gc/shenandoah/mode/shenandoahPassiveMode.hpp" #include "gc/shenandoah/mode/shenandoahSATBMode.hpp" +#include "gc/shenandoah/shenandoahAllocator.hpp" #include "gc/shenandoah/shenandoahAllocRate.inline.hpp" #include "gc/shenandoah/shenandoahAllocRequest.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" @@ -67,6 +68,7 @@ #include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahPadding.hpp" #include "gc/shenandoah/shenandoahParallelCleaning.inline.hpp" +#include "gc/shenandoah/shenandoahPartitionAllocator.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "gc/shenandoah/shenandoahReferenceProcessor.hpp" #include "gc/shenandoah/shenandoahRootProcessor.inline.hpp" @@ -434,6 +436,7 @@ jint ShenandoahHeap::initialize() { } _free_set = new ShenandoahFreeSet(this, _num_regions); + _allocator = new ShenandoahAllocator(_free_set); initialize_generations(); // We are initializing free set. We ignore cset region tallies. @@ -575,6 +578,7 @@ ShenandoahHeap::ShenandoahHeap(ShenandoahCollectorPolicy* policy) : _shenandoah_policy(policy), _gc_mode(nullptr), _free_set(nullptr), + _allocator(nullptr), _verifier(nullptr), _phase_timings(nullptr), _monitoring_support(nullptr), @@ -941,7 +945,7 @@ HeapWord* ShenandoahHeap::allocate_memory(ShenandoahAllocRequest& req) { if (req.is_mutator_alloc()) { if (!ShenandoahAllocFailureALot || !should_inject_alloc_failure()) { - result = allocate_memory_under_lock(req, in_new_region); + result = allocate_memory_work(req, in_new_region); } // Check that gc overhead is not exceeded. @@ -973,7 +977,7 @@ HeapWord* ShenandoahHeap::allocate_memory(ShenandoahAllocRequest& req) { const size_t original_count = shenandoah_policy()->full_gc_count(); while (result == nullptr && should_retry_allocation(original_count)) { control_thread()->handle_alloc_failure(req, true); - result = allocate_memory_under_lock(req, in_new_region); + result = allocate_memory_work(req, in_new_region); } if (result != nullptr) { // If our allocation request has been satisfied after it initially failed, we count this as good gc progress @@ -989,7 +993,7 @@ HeapWord* ShenandoahHeap::allocate_memory(ShenandoahAllocRequest& req) { } } else { assert(req.is_gc_alloc(), "Can only accept GC allocs here"); - result = allocate_memory_under_lock(req, in_new_region); + result = allocate_memory_work(req, in_new_region); // Do not call handle_alloc_failure() here, because we cannot block. // The allocation failure would be handled by the LRB slowpath with handle_alloc_failure_evac(). } @@ -1019,21 +1023,15 @@ inline bool ShenandoahHeap::should_retry_allocation(size_t original_full_gc_coun && !shenandoah_policy()->is_at_shutdown(); } -HeapWord* ShenandoahHeap::allocate_memory_under_lock(ShenandoahAllocRequest& req, bool& in_new_region) { - // If we are dealing with mutator allocation, then we may need to block for safepoint. - // We cannot block for safepoint for GC allocations, because there is a high chance - // we are already running at safepoint or from stack watermark machinery, and we cannot - // block again. - ShenandoahHeapLocker locker(lock(), req.is_mutator_alloc()); - - // Make sure the old generation has room for either evacuations or promotions before trying to allocate. - if (req.is_old() && !old_generation()->can_allocate(req)) { +HeapWord* ShenandoahHeap::allocate_memory_work(ShenandoahAllocRequest& req, bool& in_new_region) { + // Reserve the promotion budget up front so it is enforced atomically without the heap lock. + // If the reserve is exhausted, deny the promotion rather than overshoot it; the reservation + // is refunded below if the allocation itself fails. + if (req.is_promotion() && !old_generation()->try_expend_promoted(req.size() << LogHeapWordSize)) { return nullptr; } - // If TLAB request size is greater than available, allocate() will attempt to downsize request to fit within available - // memory. - HeapWord* result = _free_set->allocate(req, in_new_region); + HeapWord* result = _allocator->allocate(req, in_new_region); if (result != nullptr) { if (req.is_mutator_alloc()) { @@ -1044,13 +1042,13 @@ HeapWord* ShenandoahHeap::allocate_memory_under_lock(ShenandoahAllocRequest& req if (req.is_lab_alloc()) { old_generation()->configure_plab_for_current_thread(req); } else if (req.is_promotion()) { - const size_t actual_size = req.actual_size() * HeapWordSize; - log_debug(gc, plab)("Expend shared promotion of %zu bytes", actual_size); - old_generation()->expend_promoted(actual_size); + log_debug(gc, plab)("Expend shared promotion of %zu bytes", req.actual_size() * HeapWordSize); } } + } else if (req.is_promotion()) { + // Allocation failed, so refund the promotion budget reserved above. + old_generation()->unexpend_promoted(req.size() << LogHeapWordSize); } - return result; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index 33d5fa6b04f..86707c7e831 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -48,6 +48,7 @@ class ConcurrentGCTimer; class ObjectIterateScanRootClosure; +class ShenandoahAllocator; class ShenandoahCollectorPolicy; class ShenandoahGCSession; class ShenandoahGCStateResetter; @@ -533,6 +534,7 @@ class ShenandoahHeap : public CollectedHeap { ShenandoahCollectorPolicy* _shenandoah_policy; ShenandoahMode* _gc_mode; ShenandoahFreeSet* _free_set; + ShenandoahAllocator* _allocator; ShenandoahVerifier* _verifier; ShenandoahPhaseTimings* _phase_timings; @@ -557,6 +559,7 @@ class ShenandoahHeap : public CollectedHeap { ShenandoahCollectorPolicy* shenandoah_policy() const { return _shenandoah_policy; } ShenandoahMode* mode() const { return _gc_mode; } ShenandoahFreeSet* free_set() const { return _free_set; } + ShenandoahAllocator* allocator() const { return _allocator; } ShenandoahPhaseTimings* phase_timings() const { return _phase_timings; } @@ -699,7 +702,7 @@ class ShenandoahHeap : public CollectedHeap { inline HeapWord* allocate_from_gclab(Thread* thread, size_t size); private: - HeapWord* allocate_memory_under_lock(ShenandoahAllocRequest& request, bool& in_new_region); + HeapWord* allocate_memory_work(ShenandoahAllocRequest& request, bool& in_new_region); HeapWord* allocate_from_gclab_slow(Thread* thread, size_t size); HeapWord* allocate_new_gclab(size_t min_size, size_t word_size, size_t* actual_size); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp index e488c457049..0a0beaaffee 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp @@ -193,10 +193,17 @@ void ShenandoahOldGeneration::maybe_log_promotion_failure_stats(bool concurrent) } } -size_t ShenandoahOldGeneration::expend_promoted(size_t increment) { - shenandoah_assert_heaplocked_or_safepoint(); - assert(get_promoted_expended() + increment <= get_promoted_reserve(), "Do not expend more promotion than budgeted"); - return _promoted_expended.add_then_fetch(increment); +bool ShenandoahOldGeneration::try_expend_promoted(size_t increment) { + const size_t reserve = get_promoted_reserve(); + size_t cur = _promoted_expended.load_relaxed(); + while (cur + increment <= reserve) { + size_t prev = _promoted_expended.compare_exchange(cur, cur + increment); + if (prev == cur) { + return true; + } + cur = prev; + } + return false; } size_t ShenandoahOldGeneration::unexpend_promoted(size_t decrement) { @@ -245,12 +252,11 @@ ShenandoahOldGeneration::configure_plab_for_current_thread(const ShenandoahAlloc // The actual size of the allocation may be larger than the requested bytes (due to alignment on card boundaries). // If this puts us over our promotion budget, we need to disable future PLAB promotions for this thread. - if (can_promote(actual_size)) { + if (try_expend_promoted(actual_size)) { // Assume the entirety of this PLAB will be used for promotion. This prevents promotion from overreach. // When we retire this plab, we'll unexpend what we don't really use. log_debug(gc, plab)("Thread can promote using PLAB of %zu bytes. Expended: %zu, available: %zu", actual_size, get_promoted_expended(), get_promoted_reserve()); - expend_promoted(actual_size); shenandoah_plab->enable_promotions(); shenandoah_plab->set_actual_size(actual_size); } else { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp index 7e26d800e1d..f7f0a0ae0ba 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp @@ -111,8 +111,10 @@ class ShenandoahOldGeneration : public ShenandoahGeneration { // This zeros out the expended promotion count after the promotion reserve is computed void reset_promoted_expended(); - // This is incremented when allocations are made to copy promotions into the old generation - size_t expend_promoted(size_t increment); + // Atomically reserve `increment` bytes of promotion budget. Returns true if the full amount + // was reserved without exceeding the reserve. Lock-free: safe to call without the heap lock. + // Use this to gate a promotion decision before promoting. + bool try_expend_promoted(size_t increment); // This is used to return unused memory from a retired promotion LAB size_t unexpend_promoted(size_t decrement); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPartitionAllocator.cpp b/src/hotspot/share/gc/shenandoah/shenandoahPartitionAllocator.cpp new file mode 100644 index 00000000000..ed644a724e8 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahPartitionAllocator.cpp @@ -0,0 +1,172 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE THIS COPYRIGHT NOTICE 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 "gc/shared/plab.hpp" +#include "gc/shenandoah/shenandoahAllocRequest.hpp" +#include "gc/shenandoah/shenandoahFreeSet.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahHeapRegion.hpp" +#include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahPartitionAllocator.hpp" +#include "logging/log.hpp" + +template +ShenandoahPartitionAllocator::ShenandoahPartitionAllocator(ShenandoahFreeSet* free_set) + : _free_set(free_set), + _alloc_region(nullptr) {} + +template +HeapWord* ShenandoahPartitionAllocator::allocate(ShenandoahAllocRequest& req, bool& in_new_region) { + // Mutator allocations may yield to safepoint; GC allocations cannot. + ShenandoahHeapLocker locker(ShenandoahHeap::heap()->lock(), req.is_mutator_alloc()); + + // OldCollector: verify old generation has room before attempting allocation. + if constexpr (PARTITION == ShenandoahFreeSetPartitionId::OldCollector) { + if (!req.is_promotion() && !ShenandoahHeap::heap()->old_generation()->can_allocate(req)) { + return nullptr; + } + } + + bool boundary_changed = false; + size_t min_req_words = req.is_lab_alloc() ? req.min_size() : req.size(); + // Fast path: try the cached alloc region first. + if (_alloc_region != nullptr) { + constexpr ShenandoahAffiliation affiliation = + (PARTITION == ShenandoahFreeSetPartitionId::OldCollector) ? OLD_GENERATION : YOUNG_GENERATION; + assert(!_alloc_region->is_trash() && _alloc_region->affiliation() == affiliation && + _free_set->membership(_alloc_region->index()) == PARTITION, + "Cached alloc region %zu must remain a non-trash member of this partition until the free set is rebuilt", + _alloc_region->index()); + HeapWord* result = nullptr; + size_t ac_words = _alloc_region->free() >> LogHeapWordSize; + // A region is only ever cached while it has at least PLAB::min_size of capacity, and its + // free space shrinks only via allocate_in (which retires and clears it below that threshold). + // So the cached alloc region always has usable capacity here: use it when it can satisfy this + // request, otherwise keep it cached for a smaller future request and fall through. + assert(ac_words >= PLAB::min_size(), + "Cached alloc region %zu must keep at least PLAB::min_size capacity, has %zu words", + _alloc_region->index(), ac_words); + if (ac_words >= min_req_words) { + result = allocate_in(_alloc_region, req, boundary_changed); + } + if (result != nullptr) { + in_new_region = false; + _free_set->notify_allocation(PARTITION, false, boundary_changed); + return result; + } + } + + // Ask FreeSet to find a suitable region. + ShenandoahHeapRegion* r = _free_set->find_region_for_alloc(min_req_words, in_new_region); + // Collector partitions can overflow into Mutator partition. + if constexpr (PARTITION != ShenandoahFreeSetPartitionId::Mutator) { + if (r == nullptr && ShenandoahEvacReserveOverflow) { + r = _free_set->steal_from_mutator(PARTITION, req); + if (r != nullptr) { + assert(r->is_empty(), "Stolen region must be empty"); + in_new_region = true; + } + } + } + + if (r != nullptr) { + HeapWord* result = allocate_in(r, req, boundary_changed); + if (in_new_region) { + _free_set->mark_region_used(PARTITION); + boundary_changed = true; + } + _free_set->notify_allocation(PARTITION, in_new_region, boundary_changed); + return result; + } + + // Every path that mutates a partition boundary (allocate_in retire, new region, steal) returns + // above, so reaching here means no allocation and no boundary change. + return nullptr; +} + +template +HeapWord* ShenandoahPartitionAllocator::allocate_in(ShenandoahHeapRegion* r, ShenandoahAllocRequest& req, bool& boundary_changed) { + assert(_free_set->alloc_capacity(r) > 0, "Performance: should avoid full regions on this path: %zu", r->index()); + + HeapWord* result = nullptr; + + // Perform the actual allocation: LABs may be shrunk to fit. + if (req.is_lab_alloc()) { + size_t adjusted_size = req.size(); + size_t free = align_down(r->free() >> LogHeapWordSize, MinObjAlignment); + if (adjusted_size > free) { + adjusted_size = free; + } + assert(adjusted_size >= req.min_size(), + "Caller must ensure region has at least min_size capacity: free=%zu, min_size=%zu", + free, req.min_size()); + result = r->allocate(adjusted_size, req); + req.set_actual_size(adjusted_size); + } else { + size_t size = req.size(); + result = r->allocate(size, req); + req.set_actual_size(size); + } + assert(result != nullptr, "Allocation must succeed, region free: %zu, request minimal size: %zu", + r->free(), req.is_lab_alloc() ? req.min_size() : req.size()); + + // Update partition used bytes after allocation + if constexpr (PARTITION == ShenandoahFreeSetPartitionId::Mutator) { + assert(req.is_young(), "Mutator allocations always come from young generation."); + _free_set->increase_partition_used(PARTITION, req.actual_size() * HeapWordSize); + } else { + assert(req.is_gc_alloc(), "Should be gc_alloc since req wasn't mutator alloc"); + // For GC allocations, we advance update_watermark because the objects relocated into this memory during + // evacuation are not updated during evacuation. For both young and old regions, it is essential that all + // PLABs be made parsable at the end of evacuation. This is enabled by retiring all plabs at end of evacuation. + r->set_update_watermark(r->top()); + _free_set->increase_partition_used(PARTITION, (req.actual_size() + req.waste()) * HeapWordSize); + } + + // Retire the region if remaining capacity is too small for any future PLAB. + if ((r->free() >> LogHeapWordSize) < PLAB::min_size()) { + size_t idx = r->index(); + size_t waste_bytes = _free_set->retire_region(PARTITION, idx, r->used()); + boundary_changed = true; + if constexpr (PARTITION == ShenandoahFreeSetPartitionId::Mutator) { + if (waste_bytes > 0) { + req.set_waste(waste_bytes / HeapWordSize); + } + } + if (_alloc_region == r) { + _alloc_region = nullptr; + } + } else if (_alloc_region == nullptr) { + // Region still has usable capacity — cache it for next allocation. + _alloc_region = r; + } + + return result; +} + +// Explicit template instantiations for all partitions. +template class ShenandoahPartitionAllocator; +template class ShenandoahPartitionAllocator; +template class ShenandoahPartitionAllocator; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPartitionAllocator.hpp b/src/hotspot/share/gc/shenandoah/shenandoahPartitionAllocator.hpp new file mode 100644 index 00000000000..75c36d8889d --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahPartitionAllocator.hpp @@ -0,0 +1,66 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE THIS COPYRIGHT NOTICE 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_GC_SHENANDOAH_SHENANDOAHPARTITIONALLOCATOR_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHPARTITIONALLOCATOR_HPP + +#include "gc/shenandoah/shenandoahAllocRequest.hpp" +#include "gc/shenandoah/shenandoahFreeSet.hpp" +#include "gc/shenandoah/shenandoahHeapRegion.hpp" +#include "memory/allocation.hpp" + +// ShenandoahPartitionAllocator is the serial (lock-based) partition allocator. +// It uses ShenandoahFreeSet APIs to find regions and performs allocation within them +// under the heap lock. Templated on partition ID so that partition-specific behavior +// (overflow stealing for Collector/OldCollector) is resolved at compile time. +template +class ShenandoahPartitionAllocator : public CHeapObj { + +private: + ShenandoahFreeSet* const _free_set; + + // Cached allocation region with remaining capacity from the last allocation in + // this partition. Checked first on the next request to skip a FreeSet scan. + // Cleared when retired by allocate_in or by release_alloc_region. + ShenandoahHeapRegion* _alloc_region; + + // Allocate within a single region; the caller must guarantee the region has enough free + // capacity for the request. Handles LAB sizing, updates partition accounting via + // ShenandoahFreeSet, and retires the region if remaining capacity drops below PLAB::min_size(). + // boundary_changed is set to true if the region is retired or otherwise mutates the partition + // boundary; it is never reset to false. + HeapWord* allocate_in(ShenandoahHeapRegion* r, ShenandoahAllocRequest& req, bool& boundary_changed); + +public: + ShenandoahPartitionAllocator(ShenandoahFreeSet* free_set); + + // Allocate from this partition. Returns nullptr if partition cannot satisfy the request. + HeapWord* allocate(ShenandoahAllocRequest& req, bool& in_new_region); + + // Drop the cached alloc region. Must be called before the free set is rebuilt, + // since rebuild can change region affiliation/membership and invalidate the cache. + void release_alloc_region() { _alloc_region = nullptr; } +}; + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHPARTITIONALLOCATOR_HPP diff --git a/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldGeneration.cpp b/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldGeneration.cpp index 4633d8588d3..483464fba17 100644 --- a/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldGeneration.cpp +++ b/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldGeneration.cpp @@ -55,7 +55,7 @@ class ShenandoahOldGenerationTest : public ::testing::Test { old = new ShenandoahOldGeneration(8); old->set_promoted_reserve(512 * HeapWordSize); - old->expend_promoted(256 * HeapWordSize); + old->try_expend_promoted(256 * HeapWordSize); old->set_evacuation_reserve(512 * HeapWordSize); Thread* thread = Thread::current(); @@ -171,10 +171,10 @@ TEST_VM_F(ShenandoahOldGenerationTest, test_actual_size_exceeds_promotion_reserv EXPECT_FALSE(promotions_enabled()) << "New plab can only be used for evacuations"; } -TEST_VM_F(ShenandoahOldGenerationTest, test_expend_promoted_should_increase_expended) { +TEST_VM_F(ShenandoahOldGenerationTest, test_try_expend_promoted_should_increase_expended) { SKIP_IF_NOT_SHENANDOAH(); size_t expended_before = old->get_promoted_expended(); - old->expend_promoted(128); + EXPECT_TRUE(old->try_expend_promoted(128)) << "Should fit within reserve"; size_t expended_after = old->get_promoted_expended(); EXPECT_EQ(expended_before + 128, expended_after) << "Should expend promotion"; } From fe98e67e4cdbbee9d9cb4902b2c20592c656cb5c Mon Sep 17 00:00:00 2001 From: Phil Race Date: Thu, 18 Jun 2026 21:26:45 +0000 Subject: [PATCH 011/707] 8386795: Swing specification needs caveats on L&F rendering behaviors Reviewed-by: psadhukhan, kizune --- .../share/classes/javax/swing/UIManager.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/java.desktop/share/classes/javax/swing/UIManager.java b/src/java.desktop/share/classes/javax/swing/UIManager.java index f323842ae49..f6b3895fd8a 100644 --- a/src/java.desktop/share/classes/javax/swing/UIManager.java +++ b/src/java.desktop/share/classes/javax/swing/UIManager.java @@ -158,6 +158,42 @@ * expects certain defaults, so that in general * a {@code ComponentUI} provided by one look and feel will not * work with another look and feel. + * + *

System Look and Feels

+ * + * [The terms "System", "Native" and "Platform" may be used interchangeably in this context]. + *

+ * A System Look And Feel is intended to implement the native Look and Feel of the desktop. + *

+ * There is no requirement for the standard Java Look And Feel to be the default, + * therefore the System Look and Feel may be the default. + *

+ * A desktop may not have a consistent Look and Feel, for example if there are + * multiple platform-native toolkits provided to create applications for the desktop. + * Swing may elect any one of these to be its native Look and Feel. + *

+ * Installation of the native Look and Feel may depend on platform resources being available. + * In the event that required resources are not available, Swing may be unable to install + * the System Look and Feel. + *

+ * Swing's emulation of the native Look and Feel takes precedence over any component-specific + * indication of rendering. + * This means that a native Look and Feel should render in a way that is consistent with the platform, + * even if it contradicts component setting-specific documentation. + * Examples include + *

    + *
  • specified rendering of painted borders may be ignored + *
  • specified rendering of highlighting effects may be ignored + *
  • specified rendering of painted backgrounds and foregrounds may be ignored + *
  • specified rendering of selected vs unselected components may be ignored + *
  • specified rendering of enabled vs disabled components may be ignored + *
+ * These are just examples. Not an exhaustive list. + *

+ * These caveats must not be construed as an excuse to arbitrarily ignore these properties. + * They are intended to support the requirement that the platform Look and Feel be as + * consistent with the native rendering as is practical. + * *

* Warning: * Serialized objects of this class will not be compatible with From ee28630820cfb8217d7d8e4f964c37b6af691757 Mon Sep 17 00:00:00 2001 From: Anthony Scarpino Date: Thu, 18 Jun 2026 21:48:33 +0000 Subject: [PATCH 012/707] 8385978: Test javax/net/ssl/SSLSession/TestEnabledProtocols.java failed: java.security.cert.CertificateException: Unable to initialize, java.io.IOException: Too short Reviewed-by: abarashev --- .../share/classes/java/security/PEM.java | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/java.base/share/classes/java/security/PEM.java b/src/java.base/share/classes/java/security/PEM.java index 13ee9f107cf..421ae40b30f 100644 --- a/src/java.base/share/classes/java/security/PEM.java +++ b/src/java.base/share/classes/java/security/PEM.java @@ -32,6 +32,7 @@ import sun.security.util.Pem; import java.io.InputStream; +import java.lang.ref.Reference; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.Objects; @@ -201,7 +202,11 @@ public byte[] leadingData() { * @since 27 */ public byte[] content() { - return content.clone(); + try { + return content.clone(); + } finally { + Reference.reachabilityFence(this); + } } /** @@ -212,7 +217,11 @@ public byte[] content() { * @throws IllegalArgumentException if decoding fails */ public byte[] decode() { - return Base64.getMimeDecoder().decode(content); + try { + return Base64.getMimeDecoder().decode(content); + } finally { + Reference.reachabilityFence(this); + } } /** @@ -223,15 +232,23 @@ public byte[] decode() { */ @Override public String toString() { - return new String(Pem.pemEncoded(type, content), - StandardCharsets.ISO_8859_1); + try { + return new String(Pem.pemEncoded(type, content), + StandardCharsets.ISO_8859_1); + } finally { + Reference.reachabilityFence(this); + } } /* * Returns the PEM string representation as a byte array. */ byte[] toTextualByteArray() { - return Pem.pemEncoded(type, content); + try { + return Pem.pemEncoded(type, content); + } finally { + Reference.reachabilityFence(this); + } } // Clear internal content From 2d005ac20dbc27189f212343e11e92246c320c86 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Fri, 19 Jun 2026 01:32:46 +0000 Subject: [PATCH 013/707] 8386810: Improve debuggability of test/jdk/sun/nio/cs/TestStringCodingUTF8.java Reviewed-by: naoto, liach --- test/jdk/sun/nio/cs/TestStringCodingUTF8.java | 82 +++++++++++++++---- 1 file changed, 67 insertions(+), 15 deletions(-) diff --git a/test/jdk/sun/nio/cs/TestStringCodingUTF8.java b/test/jdk/sun/nio/cs/TestStringCodingUTF8.java index 50d68f408d5..5b084013528 100644 --- a/test/jdk/sun/nio/cs/TestStringCodingUTF8.java +++ b/test/jdk/sun/nio/cs/TestStringCodingUTF8.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2024, 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 @@ -21,18 +21,26 @@ * questions. */ -/* @test - @bug 7040220 8054307 - @summary Test if StringCoding and NIO result have the same de/encoding result for UTF-8 - * @run main/othervm/timeout=2000 TestStringCodingUTF8 +/* + * @test + * @bug 7040220 8054307 + * @summary Test if StringCoding and NIO result have the same de/encoding result for UTF-8 * @key randomness + * @library /test/lib + * @build jdk.test.lib.RandomFactory + * @run main/othervm/timeout=2000 TestStringCodingUTF8 */ import java.util.*; import java.nio.*; import java.nio.charset.*; +import jdk.test.lib.RandomFactory; + public class TestStringCodingUTF8 { + + private static final Random rnd = RandomFactory.getRandom(); + public static void main(String[] args) throws Throwable { test("UTF-8"); test("CESU-8"); @@ -62,7 +70,7 @@ static void test(String csn) throws Throwable { for (int i = 0; i < 0x20000; i++) { list.add(i, i); } - Collections.shuffle(list); + Collections.shuffle(list, rnd); int j = 0; char[] bmpsupp = new char[0x30000]; for (int i = 0; i < 0x20000; i++) { @@ -72,7 +80,6 @@ static void test(String csn) throws Throwable { test(cs, bmpsupp, 0, bmpsupp.length); // randomed "off" and "len" on shuffled data - Random rnd = new Random(); int maxlen = 1000; int itr = 5000; for (int i = 0; i < itr; i++) { @@ -88,11 +95,13 @@ static void test(String csn) throws Throwable { //new String(csn); if (!new String(ba, cs.name()).equals( new String(decode(cs, ba, 0, ba.length)))) - throw new RuntimeException("new String(csn) failed"); + throw new RuntimeException("new String(csn) failed for charset " + cs + + " for byte array of length " + ba.length); //new String(cs); if (!new String(ba, cs).equals( new String(decode(cs, ba, 0, ba.length)))) - throw new RuntimeException("new String(cs) failed"); + throw new RuntimeException("new String(cs) failed for charset " + cs + + " for byte array of length " + ba.length); } System.out.println("done!"); } @@ -103,21 +112,23 @@ static void test(Charset cs, char[] ca, int off, int len) throws Throwable { //getBytes(csn); byte[] baStr = str.getBytes(cs.name()); - if (!Arrays.equals(ba, baStr)) - throw new RuntimeException("getBytes(csn) failed"); + failIfMismatch(ba, baStr, "getBytes(csn) failed for charset " + cs + + ", character array length=" + ca.length + ", offset=" + off + ", len=" + len); //getBytes(cs); baStr = str.getBytes(cs); - if (!Arrays.equals(ba, baStr)) - throw new RuntimeException("getBytes(cs) failed"); + failIfMismatch(ba, baStr, "getBytes(cs) failed for charset " + cs + + ", character array length=" + ca.length + ", offset=" + off + ", len=" + len); //new String(csn); if (!new String(ba, cs.name()).equals(new String(decode(cs, ba, 0, ba.length)))) - throw new RuntimeException("new String(csn) failed"); + throw new RuntimeException("new String(csn) failed for charset " + cs + + ", character array length=" + ca.length + ", offset=" + off + ", len=" + len); //new String(cs); if (!new String(ba, cs).equals(new String(decode(cs, ba, 0, ba.length)))) - throw new RuntimeException("new String(cs) failed"); + throw new RuntimeException("new String(cs) failed for charset " + cs + + ", character array length=" + ca.length + ", offset=" + off + ", len=" + len); } // copy/paste of the StringCoding.decode() @@ -170,4 +181,45 @@ static byte[] encode(Charset cs, char[] ca, int off, int len) { } return Arrays.copyOf(ba, bb.position()); } + + private static void failIfMismatch(final byte[] expected, final byte[] actual, + final String failureMsg) { + final int firstMismatchIndex = Arrays.mismatch(expected, actual); + if (firstMismatchIndex == -1) { + // no mismatch + return; + } + System.err.println("Arrays mismatch starts at index: " + firstMismatchIndex); + System.err.println("Printing few indexes before and after the mismatch:"); + int printStartIdx = firstMismatchIndex - 20; + if (printStartIdx < 0) { + printStartIdx = 0; + } + for (int i = printStartIdx; i < firstMismatchIndex + 20; i++) { + if (i >= expected.length && i >= actual.length) { + // no more elements in either arrays, we are done + break; + } + final StringBuilder sb = new StringBuilder(); + sb.append("Index=").append(i).append(", expected="); + if (i >= expected.length) { + // "expected" array isn't that big + sb.append(""); + } else { + sb.append(expected[i]); + } + sb.append(", actual="); + if (i >= actual.length) { + // "actual" array isn't that big + sb.append(""); + } else { + sb.append(actual[i]); + } + if (i == firstMismatchIndex) { + sb.append(" <--- first mismatch"); + } + System.err.println(sb.toString()); + } + throw new RuntimeException(failureMsg); + } } From d22f5c54b51a07dafae3069a4f485f7e703d8d18 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 19 Jun 2026 06:55:12 +0000 Subject: [PATCH 014/707] 8386591: C2: wrong result because of broken truncation check in CountedLoopConverter::TruncatedIncrement::build Reviewed-by: roland, kvn, qamai --- src/hotspot/share/opto/loopnode.cpp | 28 +++-- src/hotspot/share/opto/loopnode.hpp | 1 - .../TestTruncationWrapBadCharWrap.java | 115 ++++++++++++++++++ 3 files changed, 133 insertions(+), 11 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/loopopts/TestTruncationWrapBadCharWrap.java diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 7c62e313803..1018a8ae9ab 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -3129,8 +3129,12 @@ Node* LoopLimitNode::Identity(PhaseGVN* phase) { return this; } -// Match increment with optional truncation: -// CHAR: (i+1)&0x7fff, BYTE: ((i+1)<<8)>>8, or SHORT: ((i+1)<<16)>>16 +// CHAR: (i+1)&0x7fff Note: does NOT work for char cast (0xffff) +// BYTE: ((i+1)<<8)>>8 Note: does NOT work for byte cast (<< 24 >> 24) +// SHORT: ((i+1)<<16)>>16 +// +// Note: in the future, we should fix both the BYTE and the CHAR case, +// to allow proper optimization of byte/char cast truncation. void CountedLoopConverter::TruncatedIncrement::build(Node* expr) { _is_valid = false; @@ -3146,15 +3150,19 @@ void CountedLoopConverter::TruncatedIncrement::build(Node* expr) { const TypeInteger* trunc_t = TypeInteger::bottom(_bt); if (_bt == T_INT) { - // Try to strip (n1 & M) or (n1 << N >> N) from n1. if (n1op == Op_AndI && - n1->in(2)->is_Con() && - n1->in(2)->bottom_type()->is_int()->get_con() == 0x7fff) { - // %%% This check should match any mask of 2**K-1. - t1 = n1; - n1 = t1->in(1); - n1op = n1->Opcode(); - trunc_t = TypeInt::CHAR; + n1->in(2)->is_Con()) { + // Unsigned truncation. + // Pattern: ((i+1) & mask) + jint mask = n1->in(2)->bottom_type()->is_int()->get_con(); + switch (mask) { + case 0x7fff: // Unsigned 15-bit truncation. For historical reasons. + t1 = n1; + n1 = t1->in(1); + n1op = n1->Opcode(); + trunc_t = TypeInt::make_unsigned(0, mask, 0); + break; + } } else if (n1op == Op_RShiftI && n1->in(1) != nullptr && n1->in(1)->Opcode() == Op_LShiftI && diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 9f841d958ec..71a159352a4 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -2105,7 +2105,6 @@ class CountedLoopConverter { bool is_valid() const { return _is_valid; } Node* incr() const { return _incr; } - // Optional truncation for: CHAR: (i+1)&0x7fff, BYTE: ((i+1)<<8)>>8, or SHORT: ((i+1)<<16)>>16 Node* outer_trunc() const { return _outer_trunc; } // the outermost truncating node (either the & or the final >>) Node* inner_trunc() const { return _inner_trunc; } // the inner truncating node, if applicable (the << in a <> pair) const TypeInteger* trunc_type() const { return _trunc_type; } diff --git a/test/hotspot/jtreg/compiler/loopopts/TestTruncationWrapBadCharWrap.java b/test/hotspot/jtreg/compiler/loopopts/TestTruncationWrapBadCharWrap.java new file mode 100644 index 00000000000..ed279347762 --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestTruncationWrapBadCharWrap.java @@ -0,0 +1,115 @@ +/* + * 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; + +/* + * @test + * @bug 8386591 + * @summary Test case for TruncatedIncrement::build / + * CountedLoopConverter::has_truncation_wrap where we got wrong + * results, because we confused "& 0x7fff" as range [0..65535] + * instead of [0..32767]. + * @library /test/lib / + * @run main/othervm -Xcomp + * -XX:-TieredCompilation + * -XX:CompileCommand=compileonly,${test.main.class}::test* + * ${test.main.class} + * @run main ${test.main.class} + */ + +public class TestTruncationWrapBadCharWrap { + interface TestMethod { + int call(); + } + + public static void main(String[] args) { + int failures = 0; + + failures += run("test1", () -> test1(), 1402); + failures += run("test2", () -> test2(), 2037); + failures += run("test3", () -> test3(), 171761184); + + if (failures > 0) { + throw new RuntimeException("failures: " + failures); + } + } + + static int run(String name, TestMethod t, int expected) { + for (int i = 0; i < 10_000; i++) { + int result = t.call(); + if (result != expected) { + System.out.println(name + " wrong result: " + result + " vs " + expected); + return 1; + } + } + return 0; + } + + + static int test1() { + int sum = 0; + // The entry value is outside the range [0..32767], but inside [0..65535]. + int i = (char)38405; + while (32 < i) { + sum++; + // Ignoring truncation would require values to be in range [0..32767]. + // But unfortunately, we classified this as CHAR, and checked for [0..65535]. + i = (i - 4) & 0x7fff; + } + return sum; + } + + static int test2() { + int sum = 0; + // We have 32767 - 32758 = 9 < 48, so the limit is too close to the wrap + // limit, and wrap is possible. But since 0x7fff got mapped to CHAR, + // we accidentally checked 65535 - 32758 < 48, and conclude wrap is not + // possible. + for (int i = 519; i < 32758; i = (i + 48) & 0x7fff) { + sum++; + } + return sum; + } + + static int opaqueCounter; + + static boolean opaqueCheck() { + return opaqueCounter++ > 10448; + } + + static int test3() { + opaqueCounter = 0; + int sum = 0; + int i; + // Similar as with test2: + // We should check 32767 - 32766 = 1 < 50, so wrap possible. But we + // wrongly classified it as CHAR and checked 65535 - 32766 < 50, and + // concluded there is no wrap. + for (i = 2046; i <= 32766; i = (i + 50) & 0x7fff) { + sum += i + 1; + if (opaqueCheck()) { break; } + } + return sum + i; + } +} From c2f0259304004b79d8901bd136552ef3044ffc9d Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Fri, 19 Jun 2026 07:16:03 +0000 Subject: [PATCH 015/707] 8386958: Build failure due to incorrect copyright text in src/hotspot/share/gc/shenandoah/ files Reviewed-by: ayang --- src/hotspot/share/gc/shenandoah/shenandoahAllocator.cpp | 2 +- src/hotspot/share/gc/shenandoah/shenandoahAllocator.hpp | 2 +- .../share/gc/shenandoah/shenandoahPartitionAllocator.cpp | 2 +- .../share/gc/shenandoah/shenandoahPartitionAllocator.hpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAllocator.cpp b/src/hotspot/share/gc/shenandoah/shenandoahAllocator.cpp index e656e206272..38f0c7ce045 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAllocator.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAllocator.cpp @@ -1,6 +1,6 @@ /* * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. - * DO NOT ALTER OR REMOVE THIS COPYRIGHT NOTICE OR THIS FILE HEADER. + * 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 diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAllocator.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAllocator.hpp index d2c8abb2faa..690f97e985e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAllocator.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAllocator.hpp @@ -1,6 +1,6 @@ /* * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. - * DO NOT ALTER OR REMOVE THIS COPYRIGHT NOTICE OR THIS FILE HEADER. + * 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 diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPartitionAllocator.cpp b/src/hotspot/share/gc/shenandoah/shenandoahPartitionAllocator.cpp index ed644a724e8..73c35bf65c5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahPartitionAllocator.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahPartitionAllocator.cpp @@ -1,6 +1,6 @@ /* * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. - * DO NOT ALTER OR REMOVE THIS COPYRIGHT NOTICE OR THIS FILE HEADER. + * 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 diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPartitionAllocator.hpp b/src/hotspot/share/gc/shenandoah/shenandoahPartitionAllocator.hpp index 75c36d8889d..dfcaf2b7097 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahPartitionAllocator.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahPartitionAllocator.hpp @@ -1,6 +1,6 @@ /* * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. - * DO NOT ALTER OR REMOVE THIS COPYRIGHT NOTICE OR THIS FILE HEADER. + * 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 From 2f37461c25d7bdbca35af7c2dee32e866b921e7a Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 19 Jun 2026 10:58:31 +0000 Subject: [PATCH 016/707] 8386830: C2: CountedLoopConverter::filtered_type wrongly ignores nullptr contributions to type union/meet Reviewed-by: qamai, kvn --- src/hotspot/share/opto/loopnode.cpp | 10 + .../loopopts/TestHasTruncationWrap.java | 41 +++- .../TestTruncationWrapPhiTypeUnion.java | 221 ++++++++++++++++++ 3 files changed, 264 insertions(+), 8 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/loopopts/TestTruncationWrapPhiTypeUnion.java diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 1018a8ae9ab..e7ccefa6855 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -3856,6 +3856,7 @@ const TypeInt* CountedLoopConverter::filtered_type(Node* n, Node* n_ctrl) { Node* region = phi->in(0); assert(n_ctrl == nullptr || n_ctrl == region, "ctrl parameter must be region"); if (region && region != _phase->C->top()) { + // Compute the union over the types of the paths/inputs. for (uint i = 1; i < phi->req(); i++) { Node* val = phi->in(i); Node* use_c = region->in(i); @@ -3866,10 +3867,17 @@ const TypeInt* CountedLoopConverter::filtered_type(Node* n, Node* n_ctrl) { } else { filtered_t = filtered_t->meet(val_t)->is_int(); } + } else { + // We found no constriant, so we have to assume that this path + // is unconstrained, i.e. it could have the whole int range. + filtered_t = TypeInt::INT; } } } } + + // The filtered type may be worse than what we already know + // about n, so take the intersection. const TypeInt* n_t = _phase->igvn().type(n)->is_int(); if (filtered_t != nullptr) { n_t = n_t->join(filtered_t)->is_int(); @@ -3880,6 +3888,8 @@ const TypeInt* CountedLoopConverter::filtered_type(Node* n, Node* n_ctrl) { //------------------------------filtered_type_from_dominators-------------------------------- // Return a possibly more restrictive type for val based on condition control flow of dominators +// Note: we can also return "nullptr", which means "no constraint", and should be interpreted +// as if we returned TypeInt::INT. const TypeInt* CountedLoopConverter::filtered_type_from_dominators(Node* val, Node* use_ctrl) { if (val->is_Con()) { return val->bottom_type()->is_int(); diff --git a/test/hotspot/jtreg/compiler/loopopts/TestHasTruncationWrap.java b/test/hotspot/jtreg/compiler/loopopts/TestHasTruncationWrap.java index efc3831dc3a..9a68a2fcb77 100644 --- a/test/hotspot/jtreg/compiler/loopopts/TestHasTruncationWrap.java +++ b/test/hotspot/jtreg/compiler/loopopts/TestHasTruncationWrap.java @@ -353,6 +353,24 @@ static int testIRShort2() { // testIRShort2b: short loop, ranges proved in short range via CmpI before loop. // Compared to testIRShort2, the check in the loop is an NEQ. + // + // Since the bug fix of JDK-8386830, we no longer allow this case to detect CountedLoop: + // The backedge finds no useful constraint, the "i != limit" does not give any restrictions, + // and so we have to assume it produces the full range. + // Comparing with testIRShort2, there we have a useful check "i < limit", which does + // give us a restriction, that helps us prove there is not wrap overflow. + // + // In the future, we could try to do something more smart, and combine the info about + // entry type "init < limit <= 100" with the fact that we have unity-stride, and so + // we should not be able to skip the NEQ "i != limit", and be able to canonicalize + // NEQ to LT. But for now, I consider this an edge-case that we will just have to accept + // will not be optimized to CountedLoop for now. For now, a workaround is using the + // exit condition "i < limit". + // This is really a problem about iv evolution (iv starts in range, increments by 1, + // and cannot skip exit check, so NEQ can be converted to LT), and cannot be solved + // by the type info of entry/backedge separately, so I don't have a quick fix here. + // We do this NEQ to LT canonicalization for int loops, but we would also need + // dedicated logic for it specifically combined with the wrap-detection logic. public static int testIRShort2b_gold = testIRShort2b(); @Run(test = "testIRShort2b") @@ -362,7 +380,7 @@ private static void runIRShort2b() { } @Test - @IR(counts = {IRNode.COUNTED_LOOP, "> 0"}) + @IR(counts = {IRNode.COUNTED_LOOP, "= 0"}) static int testIRShort2b() { int init = Math.max(lo, 0); // init in [0..max_int] int limit = Math.min(hi, 100); // limit in [min_int..100] @@ -374,10 +392,8 @@ static int testIRShort2b() { int sum = 0; for (int i = init; i != limit; i = (short)(i+1)) { sum = opaqueSum(sum); // work to keep loop alive - // The backedge value of i is also far - // enough from short boundaries, because of - // the loop exit check: - // i < limit <= 100 + // Unfortunately, the backedge does not produce a useful + // check with "i != limit", and so the type is unconstrained. } return sum; } @@ -589,8 +605,15 @@ static int testIRShort5c() { } // testIRShort5d: short while-loop, again similar to testIRShort2b and testIRShort5c, but with while-loop form. - // No peeling, and so the entry value is init, and so the "init >= limit" check is useful, - // and used by has_truncation_wrap. With it, C2 manages to prove no short-overflow. + // + // Same issue as with testIRShort2b: + // After JDK-8386830, we now see that the backedge type is not constrained, + // and so don't allow CountedLoop detection. + // However, we could be smarter in the future, and canonicalize NEQ + // to LT, because this is a unity-stride loop where the "i != limit" + // can provably not be skipped. For now, we just have to accept that + // we cannot optimize this, and people would have to use "i < limit", + // see testIRShort5. public static int testIRShort5d_gold = testIRShort5d(); @Run(test = "testIRShort5d") @@ -600,7 +623,7 @@ private static void runIRShort5d() { } @Test - @IR(counts = {IRNode.COUNTED_LOOP, "> 0"}) + @IR(counts = {IRNode.COUNTED_LOOP, "= 0"}) static int testIRShort5d() { int init = Math.max(lo, 0); // init in [0..max_int] int limit = Math.min(hi, 100); // limit in [min_int..100] @@ -614,6 +637,8 @@ static int testIRShort5d() { while (i != limit) { sum = opaqueSum(sum); // work to keep loop alive i = (short)(i+1); + // Unfortunately, the backedge does not produce a useful + // check with "i != limit", and so the type is unconstrained. } return sum; } diff --git a/test/hotspot/jtreg/compiler/loopopts/TestTruncationWrapPhiTypeUnion.java b/test/hotspot/jtreg/compiler/loopopts/TestTruncationWrapPhiTypeUnion.java new file mode 100644 index 00000000000..a7873359b45 --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestTruncationWrapPhiTypeUnion.java @@ -0,0 +1,221 @@ +/* + * 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; + +/* + * @test + * @bug 8386830 + * @summary Test for CountedLoopConverter::filtered_type, where we wrongly + * ignored a nullptr type, and returned a type that was too narrow, + * which led us to wrongly ignore wrapping in + * CountedLoopConverter::has_truncation_wrap + * @library /test/lib / + * @run main/othervm -Xbatch + * -XX:CompileCommand=compileonly,${test.main.class}::test* + * ${test.main.class} + * @run main ${test.main.class} + */ +public class TestTruncationWrapPhiTypeUnion { + + interface TestMethod { + int call(); + } + + public static void main(String[] args) { + int failures = 0; + + failures += run("test1", () -> test1(-1), 11111); + failures += run("test2", () -> test2(-1), 11111); + failures += run("test3", () -> test3(-100_000), -87065049); + failures += run("test4", () -> test4(32770), 10330); + failures += run("test5", () -> test5(-63), 10340); + + if (failures > 0) { + throw new RuntimeException("failures: " + failures); + } + } + + static int run(String name, TestMethod t, int expected) { + for (int i = 0; i < 20; i++) { + int result = t.call(); + if (result != expected) { + System.out.println(name + " wrong result: " + result + " vs " + expected); + return 1; + } + } + return 0; + } + + static int test1(int limit) { + int x = 0; + int sum = 0; + + limit = (byte) limit; // type BYTE = [-128..127], at runtime: -1 + + int i = 510; + while (limit < i) { + // Exit check checks for positive values, but with + // entry 510 and unsigned truncation, that can never fail. + + sum++; + + // Secondary exit check, to make sure we exit eventually. + if (++x >= 11111) { + break; + } + + // Unsigned 15-bit truncation. + // We check for wrap/truncation/underflow: + // + // } else if (stride_con < 0) { + // if (truncation.trunc_type()->lo_as_long() - phi_ft->lo_as_long() > stride_con || + // truncation.trunc_type()->hi_as_long() < phi_ft->hi_as_long()) { + // return true; // truncation may occur + // } + // } + // + // The lo of truncation is 0, and also the phi type should have a lo of 0, + // but it was wrongly determined to be 510. + // So, whereas "0 - 0 > -10" would have given us the required "true", + // we now checked "0 - 510 > -10", which was wrongly "false". + // + // The reason is that the phi_ft has been wrongly determined to be 510, + // so only considering the entry value. This is determined inside filtered_type: + // - entry: filtered_type_from_dominators discovers entry value 510. + // - backedge: filtered_type_from_dominators discovers no dominating-if, returns nullptr. + // But filtered_type skips nullptr results, as in "no extra filter". But + // we should be accumulating the entry and backedge type here! + // + // The only if on the backedge-path would have been the exit + // check: limit < i. But filtered_int_type finds nothing, returns nullptr. + i = (i - 10) & 0x7fff; + } + + return sum; + } + + // Note: this case was first discovered during JDK-8386591, which enabled 0xffff masking. + // Without allowing 0xffff masking, this did not fail. But it was the way I + // first discovered the bug, and so I wanted to add this as a test anyway. + static int test2(int limit) { + int x = 0; + int sum = 0; + + limit = (short) limit; // type SHORT = [-32768..32767], at runtime: -1 + + int i = 510; + while (limit < i) { + // Exit check checks for positive values, but with + // entry 510 and unsigned truncation, that can never fial. + + sum++; + + // Secondary exit check, to make sure we exit eventually. + if (++x >= 11111) { + break; + } + + // CHAR truncation: 0..0xffff = 0..65535 + // + // i iterates: 510, 500, ... 10, 0 + // And then, it shshould ould underflows: (0 - 10) & 0xffff = 65526 + // + // But in CountedLoopConverter::has_truncation_wrap, we wrongly + // decide there cannot be overflow. + // truncation: [0..65535] + // stride_con: -10 + // + // Accordingly, phi_ft should be in [0..65535], and so when we check + // for underflow, we check: + // + // } else if (stride_con < 0) { + // if (truncation.trunc_type()->lo_as_long() - phi_ft->lo_as_long() > stride_con || + // truncation.trunc_type()->hi_as_long() < phi_ft->hi_as_long()) { + // return true; // truncation may occur + // } + // } + // + // So we should check: 0 - 0 > -10, and we would see that truncation could occur. + // But instead, we checked 0 - 510 > -10, which wronly lead to "no truncation". + // + // The reason is that the phi_ft has been wrongly determined to be 510, + // so only considering the entry value. This is determined inside filtered_type: + // - entry: filtered_type_from_dominators discovers entry value 510. + // - backedge: filtered_type_from_dominators discovers no dominating-if, returns nullptr. + // But filtered_type skips nullptr results, as in "no extra filter". But + // we should be accumulating the entry and backedge type here! + // + // The only if on the backedge-path would have been the exit + // check: limit < i. But filtered_int_type finds nothing, returns nullptr. + i = (i - 10) & 0xffff; + } + + return sum; + } + + // Another fuzzer find, this one with short truncation. + static int test3(int limit) { + int x = 0; + int sum = 0; + + // Range: [min_int..8192], at runtime: -100_000 + limit = Math.min(limit, 8192); + int i; + for (i = 128; limit <= i; i = (short)(i - 16384)) { + sum = sum + i + 1; + if (x++ > 10789) { + break; + } + } + return sum + i; + } + + // Another fuzzer find, this one with short truncation. + static int test4(int limit) { + int sum = 0; + int x = 0; + limit = (short) limit; + for (int i = -1025; limit <= i; i = (short) (i + -7)) { + sum = sum + 1; + if (x++ > 10328) { + break; + } + } + return sum; + } + + // Another fuzzer find, again with 0x7fff mask. + static int test5(int limit) { + int x = 0; + int sum = 0; + limit = (byte)limit; + for (int i = 8192; limit < i; i = ((i + -8) & 0x7fff)) { + sum++; + if (x++ > 10338) { + break; + } + } + return sum; + } +} From dbbabfd4f4bfbceb391d6f7f7fb4ec9c4e1b104d Mon Sep 17 00:00:00 2001 From: William Kemper Date: Fri, 19 Jun 2026 19:10:36 +0000 Subject: [PATCH 017/707] 8386798: Shenandoah: Missing load barrier when making assertions about mark bitmap Reviewed-by: xpeng, kdnilsen --- .../share/gc/shenandoah/shenandoahAsserts.cpp | 10 ++++++++++ .../share/gc/shenandoah/shenandoahAsserts.hpp | 7 +++++++ .../share/gc/shenandoah/shenandoahFreeSet.cpp | 12 +++++------- .../gc/shenandoah/shenandoahHeapRegion.cpp | 17 ++--------------- .../gc/shenandoah/shenandoahMarkingContext.cpp | 6 ++++-- 5 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp index 278c04b35d6..2eceeb2eca6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp @@ -31,6 +31,7 @@ #include "gc/shenandoah/shenandoahUtils.hpp" #include "memory/resourceArea.hpp" #include "oops/oop.inline.hpp" +#include "runtime/orderAccess.hpp" #include "runtime/os.hpp" #include "utilities/vmError.hpp" @@ -425,6 +426,15 @@ void ShenandoahAsserts::assert_marked_strong(void *interior_loc, oop obj, const } } +void ShenandoahAsserts::assert_bitmap_clear_above_top(ShenandoahHeapRegion* region) { + ShenandoahMarkingContext* const ctx = ShenandoahHeap::heap()->marking_context(); + const HeapWord* top_bitmap = ctx->top_bitmap(region); + // Make sure that top is loaded before any of the marks from the bitmap are loaded. If another + // thread has cleared the bitmap we must not allow any stale reads. + OrderAccess::loadload(); + assert(ctx->is_bitmap_range_within_region_clear(top_bitmap, region->end()), "Bitmap above top_bitmap() must be clear"); +} + void ShenandoahAsserts::assert_mark_complete(HeapWord* obj, const char* file, int line) { const ShenandoahHeap* heap = ShenandoahHeap::heap(); const ShenandoahHeapRegion* region = heap->heap_region_containing(obj); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp index 545415a6531..44330d54303 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp @@ -31,6 +31,8 @@ #include "runtime/mutex.hpp" #include "utilities/formatBuffer.hpp" +class ShenandoahHeapRegion; + typedef FormatBuffer<8192> ShenandoahMessageBuffer; class ShenandoahAsserts { @@ -65,6 +67,7 @@ class ShenandoahAsserts { static void assert_marked(void* interior_loc, oop obj, const char* file, int line); static void assert_marked_weak(void* interior_loc, oop obj, const char* file, int line); static void assert_marked_strong(void* interior_loc, oop obj, const char* file, int line); + static void assert_bitmap_clear_above_top(ShenandoahHeapRegion* region); // Assert that marking is complete for the generation where this obj resides static void assert_mark_complete(HeapWord* obj, const char* file, int line); @@ -137,6 +140,9 @@ class ShenandoahAsserts { #define shenandoah_assert_marked_strong(interior_loc, obj) \ ShenandoahAsserts::assert_marked_strong(interior_loc, obj, __FILE__, __LINE__) +#define shenandoah_assert_clear_above_top(region) \ + ShenandoahAsserts::assert_bitmap_clear_above_top(region) + #define shenandoah_assert_mark_complete(obj) \ ShenandoahAsserts::assert_mark_complete(obj, __FILE__, __LINE__) @@ -227,6 +233,7 @@ class ShenandoahAsserts { #define shenandoah_assert_marked_strong_except(interior_loc, obj, exception) #define shenandoah_assert_marked_strong(interior_loc, obj) +#define shenandoah_assert_clear_above_top(region) #define shenandoah_assert_mark_complete(obj) #define shenandoah_assert_in_cset_if(interior_loc, obj, condition) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp index 3bbca7de1ff..24b3743281a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp @@ -1,7 +1,7 @@ /* * Copyright (c) 2016, 2021, Red Hat, Inc. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 @@ -36,7 +36,6 @@ #include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "logging/logStream.hpp" #include "memory/resourceArea.hpp" -#include "runtime/orderAccess.hpp" static const char* partition_name(ShenandoahFreeSetPartitionId t) { switch (t) { @@ -1685,11 +1684,10 @@ HeapWord* ShenandoahFreeSet::try_allocate_in(ShenandoahHeapRegion* r, Shenandoah // coalesce-and-fill processing. r->end_preemptible_coalesce_and_fill(); } -#ifdef ASSERT - ShenandoahMarkingContext* const ctx = _heap->marking_context(); - assert(ctx->top_at_mark_start(r) == r->bottom(), "Newly established allocation region starts with TAMS equal to bottom"); - assert(ctx->is_bitmap_range_within_region_clear(ctx->top_bitmap(r), r->end()), "Bitmap above top_bitmap() must be clear"); -#endif + + assert(_heap->marking_context()->top_at_mark_start(r) == r->bottom(), + "Newly established allocation region (%zu) must start with TAMS equal to bottom", r->index()); + shenandoah_assert_clear_above_top(r); log_debug(gc, free)("Using new region (%zu) for %s (" PTR_FORMAT ").", r->index(), req.type_string(), p2i(&req)); } else { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp index 063c55ac9c3..66eaed2c222 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, 2020, Red Hat, Inc. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -32,7 +32,6 @@ #include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.hpp" -#include "gc/shenandoah/shenandoahHeapRegionSet.inline.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" #include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" @@ -40,14 +39,11 @@ #include "jfr/jfrEvents.hpp" #include "memory/allocation.hpp" #include "memory/iterator.inline.hpp" -#include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/oop.inline.hpp" #include "runtime/globals_extension.hpp" #include "runtime/java.hpp" -#include "runtime/mutexLocker.hpp" #include "runtime/os.hpp" -#include "runtime/safepoint.hpp" #include "utilities/powerOfTwo.hpp" size_t ShenandoahHeapRegion::RegionCount = 0; @@ -846,16 +842,7 @@ void ShenandoahHeapRegion::set_affiliation(ShenandoahAffiliation new_affiliation p2i(top()), p2i(ctx->top_at_mark_start(this)), p2i(_update_watermark.load_relaxed()), p2i(ctx->top_bitmap(this))); } -#ifdef ASSERT - { - size_t idx = this->index(); - HeapWord* top_bitmap = ctx->top_bitmap(this); - - assert(ctx->is_bitmap_range_within_region_clear(top_bitmap, _end), - "Region %zu, bitmap should be clear between top_bitmap: " PTR_FORMAT " and end: " PTR_FORMAT, idx, - p2i(top_bitmap), p2i(_end)); - } -#endif + shenandoah_assert_clear_above_top(this); if (region_affiliation == new_affiliation) { return; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp index 0bcdfa9fd2c..87629cefb0d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2018, 2026, Red Hat, Inc. All rights reserved. + * Copyright (c) 2018, 2021, Red Hat, Inc. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. - * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 @@ -91,6 +91,8 @@ void ShenandoahMarkingContext::clear_bitmap(ShenandoahHeapRegion* r) { if (top_bitmap > bottom) { _mark_bit_map.clear_range_large(MemRegion(bottom, top_bitmap)); + // All bitmap writes must complete before we update top at bitmap. If these writes were reordered, + // other threads could see stale marks above top, which is not valid. OrderAccess::storestore(); _top_bitmaps[r->index()] = bottom; } From be303d02a780adce8fc42835514045cdae09d07f Mon Sep 17 00:00:00 2001 From: Xiaolong Peng Date: Fri, 19 Jun 2026 23:20:39 +0000 Subject: [PATCH 018/707] 8385594: Shenandoah: Remove legacy allocation methods from ShenandoahFreeSet Reviewed-by: ruili, wkemper, kdnilsen --- .../share/gc/shenandoah/shenandoahFreeSet.cpp | 354 +----------------- .../share/gc/shenandoah/shenandoahFreeSet.hpp | 35 -- 2 files changed, 5 insertions(+), 384 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp index 24b3743281a..9db0f662705 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp @@ -247,10 +247,6 @@ size_t ShenandoahFreeSet::alloc_capacity(size_t idx) const { return alloc_capacity(r); } -bool ShenandoahFreeSet::has_alloc_capacity(ShenandoahHeapRegion *r) const { - return alloc_capacity(r) > 0; -} - // This is used for unit testing. Do not use in production code. void ShenandoahFreeSet::resize_old_collector_capacity(size_t regions) { shenandoah_assert_heaplocked(); @@ -413,11 +409,9 @@ ShenandoahHeapRegion* ShenandoahFreeSet::find_region_for_alloc(size_t min_size_w if constexpr (PARTITION == ShenandoahFreeSetPartitionId::OldCollector) { result->end_preemptible_coalesce_and_fill(); } -#ifdef ASSERT - ShenandoahMarkingContext* const ctx = _heap->marking_context(); - assert(ctx->top_at_mark_start(result) == result->bottom(), "TAMS must equal bottom for new region"); - assert(ctx->is_bitmap_range_within_region_clear(ctx->top_bitmap(result), result->end()), "Bitmap must be clear"); -#endif + assert(_heap->marking_context()->top_at_mark_start(result) == result->bottom(), + "Newly established allocation region (%zu) must start with TAMS equal to bottom", result->index()); + shenandoah_assert_clear_above_top(result); } return result; } @@ -1481,81 +1475,6 @@ void ShenandoahFreeSet::add_promoted_in_place_region_to_old_collector(Shenandoah _partitions.assert_bounds(); } -template -HeapWord* ShenandoahFreeSet::allocate_with_affiliation(Iter& iterator, - ShenandoahAffiliation affiliation, - ShenandoahAllocRequest& req, - bool& in_new_region) { - assert(affiliation != ShenandoahAffiliation::FREE, "Must not"); - ShenandoahHeapRegion* free_region = nullptr; - for (idx_t idx = iterator.current(); iterator.has_next(); idx = iterator.next()) { - ShenandoahHeapRegion* r = _heap->get_region(idx); - if (r->affiliation() == affiliation) { - HeapWord* result = try_allocate_in(r, req, in_new_region); - if (result != nullptr) { - return result; - } - } else if (free_region == nullptr && r->affiliation() == FREE) { - free_region = r; - } - } - // Failed to allocate within any affiliated region, try the first free region in the partition. - if (free_region != nullptr) { - HeapWord* result = try_allocate_in(free_region, req, in_new_region); - assert(result != nullptr, "Allocate in free region in the partition always succeed."); - return result; - } - log_debug(gc, free)("Could not allocate collector region with affiliation: %s for request " PTR_FORMAT, - shenandoah_affiliation_name(affiliation), p2i(&req)); - return nullptr; -} - -HeapWord* ShenandoahFreeSet::allocate_single(ShenandoahAllocRequest& req, bool& in_new_region) { - shenandoah_assert_heaplocked(); - - // Scan the bitmap looking for a first fit. - // - // Leftmost and rightmost bounds provide enough caching to walk bitmap efficiently. Normally, - // we would find the region to allocate at right away. - // - // Allocations are biased: GC allocations are taken from the high end of the heap. Regular (and TLAB) - // mutator allocations are taken from the middle of heap, below the memory reserved for Collector. - // Humongous mutator allocations are taken from the bottom of the heap. - // - // Free set maintains mutator and collector partitions. Normally, each allocates only from its partition, - // except in special cases when the collector steals regions from the mutator partition. - - // Overwrite with non-zero (non-null) values only if necessary for allocation bookkeeping. - - if (req.is_mutator_alloc()) { - return allocate_for_mutator(req, in_new_region); - } else { - return allocate_for_collector(req, in_new_region); - } -} - -HeapWord* ShenandoahFreeSet::allocate_for_mutator(ShenandoahAllocRequest &req, bool &in_new_region) { - update_allocation_bias(); - - if (_partitions.is_empty(ShenandoahFreeSetPartitionId::Mutator)) { - // There is no recovery. Mutator does not touch collector view at all. - return nullptr; - } - - // Try to allocate in the mutator view - if (_partitions.alloc_from_left_bias(ShenandoahFreeSetPartitionId::Mutator)) { - // Allocate from low to high memory. This keeps the range of fully empty regions more tightly packed. - // Note that the most recently allocated regions tend not to be evacuated in a given GC cycle. So this - // tends to accumulate "fragmented" uncollected regions in high memory. - ShenandoahLeftRightIterator iterator(&_partitions, ShenandoahFreeSetPartitionId::Mutator); - return allocate_from_regions(iterator, req, in_new_region); - } - - // Allocate from high to low memory. This preserves low memory for humongous allocations. - ShenandoahRightLeftIterator iterator(&_partitions, ShenandoahFreeSetPartitionId::Mutator); - return allocate_from_regions(iterator, req, in_new_region); -} - void ShenandoahFreeSet::update_allocation_bias() { if (_alloc_bias_weight-- <= 0) { // We have observed that regions not collected in previous GC cycle tend to congregate at one end or the other @@ -1582,242 +1501,6 @@ void ShenandoahFreeSet::update_allocation_bias() { } } -template -HeapWord* ShenandoahFreeSet::allocate_from_regions(Iter& iterator, ShenandoahAllocRequest &req, bool &in_new_region) { - for (idx_t idx = iterator.current(); iterator.has_next(); idx = iterator.next()) { - ShenandoahHeapRegion* r = _heap->get_region(idx); - size_t min_size = req.is_lab_alloc() ? req.min_size() : req.size(); - if (alloc_capacity(r) >= min_size * HeapWordSize) { - HeapWord* result = try_allocate_in(r, req, in_new_region); - if (result != nullptr) { - return result; - } - } - } - return nullptr; -} - -HeapWord* ShenandoahFreeSet::allocate_for_collector(ShenandoahAllocRequest &req, bool &in_new_region) { - shenandoah_assert_heaplocked(); - ShenandoahFreeSetPartitionId which_partition = req.is_old()? ShenandoahFreeSetPartitionId::OldCollector: ShenandoahFreeSetPartitionId::Collector; - HeapWord* result = nullptr; - if (_partitions.alloc_from_left_bias(which_partition)) { - ShenandoahLeftRightIterator iterator(&_partitions, which_partition); - result = allocate_with_affiliation(iterator, req.affiliation(), req, in_new_region); - } else { - ShenandoahRightLeftIterator iterator(&_partitions, which_partition); - result = allocate_with_affiliation(iterator, req.affiliation(), req, in_new_region); - } - - if (result != nullptr) { - return result; - } - - // No dice. Can we borrow space from mutator view? - if (!ShenandoahEvacReserveOverflow) { - return nullptr; - } - - if (_partitions.get_empty_region_counts(ShenandoahFreeSetPartitionId::Mutator) > 0) { - // Try to steal an empty region from the mutator view. - result = try_allocate_from_mutator(req, in_new_region); - } - - // This is it. Do not try to mix mutator and GC allocations, because adjusting region UWM - // due to GC allocations would expose unparsable mutator allocations. - return result; -} - -HeapWord* ShenandoahFreeSet::try_allocate_from_mutator(ShenandoahAllocRequest& req, bool& in_new_region) { - // The collector prefers to keep longer lived regions toward the right side of the heap, so it always - // searches for regions from right to left here. - ShenandoahRightLeftIterator iterator(&_partitions, ShenandoahFreeSetPartitionId::Mutator, true); - for (idx_t idx = iterator.current(); iterator.has_next(); idx = iterator.next()) { - ShenandoahHeapRegion* r = _heap->get_region(idx); - if (can_allocate_from(r)) { - if (req.is_old()) { - if (!flip_to_old_gc(r)) { - continue; - } - } else { - flip_to_gc(r); - } - // Region r is entirely empty. If try_allocate_in fails on region r, something else is really wrong. - // Don't bother to retry with other regions. - log_debug(gc, free)("Flipped region %zu to gc for request: " PTR_FORMAT, idx, p2i(&req)); - return try_allocate_in(r, req, in_new_region); - } - } - - return nullptr; -} - - -HeapWord* ShenandoahFreeSet::try_allocate_in(ShenandoahHeapRegion* r, ShenandoahAllocRequest& req, bool& in_new_region) { - assert (has_alloc_capacity(r), "Performance: should avoid full regions on this path: %zu", r->index()); - if (_heap->is_concurrent_weak_root_in_progress() && r->is_trash()) { - // We cannot use this region for allocation when weak roots are in progress because the collector may need - // to reference unmarked oops during concurrent classunloading. The collector also needs accurate marking - // information to determine which weak handles need to be null'd out. If the region is recycled before weak - // roots processing has finished, weak root processing may fail to null out a handle into a trashed region. - // This turns the handle into a dangling pointer and will crash or corrupt the heap. - return nullptr; - } - HeapWord* result = nullptr; - // We must call try_recycle_under_lock() even if !r->is_trash(). The reason is that if r is being recycled at this - // moment by a GC worker thread, it may appear to be not trash even though it has not yet been fully recycled. If - // we proceed without waiting for the worker to finish recycling the region, the worker thread may overwrite the - // region's affiliation with FREE after we set the region's affiliation to req.affiliation() below - r->try_recycle_under_lock(); - in_new_region = r->is_empty(); - if (in_new_region) { - log_debug(gc, free)("Using new region (%zu) for %s (" PTR_FORMAT ").", - r->index(), req.type_string(), p2i(&req)); - assert(!r->is_affiliated(), "New region %zu should be unaffiliated", r->index()); - r->set_affiliation(req.affiliation()); - if (r->is_old()) { - // Any OLD region allocated during concurrent coalesce-and-fill does not need to be coalesced and filled because - // all objects allocated within this region are above TAMS (and thus are implicitly marked). In case this is an - // OLD region and concurrent preparation for mixed evacuations visits this region before the start of the next - // old-gen concurrent mark (i.e. this region is allocated following the start of old-gen concurrent mark but before - // concurrent preparations for mixed evacuations are completed), we mark this region as not requiring any - // coalesce-and-fill processing. - r->end_preemptible_coalesce_and_fill(); - } - - assert(_heap->marking_context()->top_at_mark_start(r) == r->bottom(), - "Newly established allocation region (%zu) must start with TAMS equal to bottom", r->index()); - shenandoah_assert_clear_above_top(r); - log_debug(gc, free)("Using new region (%zu) for %s (" PTR_FORMAT ").", - r->index(), req.type_string(), p2i(&req)); - } else { - assert(r->is_affiliated(), "Region %zu that is not new should be affiliated", r->index()); - if (r->affiliation() != req.affiliation()) { - assert(_heap->mode()->is_generational(), "Request for %s from %s region should only happen in generational mode.", - req.affiliation_name(), r->affiliation_name()); - return nullptr; - } - } - - // req.size() is in words, r->free() is in bytes. - if (req.is_lab_alloc()) { - size_t adjusted_size = req.size(); - size_t free = align_down(r->free() >> LogHeapWordSize, MinObjAlignment); - if (adjusted_size > free) { - adjusted_size = free; - } - if (adjusted_size >= req.min_size()) { - result = r->allocate(adjusted_size, req); - assert (result != nullptr, "Allocation must succeed: free %zu, actual %zu", free, adjusted_size); - req.set_actual_size(adjusted_size); - } else { - log_trace(gc, free)("Failed to shrink LAB request (%zu) in region %zu to %zu" - " because min_size() is %zu", req.size(), r->index(), adjusted_size, req.min_size()); - } - } else { - size_t size = req.size(); - result = r->allocate(size, req); - if (result != nullptr) { - // Record actual allocation size - req.set_actual_size(size); - } - } - - if (result != nullptr) { - // Allocation successful, bump stats: - if (req.is_mutator_alloc()) { - assert(req.is_young(), "Mutator allocations always come from young generation."); - _partitions.increase_used(ShenandoahFreeSetPartitionId::Mutator, req.actual_size() * HeapWordSize); - } else { - assert(req.is_gc_alloc(), "Should be gc_alloc since req wasn't mutator alloc"); - - // For GC allocations, we advance update_watermark because the objects relocated into this memory during - // evacuation are not updated during evacuation. For both young and old regions r, it is essential that all - // PLABs be made parsable at the end of evacuation. This is enabled by retiring all plabs at end of evacuation. - r->set_update_watermark(r->top()); - if (r->is_old()) { - _partitions.increase_used(ShenandoahFreeSetPartitionId::OldCollector, (req.actual_size() + req.waste()) * HeapWordSize); - } else { - _partitions.increase_used(ShenandoahFreeSetPartitionId::Collector, (req.actual_size() + req.waste()) * HeapWordSize); - } - } - } - - ShenandoahFreeSetPartitionId orig_partition; - if (req.is_mutator_alloc()) { - orig_partition = ShenandoahFreeSetPartitionId::Mutator; - } else if (req.is_old()) { - orig_partition = ShenandoahFreeSetPartitionId::OldCollector; - } else { - // Not old collector alloc, so this is a young collector gclab or shared allocation - orig_partition = ShenandoahFreeSetPartitionId::Collector; - } - DEBUG_ONLY(bool boundary_changed = false;) - if ((result != nullptr) && in_new_region) { - _partitions.one_region_is_no_longer_empty(orig_partition); - DEBUG_ONLY(boundary_changed = true;) - } - - if (alloc_capacity(r) < PLAB::min_size() * HeapWordSize) { - // Regardless of whether this allocation succeeded, if the remaining memory is less than PLAB:min_size(), retire this region. - // Note that retire_from_partition() increases used to account for waste. - - size_t idx = r->index(); - size_t waste_bytes = _partitions.retire_from_partition(orig_partition, idx, r->used()); - DEBUG_ONLY(boundary_changed = true;) - if (req.is_mutator_alloc() && (waste_bytes > 0)) { - req.set_waste(waste_bytes / HeapWordSize); - } - } - - switch (orig_partition) { - case ShenandoahFreeSetPartitionId::Mutator: - recompute_total_used(); - if (in_new_region) { - recompute_total_affiliated(); - } - break; - case ShenandoahFreeSetPartitionId::Collector: - recompute_total_used(); - if (in_new_region) { - recompute_total_affiliated(); - } - break; - case ShenandoahFreeSetPartitionId::OldCollector: - recompute_total_used(); - if (in_new_region) { - recompute_total_affiliated(); - } - break; - case ShenandoahFreeSetPartitionId::NotFree: - default: - assert(false, "won't happen"); - } -#ifdef ASSERT - if (boundary_changed) { - _partitions.assert_bounds(); - } else { - _partitions.assert_bounds_sanity(); - } -#endif - return result; -} - HeapWord* ShenandoahFreeSet::allocate_contiguous(ShenandoahAllocRequest& req, bool is_humongous) { assert(req.is_mutator_alloc(), "All contiguous allocations are performed by mutator"); shenandoah_assert_heaplocked(); @@ -2088,8 +1771,8 @@ void ShenandoahFreeSet::flip_to_gc(ShenandoahHeapRegion* r) { /* AffiliatedChangesAreYoungNeutral */ true, /* AffiliatedChangesAreGlobalNeutral */ true, /* UnaffiliatedChangesAreYoungNeutral */ true>(); _partitions.assert_bounds(); - // We do not ensure that the region is no longer trash, relying on try_allocate_in(), which always comes next, - // to recycle trash before attempting to allocate anything in the region. + // We do not ensure that the region is no longer trash, relying on the caller, which always recycles + // trash before attempting to allocate anything in the region. } void ShenandoahFreeSet::clear() { @@ -3273,33 +2956,6 @@ void ShenandoahFreeSet::decrease_humongous_waste_for_regular_bypass(ShenandoahHe _total_humongous_waste -= waste; } - -HeapWord* ShenandoahFreeSet::allocate(ShenandoahAllocRequest& req, bool& in_new_region) { - shenandoah_assert_heaplocked(); - if (ShenandoahHeapRegion::requires_humongous(req.size())) { - switch (req.type()) { - case ShenandoahAllocRequest::_alloc_shared: - case ShenandoahAllocRequest::_alloc_shared_gc: - in_new_region = true; - return allocate_contiguous(req, /* is_humongous = */ true); - case ShenandoahAllocRequest::_alloc_cds: - in_new_region = true; - return allocate_contiguous(req, /* is_humongous = */ false); - case ShenandoahAllocRequest::_alloc_plab: - case ShenandoahAllocRequest::_alloc_gclab: - case ShenandoahAllocRequest::_alloc_tlab: - in_new_region = false; - assert(false, "Trying to allocate TLAB in humongous region: %zu", req.size()); - return nullptr; - default: - ShouldNotReachHere(); - return nullptr; - } - } else { - return allocate_single(req, in_new_region); - } -} - void ShenandoahFreeSet::print_on(outputStream* out) const { out->print_cr("Mutator Free Set: %zu", _partitions.count(ShenandoahFreeSetPartitionId::Mutator)); ShenandoahLeftRightIterator mutator(const_cast(&_partitions), ShenandoahFreeSetPartitionId::Mutator); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp index 4d3be955ddc..4ee9933794d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp @@ -561,16 +561,6 @@ using idx_t = ShenandoahSimpleBitMap::idx_t; #endif } - // Increases used memory for the partition if the allocation is successful. `in_new_region` will be set - // if this is the first allocation in the region. - HeapWord* try_allocate_in(ShenandoahHeapRegion* region, ShenandoahAllocRequest& req, bool& in_new_region); - - // While holding the heap lock, allocate memory for a single object or LAB which is to be entirely contained - // within a single HeapRegion as characterized by req. - // - // Precondition: !ShenandoahHeapRegion::requires_humongous(req.size()) - HeapWord* allocate_single(ShenandoahAllocRequest& req, bool& in_new_region); - bool transfer_one_region_from_mutator_to_old_collector(size_t idx, size_t alloc_capacity); // Change region r from the Mutator partition to the GC's Collector or OldCollector partition. This requires that the @@ -585,30 +575,9 @@ using idx_t = ShenandoahSimpleBitMap::idx_t; // Return true if and only if the given region is successfully flipped to the old partition bool flip_to_old_gc(ShenandoahHeapRegion* r); - // Handle allocation for mutator. - HeapWord* allocate_for_mutator(ShenandoahAllocRequest &req, bool &in_new_region); - // Update allocation bias and decided whether to allocate from the left or right side of the heap. void update_allocation_bias(); - // Search for regions to satisfy allocation request using iterator. - template - HeapWord* allocate_from_regions(Iter& iterator, ShenandoahAllocRequest &req, bool &in_new_region); - - // Handle allocation for collector (for evacuation). - HeapWord* allocate_for_collector(ShenandoahAllocRequest& req, bool& in_new_region); - - // Search for allocation in region with same affiliation as request, using given iterator, - // or affiliate the first usable FREE region with given affiliation and allocate in. - template - HeapWord* allocate_with_affiliation(Iter& iterator, - ShenandoahAffiliation affiliation, - ShenandoahAllocRequest& req, - bool& in_new_region); - - // Attempt to allocate memory for an evacuation from the mutator's partition. - HeapWord* try_allocate_from_mutator(ShenandoahAllocRequest& req, bool& in_new_region); - void clear_internal(); // Returns true iff this region is entirely available, either because it is empty() or because it has been found to represent @@ -617,8 +586,6 @@ using idx_t = ShenandoahSimpleBitMap::idx_t; bool can_allocate_from(ShenandoahHeapRegion *r) const; bool can_allocate_from(size_t idx) const; - bool has_alloc_capacity(ShenandoahHeapRegion *r) const; - void transfer_empty_regions_from_to(ShenandoahFreeSetPartitionId source_partition, ShenandoahFreeSetPartitionId dest_partition, size_t num_regions); @@ -854,8 +821,6 @@ using idx_t = ShenandoahSimpleBitMap::idx_t; void decrease_humongous_waste_for_regular_bypass(ShenandoahHeapRegion* r, size_t waste); - HeapWord* allocate(ShenandoahAllocRequest& req, bool& in_new_region); - /* * Internal fragmentation metric: describes how fragmented the heap regions are. * From bab561b0c5079979cf1c364bc46dcadb3b28b4b5 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Sat, 20 Jun 2026 16:46:50 +0000 Subject: [PATCH 019/707] 8379555: Test compiler/igvn/ExpressionFuzzer.java crashed with -Xcomp: Not monotonic Reviewed-by: kvn, epeter, hgreule --- src/hotspot/share/opto/intrinsicnode.cpp | 241 +++++------------- src/hotspot/share/opto/rangeinference.hpp | 129 +++++++++- .../gtest/opto/test_rangeinference.cpp | 29 ++- .../ccp/TestCompressBitsMonotonicity.java | 60 +++++ 4 files changed, 262 insertions(+), 197 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/ccp/TestCompressBitsMonotonicity.java diff --git a/src/hotspot/share/opto/intrinsicnode.cpp b/src/hotspot/share/opto/intrinsicnode.cpp index 16ba829728b..887681233f1 100644 --- a/src/hotspot/share/opto/intrinsicnode.cpp +++ b/src/hotspot/share/opto/intrinsicnode.cpp @@ -22,14 +22,13 @@ * */ -#include "opto/addnode.hpp" #include "opto/intrinsicnode.hpp" #include "opto/memnode.hpp" #include "opto/mulnode.hpp" +#include "opto/opcodes.hpp" #include "opto/phaseX.hpp" -#include "utilities/count_leading_zeros.hpp" +#include "opto/rangeinference.hpp" #include "utilities/globalDefinitions.hpp" -#include "utilities/population_count.hpp" //============================================================================= // Do not match memory edge. @@ -231,171 +230,68 @@ Node* ExpandBitsNode::Identity(PhaseGVN* phase) { return compress_expand_identity(phase, this); } -static const Type* bitshuffle_value(const TypeInteger* src_type, const TypeInteger* mask_type, int opc, BasicType bt) { - +// Bit expansion is a reverse process of bit compression. It sequentially reads source bits +// starting from LSB and places them at bit positions in result value where corresponding mask bits +// are 1. Thus, bit expansion for non-negative mask value will always generate a +ve value, this is +// because sign bit of result will never be set to 1 as corresponding mask bit is always 0. +static const Type* expand_bits_value(const TypeInteger* mask_type, BasicType bt) { + assert(bt == T_INT || bt == T_LONG, "unexpected BasicType %s", type2name(bt)); jlong hi = bt == T_INT ? max_jint : max_jlong; jlong lo = bt == T_INT ? min_jint : min_jlong; - assert(bt == T_INT || bt == T_LONG, ""); - - // Rule 1: Bit compression selects the source bits corresponding to true mask bits, - // packs them and places them contiguously at destination bit positions - // starting from least significant bit, remaining higher order bits are set - // to zero. - // Rule 2: Bit expansion is a reverse process, which sequentially reads source bits - // starting from LSB and places them at bit positions in result value where - // corresponding mask bits are 1. Thus, bit expansion for non-negative mask - // value will always generate a +ve value, this is because sign bit of result - // will never be set to 1 as corresponding mask bit is always 0. - - // Case A) Constant mask if (mask_type->is_con()) { + // Case A) Constant mask jlong maskcon = mask_type->get_con_as_long(bt); - if (opc == Op_CompressBits) { - // Case A.1 bit compression:- - // For an outlier mask value of -1 upper bound of the result equals - // maximum integral value, for any other mask value its computed using - // following formula - // Result.Hi = 1 << popcount(mask_bits) - 1 - // - // For mask values other than -1, lower bound of the result is estimated - // as zero, by assuming at least one mask bit is zero and corresponding source - // bit will be masked, hence result of bit compression will always be - // non-negative value. For outlier mask value of -1, assume all source bits - // apart from most significant bit were set to 0, thereby resulting in - // a minimum integral value. - // e.g. - // src = 0xXXXXXXXX (non-constant source) - // mask = 0xEFFFFFFF (constant mask) - // result.hi = 0x7FFFFFFF - // result.lo = 0 - if (maskcon != -1L) { - int bitcount = population_count(static_cast(bt == T_INT ? maskcon & 0xFFFFFFFFL : maskcon)); - hi = right_n_bits(bitcount); - lo = 0L; - } else { - // preserve originally assigned hi (MAX_INT/LONG) and lo (MIN_INT/LONG) values - // for unknown source bits. - assert(hi == (bt == T_INT ? max_jint : max_jlong), ""); - assert(lo == (bt == T_INT ? min_jint : min_jlong), ""); - } + if (maskcon >= 0L) { + // Case A.2.1 constant mask >= 0 + // Result.Hi = mask, optimistically assuming all source bits + // read starting from least significant bit positions are 1. + // Result.Lo = 0, because at least one bit in mask is zero. + // e.g. + // src = 0xXXXXXXXX (non-constant source) + // mask = 0x7FFFFFFF (constant mask >= 0) + // result.hi = 0x7FFFFFFF + // result.lo = 0 + hi = maskcon; + lo = 0L; } else { - // Case A.2 bit expansion:- - assert(opc == Op_ExpandBits, ""); - if (maskcon >= 0L) { - // Case A.2.1 constant mask >= 0 - // Result.Hi = mask, optimistically assuming all source bits - // read starting from least significant bit positions are 1. - // Result.Lo = 0, because at least one bit in mask is zero. - // e.g. - // src = 0xXXXXXXXX (non-constant source) - // mask = 0x7FFFFFFF (constant mask >= 0) - // result.hi = 0x7FFFFFFF - // result.lo = 0 - hi = maskcon; - lo = 0L; - } else { - // Case A.2.2) mask < 0 - // For constant mask strictly less than zero, the maximum result value will be - // the same as the mask value with its sign bit flipped, assuming all source bits - // except the MSB bit are set(one). - // - // To compute minimum result value we assume all but last read source bit as zero, - // this is because sign bit of result will always be set to 1 while other bit - // corresponding to set mask bit should be zero. - // e.g. - // src = 0xXXXXXXXX (non-constant source) - // mask = 0xEFFFFFFF (constant mask) - // result.hi = 0xEFFFFFFF ^ 0x80000000 = 0x6FFFFFFF - // result.lo = 0x80000000 - // - hi = maskcon ^ lo; - // lo still retains MIN_INT/LONG. - assert(lo == (bt == T_INT ? min_jint : min_jlong), ""); - } + // Case A.2.2) mask < 0 + // For constant mask strictly less than zero, the maximum result value will be + // the same as the mask value with its sign bit flipped, assuming all source bits + // except the MSB bit are set(one). + // + // To compute minimum result value we assume all but last read source bit as zero, + // this is because sign bit of result will always be set to 1 while other bit + // corresponding to set mask bit should be zero. + // e.g. + // src = 0xXXXXXXXX (non-constant source) + // mask = 0xEFFFFFFF (constant mask) + // result.hi = 0xEFFFFFFF ^ 0x80000000 = 0x6FFFFFFF + // result.lo = 0x80000000 + // + hi = maskcon ^ lo; + // lo still retains MIN_INT/LONG. + assert(lo == (bt == T_INT ? min_jint : min_jlong), ""); } - } - - // Case B) Non-constant mask. - if (!mask_type->is_con()) { - if ( opc == Op_CompressBits) { - int result_bit_width; - int mask_bit_width = bt == T_INT ? 32 : 64; - if ((mask_type->lo_as_long() < 0L && mask_type->hi_as_long() >= -1L)) { - // Case B.1 The mask value range includes -1, hence we may use all bits, - // the result has the whole value range. - result_bit_width = mask_bit_width; - } else if (mask_type->hi_as_long() < -1L) { - // Case B.2 Mask value range is strictly less than -1, this indicates presence of at least - // one unset(zero) bit in mask value, thus as per Rule 1, bit compression will always - // result in a non-negative value. This guarantees that MSB bit of result value will - // always be set to zero. - result_bit_width = mask_bit_width - 1; - } else { - assert(mask_type->lo_as_long() >= 0, ""); - // Case B.3 Mask value range only includes non-negative values. Since all integral - // types honours an invariant that TypeInteger._lo <= TypeInteger._hi, thus computing - // leading zero bits of upper bound of mask value will allow us to ascertain - // optimistic upper bound of result i.e. all the bits other than leading zero bits - // can be assumed holding 1 value. - jlong clz = count_leading_zeros(mask_type->hi_as_long()); - // Here, result of clz is w.r.t to long argument, hence for integer argument - // we explicitly subtract 32 from the result. - clz = bt == T_INT ? clz - 32 : clz; - result_bit_width = mask_bit_width - clz; - } - // If the number of bits required to for the mask value range is less than the - // full bit width of the integral type, then the MSB bit is guaranteed to be zero, - // thus the compression result will never be a -ve value and we can safely set the - // lower bound of the bit compression to zero. - lo = result_bit_width == mask_bit_width ? lo : 0L; - - assert(hi == (bt == T_INT ? max_jint : max_jlong), ""); - assert(lo == (bt == T_INT ? min_jint : min_jlong) || lo == 0, ""); - - if (src_type->lo_as_long() >= 0) { - // Lemma 1: For strictly non-negative src, the result of the compression will never be - // greater than src. - // Proof: Since src is a non-negative value, its most significant bit is always 0. - // Thus even if the corresponding MSB of the mask is one, the result will be a +ve - // value. There are three possible cases - // a. All the mask bits corresponding to set source bits are unset(zero). - // b. All the mask bits corresponding to set source bits are set(one) - // c. Some mask bits corresponding to set source bits are set(one) while others are unset(zero) - // - // Case a. results into an allzero result, while Case b. gives us the upper bound which is equals source - // value, while for Case c. the result will lie within [0, src] - // - hi = src_type->hi_as_long(); - lo = 0L; - } - - if (result_bit_width < mask_bit_width) { - // Rule 3: - // We can further constrain the upper bound of bit compression if the number of bits - // which can be set(one) is less than the maximum number of bits of integral type. - hi = MIN2(right_n_bits(result_bit_width), hi); - } + } else { + // Case B) Non-constant mask. + jlong max_mask = mask_type->hi_as_long(); + jlong min_mask = mask_type->lo_as_long(); + // Since mask here a range and not a constant value, hence being + // conservative in determining the value range of result. + if (min_mask >= 0L) { + // Lemma 2: Based on the integral type invariant ie. TypeInteger.lo <= TypeInteger.hi, + // if the lower bound of non-constant mask is a non-negative value then result can never + // be greater than the mask. + // Proof: Since lower bound of the mask is a non-negative value, hence most significant + // bit of its entire value must be unset(zero). If all the lower order 'n' source bits + // where n corresponds to popcount of mask are set(ones) then upper bound of the result equals + // mask. In order to compute the lower bound, we pssimistically assume all the lower order 'n' + // source bits are unset(zero) there by resuling into a zero value. + hi = max_mask; + lo = 0; } else { - assert(opc == Op_ExpandBits, ""); - jlong max_mask = mask_type->hi_as_long(); - jlong min_mask = mask_type->lo_as_long(); - // Since mask here a range and not a constant value, hence being - // conservative in determining the value range of result. - if (min_mask >= 0L) { - // Lemma 2: Based on the integral type invariant ie. TypeInteger.lo <= TypeInteger.hi, - // if the lower bound of non-constant mask is a non-negative value then result can never - // be greater than the mask. - // Proof: Since lower bound of the mask is a non-negative value, hence most significant - // bit of its entire value must be unset(zero). If all the lower order 'n' source bits - // where n corresponds to popcount of mask are set(ones) then upper bound of the result equals - // mask. In order to compute the lower bound, we pssimistically assume all the lower order 'n' - // source bits are unset(zero) there by resuling into a zero value. - hi = max_mask; - lo = 0; - } else { - // preserve the lo and hi bounds estimated till now. - } + // preserve the lo and hi bounds estimated till now. } } @@ -423,25 +319,12 @@ const Type* CompressBitsNode::Value(PhaseGVN* phase) const { } BasicType bt = bottom_type()->basic_type(); - const TypeInteger* src_type = t1->is_integer(bt); - const TypeInteger* mask_type = t2->is_integer(bt); - int w = bt == T_INT ? 32 : 64; - - // Constant fold if both src and mask are constants. - if (src_type->is_con() && mask_type->is_con()) { - jlong src = src_type->get_con_as_long(bt); - jlong mask = mask_type->get_con_as_long(bt); - jlong res = compress_bits(src, mask, w); - return bt == T_INT ? static_cast(TypeInt::make(res)) : - static_cast(TypeLong::make(res)); - } - - // Result is zero if src is zero irrespective of mask value. - if (src_type == TypeInteger::zero(bt)) { - return TypeInteger::zero(bt); + if (bt == T_INT) { + return RangeInference::infer_compress_bits(t1->is_int(), t2->is_int()); + } else { + assert(bt == T_LONG, "unexpected BasicType %s", type2name(bt)); + return RangeInference::infer_compress_bits(t1->is_long(), t2->is_long()); } - - return bitshuffle_value(src_type, mask_type, Op_CompressBits, bt); } jlong ExpandBitsNode::expand_bits(jlong src, jlong mask, int bit_count) { @@ -482,5 +365,5 @@ const Type* ExpandBitsNode::Value(PhaseGVN* phase) const { return TypeInteger::zero(bt); } - return bitshuffle_value(src_type, mask_type, Op_ExpandBits, bt); + return expand_bits_value(mask_type, bt); } diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index 7c0f12f6ef7..e5e34051587 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -222,12 +222,16 @@ class TypeIntMirror { return TypeIntHelper::int_type_union(this, &o); } + bool contains(U u) const { + S s = S(u); + return s >= _lo && s <= _hi && u >= _ulo && u <= _uhi && _bits.is_satisfied_by(u); + } + // These allow TypeIntMirror to mimick the behaviors of TypeInt* and TypeLong*, so they can be // passed into RangeInference methods. These are only used in testing, so they are implemented in // the test file. static TypeIntMirror make(const TypeIntMirror& t, int widen); const TypeIntMirror* operator->() const; - bool contains(U u) const; bool contains(const TypeIntMirror& o) const; bool operator==(const TypeIntMirror& o) const; @@ -365,20 +369,23 @@ class RangeInference { return CT::make(res, MAX2(t1->_widen, t2->_widen)); } + template + static TypeIntMirror, U> infer_and_impl(const TypeIntMirror, U>& st1, const TypeIntMirror, U>& st2) { + S lo = std::numeric_limits>::min(); + S hi = std::numeric_limits>::max(); + U ulo = std::numeric_limits>::min(); + // The unsigned value of the result of 'and' is always not greater than both of its inputs + // since there is no position at which the bit is 1 in the result and 0 in either input + U uhi = MIN2(st1._uhi, st2._uhi); + U zeros = st1._bits._zeros | st2._bits._zeros; + U ones = st1._bits._ones & st2._bits._ones; + return TypeIntMirror, U>::make(TypeIntPrototype, U>{{lo, hi}, {ulo, uhi}, {zeros, ones}}); + } + public: template static CTP infer_and(CTP t1, CTP t2) { - return infer_binary(t1, t2, [&](const TypeIntMirror, U>& st1, const TypeIntMirror, U>& st2) { - S lo = std::numeric_limits>::min(); - S hi = std::numeric_limits>::max(); - U ulo = std::numeric_limits>::min(); - // The unsigned value of the result of 'and' is always not greater than both of its inputs - // since there is no position at which the bit is 1 in the result and 0 in either input - U uhi = MIN2(st1._uhi, st2._uhi); - U zeros = st1._bits._zeros | st2._bits._zeros; - U ones = st1._bits._ones & st2._bits._ones; - return TypeIntMirror, U>::make(TypeIntPrototype, U>{{lo, hi}, {ulo, uhi}, {zeros, ones}}); - }); + return infer_binary(t1, t2, infer_and_impl); } template @@ -442,6 +449,104 @@ class RangeInference { TypeIntPrototype, U> proto{{slo, shi}, {ulo, uhi}, known_bits}; return CT::make(proto, t1->_widen); } + + // Bit compression selects the source bits corresponding to true mask bits, packs them and places + // them contiguously at destination bit positions starting from least significant bit, remaining + // higher order bits are set to zero. + template + static CTP infer_compress_bits(CTP t1, CTP t2) { + return infer_binary(t1, t2, [](const TypeIntMirror, U>& st1, const TypeIntMirror, U>& st2) { + S lo = std::numeric_limits>::min(); + const S hi = std::numeric_limits>::max(); + const U ulo = U(0); + // Integer.compress(v, mask) == Integer.compress(v & mask, mask) + // Integer.compress(v, mask) u<= v + // So, Integer.compress(v, mask) u<= (v & mask) + const U uhi = infer_and_impl(st1, st2)._uhi; + // If the mask has at least 1 unset bit, then the result must have its highest bit unset, and + // since the only value with no unset bit is the maximum unsigned value, if st2 does not + // contain that value, the result must be non-negative + if (!st2.contains(std::numeric_limits>::max())) { + lo = S(0); + } + + U zeros = U(0); + U ones = U(0); + // Firstly, try to collect known bits by traversing from the lowest to the highest bits, we + // can collect bits up to the first position at which the corresponding bit in the second + // operand is unknown. + // For example, consider Integer.compress(v, mask), with: + // v = 0bxyztuv + // mask = 0b*1*110 + // we can walk the lowest 3 bits of the operands, and determine that the result must be + // 0b****tu + { + // The bit index in result that will be taken from the current bit in the first operand, + // can only be known if we have not encountered any unknown bit in the second operand + int res_bit_idx = 0; + for (int op_bit_idx = 0; op_bit_idx < HotSpotNumerics::type_width>(); op_bit_idx++) { + // If the bit is 0 in the second operand, the corresponding bit value in the first + // operand is irrelevant + U op_bit_mask = U(1) << op_bit_idx; + if ((st2._bits._zeros & op_bit_mask) != U(0)) { + continue; + } + + // No further analysis is possible + if ((st2._bits._ones & op_bit_mask) == U(0)) { + break; + } + + // The bit of the second operand at op_bit_idx must be 1 + U res_bit_mask = U(1) << res_bit_idx; + if ((st1._bits._zeros & op_bit_mask) != U(0)) { + zeros |= res_bit_mask; + } else if ((st1._bits._ones & op_bit_mask) != U(0)) { + ones |= res_bit_mask; + } + res_bit_idx++; + } + } + + // Secondly, try to infer the number of leading zeros by traversing from the highest to the + // lowest bits. Integer.compress(v, mask) == Integer.compress(v & mask, mask), so the number + // of leading zeros in the result is not less than the number of leading zeros in (v & mask). + // Furthermore, in the remaining bits, for each bit in the second operand that must be 0, an + // addition leading zero in result is guaranteed. + // For example, consider Integer.compress(v, mask), with: + // v = 0b*01*** + // mask = 0b0x1*0* + // v & mask = 0b001*0* + // So the result must have at least 2 leading zeros. Furthermore, we can see that it is + // irrelevant whether the bit x in mask is 0 or 1, because the bit in result corresponding to + // x must be 0, and the result must have no higher set bit in either case. As a result, we + // can assume mask = 0b001*0*. And since mask has at least 3 unset bits, the result must have + // at least 3 leading zeros. + { + // The bit index in result that is determined to be 0 + int res_bit_idx = HotSpotNumerics::type_width>() - 1; + // Whether we have encountered a bit that is not known 0 in either the first or the second + // operand + bool leading_zeros = true; + for (int op_bit_idx = HotSpotNumerics::type_width>() - 1; op_bit_idx >= 0; op_bit_idx--) { + U op_bit_mask = U(1) << op_bit_idx; + if ((st2._bits._zeros & op_bit_mask) != U(0)) { + zeros |= (U(1) << res_bit_idx); + res_bit_idx--; + } else if (leading_zeros) { + if ((st1._bits._zeros & op_bit_mask) != U(0)) { + zeros |= (U(1) << res_bit_idx); + res_bit_idx--; + } else { + leading_zeros = false; + } + } + } + } + + return TypeIntMirror, U>::make(TypeIntPrototype, U>{{lo, hi}, {ulo, uhi}, {zeros, ones}}); + }); + } }; #endif // SHARE_OPTO_RANGEINFERENCE_HPP diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp index 641edaba4da..6f1dedf6923 100644 --- a/test/hotspot/gtest/opto/test_rangeinference.cpp +++ b/test/hotspot/gtest/opto/test_rangeinference.cpp @@ -22,6 +22,7 @@ * */ +#include "opto/intrinsicnode.hpp" #include "opto/rangeinference.hpp" #include "opto/type.hpp" #include "runtime/os.hpp" @@ -225,12 +226,6 @@ const TypeIntMirror* TypeIntMirror::operator->() const { return this; } -template -bool TypeIntMirror::contains(U u) const { - S s = S(u); - return s >= _lo && s <= _hi && u >= _ulo && u <= _uhi && _bits.is_satisfied_by(u); -} - template bool TypeIntMirror::contains(const TypeIntMirror& o) const { return TypeIntHelper::int_type_is_subset(*this, o); @@ -745,9 +740,31 @@ class InferXor { } }; +template +class OpCompressBits { +public: + U operator()(U v1, U v2) const { + constexpr int W = HotSpotNumerics::type_width(); + if constexpr (W == 64) { + return CompressBitsNode::compress_bits(v1, v2, W); + } else { + return U(uint(CompressBitsNode::compress_bits(uint(v1), uint(v2), W))); + } + } +}; + +template +class InferCompressBits { +public: + CTP operator()(CTP t1, CTP t2) const { + return RangeInference::infer_compress_bits(t1, t2); + } +}; + TEST(opto, range_inference) { test_binary(); test_binary(); test_binary(); + test_binary(); test_lshift(); } diff --git a/test/hotspot/jtreg/compiler/ccp/TestCompressBitsMonotonicity.java b/test/hotspot/jtreg/compiler/ccp/TestCompressBitsMonotonicity.java new file mode 100644 index 00000000000..732ba6f3502 --- /dev/null +++ b/test/hotspot/jtreg/compiler/ccp/TestCompressBitsMonotonicity.java @@ -0,0 +1,60 @@ +/* + * 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.ccp; + +/* + * @test + * @bug 8379555 + * @summary Test that CompressBitsNode::Value does not violate monotonicity + * @run main/othervm -Xbatch -XX:CompileOnly=${test.main.class}::test* ${test.main.class} + */ +public class TestCompressBitsMonotonicity { + public static void main(String[] args) { + for (int i = 0; i < 10000; i++) { + testInt(0); + testLong(0); + } + } + + private static int testInt(int v) { + v &= 0b1101; + int mask = 0b1111; + int sum = 0; + for (int i = 1; i < 10; i *= 2) { + mask = Integer.compress(v, mask); + sum += mask; + } + return sum; + } + + private static long testLong(long v) { + v &= 0b1101; + long mask = 0b1111; + long sum = 0; + for (int i = 1; i < 10; i *= 2) { + mask = Long.compress(v, mask); + sum += mask; + } + return sum; + } +} From 508dcd6fb80078650c6c72210778e57a84fd5cb9 Mon Sep 17 00:00:00 2001 From: Rui Li Date: Sun, 21 Jun 2026 03:25:12 +0000 Subject: [PATCH 020/707] 8386986: Problemlist gc/stress/jfr/TestStressAllocationGCEventsWithShenandoah.java Reviewed-by: kdnilsen, wkemper --- test/hotspot/jtreg/ProblemList.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 41a93a8b10b..9bc858d1cea 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -86,6 +86,10 @@ gc/TestAllocHumongousFragment.java#aggressive 8298781 generic-all gc/TestAllocHumongousFragment.java#g1 8298781 generic-all gc/TestAllocHumongousFragment.java#static 8298781 generic-all gc/shenandoah/oom/TestAllocOutOfMemory.java#large 8344312 linux-ppc64le +gc/stress/jfr/TestStressAllocationGCEventsWithShenandoah.java#generational 8386964 generic-all +gc/stress/jfr/TestStressAllocationGCEventsWithShenandoah.java#default 8386964 generic-all +gc/stress/jfr/TestStressBigAllocationGCEventsWithShenandoah.java#generational 8386964 generic-all +gc/stress/jfr/TestStressBigAllocationGCEventsWithShenandoah.java#default 8386964 generic-all ############################################################################# From 2e179fec7b5113a3b526ee4ad5c66d6b7f0179e2 Mon Sep 17 00:00:00 2001 From: Gui Cao Date: Sun, 21 Jun 2026 06:57:42 +0000 Subject: [PATCH 021/707] 8386945: RISC-V: Auto-enable Zvbb extension features Reviewed-by: fyang, dzhang --- src/hotspot/cpu/riscv/globals_riscv.hpp | 2 +- src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hotspot/cpu/riscv/globals_riscv.hpp b/src/hotspot/cpu/riscv/globals_riscv.hpp index f05b9ff7791..dc3915aa398 100644 --- a/src/hotspot/cpu/riscv/globals_riscv.hpp +++ b/src/hotspot/cpu/riscv/globals_riscv.hpp @@ -117,7 +117,7 @@ define_pd_global(intx, InlineSmallCode, 1000); product(bool, UseZihintpause, false, EXPERIMENTAL, \ "Use Zihintpause instructions") \ product(bool, UseZtso, false, EXPERIMENTAL, "Assume Ztso memory model") \ - product(bool, UseZvbb, false, EXPERIMENTAL, "Use Zvbb instructions") \ + product(bool, UseZvbb, false, DIAGNOSTIC, "Use Zvbb instructions") \ product(bool, UseZvbc, false, EXPERIMENTAL, "Use Zvbc instructions") \ product(bool, UseZvfh, false, DIAGNOSTIC, "Use Zvfh instructions") \ product(bool, UseZvkg, false, DIAGNOSTIC, "Use Zvkg instructions") \ diff --git a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp index a3bd1bfa870..f48df178ce6 100644 --- a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp +++ b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp @@ -239,13 +239,13 @@ void RiscvHwprobe::add_features_from_query_result() { if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZTSO)) { VM_Version::ext_Ztso.enable_feature(); } - if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVBB)) { - VM_Version::ext_Zvbb.enable_feature(); - } if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVBC)) { VM_Version::ext_Zvbc.enable_feature(); } #endif + if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVBB)) { + VM_Version::ext_Zvbb.enable_feature(); + } if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVFH)) { VM_Version::ext_Zvfh.enable_feature(); } From 0de4ef76d8c5da9f8e323cd6ef3aacbd6dfd9ae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Mon, 22 Jun 2026 07:58:53 +0000 Subject: [PATCH 022/707] 8384251: Test java/lang/instrument/GetObjectSizeIntrinsicsTest.java crashed: fatal error: Not compilable at tier 1: CodeBuffer overflow Reviewed-by: rcastanedalo, syan --- .../lang/instrument/GetObjectSizeIntrinsicsTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java b/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java index cdaa94e8289..e1b026c56ef 100644 --- a/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java +++ b/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java @@ -26,6 +26,7 @@ * @bug 8253525 * @summary Test for fInst.getObjectSize with 32-bit compressed oops * @library /test/lib + * @requires !vm.opt.VerifyOops * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest @@ -54,6 +55,7 @@ * @summary Test for fInst.getObjectSize with zero-based compressed oops * @library /test/lib * @requires vm.bits == 64 + * @requires !vm.opt.VerifyOops * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest @@ -82,6 +84,7 @@ * @summary Test for fInst.getObjectSize without compressed oops * @library /test/lib * @requires vm.bits == 64 + * @requires !vm.opt.VerifyOops * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest @@ -110,6 +113,7 @@ * @summary Test for fInst.getObjectSize with 32-bit compressed oops * @library /test/lib * @requires vm.debug + * @requires !vm.opt.VerifyOops * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest @@ -142,6 +146,7 @@ * @library /test/lib * @requires vm.bits == 64 * @requires vm.debug + * @requires !vm.opt.VerifyOops * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest @@ -174,6 +179,7 @@ * @library /test/lib * @requires vm.bits == 64 * @requires vm.debug + * @requires !vm.opt.VerifyOops * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest @@ -206,6 +212,7 @@ * @library /test/lib * @requires vm.bits == 64 * @requires vm.debug + * @requires !vm.opt.VerifyOops * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest @@ -238,6 +245,7 @@ * @library /test/lib * @requires vm.bits == 64 * @requires vm.debug + * @requires !vm.opt.VerifyOops * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest @@ -271,6 +279,7 @@ * @requires vm.bits == 64 * @requires vm.debug * @requires os.maxMemory >= 10G + * @requires !vm.opt.VerifyOops * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest From 1fa4eb1b6afba09f15bb4fb0d02ff88db8e45aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joel=20Sikstr=C3=B6m?= Date: Mon, 22 Jun 2026 08:04:49 +0000 Subject: [PATCH 023/707] 8387003: Stale doc comment in TrustFinalFields.java after JDK-8376777 Reviewed-by: shade, alanb --- .../classes/jdk/internal/vm/annotation/TrustFinalFields.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/jdk/internal/vm/annotation/TrustFinalFields.java b/src/java.base/share/classes/jdk/internal/vm/annotation/TrustFinalFields.java index a94f58159a2..735e3447b37 100644 --- a/src/java.base/share/classes/jdk/internal/vm/annotation/TrustFinalFields.java +++ b/src/java.base/share/classes/jdk/internal/vm/annotation/TrustFinalFields.java @@ -35,7 +35,7 @@ /// /// The compiler already treats static final fields and instance final fields in /// record classes and hidden classes as constant. All classes in select -/// packages (Defined in `trust_final_non_static_fields` in `ciField.cpp`) in +/// packages (Defined in `trust_final_nonstatic_fields` in `ciField.cpp`) in /// the boot class loader also have their instance final fields trusted. This /// annotation is not necessary in these cases. /// From b16d8fa414e44367a208fbb28015a3adc50cc5a8 Mon Sep 17 00:00:00 2001 From: Sorna Sarathi N Date: Mon, 22 Jun 2026 08:17:10 +0000 Subject: [PATCH 024/707] 8376803: Jtreg test compiler/vectorization/TestVectorAlgorithms.java fails after JDK-8373026 Reviewed-by: amitkumar, epeter --- test/hotspot/jtreg/ProblemList.txt | 4 --- .../vectorization/VectorAlgorithmsImpl.java | 31 +++++++++++++------ .../vm/compiler/VectorAlgorithmsImpl.java | 31 +++++++++++++------ 3 files changed, 44 insertions(+), 22 deletions(-) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 9bc858d1cea..8d9de094323 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -58,10 +58,6 @@ compiler/codecache/jmx/PoolsIndependenceTest.java 8264632 macosx-all compiler/vectorapi/VectorRebracket128Test.java 8330538 generic-all -compiler/vectorization/TestVectorAlgorithms.java#noSuperWord 8376803 aix-ppc64,linux-s390x -compiler/vectorization/TestVectorAlgorithms.java#vanilla 8376803 aix-ppc64,linux-s390x -compiler/vectorization/TestVectorAlgorithms.java#noOptimizeFill 8376803 aix-ppc64,linux-s390x - compiler/floatingpoint/TestSubnormalFloat.java 8317810 generic-i586 compiler/floatingpoint/TestSubnormalDouble.java 8317810 generic-i586 diff --git a/test/hotspot/jtreg/compiler/vectorization/VectorAlgorithmsImpl.java b/test/hotspot/jtreg/compiler/vectorization/VectorAlgorithmsImpl.java index c06473d26c5..404412e5fc7 100644 --- a/test/hotspot/jtreg/compiler/vectorization/VectorAlgorithmsImpl.java +++ b/test/hotspot/jtreg/compiler/vectorization/VectorAlgorithmsImpl.java @@ -541,21 +541,34 @@ public static int compute(byte[] a) { int next = REVERSE_POWERS_OF_31_STEP_4[0]; // 31^L var vcoef = IntVector.fromArray(SPECIES_I, REVERSE_POWERS_OF_31_STEP_4, 1); // W var vresult = IntVector.zero(SPECIES_I); + final boolean isLE = java.nio.ByteOrder.nativeOrder() == java.nio.ByteOrder.LITTLE_ENDIAN; int i; for (i = 0; i < SPECIES_B.loopBound(a.length); i += SPECIES_B.length()) { var vb = ByteVector.fromArray(SPECIES_B, a, i); // Add 128 to each byte. var vs = vb.lanewise(VectorOperators.XOR, (byte)0x80) .reinterpretAsShorts(); - // Each short lane contains 2 bytes, crunch them. - var vi = vs.and((short)0xff) // lower byte - .mul((short)31) - .add(vs.lanewise(VectorOperators.LSHR, 8)) // upper byte - .reinterpretAsInts(); - // Each int contains 2 shorts, crunch them. - var v = vi.and(0xffff) // lower short - .mul(31 * 31) - .add(vi.lanewise(VectorOperators.LSHR, 16)); // upper short + // Each short lane contains 2 bytes. + // Extract them in logical byte order (b0, b1), independent of platform endianness. + ShortVector firstByte = isLE ? vs.and((short)0xff) // b0 + : vs.lanewise(VectorOperators.LSHR, 8); // b0 on BE + ShortVector secondByte = isLE ? vs.lanewise(VectorOperators.LSHR, 8) // b1 + : vs.and((short)0xff); // b1 on BE + // Combine each byte pair into a pairwise hash value. + var vi = firstByte.mul((short)31) + .add(secondByte) + .reinterpretAsInts(); + // Each int lane contains two pairwise hash chunks: + // p0 = b0 * 31 + b1 + // p1 = b2 * 31 + b3 + // Extract them in logical order, independent of platform endianness. + IntVector firstPair = isLE ? vi.and(0xffff) // p0 + : vi.lanewise(VectorOperators.LSHR, 16); // p0 on BE + IntVector secondPair = isLE ? vi.lanewise(VectorOperators.LSHR, 16) // p1 + : vi.and(0xffff); // p1 on BE + // Crunch the pairwise results into one value. + var v = firstPair.mul(31 * 31) + .add(secondPair); // Add the correction for the 128 additions above. v = v.add(-128 * (31*31*31 + 31*31 + 31 + 1)); // Every element of v now contains a crunched int-package of 4 bytes. diff --git a/test/micro/org/openjdk/bench/vm/compiler/VectorAlgorithmsImpl.java b/test/micro/org/openjdk/bench/vm/compiler/VectorAlgorithmsImpl.java index a60ecc0f41a..fabd4360abb 100644 --- a/test/micro/org/openjdk/bench/vm/compiler/VectorAlgorithmsImpl.java +++ b/test/micro/org/openjdk/bench/vm/compiler/VectorAlgorithmsImpl.java @@ -541,21 +541,34 @@ public static int compute(byte[] a) { int next = REVERSE_POWERS_OF_31_STEP_4[0]; // 31^L var vcoef = IntVector.fromArray(SPECIES_I, REVERSE_POWERS_OF_31_STEP_4, 1); // W var vresult = IntVector.zero(SPECIES_I); + final boolean isLE = java.nio.ByteOrder.nativeOrder() == java.nio.ByteOrder.LITTLE_ENDIAN; int i; for (i = 0; i < SPECIES_B.loopBound(a.length); i += SPECIES_B.length()) { var vb = ByteVector.fromArray(SPECIES_B, a, i); // Add 128 to each byte. var vs = vb.lanewise(VectorOperators.XOR, (byte)0x80) .reinterpretAsShorts(); - // Each short lane contains 2 bytes, crunch them. - var vi = vs.and((short)0xff) // lower byte - .mul((short)31) - .add(vs.lanewise(VectorOperators.LSHR, 8)) // upper byte - .reinterpretAsInts(); - // Each int contains 2 shorts, crunch them. - var v = vi.and(0xffff) // lower short - .mul(31 * 31) - .add(vi.lanewise(VectorOperators.LSHR, 16)); // upper short + // Each short lane contains 2 bytes. + // Extract them in logical byte order (b0, b1), independent of platform endianness. + ShortVector firstByte = isLE ? vs.and((short)0xff) // b0 + : vs.lanewise(VectorOperators.LSHR, 8); // b0 on BE + ShortVector secondByte = isLE ? vs.lanewise(VectorOperators.LSHR, 8) // b1 + : vs.and((short)0xff); // b1 on BE + // Combine each byte pair into a pairwise hash value. + var vi = firstByte.mul((short)31) + .add(secondByte) + .reinterpretAsInts(); + // Each int lane contains two pairswise hash chunks: + // p0 = b0 * 31 + b1 + // p1 = b2 * 31 + b3 + // Extract them in logical order, independent of platform endianness. + IntVector pair0 = isLE ? vi.and(0xffff) // p0 + : vi.lanewise(VectorOperators.LSHR, 16); // p0 on BE + IntVector pair1 = isLE ? vi.lanewise(VectorOperators.LSHR, 16) // p1 + : vi.and(0xffff); // p1 on BE + // Crunch the pairwise results into one value. + var v = pair0.mul(31 * 31) + .add(pair1); // Add the correction for the 128 additions above. v = v.add(-128 * (31*31*31 + 31*31 + 31 + 1)); // Every element of v now contains a crunched int-package of 4 bytes. From 3a3206b8f272dcd52bba05d4ad2079c223826c23 Mon Sep 17 00:00:00 2001 From: Ozan Cetin Date: Mon, 22 Jun 2026 09:25:41 +0000 Subject: [PATCH 025/707] 8379983: G1: Fix up friend class declarations Reviewed-by: stefank, tschatzl --- src/hotspot/share/gc/g1/g1CardSet.hpp | 6 ++--- src/hotspot/share/gc/g1/g1CollectedHeap.hpp | 27 +++++++------------ src/hotspot/share/gc/g1/g1ConcurrentMark.hpp | 2 +- .../share/gc/g1/g1HeapRegionManager.hpp | 2 +- src/hotspot/share/gc/g1/g1HeapRegionType.hpp | 2 +- .../share/gc/g1/g1MonitoringSupport.hpp | 6 ++--- src/hotspot/share/gc/g1/g1ServiceThread.hpp | 2 +- 7 files changed, 19 insertions(+), 28 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CardSet.hpp b/src/hotspot/share/gc/g1/g1CardSet.hpp index 64ddf0ca6a4..21a2f5f045e 100644 --- a/src/hotspot/share/gc/g1/g1CardSet.hpp +++ b/src/hotspot/share/gc/g1/g1CardSet.hpp @@ -185,13 +185,11 @@ class G1CardSetCoarsenStats { // it. // See its description below for more information. class G1CardSet : public CHeapObj { - friend class G1CardSetTest; friend class G1CardSetMtTestTask; + friend class G1CardSetTest; friend class G1CheckCardClosure; - - friend class G1TransferCard; - friend class G1ReleaseCardsets; + friend class G1TransferCard; // When splitting addresses into region and card within that region, the logical // shift value to get the region. diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index a68d1030636..a60596e67ae 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -146,27 +146,20 @@ class G1JavaThreadsListClaimer : public StackObj { }; class G1CollectedHeap : public CollectedHeap { - friend class VM_G1CollectForAllocation; - friend class VM_G1CollectFull; - friend class VM_G1TryInitiateConcMark; - friend class VMStructs; - friend class MutatorAllocRegion; + friend class G1CheckRegionAttrTableClosure; + friend class G1EvacuateRegionsTask; friend class G1FullCollector; friend class G1GCAllocRegion; - friend class G1HeapVerifier; - - friend class G1YoungGCVerifierMark; - - // Closures used in implementation. - friend class G1EvacuateRegionsTask; - friend class G1PLABAllocator; - - // Other related classes. friend class G1HeapPrinterMark; friend class G1HeapRegionClaimer; - - // Testing classes. - friend class G1CheckRegionAttrTableClosure; + friend class G1HeapVerifier; + friend class G1PLABAllocator; + friend class G1YoungGCVerifierMark; + friend class MutatorAllocRegion; + friend class VM_G1CollectForAllocation; + friend class VM_G1CollectFull; + friend class VM_G1TryInitiateConcMark; + friend class VMStructs; private: // GC Overhead Limit functionality related members. diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index 1ab4654a490..2040916b8e7 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -323,6 +323,7 @@ class G1CMRootMemRegions { // This class manages data structures and methods for doing liveness analysis in // G1's concurrent cycle. class G1ConcurrentMark : public CHeapObj { + friend class G1ClearBitMapTask; friend class G1CMBitMapClosure; friend class G1CMConcurrentMarkingTask; friend class G1CMDrainMarkingStackClosure; @@ -331,7 +332,6 @@ class G1ConcurrentMark : public CHeapObj { friend class G1CMRemarkTask; friend class G1CMRootRegionScanTask; friend class G1CMTask; - friend class G1ClearBitMapTask; friend class G1CollectorState; friend class G1ConcurrentMarkThread; diff --git a/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp b/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp index eb593ff408e..e24610c55d8 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp @@ -71,8 +71,8 @@ class G1HeapRegionTable : public G1BiasedMappedArray { // class G1HeapRegionManager: public CHeapObj { - friend class VMStructs; friend class G1HeapRegionClaimer; + friend class VMStructs; G1RegionToSpaceMapper* _bot_mapper; G1RegionToSpaceMapper* _card_table_mapper; diff --git a/src/hotspot/share/gc/g1/g1HeapRegionType.hpp b/src/hotspot/share/gc/g1/g1HeapRegionType.hpp index 839df68febd..92d3efc2f87 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionType.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionType.hpp @@ -32,7 +32,7 @@ assert(is_valid((tag)), "invalid HR type: %u", (uint) (tag)) class G1HeapRegionType { -friend class VMStructs; + friend class VMStructs; private: // We encode the value of the heap region type so the generation can be diff --git a/src/hotspot/share/gc/g1/g1MonitoringSupport.hpp b/src/hotspot/share/gc/g1/g1MonitoringSupport.hpp index 0293e8a6e23..8a7040e626d 100644 --- a/src/hotspot/share/gc/g1/g1MonitoringSupport.hpp +++ b/src/hotspot/share/gc/g1/g1MonitoringSupport.hpp @@ -122,10 +122,10 @@ class MemoryPool; // path as low-overhead as possible. class G1MonitoringSupport : public CHeapObj { - friend class VMStructs; - friend class G1YoungGCMonitoringScope; - friend class G1FullGCMonitoringScope; friend class G1ConcGCMonitoringScope; + friend class G1FullGCMonitoringScope; + friend class G1YoungGCMonitoringScope; + friend class VMStructs; G1CollectedHeap* _g1h; diff --git a/src/hotspot/share/gc/g1/g1ServiceThread.hpp b/src/hotspot/share/gc/g1/g1ServiceThread.hpp index ff58a5f26f2..f6d8c9bf138 100644 --- a/src/hotspot/share/gc/g1/g1ServiceThread.hpp +++ b/src/hotspot/share/gc/g1/g1ServiceThread.hpp @@ -32,8 +32,8 @@ class G1ServiceTaskQueue; class G1ServiceThread; class G1ServiceTask : public CHeapObj { - friend class G1ServiceTaskQueue; friend class G1ServiceThread; + friend class G1ServiceTaskQueue; // The next absolute time this task should be executed. jlong _time; From b1fd4eb0387d6458b717711f1c33dcb9392884e7 Mon Sep 17 00:00:00 2001 From: David Briemann Date: Mon, 22 Jun 2026 10:35:32 +0000 Subject: [PATCH 026/707] 8386879: PPC64: or_unchecked in OrI instructs can emit unintended SMT priority hints Reviewed-by: mdoerr --- src/hotspot/cpu/ppc/ppc.ad | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 4984e527ccb..9bec99e90cc 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -9294,7 +9294,7 @@ instruct orI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ format %{ "OR $dst, $src1, $src2" %} size(4); ins_encode %{ - __ or_unchecked($dst$$Register, $src1$$Register, $src2$$Register); + __ orr($dst$$Register, $src1$$Register, $src2$$Register); %} ins_pipe(pipe_class_default); %} @@ -9306,7 +9306,7 @@ instruct orI_reg_reg_2(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ format %{ "OR $dst, $src1, $src2" %} size(4); ins_encode %{ - __ or_unchecked($dst$$Register, $src1$$Register, $src2$$Register); + __ orr($dst$$Register, $src1$$Register, $src2$$Register); %} ins_pipe(pipe_class_default); %} @@ -9344,7 +9344,7 @@ instruct orL_reg_reg(iRegLdst dst, iRegLsrc src1, iRegLsrc src2) %{ size(4); format %{ "OR $dst, $src1, $src2 \t// long" %} ins_encode %{ - __ or_unchecked($dst$$Register, $src1$$Register, $src2$$Register); + __ orr($dst$$Register, $src1$$Register, $src2$$Register); %} ins_pipe(pipe_class_default); %} @@ -9357,7 +9357,7 @@ instruct orI_regL_regL(iRegIdst dst, iRegLsrc src1, iRegLsrc src2) %{ format %{ "OR $dst, $src1, $src2 \t// long + l2i" %} size(4); ins_encode %{ - __ or_unchecked($dst$$Register, $src1$$Register, $src2$$Register); + __ orr($dst$$Register, $src1$$Register, $src2$$Register); %} ins_pipe(pipe_class_default); %} From 0e4479b9cbe8127cf28200f224ce8c03677e569c Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Mon, 22 Jun 2026 11:46:03 +0000 Subject: [PATCH 027/707] 8387013: Update GitHub Actions Reviewed-by: shade, erikj --- .github/actions/build-jtreg/action.yml | 6 +++--- .github/actions/do-build/action.yml | 4 ++-- .github/actions/get-bootjdk/action.yml | 2 +- .github/actions/get-bundles/action.yml | 6 +++--- .github/actions/get-gtest/action.yml | 2 +- .github/actions/get-jtreg/action.yml | 2 +- .github/actions/get-msys2/action.yml | 4 ++-- .github/actions/upload-bundles/action.yml | 2 +- .github/workflows/build-alpine-linux.yml | 2 +- .github/workflows/build-cross-compile.yml | 4 ++-- .github/workflows/build-linux.yml | 2 +- .github/workflows/build-macos.yml | 2 +- .github/workflows/build-windows.yml | 2 +- .github/workflows/main.yml | 2 +- .github/workflows/test.yml | 6 +++--- 15 files changed, 24 insertions(+), 24 deletions(-) diff --git a/.github/actions/build-jtreg/action.yml b/.github/actions/build-jtreg/action.yml index 334812e8341..e94fdc9fbd4 100644 --- a/.github/actions/build-jtreg/action.yml +++ b/.github/actions/build-jtreg/action.yml @@ -37,13 +37,13 @@ runs: - name: 'Check cache for already built JTReg' id: get-cached - uses: actions/cache@v5 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: jtreg/installed key: jtreg-${{ steps.version.outputs.value }} - name: 'Checkout the JTReg source' - uses: actions/checkout@v6 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: openjdk/jtreg ref: jtreg-${{ steps.version.outputs.value }} @@ -61,7 +61,7 @@ runs: if: (steps.get-cached.outputs.cache-hit != 'true') - name: 'Upload JTReg artifact' - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: bundles-jtreg-${{ steps.version.outputs.value }} path: jtreg/installed diff --git a/.github/actions/do-build/action.yml b/.github/actions/do-build/action.yml index 6f6bbdabb68..5d015079f54 100644 --- a/.github/actions/do-build/action.yml +++ b/.github/actions/do-build/action.yml @@ -66,7 +66,7 @@ runs: shell: bash - name: 'Upload build logs' - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: failure-logs-${{ inputs.platform }}${{ inputs.debug-suffix }} path: failure-logs @@ -74,7 +74,7 @@ runs: # This is the best way I found to abort the job with an error message - name: 'Notify about build failures' - uses: actions/github-script@v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: core.setFailed('Build failed. See summary for details.') if: steps.check.outputs.failure == 'true' diff --git a/.github/actions/get-bootjdk/action.yml b/.github/actions/get-bootjdk/action.yml index d531358b7dd..eca7e1b29d8 100644 --- a/.github/actions/get-bootjdk/action.yml +++ b/.github/actions/get-bootjdk/action.yml @@ -65,7 +65,7 @@ runs: - name: 'Check cache for BootJDK' id: get-cached-bootjdk - uses: actions/cache@v5 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: bootjdk/jdk key: boot-jdk-${{ inputs.platform }}-${{ steps.sha256.outputs.value }} diff --git a/.github/actions/get-bundles/action.yml b/.github/actions/get-bundles/action.yml index 55fa0e842d2..3884d169d9c 100644 --- a/.github/actions/get-bundles/action.yml +++ b/.github/actions/get-bundles/action.yml @@ -54,14 +54,14 @@ runs: steps: - name: 'Download bundles artifact' id: download-bundles - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: bundles-${{ inputs.platform }}${{ inputs.debug-suffix }} path: bundles continue-on-error: true - name: 'Download bundles artifact (retry)' - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: bundles-${{ inputs.platform }}${{ inputs.debug-suffix }} path: bundles @@ -69,7 +69,7 @@ runs: - name: 'Download static bundles artifact' id: download-static-bundles - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: bundles-${{ inputs.platform }}${{ inputs.debug-suffix }}${{ inputs.static-suffix }} path: bundles diff --git a/.github/actions/get-gtest/action.yml b/.github/actions/get-gtest/action.yml index bc53fa2a3b1..da10e7660a1 100644 --- a/.github/actions/get-gtest/action.yml +++ b/.github/actions/get-gtest/action.yml @@ -40,7 +40,7 @@ runs: var: GTEST_VERSION - name: 'Checkout GTest source' - uses: actions/checkout@v6 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: google/googletest ref: 'v${{ steps.version.outputs.value }}' diff --git a/.github/actions/get-jtreg/action.yml b/.github/actions/get-jtreg/action.yml index 8c75ae10c7f..35d1f93bd3c 100644 --- a/.github/actions/get-jtreg/action.yml +++ b/.github/actions/get-jtreg/action.yml @@ -41,7 +41,7 @@ runs: - name: 'Download JTReg artifact' id: download-jtreg - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: bundles-jtreg-${{ steps.version.outputs.value }} path: jtreg/installed diff --git a/.github/actions/get-msys2/action.yml b/.github/actions/get-msys2/action.yml index 4082aad0c1a..79103b2fe46 100644 --- a/.github/actions/get-msys2/action.yml +++ b/.github/actions/get-msys2/action.yml @@ -35,7 +35,7 @@ runs: steps: - name: 'Install MSYS2 on x86.x64' id: msys2-x64 - uses: msys2/setup-msys2@v2.31.0 + uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2.32.0 with: install: 'autoconf tar unzip zip make' path-type: minimal @@ -44,7 +44,7 @@ runs: - name: 'Install MSYS2 on ARM64' id: msys2-arm64 - uses: msys2/setup-msys2@v2.31.0 + uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2.32.0 with: install: 'autoconf tar unzip zip make' path-type: minimal diff --git a/.github/actions/upload-bundles/action.yml b/.github/actions/upload-bundles/action.yml index 94308002ea7..dc6acdbec3e 100644 --- a/.github/actions/upload-bundles/action.yml +++ b/.github/actions/upload-bundles/action.yml @@ -87,7 +87,7 @@ runs: shell: bash - name: 'Upload bundles artifact' - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: bundles-${{ inputs.platform }}${{ inputs.debug-suffix }}${{ inputs.static-suffix }}${{ inputs.bundle-suffix }} path: bundles diff --git a/.github/workflows/build-alpine-linux.yml b/.github/workflows/build-alpine-linux.yml index 6863da9016e..545993b5387 100644 --- a/.github/workflows/build-alpine-linux.yml +++ b/.github/workflows/build-alpine-linux.yml @@ -74,7 +74,7 @@ jobs: steps: - name: 'Checkout the JDK source' - uses: actions/checkout@v6 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: 'Install toolchain and dependencies' run: | diff --git a/.github/workflows/build-cross-compile.yml b/.github/workflows/build-cross-compile.yml index c80f676864e..84025981f3a 100644 --- a/.github/workflows/build-cross-compile.yml +++ b/.github/workflows/build-cross-compile.yml @@ -87,7 +87,7 @@ jobs: steps: - name: 'Checkout the JDK source' - uses: actions/checkout@v6 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: 'Get the BootJDK' id: bootjdk @@ -115,7 +115,7 @@ jobs: - name: 'Check cache for sysroot' id: get-cached-sysroot - uses: actions/cache@v5 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: sysroot key: sysroot-${{ matrix.debian-arch }}-${{ hashFiles('./.github/workflows/build-cross-compile.yml') }} diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index a77ebece7e2..9c65492e838 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -92,7 +92,7 @@ jobs: steps: - name: 'Checkout the JDK source' - uses: actions/checkout@v6 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: 'Get the BootJDK' id: bootjdk diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 435576f4afd..6c13f32d10e 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -75,7 +75,7 @@ jobs: steps: - name: 'Checkout the JDK source' - uses: actions/checkout@v6 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: 'Get the BootJDK' id: bootjdk diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 002cbe7cd56..e95f36a388a 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -86,7 +86,7 @@ jobs: steps: - name: 'Checkout the JDK source' - uses: actions/checkout@v6 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: 'Get MSYS2' uses: ./.github/actions/get-msys2 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bcb9ea6e0b8..57f81f7fd51 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -76,7 +76,7 @@ jobs: steps: - name: 'Checkout the scripts' - uses: actions/checkout@v6 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: sparse-checkout: | .github diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6270e44d746..054bd00beb9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -131,7 +131,7 @@ jobs: steps: - name: 'Checkout the JDK source' - uses: actions/checkout@v6 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: 'Get MSYS2' uses: ./.github/actions/get-msys2 @@ -244,7 +244,7 @@ jobs: if: always() - name: 'Upload test results' - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: path: results name: ${{ steps.package.outputs.artifact-name }} @@ -252,7 +252,7 @@ jobs: # This is the best way I found to abort the job with an error message - name: 'Notify about test failures' - uses: actions/github-script@v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: core.setFailed('${{ steps.run-tests.outputs.error-message }}') if: steps.run-tests.outputs.failure == 'true' From a0c0ab80d3b0206494b3495b4e1b483a7b61636d Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Mon, 22 Jun 2026 12:41:43 +0000 Subject: [PATCH 028/707] 8386944: Warning message was not printed on PAC enabled AArch64 Linux Reviewed-by: cjplummer, kevinw --- src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c | 2 +- src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c index c500360f39d..e15e73d0fd4 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c @@ -298,7 +298,7 @@ static bool core_handle_note(struct ps_prochandle* ph, ELF_PHDR* note_phdr) { ph->core->vdso_addr = auxv->a_un.a_val; #ifdef __aarch64__ } else if (auxv->a_type == AT_HWCAP) { - ph->pac_enabled = auxv->a_un.a_val & HWCAP_PACA; + ph->pac_enabled = (auxv->a_un.a_val & HWCAP_PACA) == HWCAP_PACA; #endif } auxv++; diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c index 9cbde7319f0..4ab1302c9d2 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c @@ -472,7 +472,7 @@ Pgrab(pid_t pid, char* err_buf, size_t err_buf_len) { } #ifdef __aarch64__ - ph->pac_enabled = HWCAP_PACA & getauxval(AT_HWCAP); + ph->pac_enabled = (HWCAP_PACA & getauxval(AT_HWCAP)) == HWCAP_PACA; #endif // initialize ps_prochandle From 9939fc67eb8d86220d98262d8360973cdbe4b75b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Mon, 22 Jun 2026 13:53:15 +0000 Subject: [PATCH 029/707] 8387017: java/lang/instrument/GetObjectSizeIntrinsicsTest.java fails with Error evaluating expression: invalid boolean value: `null' for expression `vm.opt.VerifyOops' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Joel Sikström Reviewed-by: jpai, jsikstro, epeter --- .../GetObjectSizeIntrinsicsTest.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java b/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java index e1b026c56ef..6cb71022c0c 100644 --- a/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java +++ b/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java @@ -26,7 +26,7 @@ * @bug 8253525 * @summary Test for fInst.getObjectSize with 32-bit compressed oops * @library /test/lib - * @requires !vm.opt.VerifyOops + * @requires (vm.opt.VerifyOops == "null" | !vm.opt.VerifyOops) * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest @@ -55,7 +55,7 @@ * @summary Test for fInst.getObjectSize with zero-based compressed oops * @library /test/lib * @requires vm.bits == 64 - * @requires !vm.opt.VerifyOops + * @requires (vm.opt.VerifyOops == "null" | !vm.opt.VerifyOops) * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest @@ -84,7 +84,7 @@ * @summary Test for fInst.getObjectSize without compressed oops * @library /test/lib * @requires vm.bits == 64 - * @requires !vm.opt.VerifyOops + * @requires (vm.opt.VerifyOops == "null" | !vm.opt.VerifyOops) * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest @@ -113,7 +113,7 @@ * @summary Test for fInst.getObjectSize with 32-bit compressed oops * @library /test/lib * @requires vm.debug - * @requires !vm.opt.VerifyOops + * @requires (vm.opt.VerifyOops == "null" | !vm.opt.VerifyOops) * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest @@ -146,7 +146,7 @@ * @library /test/lib * @requires vm.bits == 64 * @requires vm.debug - * @requires !vm.opt.VerifyOops + * @requires (vm.opt.VerifyOops == "null" | !vm.opt.VerifyOops) * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest @@ -179,7 +179,7 @@ * @library /test/lib * @requires vm.bits == 64 * @requires vm.debug - * @requires !vm.opt.VerifyOops + * @requires (vm.opt.VerifyOops == "null" | !vm.opt.VerifyOops) * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest @@ -212,7 +212,7 @@ * @library /test/lib * @requires vm.bits == 64 * @requires vm.debug - * @requires !vm.opt.VerifyOops + * @requires (vm.opt.VerifyOops == "null" | !vm.opt.VerifyOops) * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest @@ -245,7 +245,7 @@ * @library /test/lib * @requires vm.bits == 64 * @requires vm.debug - * @requires !vm.opt.VerifyOops + * @requires (vm.opt.VerifyOops == "null" | !vm.opt.VerifyOops) * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest @@ -279,7 +279,7 @@ * @requires vm.bits == 64 * @requires vm.debug * @requires os.maxMemory >= 10G - * @requires !vm.opt.VerifyOops + * @requires (vm.opt.VerifyOops == "null" | !vm.opt.VerifyOops) * * @build jdk.test.whitebox.WhiteBox * @run build GetObjectSizeIntrinsicsTest From f2b5c6f5fe97fce5442d87bf2b0c1e6818c6c11b Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Mon, 22 Jun 2026 16:51:38 +0000 Subject: [PATCH 030/707] 8385574: JFR: Redaction should check file Reviewed-by: mgronlun --- .../share/jfr/periodic/jfrRedactedEvents.cpp | 21 +++++++++++++++++++ .../share/jfr/periodic/jfrRedactedEvents.hpp | 1 + 2 files changed, 22 insertions(+) diff --git a/src/hotspot/share/jfr/periodic/jfrRedactedEvents.cpp b/src/hotspot/share/jfr/periodic/jfrRedactedEvents.cpp index 5ace0d0fae4..331c28cffa2 100644 --- a/src/hotspot/share/jfr/periodic/jfrRedactedEvents.cpp +++ b/src/hotspot/share/jfr/periodic/jfrRedactedEvents.cpp @@ -609,6 +609,9 @@ bool JfrRedactedEvents::match_key(StringArray* filters, const char* text) { } bool JfrRedactedEvents::read_file(StringArray* target, const char* filename) { + if (!is_valid_redaction_file(filename)) { + return false; + } FILE* file = os::fopen(filename, "r"); if (file == nullptr) { log_error(jfr, redact)("Failed to open redaction file: %s", filename); @@ -661,3 +664,21 @@ StringArray* JfrRedactedEvents::split(const char* text, char separator) { } return result; } + +bool JfrRedactedEvents::is_valid_redaction_file(const char* filename) { + struct stat st; + int ret = os::stat(filename, &st); + if (ret != 0) { + log_error(jfr, redact)("Failed to access redaction file %s", filename); + return false; + } + if ((st.st_mode & S_IFMT) != S_IFREG) { + log_error(jfr, redact)("Redaction file %s is not a regular file", filename); + return false; + } + if (st.st_size > 1024*1024) { + log_error(jfr, redact)("Redaction file %s is too large (1024 KB).", filename); + return false; + } + return true; +} diff --git a/src/hotspot/share/jfr/periodic/jfrRedactedEvents.hpp b/src/hotspot/share/jfr/periodic/jfrRedactedEvents.hpp index 38c93310365..dc972190b6c 100644 --- a/src/hotspot/share/jfr/periodic/jfrRedactedEvents.hpp +++ b/src/hotspot/share/jfr/periodic/jfrRedactedEvents.hpp @@ -205,6 +205,7 @@ class JfrRedactedEvents: public AllStatic { static bool equals_case_insensitive(char a, char b); static bool is_redacted_key(const char* key); static bool is_separator(char c); + static bool is_valid_redaction_file(const char* filename); static bool is_whitespace(char c); static void ensure_initialized(); static StringArray* make_java_args_array(); From 12d7e6186622506cb19aad0e3d9e9256531e2851 Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Mon, 22 Jun 2026 16:54:20 +0000 Subject: [PATCH 031/707] 8385927: JDK 27 RDP1 L10n resource files update Reviewed-by: naoto --- .../launcher/resources/launcher_de.properties | 6 +-- .../launcher/resources/launcher_ja.properties | 6 +-- .../resources/launcher_zh_CN.properties | 4 +- .../keytool/resources/keytool_de.properties | 2 +- .../resources/keytool_zh_CN.properties | 6 +-- .../util/resources/auth_zh_CN.properties | 8 +-- .../util/resources/security_zh_CN.properties | 10 ++-- .../internal/res/XSLTErrorResources_de.java | 4 +- .../internal/res/XMLErrorResources_de.java | 2 +- .../javac/resources/compiler_de.properties | 28 ++++++++-- .../javac/resources/compiler_ja.properties | 28 ++++++++-- .../javac/resources/compiler_zh_CN.properties | 32 +++++++++-- .../tools/javac/resources/javac_ja.properties | 4 +- .../javac/resources/javac_zh_CN.properties | 2 +- .../resources/jarsigner_zh_CN.properties | 14 ++--- .../sun/tools/jar/resources/jar_de.properties | 2 + .../sun/tools/jar/resources/jar_ja.properties | 2 + .../tools/jar/resources/jar_zh_CN.properties | 2 + .../html/resources/standard_de.properties | 12 +++-- .../html/resources/standard_ja.properties | 12 +++-- .../html/resources/standard_zh_CN.properties | 12 +++-- .../toolkit/resources/doclets_de.properties | 11 ++-- .../toolkit/resources/doclets_ja.properties | 11 ++-- .../resources/doclets_zh_CN.properties | 11 ++-- .../jimage/resources/jimage_de.properties | 9 ++-- .../jimage/resources/jimage_ja.properties | 9 ++-- .../jimage/resources/jimage_zh_CN.properties | 9 ++-- .../jlink/resources/plugins_ja.properties | 2 +- .../resources/LinuxResources_de.properties | 16 +++--- .../resources/LinuxResources_ja.properties | 16 +++--- .../resources/LinuxResources_zh_CN.properties | 16 +++--- .../resources/MacResources_de.properties | 41 +++++++------- .../resources/MacResources_ja.properties | 43 ++++++++------- .../resources/MacResources_zh_CN.properties | 39 +++++++------- .../resources/HelpResources_de.properties | 14 ++--- .../resources/HelpResources_ja.properties | 14 ++--- .../resources/HelpResources_zh_CN.properties | 14 ++--- .../resources/MainResources_de.properties | 53 +++++++++++++++---- .../resources/MainResources_ja.properties | 53 +++++++++++++++---- .../resources/MainResources_zh_CN.properties | 53 +++++++++++++++---- .../resources/WinResources_de.properties | 20 +++---- .../resources/WinResources_ja.properties | 20 +++---- .../resources/WinResources_zh_CN.properties | 20 +++---- 43 files changed, 433 insertions(+), 259 deletions(-) diff --git a/src/java.base/share/classes/sun/launcher/resources/launcher_de.properties b/src/java.base/share/classes/sun/launcher/resources/launcher_de.properties index e80869b868c..f01d079da64 100644 --- a/src/java.base/share/classes/sun/launcher/resources/launcher_de.properties +++ b/src/java.base/share/classes/sun/launcher/resources/launcher_de.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2007, 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 @@ -34,8 +34,8 @@ java.launcher.opt.footer = \ -cp ...|:]\n -enableassertions[:...|:]\n Aktiviert Assertions mit angegebener Granularität\n -da[:...|:]\n -disableassertions[:...|:]\n Deaktiviert Assertions mit angegebener Granularität\n -esa | -enablesystemassertions\n Aktiviert System-Assertions\n -dsa | -disablesystemassertions\n Deaktiviert System-Assertions\n -agentlib:[=]\n Lädt die native Agent Library . Beispiel: -agentlib:jdwp\n siehe auch -agentlib:jdwp=help\n -agentpath:[=]\n Lädt die native Agent Library mit dem vollständigen Pfadnamen\n -javaagent:[=]\n Lädt den Java-Programmiersprachen-Agent, siehe java.lang.instrument\n -splash:\n Zeigt den Startbildschirm mit einem angegebenen Bild an\n Skalierte HiDPI-Bilder werden automatisch unterstützt und verwendet,\n falls verfügbar. Der nicht skalierte Bilddateiname (Beispiel: image.ext)\n muss immer als Argument an die Option "-splash" übergeben werden.\n Das am besten geeignete angegebene skalierte Bild wird\n automatisch ausgewählt.\n Weitere Informationen finden Sie in der Dokumentation zur SplashScreen-API\n @argument files\n Eine oder mehrere Argumentdateien mit Optionen\n --disable-@files\n Verhindert die weitere Erweiterung von Argumentdateien\n --enable-preview\n Lässt zu, das Klassen von Vorschaufeatures dieses Release abhängig sind\nUm ein Argument für eine lange Option anzugeben, können Sie --= oder\n-- verwenden.\n # Translators please note do not translate the options themselves -java.launcher.X.usage=\n -Xbatch Deaktiviert die Hintergrundkompilierung\n -Xbootclasspath/a:\n An das Ende des Bootstrap Classpaths anhängen\n -Xcheck:jni Führt zusätzliche Prüfungen für JNI-Funktionen aus\n -Xcomp Erzwingt die Kompilierung von Methoden beim ersten Aufruf\n -Xdebug Führt keine Aktion aus. Ist veraltet und wird in einem zukünftigen Release entfernt.\n -Xdiag Zeigt zusätzliche Diagnosemeldungen an\n -Xint Nur Ausführung im interpretierten Modus\n -Xinternalversion\n Zeigt detailliertere JVM-Versionsinformationen an als die\n Option -version\n -Xlog: Konfiguriert oder aktiviert Logging mit dem einheitlichen Java Virtual\n Machine-(JVM-)Logging-Framework. Verwenden Sie -Xlog:help\n für weitere Einzelheiten.\n -Xloggc: Protokolliert den GC-Status in einer Datei mit Zeitstempeln.\n Diese Option ist veraltet und kann in einem\n zukünftigen Release entfernt werden. Wird durch -Xlog:gc: ersetzt.\n -Xmixed Ausführung im gemischten Modus (Standard)\n -Xmn Legt die anfängliche und maximale Größe (in Byte) des Heaps\n für die Young Generation (Nursery) fest\n -Xms Legt die minimale und die anfängliche Java-Heap-Größe fest\n -Xmx Legt die maximale Java-Heap-Größe fest\n -Xnoclassgc Deaktiviert die Klassen-Garbage Collection\n -Xrs Reduziert die Verwendung von BS-Signalen durch Java/VM (siehe Dokumentation)\n -Xshare:auto Verwendet freigegebene Klassendaten, wenn möglich (Standard)\n -Xshare:off Versucht nicht, freigegebene Klassendaten zu verwenden\n -Xshare:on Erfordert die Verwendung freigegebener Klassendaten, verläuft sonst nicht erfolgreich.\n Diese Testoption kann zeitweise zu\n Fehlern führen. Sie darf nicht in Produktionsumgebungen verwendet werden.\n -XshowSettings Zeigt alle Einstellungen an und fährt fort\n -XshowSettings:all\n Zeigt alle Einstellungen als Verbose-Ausgabe an und fährt fort\n -XshowSettings:locale\n Zeigt alle gebietsschemabezogenen Einstellungen an und fährt fort\n -XshowSettings:properties\n Zeigt alle Eigenschaftseinstellungen an und fährt fort\n -XshowSettings:vm\n Zeigt alle VM-bezogenen Einstellungen an und fährt fort\n -XshowSettings:security\n Zeigt alle Sicherheitseinstellungen an und fährt fort\n -XshowSettings:security:all\n Zeigt alle Sicherheitseinstellungen an und fährt fort\n -XshowSettings:security:properties\n Zeigt Sicherheitseigenschaften an und fährt fort\n -XshowSettings:security:providers\n Zeigt statische Sicherheitsprovidereinstellungen an und fährt fort\n -XshowSettings:security:tls\n Zeigt TLS-bezogene Sicherheitseinstellungen an und fährt fort\n -XshowSettings:system\n (Nur Linux) Zeigt die Konfiguration des Hostsystems oder Containers an\n und fährt fort\n -Xss Legt die Stackgröße des Java-Threads fest\n Die tatsächliche Größe kann auf ein Vielfaches der\n Systemseitengröße aufgerundet werden, wenn für das Betriebssystem erforderlich.\n -Xverify Legt den Modus der Bytecodeverifizierung fest\n \ - Beachten Sie, dass die Option -Xverify:none veraltet ist und\n in einem zukünftigen Release entfernt werden kann.\n --add-reads =(,)*\n Aktualisiert , damit gelesen wird, ungeachtet\n der Moduldeklaration. \n kann ALL-UNNAMED sein, um alle unbenannten\n Module zu lesen.\n --add-exports /=(,)*\n Aktualisiert , um in zu exportieren,\n ungeachtet der Moduldeklaration.\n kann ALL-UNNAMED sein, um in alle\n unbenannten Module zu exportieren.\n --add-opens /=(,)*\n Aktualisiert , um in\n zu öffnen, ungeachtet der Moduldeklaration.\n --limit-modules [,...]\n Grenzt die Gesamtmenge der beobachtbaren Module ein\n --patch-module =({0})*\n Überschreibt oder erweitert ein Modul mit Klassen und Ressourcen\n in JAR-Dateien oder Verzeichnissen.\n --source \n Legt die Version der Quelle im Quelldateimodus fest.\n --finalization=\n Steuert, ob die JVM Objekte finalisiert.\n Dabei ist entweder "enabled" oder "disabled".\n Die Finalisierung ist standardmäßig aktiviert.\n --sun-misc-unsafe-memory-access=\n Verwendung der nicht unterstützten API sun.misc.Unsafe zulassen oder verweigern\n ist "allow", "warn", "debug" oder "deny".\n Der Standardwert ist "warn".\n\nDiese zusätzlichen Optionen können jederzeit ohne vorherige Ankündigung geändert werden.\n +java.launcher.X.usage=\n -Xbatch Deaktiviert die Hintergrundkompilierung\n -Xbootclasspath/a:\n An das Ende des Bootstrap Classpaths anhängen\n -Xcheck:jni Führt zusätzliche Prüfungen für JNI-Funktionen aus\n -Xcomp Erzwingt die Kompilierung von Methoden beim ersten Aufruf\n -Xdebug Führt keine Aktion aus. Ist veraltet und wird in einem zukünftigen Release entfernt.\n -Xdiag Zeigt zusätzliche Diagnosemeldungen an\n -Xint Nur Ausführung im interpretierten Modus\n -Xinternalversion\n Zeigt detailliertere JVM-Versionsinformationen an als die\n Option -version\n -Xlog: Konfiguriert oder aktiviert Logging mit dem einheitlichen Java Virtual\n Machine-(JVM-)Logging-Framework. Verwenden Sie -Xlog:help\n für weitere Einzelheiten.\n -Xloggc: Protokolliert den GC-Status in einer Datei mit Zeitstempeln.\n Diese Option ist veraltet und kann in einem\n zukünftigen Release entfernt werden. Wird durch -Xlog:gc: ersetzt.\n -Xmixed Ausführung im gemischten Modus (Standard)\n -Xmn Legt die anfängliche und maximale Größe (in Byte) des Heaps\n für die Young Generation (Nursery) fest\n -Xms Legt die minimale und die anfängliche Java-Heap-Größe fest\n -Xmx Legt die maximale Java-Heap-Größe fest\n -Xnoclassgc Deaktiviert die Klassen-Garbage Collection\n -Xrs Reduziert die Verwendung von BS-Signalen durch Java/VM (siehe Dokumentation)\n -Xshare:auto Verwendet freigegebene Klassendaten, wenn möglich (Standard)\n -Xshare:off Versucht nicht, freigegebene Klassendaten zu verwenden\n -Xshare:on Erfordert die Verwendung freigegebener Klassendaten, verläuft sonst nicht erfolgreich.\n Diese Testoption kann zeitweise zu\n Fehlern führen. Sie darf nicht in Produktionsumgebungen verwendet werden.\n -XshowSettings Zeigt alle Einstellungen an und fährt fort\n -XshowSettings:all\n Zeigt alle Einstellungen als Verbose-Ausgabe an und fährt fort\n -XshowSettings:locale\n Zeigt alle gebietsschemabezogenen Einstellungen an und fährt fort\n -XshowSettings:properties\n Zeigt alle Eigenschaftseinstellungen an und fährt fort\n -XshowSettings:vm\n Zeigt alle VM-bezogenen Einstellungen an und fährt fort\n -XshowSettings:security\n Zeigt alle Sicherheitseinstellungen an und fährt fort\n -XshowSettings:security:all\n Zeigt alle Sicherheitseinstellungen an und fährt fort\n -XshowSettings:security:properties\n Zeigt Sicherheitseigenschaften an und fährt fort\n -XshowSettings:security:providers\n Zeigt statische Sicherheitsprovidereinstellungen an und fährt fort\n -XshowSettings:security:tls\n Zeigt TLS-bezogene Sicherheitseinstellungen an und fährt fort\n -XshowSettings:system\n (Nur Linux) Zeigt die Konfiguration des Hostsystems oder Containers an\n und fährt fort\n -Xss Legt die Stackgröße des Java-Threads fest\n Die tatsächliche Größe kann auf ein Vielfaches der\n Systemseitengröße aufgerundet werden, wenn für das Betriebssystem erforderlich.\n -Xverify Legt den Modus der Bytecodeverifizierung fest\n \ +--add-reads =(,)*\n Aktualisiert , damit gelesen wird, ungeachtet\n der Moduldeklaration. \n kann ALL-UNNAMED sein, um alle unbenannten\n Module zu lesen.\n --add-exports /=(,)*\n Aktualisiert , um in zu exportieren,\n ungeachtet der Moduldeklaration.\n kann ALL-UNNAMED sein, um in alle\n unbenannten Module zu exportieren.\n --add-opens /=(,)*\n Aktualisiert , um in\n zu öffnen, ungeachtet der Moduldeklaration.\n --limit-modules [,...]\n Grenzt die Gesamtmenge der beobachtbaren Module ein\n --patch-module =({0})*\n Überschreibt oder erweitert ein Modul mit Klassen und Ressourcen\n in JAR-Dateien oder Verzeichnissen.\n --source \n Legt die Version der Quelle im Quelldateimodus fest.\n --finalization=\n Steuert, ob die JVM Objekte finalisiert.\n Dabei ist entweder "enabled" oder "disabled".\n Die Finalisierung ist standardmäßig aktiviert.\n --sun-misc-unsafe-memory-access=\n Verwendung der nicht unterstützten API sun.misc.Unsafe zulassen oder verweigern\n ist "allow", "warn", "debug" oder "deny".\n Der Standardwert ist "warn".\n\nDiese zusätzlichen Optionen können jederzeit ohne vorherige Ankündigung geändert werden.\n # Translators please note do not translate the options themselves java.launcher.X.macosx.usage=\nDie folgenden Optionen sind für macOS spezifisch:\n -XstartOnFirstThread\n Führt die main()-Methode für den ersten (AppKit-)Thread aus\n -Xdock:name=\n Setzt den im Dock angezeigten Standardanwendungsnamen außer Kraft\n -Xdock:icon=\n Setzt das im Dock angezeigte Standardsymbol außer Kraft\n\n diff --git a/src/java.base/share/classes/sun/launcher/resources/launcher_ja.properties b/src/java.base/share/classes/sun/launcher/resources/launcher_ja.properties index 49712b21c52..e03c385b97e 100644 --- a/src/java.base/share/classes/sun/launcher/resources/launcher_ja.properties +++ b/src/java.base/share/classes/sun/launcher/resources/launcher_ja.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2007, 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 @@ -37,8 +37,8 @@ java.launcher.opt.footer = \ -cp <ディレクトリおよびzip/jarファイ # Translators please note do not translate the options themselves java.launcher.X.usage=\n -Xbatch バックグラウンド・コンパイルを無効にします\n -Xbootclasspath/a:\n ブートストラップ・クラス・パスの最後に追加します\n -Xcheck:jni JNI関数に対する追加のチェックを実行します\n -Xcomp 初回呼出し時にメソッドのコンパイルを強制します\n -Xdebug 何も実行されません。将来のリリースで削除されるため、非推奨になりました。\n -Xdiag 追加の診断メッセージを表示します\n -Xint インタプリタ・モードの実行のみ\n -Xinternalversion\n -versionオプションより詳細なJVMバージョン情報を\n 表示します\n -Xlog: Java Virtual Machine (JVM)統合ロギング・フレームワークでの\n ロギングを構成または有効化します。詳細は、-Xlog:helpを\n 使用してください。\n -Xloggc: タイムスタンプが付いたファイルにGCステータスのログを記録します\n このオプションは非推奨であり、将来のリリースで削除される\n 可能性があります。-Xlog:gc:で置換されています。\n -Xmixed 混合モードの実行(デフォルト)\n -Xmn 若い世代(ナーサリ)のヒープの初期サイズおよび最大サイズ\n (バイト単位)を設定します\n -Xms Javaの最小および初期のヒープ・サイズを設定します\n -Xmx Javaの最大ヒープ・サイズを設定します\n -Xnoclassgc クラスのガベージ・コレクションを無効にします\n -Xrs Java/VMによるOSシグナルの使用を削減します(ドキュメントを参照)\n -Xshare:auto 可能であれば共有クラス・データを使用します(デフォルト)\n -Xshare:off 共有クラス・データの使用を試みません\n -Xshare:on 共有クラス・データの使用を必須にし、できなければ失敗します。\n \ -これはテスト・オプションであり、断続的な失敗につながる\n 可能性があります。本番環境では使用しないでください。\n -XshowSettings すべての設定を表示して続行します\n -XshowSettings:all\n すべての設定を詳細に表示して続行します\n -XshowSettings:locale\n すべてのロケール関連の設定を表示して続行します\n -XshowSettings:properties\n すべてのプロパティ設定を表示して続行します\n -XshowSettings:vm\n すべてのVM関連の設定を表示して続行します\n -XshowSettings:security\n すべてのセキュリティ設定を表示して続行します\n -XshowSettings:security:all\n すべてのセキュリティ設定を表示して続行します\n -XshowSettings:security:properties\n セキュリティ・プロパティを表示して続行します\n -XshowSettings:security:providers\n 静的セキュリティ・プロバイダ設定を表示して続行します\n -XshowSettings:security:tls\n TLS関連のセキュリティ設定を表示して続行します\n -XshowSettings:system\n (Linuxのみ)ホスト・システムまたはコンテナを表示します\n 構成して続行します\n -Xss javaスレッドのスタック・サイズを設定します\n 実際のサイズは、次の倍数に切り上げられる場合があります: \n オペレーティング・システムの要件に応じたシステム・ページ・サイズ。\n -Xverify バイトコード・ベリファイアのモードを設定します\n オプション-Xverify:noneは非推奨になり、\n 将来のリリースで削除される可能性があります。\n --add-reads =(,)*\n モジュール宣言に関係なく、を更新してを\n 読み取ります。 \n をALL-UNNAMEDに設定すると、すべての名前のないモジュールを\n 読み取ることができます。\n --add-exports \ -/=(,)*\n モジュール宣言に関係なく、を更新してに\n エクスポートします。\n をALL-UNNAMEDに設定すると、すべての名前のないモジュールに\n エクスポートできます。\n --add-opens /=(,)*\n モジュール宣言に関係なく、を更新してを\n に開きます。\n --limit-modules [,...]\n 参照可能なモジュールの領域を制限します\n --patch-module =({0})*\n JARファイルまたはディレクトリのクラスおよびリソースで\n モジュールをオーバーライドまたは拡張します。\n --source \n ソースファイル・モードでソースのバージョンを設定します。\n --finalization=\n JVMがオブジェクトのファイナライズを実行するかどうかを制御します\n は"enabled"または"disabled"のいずれかです。\n ファイナライズはデフォルトで有効になっています。\n --sun-misc-unsafe-memory-access=\n サポートされていないAPI sun.misc.Unsafeの使用を許可または拒否します\n は"allow"、"warn"、"debug"または"deny"のいずれかです。\n デフォルト値は"warn"です。\n\nこの追加オプションは予告なしに変更されることがあります。\n +これはテスト・オプションであり、断続的な失敗につながる\n 可能性があります。本番環境では使用しないでください。\n -XshowSettings すべての設定を表示して続行します\n -XshowSettings:all\n すべての設定を詳細に表示して続行します\n -XshowSettings:locale\n すべてのロケール関連の設定を表示して続行します\n -XshowSettings:properties\n すべてのプロパティ設定を表示して続行します\n -XshowSettings:vm\n すべてのVM関連の設定を表示して続行します\n -XshowSettings:security\n すべてのセキュリティ設定を表示して続行します\n -XshowSettings:security:all\n すべてのセキュリティ設定を表示して続行します\n -XshowSettings:security:properties\n セキュリティ・プロパティを表示して続行します\n -XshowSettings:security:providers\n 静的セキュリティ・プロバイダ設定を表示して続行します\n -XshowSettings:security:tls\n TLS関連のセキュリティ設定を表示して続行します\n -XshowSettings:system\n (Linuxのみ)ホスト・システムまたはコンテナを表示します\n 構成して続行します\n -Xss javaスレッドのスタック・サイズを設定します\n 実際のサイズは、次の倍数に切り上げられる場合があります: \n オペレーティング・システムの要件に応じたシステム・ページ・サイズ。\n -Xverify バイトコード・ベリファイアのモードを設定します\n --add-reads =(,)*\n モジュール宣言に関係なく、を更新してを\n 読み取ります。 \n をALL-UNNAMEDに設定すると、すべての名前のないモジュールを\n 読み取ることができます。\n --add-exports /=(,)*\n モジュール宣言に関係なく、を更新してに\n \ +エクスポートします。\n をALL-UNNAMEDに設定すると、すべての名前のないモジュールに\n エクスポートできます。\n --add-opens /=(,)*\n モジュール宣言に関係なく、を更新してを\n に開きます。\n --limit-modules [,...]\n 参照可能なモジュールの領域を制限します\n --patch-module =({0})*\n JARファイルまたはディレクトリのクラスおよびリソースで\n モジュールをオーバーライドまたは拡張します。\n --source \n ソースファイル・モードでソースのバージョンを設定します。\n --finalization=\n JVMがオブジェクトのファイナライズを実行するかどうかを制御します\n は"enabled"または"disabled"のいずれかです。\n ファイナライズはデフォルトで有効になっています。\n --sun-misc-unsafe-memory-access=\n サポートされていないAPI sun.misc.Unsafeの使用を許可または拒否します\n は"allow"、"warn"、"debug"または"deny"のいずれかです。\n デフォルト値は"warn"です。\n\nこの追加オプションは予告なしに変更されることがあります。\n # Translators please note do not translate the options themselves java.launcher.X.macosx.usage=\n次のオプションはmacOS固有です:\n -XstartOnFirstThread\n main()メソッドを最初(AppKit)のスレッドで実行する\n -Xdock:name=\n Dockに表示されるデフォルト・アプリケーション名をオーバーライドする\n -Xdock:icon=\n Dockに表示されるデフォルト・アイコンをオーバーライドする\n\n diff --git a/src/java.base/share/classes/sun/launcher/resources/launcher_zh_CN.properties b/src/java.base/share/classes/sun/launcher/resources/launcher_zh_CN.properties index b3c0268f953..734fc9b50f7 100644 --- a/src/java.base/share/classes/sun/launcher/resources/launcher_zh_CN.properties +++ b/src/java.base/share/classes/sun/launcher/resources/launcher_zh_CN.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2007, 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 @@ -36,7 +36,7 @@ java.launcher.opt.footer = \ -cp <目录和 zip/jar 文件的类搜索路径> # Translators please note do not translate the options themselves java.launcher.X.usage=\n -Xbatch 禁用后台编译\n -Xbootclasspath/a:<以 {0} 分隔的目录和 zip/jar 文件>\n 附加在引导类路径末尾\n -Xcheck:jni 对 JNI 函数执行其他检查\n -Xcomp 强制在首次调用时编译方法\n -Xdebug 不执行任何操作;已过时,将在未来发行版中删除。\n -Xdiag 显示附加诊断消息\n -Xint 仅解释模式执行\n -Xinternalversion\n 显示比 -version 选项更详细的\n JVM 版本信息\n -Xlog: 配置或启用采用 Java 虚拟\n 机 (Java Virtual Machine, JVM) 统一记录框架进行事件记录。使用 -Xlog:help\n 可了解详细信息。\n -Xloggc: 将 GC 状态记录在文件中(带时间戳)。\n 此选项已过时,可能会在\n 将来的发行版中删除。它将替换为 -Xlog:gc:。\n -Xmixed 混合模式执行(默认值)\n -Xmn 为年轻代(新生代)设置初始和最大堆大小\n (以字节为单位)\n -Xms 设置最小和初始 Java 堆大小\n -Xmx 设置最大 Java 堆大小\n -Xnoclassgc 禁用类垃圾收集\n -Xrs 减少 Java/VM 对操作系统信号的使用(请参见文档)\n -Xshare:auto 在可能的情况下使用共享类数据(默认值)\n -Xshare:off 不尝试使用共享类数据\n -Xshare:on 要求使用共享类数据,否则将失败。\n 这是一个测试选项,可能导致间歇性\n 故障。不应在生产环境中使用它。\n -XshowSettings 显示所有设置并继续\n -XshowSettings:all\n 详细显示所有设置并继续\n -XshowSettings:locale\n 显示所有与区域设置相关的设置并继续\n -XshowSettings:properties\n 显示所有属性设置并继续\n -XshowSettings:vm\n 显示所有与 vm 相关的设置并继续\n -XshowSettings:security\n 显示所有安全设置并继续\n -XshowSettings:security:all\n 显示所有安全设置并继续\n -XshowSettings:security:properties\n 显示安全属性并继续\n -XshowSettings:security:providers\n 显示静态安全提供方设置并继续\n -XshowSettings:security:tls\n 显示与 TLS \ -相关的安全设置并继续\n -XshowSettings:system\n (仅 Linux)显示主机系统或容器\n 配置并继续\n -Xss 设置 Java 线程堆栈大小\n 实际大小可以舍入到\n 操作系统要求的系统页面大小的倍数。\n -Xverify 设置字节码验证器的模式\n 请注意,选项 -Xverify:none 已过时,\n 可能会在未来发行版中删除。\n --add-reads =(,)*\n 更新 以读取 ,而无论\n 模块如何声明。 \n 可以是 ALL-UNNAMED,将读取所有未命名\n 模块。\n --add-exports /=(,)*\n 更新 以将 导出到 ,\n 而无论模块如何声明。\n 可以是 ALL-UNNAMED,将导出到所有\n 未命名模块。\n --add-opens /=(,)*\n 更新 以在 中打开\n ,而无论模块如何声明。\n --limit-modules [,...]\n 限制可观察模块的领域\n --patch-module =({0})*\n 使用 JAR 文件或目录中的类和资源\n 覆盖或增强模块。\n --source \n 设置源文件模式中源的版本。\n --finalization=\n 控制 JVM 是否执行对象最终处理,\n 其中 为 "enabled" 或 "disabled" 之一。\n 默认情况下,最终处理处于启用状态。\n --sun-misc-unsafe-memory-access=\n 允许或拒绝使用不受支持的 API sun.misc.Unsafe\n 为 "allow"、"warn"、"debug" 或 "deny" 之一。\n 默认值为 "warn"。\n\n这些额外选项如有更改, 恕不另行通知。\n +相关的安全设置并继续\n -XshowSettings:system\n (仅 Linux)显示主机系统或容器\n 配置并继续\n -Xss 设置 Java 线程堆栈大小\n 实际大小可以舍入到\n 操作系统要求的系统页面大小的倍数。\n -Xverify 设置字节码验证器的模式\n --add-reads =(,)*\n 更新 以读取 ,而无论\n 模块如何声明。 \n 可以是 ALL-UNNAMED,将读取所有未命名\n 模块。\n --add-exports /=(,)*\n 更新 以将 导出到 ,\n 而无论模块如何声明。\n 可以是 ALL-UNNAMED,将导出到所有\n 未命名模块。\n --add-opens /=(,)*\n 更新 以在 中打开\n ,而无论模块如何声明。\n --limit-modules [,...]\n 限制可观察模块的领域\n --patch-module =({0})*\n 使用 JAR 文件或目录中的类和资源\n 覆盖或增强模块。\n --source \n 设置源文件模式中源的版本。\n --finalization=\n 控制 JVM 是否执行对象最终处理,\n 其中 为 "enabled" 或 "disabled" 之一。\n 默认情况下,最终处理处于启用状态。\n --sun-misc-unsafe-memory-access=\n 允许或拒绝使用不受支持的 API sun.misc.Unsafe\n 为 "allow"、"warn"、"debug" 或 "deny" 之一。\n 默认值为 "warn"。\n\n这些额外选项如有更改, 恕不另行通知。\n # Translators please note do not translate the options themselves java.launcher.X.macosx.usage=\n以下选项是特定于 macOS 的选项:\n -XstartOnFirstThread\n 在第一个 (AppKit) 线程上运行 main() 方法\n -Xdock:name=\n 覆盖停靠栏中显示的默认应用程序名称\n -Xdock:icon=\n 覆盖停靠栏中显示的默认图标\n\n diff --git a/src/java.base/share/classes/sun/security/tools/keytool/resources/keytool_de.properties b/src/java.base/share/classes/sun/security/tools/keytool/resources/keytool_de.properties index a452dd34e9d..479c536b7c8 100644 --- a/src/java.base/share/classes/sun/security/tools/keytool/resources/keytool_de.properties +++ b/src/java.base/share/classes/sun/security/tools/keytool/resources/keytool_de.properties @@ -52,7 +52,7 @@ Imports.entries.from.a.JDK.1.1.x.style.identity.database=Importiert Einträge au Imports.a.certificate.or.a.certificate.chain=Importiert ein Zertifikat oder eine Zertifikatskette Imports.a.password=Importiert ein Kennwort Imports.one.or.all.entries.from.another.keystore=Importiert einen oder alle Einträge aus einem anderen Keystore -Clones.a.key.entry=Clont einen Schlüsseleintrag +Clones.a.key.entry=Klont einen Schlüsseleintrag Changes.the.key.password.of.an.entry=Ändert das Schlüsselkennwort eines Eintrags Lists.entries.in.a.keystore=Listet die Einträge in einem Keystore auf Prints.the.content.of.a.certificate=Druckt den Content eines Zertifikats diff --git a/src/java.base/share/classes/sun/security/tools/keytool/resources/keytool_zh_CN.properties b/src/java.base/share/classes/sun/security/tools/keytool/resources/keytool_zh_CN.properties index 435e74e468f..622b7545183 100644 --- a/src/java.base/share/classes/sun/security/tools/keytool/resources/keytool_zh_CN.properties +++ b/src/java.base/share/classes/sun/security/tools/keytool/resources/keytool_zh_CN.properties @@ -114,7 +114,7 @@ verbose.output=详细输出 validity.number.of.days=有效天数 Serial.ID.of.cert.to.revoke=要撤销的证书的序列 ID # keytool: Running part -keytool.error.=keytool 错误:\u0020 +keytool.error.=keytool 错误: Illegal.option.=非法选项: \u0020 Illegal.value.=非法值:\u0020 Unknown.password.type.=未知口令类型:\u0020 @@ -216,7 +216,7 @@ Do.you.still.want.to.add.it.no.=是否仍要添加? [否]: \u0020 Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias.=在别名 <{0}> 之下, 证书已经存在于系统范围的 CA 密钥库中 Do.you.still.want.to.add.it.to.your.own.keystore.no.=是否仍要将它添加到自己的密钥库? [否]: \u0020 Trust.this.certificate.no.=是否信任此证书? [否]: \u0020 -New.prompt.=新 {0}:\u0020 +New.prompt.=新 {0}: Passwords.must.differ=口令不能相同 Re.enter.new.prompt.=重新输入新{0}:\u0020 Re.enter.password.=再次输入口令:\u0020 @@ -269,7 +269,7 @@ Please.provide.keysize.for.secret.key.generation=请提供 -keysize 以生成密 warning.not.verified.make.sure.keystore.is.correct=警告: 未验证。请确保密钥库是正确的。 warning.not.verified.make.sure.keystore.is.correct.or.specify.trustcacerts=警告:未验证。请确保密钥库是正确的,或者指定 -trustcacerts。 -Extensions.=扩展:\u0020 +Extensions.=扩展: .Empty.value.=(空值) Extension.Request.=扩展请求: Unknown.keyUsage.type.=未知 keyUsage 类型:\u0020 diff --git a/src/java.base/share/classes/sun/security/util/resources/auth_zh_CN.properties b/src/java.base/share/classes/sun/security/util/resources/auth_zh_CN.properties index 663b3f8993f..209ebcecfa7 100644 --- a/src/java.base/share/classes/sun/security/util/resources/auth_zh_CN.properties +++ b/src/java.base/share/classes/sun/security/util/resources/auth_zh_CN.properties @@ -53,8 +53,8 @@ Configuration.Error.Line.line.expected.expect.=配置错误: \n\t行 {0}: 应为 Configuration.Error.Line.line.system.property.value.expanded.to.empty.value=配置错误: \n\t行 {0}: 系统属性 [{1}] 扩展到空值 # com.sun.security.auth.module.JndiLoginModule -username.=用户名:\u0020 -password.=密码:\u0020 +username.=用户名: +password.=密码: # com.sun.security.auth.module.KeyStoreLoginModule Please.enter.keystore.information=请输入密钥库信息 @@ -63,5 +63,5 @@ Keystore.password.=密钥库口令:\u0020 Private.key.password.optional.=私有密钥口令 (可选):\u0020 # com.sun.security.auth.module.Krb5LoginModule -Kerberos.username.defUsername.=Kerberos 用户名 [{0}]:\u0020 -Kerberos.password.for.username.={0} 的 Kerberos 密码:\u0020 +Kerberos.username.defUsername.=Kerberos 用户名 [{0}]: +Kerberos.password.for.username.={0} 的 Kerberos 密码: diff --git a/src/java.base/share/classes/sun/security/util/resources/security_zh_CN.properties b/src/java.base/share/classes/sun/security/util/resources/security_zh_CN.properties index a322cb7b1e8..d86f05ecb09 100644 --- a/src/java.base/share/classes/sun/security/util/resources/security_zh_CN.properties +++ b/src/java.base/share/classes/sun/security/util/resources/security_zh_CN.properties @@ -44,16 +44,16 @@ NEWLINE=\n invalid.null.action.provided=提供了无效的空操作 invalid.null.Class.provided=提供了无效的空类 Subject.=主体:\n -.Principal.=\t主用户:\u0020 -.Public.Credential.=\t公共身份证明:\u0020 -.Private.Credential.=\t专用身份证明:\u0020 +.Principal.=\t主用户: +.Public.Credential.=\t公共身份证明: +.Private.Credential.=\t专用身份证明: .Private.Credential.inaccessible.=\t无法访问专用身份证明\n Subject.is.read.only=主体为只读 attempting.to.add.an.object.which.is.not.an.instance.of.java.security.Principal.to.a.Subject.s.Principal.Set=正在尝试将一个非 java.security.Principal 实例的对象添加到主体的主用户集中 attempting.to.add.an.object.which.is.not.an.instance.of.class=正在尝试添加一个非{0}实例的对象 # javax.security.auth.login.AppConfigurationEntry -LoginModuleControlFlag.=LoginModuleControlFlag:\u0020 +LoginModuleControlFlag.=LoginModuleControlFlag: # javax.security.auth.login.LoginContext Invalid.null.input.name=无效空输入: 名称 @@ -73,7 +73,7 @@ line.number.msg=第 {0} 行:{1} line.number.expected.expect.found.actual.=行号 {0}: 应为 [{1}], 找到 [{2}] # sun.security.pkcs11.SunPKCS11 -PKCS11.Token.providerName.Password.=PKCS11 标记 [{0}] 密码:\u0020 +PKCS11.Token.providerName.Password.=PKCS11 标记 [{0}] 密码: # sun.security.util.Password warning.input.may.be.visible.on.screen=[警告:输入可能显示在屏幕上]\u0020 diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/res/XSLTErrorResources_de.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/res/XSLTErrorResources_de.java index 906bbb88252..806b37c8bb0 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/res/XSLTErrorResources_de.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/res/XSLTErrorResources_de.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -641,7 +641,7 @@ public Object[][] getContents() "Vorlagen m\u00FCssen entweder ein \"match\"- oder ein \"name\"-Attribut haben"}, { ER_NO_CLONE_OF_DOCUMENT_FRAG, - "Kein Clone eines Dokumentfragments."}, + "Kein Klon eines Dokumentfragments."}, { ER_CANT_CREATE_ITEM, "Element in Ergebnisbaum kann nicht erstellt werden: {0}"}, diff --git a/src/java.xml/share/classes/com/sun/org/apache/xml/internal/res/XMLErrorResources_de.java b/src/java.xml/share/classes/com/sun/org/apache/xml/internal/res/XMLErrorResources_de.java index 3c53ea08956..9337b13b8fc 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xml/internal/res/XMLErrorResources_de.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xml/internal/res/XMLErrorResources_de.java @@ -223,7 +223,7 @@ public class XMLErrorResources_de extends ListResourceBundle "Fehler: Iterator f\u00FCr Achse {0} nicht implementiert "}, { ER_ITERATOR_CLONE_NOT_SUPPORTED, - "Iteratorclone nicht unterst\u00FCtzt"}, + "Iteratorklon nicht unterst\u00FCtzt"}, { ER_UNKNOWN_AXIS_TYPE, "Unbekannter Achsendurchlauftyp: {0}"}, diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_de.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_de.properties index b8fa413adba..36a71fa424f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_de.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_de.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1999, 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 @@ -710,7 +710,11 @@ compiler.err.lambda.body.neither.value.nor.void.compatible=Lambda-Body ist weder # 0: list of type compiler.err.incompatible.thrown.types.in.mref=Inkompatible ausgelöste Typen {0} in Funktionsausdruck -compiler.misc.incompatible.arg.types.in.lambda=Inkompatible Parametertypen in Lambda-Ausdruck +# 0: list of type or message segment, 1: list of type or message segment +compiler.misc.incompatible.arg.types.in.lambda=Inkompatible Parametertypen in Lambda-Ausdruck\nErforderlich: {0}\nGefunden: {1} + +# 0: symbol +compiler.misc.wrong.number.args.in.lambda=Falsche Anzahl an Parametern in Lambda-Ausdruck für funktionale Schnittstelle {0} compiler.misc.incompatible.arg.types.in.mref=Inkompatible Parametertypen in Methodenreferenz @@ -1046,6 +1050,19 @@ compiler.err.not.exhaustive=Der Switch-Ausdruck deckt nicht alle möglichen Eing compiler.err.not.exhaustive.statement=Die Switch-Anweisung deckt nicht alle möglichen Eingabewerte ab +compiler.err.not.exhaustive.details=Der Switch-Ausdruck deckt nicht alle möglichen Eingabewerte ab\nFehlende Muster: + +compiler.err.not.exhaustive.statement.details=Die Switch-Anweisung deckt nicht alle möglichen Eingabewerte ab\nFehlende Muster: + +# 0: type +compiler.misc.binding.pattern={0} _ + +# 0: type, 1: list of diagnostic +compiler.misc.record.pattern={0}({1}) + +# 0: type, 1: name +compiler.misc.enum.constant.pattern={0}.{1} + compiler.err.initializer.must.be.able.to.complete.normally=Initializer muss normal abgeschlossen werden können compiler.err.initializer.not.allowed=Initializer in Schnittstellen nicht zulässig @@ -1213,6 +1230,9 @@ compiler.note.mref.stat=Methodenreferenz wird übersetzt\nAlternative Metafactor # 0: boolean, 1: symbol compiler.note.mref.stat.1=Methodenreferenz wird übersetzt\nAlternative Metafactory = {0}\nBridgemethode = {1} +# 0: string, 1: string, 2: string, 3: number, 4: string, 5: string, 6: string, 7: string +compiler.note.lambda.deserialization.stat=Lambda-Deserialisierung wird generiert\nfunctionalInterfaceClass: {0}\nfunctionalInterfaceMethodName: {1}\nFunctionalInterfaceMethodSignature:{2}\nimplMethodKind: {3}\nimplClass: {4}\nimplMethodName: {5}\nimplMethodSignature: {6}\ninstantiatedMethodType: {7} + compiler.note.note=Hinweis:\u0020 # 0: file name @@ -1383,7 +1403,7 @@ compiler.warn.incubating.modules=Inkubatormodul(e) verwendet: {0} # 0: symbol, 1: symbol # lint: deprecation -# flags: aggregate, mandatory, default-enabled +# flags: aggregate, mandatory, default-enabled, deprecation-sensitive compiler.warn.has.been.deprecated={0} in {1} ist veraltet # 0: symbol, 1: symbol @@ -1809,9 +1829,11 @@ compiler.warn.annotation.method.not.found.reason=Annotationsmethode "{1}()" kann compiler.err.cant.attach.type.annotations=Typannotationen {0} können nicht an {1}.{2} angehängt werden:\n{3} # 0: file object, 1: symbol, 2: name +# lint: classfile compiler.warn.unknown.enum.constant=Unbekannte Enum-Konstante {1}.{2} # 0: file object, 1: symbol, 2: name, 3: message segment +# lint: classfile compiler.warn.unknown.enum.constant.reason=Unbekannte Enum-Konstante {1}.{2}\nGrund: {3} # 0: type, 1: type diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_ja.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_ja.properties index 89bdc893a43..a9aaa7ba48b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_ja.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_ja.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1999, 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 @@ -710,7 +710,11 @@ compiler.err.lambda.body.neither.value.nor.void.compatible=ラムダ・ボディ # 0: list of type compiler.err.incompatible.thrown.types.in.mref=機能式でスローされたタイプ{0}は不適合です -compiler.misc.incompatible.arg.types.in.lambda=ラムダ式のパラメータ型は不適合です +# 0: list of type or message segment, 1: list of type or message segment +compiler.misc.incompatible.arg.types.in.lambda=ラムダ式のパラメータ型は不適合です\n期待値: {0}\n検出値: {1} + +# 0: symbol +compiler.misc.wrong.number.args.in.lambda=機能インタフェース{0}のラムダ式のパラメータ数が間違っています compiler.misc.incompatible.arg.types.in.mref=メソッド参照のパラメータ型は不適合です @@ -1046,6 +1050,19 @@ compiler.err.not.exhaustive=switch式がすべての可能な入力値をカバ compiler.err.not.exhaustive.statement=switch文がすべての可能な入力値をカバーしていません +compiler.err.not.exhaustive.details=switch式がすべての可能な入力値をカバーしていません\n欠落パターン: + +compiler.err.not.exhaustive.statement.details=switch文がすべての可能な入力値をカバーしていません\n欠落パターン: + +# 0: type +compiler.misc.binding.pattern={0} _ + +# 0: type, 1: list of diagnostic +compiler.misc.record.pattern={0}({1}) + +# 0: type, 1: name +compiler.misc.enum.constant.pattern={0}.{1} + compiler.err.initializer.must.be.able.to.complete.normally=初期化子は正常に完了できる必要があります compiler.err.initializer.not.allowed=イニシャライザはinterfacesでは許可されません @@ -1213,6 +1230,9 @@ compiler.note.mref.stat=メソッド参照を変換しています\n代替metafa # 0: boolean, 1: symbol compiler.note.mref.stat.1=メソッド参照を変換しています\n代替metafactory = {0}\nブリッジ・メソッド = {1} +# 0: string, 1: string, 2: string, 3: number, 4: string, 5: string, 6: string, 7: string +compiler.note.lambda.deserialization.stat=ラムダ・デシリアライゼーションを生成しています\nfunctionalInterfaceClass: {0}\nfunctionalInterfaceMethodName: {1}\nfunctionalInterfaceMethodSignature:{2}\nimplMethodKind: {3}\nimplClass: {4}\nimplMethodName: {5}\nimplMethodSignature: {6}\ninstantiatedMethodType: {7} + compiler.note.note=ノート:\u0020 # 0: file name @@ -1383,7 +1403,7 @@ compiler.warn.incubating.modules=実験的なモジュールを使用してい # 0: symbol, 1: symbol # lint: deprecation -# flags: aggregate, mandatory, default-enabled +# flags: aggregate, mandatory, default-enabled, deprecation-sensitive compiler.warn.has.been.deprecated={1}の{0}は推奨されません # 0: symbol, 1: symbol @@ -1809,9 +1829,11 @@ compiler.warn.annotation.method.not.found.reason=タイプ''{0}''内に注釈メ compiler.err.cant.attach.type.annotations=タイプ注釈{0}を{1}.{2}に添付できません:\n{3} # 0: file object, 1: symbol, 2: name +# lint: classfile compiler.warn.unknown.enum.constant=不明な列挙型定数です{1}.{2} # 0: file object, 1: symbol, 2: name, 3: message segment +# lint: classfile compiler.warn.unknown.enum.constant.reason=不明な列挙型定数です{1}.{2}\n理由: {3} # 0: type, 1: type diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_zh_CN.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_zh_CN.properties index 900557a29da..befb5f6262d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_zh_CN.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_zh_CN.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1999, 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 @@ -710,7 +710,11 @@ compiler.err.lambda.body.neither.value.nor.void.compatible=lambda 主体不是 # 0: list of type compiler.err.incompatible.thrown.types.in.mref=函数表达式中抛出的类型 {0} 不兼容 -compiler.misc.incompatible.arg.types.in.lambda=lambda 表达式中的参数类型不兼容 +# 0: list of type or message segment, 1: list of type or message segment +compiler.misc.incompatible.arg.types.in.lambda=lambda 表达式中的参数类型不兼容\n需要:{0}\n找到:{1} + +# 0: symbol +compiler.misc.wrong.number.args.in.lambda=函数接口 {0} 的 lambda 表达式中的参数数量错误 compiler.misc.incompatible.arg.types.in.mref=方法引用中的参数类型不兼容 @@ -1042,9 +1046,22 @@ compiler.misc.cant.apply.diamond.1=无法推断{0}的类型参数\n原因: {1} compiler.err.unreachable.stmt=无法访问的语句 -compiler.err.not.exhaustive=switch 表达式不包含所有可能的输入值 +compiler.err.not.exhaustive=switch 表达式未涵盖所有可能的输入值 + +compiler.err.not.exhaustive.statement=switch 语句未涵盖所有可能的输入值 + +compiler.err.not.exhaustive.details=switch 表达式未涵盖所有可能的输入值\n缺少的模式: + +compiler.err.not.exhaustive.statement.details=switch 语句未涵盖所有可能的输入值\n缺少的模式: + +# 0: type +compiler.misc.binding.pattern={0} _ -compiler.err.not.exhaustive.statement=并非所有可能的输入值都包含在 switch 语句中 +# 0: type, 1: list of diagnostic +compiler.misc.record.pattern={0}({1}) + +# 0: type, 1: name +compiler.misc.enum.constant.pattern={0}.{1} compiler.err.initializer.must.be.able.to.complete.normally=初始化程序必须能够正常完成 @@ -1213,6 +1230,9 @@ compiler.note.mref.stat=转换方法引用\n替代 metafactory = {0}\n # 0: boolean, 1: symbol compiler.note.mref.stat.1=转换方法引用\n替代 metafactory = {0}\nbridge 方法 = {1} +# 0: string, 1: string, 2: string, 3: number, 4: string, 5: string, 6: string, 7: string +compiler.note.lambda.deserialization.stat=正在生成 lambda 反序列化\nfunctionalInterfaceClass:{0}\nfunctionalInterfaceMethodName:{1}\nfunctionalInterfaceMethodSignature:{2}\nimplMethodKind:{3}\nimplClass:{4}\nimplMethodName:{5}\nimplMethodSignature:{6}\ninstantiatedMethodType:{7} + compiler.note.note=注:\u0020 # 0: file name @@ -1383,7 +1403,7 @@ compiler.warn.incubating.modules=使用 incubating 模块: {0} # 0: symbol, 1: symbol # lint: deprecation -# flags: aggregate, mandatory, default-enabled +# flags: aggregate, mandatory, default-enabled, deprecation-sensitive compiler.warn.has.been.deprecated={1}中的{0}已过时 # 0: symbol, 1: symbol @@ -1809,9 +1829,11 @@ compiler.warn.annotation.method.not.found.reason=无法找到类型 ''{0}'' 的 compiler.err.cant.attach.type.annotations=无法将类型批注 {0} 附加到 {1}.{2}:\n{3} # 0: file object, 1: symbol, 2: name +# lint: classfile compiler.warn.unknown.enum.constant=未知的枚举常量 {1}.{2} # 0: file object, 1: symbol, 2: name, 3: message segment +# lint: classfile compiler.warn.unknown.enum.constant.reason=未知的枚举常量 {1}.{2}\n原因: {3} # 0: type, 1: type diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_ja.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_ja.properties index 3ae7ab1690e..03b1a7d3350 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_ja.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_ja.properties @@ -103,7 +103,7 @@ javac.opt.Xlint=推奨lint警告カテゴリを有効にします。このリリ javac.opt.Xlint.all=すべてのlint警告カテゴリを有効にします javac.opt.Xlint.none=すべてのlint警告カテゴリを無効にします #L10N: do not localize: -Xlint -javac.opt.arg.Xlint=(,)* +javac.opt.arg.Xlint=<キー>(,<キー>)* javac.opt.Xlint.custom=有効または無効にするLint警告カテゴリ(カンマ区切り)。\n指定されたカテゴリを無効にするには、キーの前に''-''を指定します。サポートされているキーと\nデフォルトで有効になっているカテゴリを表示するには、\n''--help-lint''を使用します。 javac.opt.Xlint.desc.auxiliaryclass=ソース・ファイルで非表示になっているが他のファイルから使用されている補助クラスについて警告します。 @@ -206,7 +206,7 @@ javac.opt.printProcessorInfo=プロセッサが処理を依頼される注釈に javac.opt.userpathsfirst=ブート・クラスパスの後ではなく、ブート・クラスパスの前にクラスのクラスパスおよびソース・パスを検索する javac.opt.prefer=暗黙的にコンパイルされるクラスについて、ソース・ファイルとクラス・ファイルの両方が見つかった際どちらを読み込むか指定する # L10N: do not localize: ''preview'' -javac.opt.preview=プレビュー言語機能を有効にします。\nまた、''preview'' lintカテゴリも無効にします。\n-sourceまたは--releaseとともに使用されます。 +javac.opt.preview=プレビュー言語機能を有効にします。\nまた、''preview''lintカテゴリも無効にします。\n-sourceまたは--releaseとともに使用されます。 javac.opt.AT=ファイルからの読取りオプションおよびファイル名 javac.opt.diags=診断モードの選択 javac.opt.addExports=がALL-UNNAMEDである場合、その定義モジュールから、追加モジュールまたは\n すべての名前のないモジュールにエクスポート済とみなされるようにパッケージを指定します。 diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_zh_CN.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_zh_CN.properties index 447d0d26239..1d1d7250c86 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_zh_CN.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_zh_CN.properties @@ -206,7 +206,7 @@ javac.opt.printProcessorInfo=输出有关请求处理程序处理哪些批注的 javac.opt.userpathsfirst=在引导类路径之前而不是之后搜索类的类路径和源路径 javac.opt.prefer=指定读取文件, 当同时找到隐式编译类的源文件和类文件时 # L10N: do not localize: ''preview'' -javac.opt.preview=启用预览语言功能。\n还禁用''preview''lint 类别。\n要与 -source 或 --release 一起使用。 +javac.opt.preview=启用预览语言功能。\n还禁用 ''preview'' lint 类别。\n要与 -source 或 --release 一起使用。 javac.opt.AT=从文件读取选项和文件名 javac.opt.diags=选择诊断模式 javac.opt.addExports=指定被视为已从其定义模块导出到其他模块或者导出到所有\n 未命名模块 (如果 为 ALL-UNNAMED) 的程序包。 diff --git a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/resources/jarsigner_zh_CN.properties b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/resources/jarsigner_zh_CN.properties index f780bd1f1c3..62ef135cb8a 100644 --- a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/resources/jarsigner_zh_CN.properties +++ b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/resources/jarsigner_zh_CN.properties @@ -104,7 +104,7 @@ jar.verified.with.signer.errors.=jar 已验证, 但出现签名者错误。 history.with.ts=- 由 "%1$s" 签名\n 摘要算法: %2$s\n 签名算法: %3$s, %4$s\n 由 "%6$s" 于 %5$tc 加时间戳\n 时间戳摘要算法: %7$s\n 时间戳签名算法: %8$s, %9$s history.without.ts=- 由 "%1$s" 签名\n 摘要算法: %2$s\n 签名算法: %3$s, %4$s -history.nonexistent.entries=\ 警告:不存在的签名条目:\u0020 +history.nonexistent.entries=\ 警告:不存在的签名条目: history.unparsable=- 无法解析的与签名相关的文件 %s history.nosf=- 缺少与签名相关的文件 META-INF/%s.SF history.nobk=- 与签名相关的文件 META-INF/%s.SF 缺少块文件 @@ -119,13 +119,13 @@ key.bit.disabled=%s 密钥(禁用) nonexistent.entries.found=此 jar 的文件包含不存在的签名条目。有关更多详细信息,请参见 -verbose 输出。 external.file.attributes.detected=检测到 POSIX 文件权限和/或 symlink 属性。这些属性在进行签名时会被忽略,不受该签名的保护。 -jarsigner.=jarsigner:\u0020 +jarsigner.=jarsigner: signature.filename.must.consist.of.the.following.characters.A.Z.0.9.or.=签名文件名必须包含以下字符: A-Z, 0-9, _ 或 - unable.to.open.jar.file.=无法打开 jar 文件:\u0020 unable.to.create.=无法创建:\u0020 -.adding.=\ 正在添加:\u0020 -.updating.=\ 正在更新:\u0020 -.signing.=\ 正在签名:\u0020 +.adding.=\ 正在添加: +.updating.=\ 正在更新: +.signing.=\ 正在签名: attempt.to.rename.signedJarFile.to.jarFile.failed=尝试将{0}重命名为{1}时失败 attempt.to.rename.jarFile.to.origJar.failed=尝试将{0}重命名为{1}时失败 unable.to.sign.jar.=无法对 jar 进行签名:\u0020 @@ -156,8 +156,8 @@ no.response.from.the.Timestamping.Authority.=时间戳颁发机构没有响应 or=或 Certificate.not.found.for.alias.alias.must.reference.a.valid.KeyStore.entry.containing.an.X.509.public.key.certificate.for.the=找不到{0}的证书。{1}必须引用包含时间戳颁发机构的 X.509 公共密钥证书的有效密钥库条目。 entry.was.signed.on=条目的签名日期为 {0} -Warning.=警告:\u0020 -Error.=错误:\u0020 +Warning.=警告: +Error.=错误: ...Signer=>>> 签名者 ...TSA=>>> TSA trusted.certificate=可信证书 diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_de.properties b/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_de.properties index 292ec9c963d..2b4c8c27261 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_de.properties +++ b/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_de.properties @@ -82,6 +82,8 @@ error.validator.info.version.notequal={0}: module-info.class in einem versionier error.validator.info.manclass.notequal={0}: module-info.class in einem versionierten Verzeichnis enthält unterschiedlichen "main-class"-Wert error.validator.metainf.wrong.position=Eintrag META-INF/ an Position 0 erwartet, aber an Position {0} gefunden error.validator.manifest.wrong.position=Eintrag META-INF/MANIFEST.MF an Position 0 oder 1 erwartet, aber an Position {0} gefunden +error.validator.manifest.invalid.automatic.module.name=Ungültiger Modulname des "Automatic-Module-Name"-Eintrags in Manifest: {0} +error.validator.manifest.inconsistent.automatic.module.name=Es wird erwartet, dass der "Automatic-Module-Name"-Eintrag in Manifest "{0}" mit dem Namen des kompilierten Moduls "{1}" übereinstimmt warn.validator.identical.entry=Warnung: Eintrag {0} enthält eine Klasse, die mit\neinem bereits in der JAR-Datei enthaltenen Eintrag identisch ist warn.validator.resources.with.same.name=Warnung: Eintrag {0}, mehrere Ressourcen mit demselben Namen warn.validator.concealed.public.class=Warnung: Eintrag {0} ist eine öffentliche Klasse\nin einem verdeckten Package. Wenn Sie diese JAR-Datei in den Classpath einfügen, kommt es\nzu nicht kompatiblen öffentlichen Schnittstellen diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_ja.properties b/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_ja.properties index 0d0f91ad791..4ea0130846d 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_ja.properties +++ b/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_ja.properties @@ -82,6 +82,8 @@ error.validator.info.version.notequal={0}: バージョニングされたディ error.validator.info.manclass.notequal={0}: バージョニングされたディレクトリのmodule-info.classに異なる"main-class"が含まれています error.validator.metainf.wrong.position=エントリMETA-INF/は0の位置にある必要がありますが、見つかりました: {0} error.validator.manifest.wrong.position=エントリMETA-INF/MANIFEST.MFは0または1の位置にある必要がありますが、位置: {0}で見つかりました +error.validator.manifest.invalid.automatic.module.name=マニフェスト内のAutomatic-Module-Nameエントリのモジュール名が無効です: {0} +error.validator.manifest.inconsistent.automatic.module.name=マニフェスト内のAutomatic-Module-Nameエントリ: {0}は、コンパイル済モジュールの名前と一致する必要があります: {1} warn.validator.identical.entry=警告 : エントリ{0}には、jarにすでに存在する\nエントリと同じクラスが含まれます warn.validator.resources.with.same.name=警告 : エントリ{0}、同じ名前を持つ複数のリソース warn.validator.concealed.public.class=警告 : エントリ{0}は、隠しパッケージ内のpublicクラスです。\nクラスパスにこのjarを配置すると、互換性のない\npublicインタフェースが生成されます diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_zh_CN.properties b/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_zh_CN.properties index 41833d28bfc..0eedd48e05d 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_zh_CN.properties +++ b/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar_zh_CN.properties @@ -82,6 +82,8 @@ error.validator.info.version.notequal={0}: 版本化目录中的 module-info.cla error.validator.info.manclass.notequal={0}: 版本化目录中的 module-info.class 包含不同的 "main-class" error.validator.metainf.wrong.position=条目 META-INF/ 应位于位置 0 处,但发现:{0} error.validator.manifest.wrong.position=条目 META-INF/MANIFEST.MF 应位于位置 0 或 1 处,但发现该条目位于位置 {0} 处 +error.validator.manifest.invalid.automatic.module.name=清单 {0} 中 Automatic-Module-Name 条目的模块名称无效 +error.validator.manifest.inconsistent.automatic.module.name=需要清单 {0} 中的 Automatic-Module-Name 条目才能与已编译模块 {1} 的名称匹配 warn.validator.identical.entry=警告: 条目 {0} 包含与 jar 中的\n现有条目相同的类 warn.validator.resources.with.same.name=警告: 条目 {0}, 多个资源具有相同名称 warn.validator.concealed.public.class=警告: 条目 {0} 是已隐藏程序包中的\n公共类, 将此 jar 放置在类路径中\n将导致公共接口不兼容 diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_de.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_de.properties index a380b29d553..999d11bed3d 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_de.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_de.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2010, 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 @@ -178,7 +178,8 @@ doclet.Inheritance_Tree=Vererbungsbaum doclet.DefinedIn=Definiert in doclet.ReferencedIn=Referenziert in doclet.External_Specifications=Externe Spezifikationen -doclet.External_Specifications.All_Specifications=Alle Spezifikationen +doclet.External_Specifications.by-host=Spezifikationen nach Hostnamen anzeigen: +doclet.External_Specifications.all-hosts=Alle Hostnamen doclet.External_Specifications.no-host=Lokal doclet.Specification=Spezifikation doclet.Summary_Page=Zusammenfassung (Seite) @@ -388,7 +389,7 @@ doclet.usage.version.description=@version-Absätze aufnehmen doclet.usage.author.description=@author-Absätze aufnehmen -doclet.usage.docfilessubdirs.description=Ermöglicht Deep Copying von "doc-files"-Verzeichnissen. Unterverzeichnisse und alle\nInhalte werden rekursiv in das Ziel kopiert +doclet.usage.docfilessubdirs.description=Die Option "-docfilessubdirs" ist nicht mehr erforderlich und\nwird möglicherweise in einem zukünftigen Release entfernt. doclet.usage.splitindex.description=Index in eine Datei pro Buchstabe aufteilen @@ -432,7 +433,7 @@ doclet.usage.link-platform-properties.parameters=< URL> doclet.usage.link-platform-properties.description=Link zu Plattformdokumentations-URLs, die in der Eigenschaftendatei auf deklariert sind doclet.usage.excludedocfilessubdir.parameters=,,... -doclet.usage.excludedocfilessubdir.description=Schließen Sie alle "doc-files"-Unterverzeichnisse mit einem angegebenen Namen aus.\n":" kann überall im Argument als Trennzeichen verwendet werden. +doclet.usage.excludedocfilessubdir.description=Schließen Sie alle "doc-files"-Unterverzeichnisse mit dem angegebenen Namen aus.\nVerwenden Sie "*", um alle Unterverzeichnisse auszuschließen. ":" kann\nüberall im Argument als Trennzeichen verwendet werden. doclet.usage.group.parameters= ,... doclet.usage.group.description=Angegebene Packages oder Module auf Überblickseite gruppieren.\n":" kann überall im Argument als Trennzeichen verwendet werden. @@ -544,4 +545,7 @@ doclet.NoFrames_specified=Die Option --no-frames wird nicht mehr benötigt und w # L10N: do not localize the option name -footer doclet.footer_specified=Die Option -footer wird nicht mehr unterstützt und wird ignoriert.\nSie wird möglicherweise in einem zukünftigen Release entfernt. +# L10N: do not localize the option name -docfilessubdirs +doclet.docfilessubdirs_specified=Hinweis: Die Option "-docfilessubdirs" ist nicht mehr erforderlich und\nwird möglicherweise in einem zukünftigen Release entfernt. + doclet.selectModule=Wählen Sie das Modul aus, in dem gesucht werden soll. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_ja.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_ja.properties index 69cdc862b4c..c4b8b1e099c 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_ja.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_ja.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2010, 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 @@ -178,7 +178,8 @@ doclet.Inheritance_Tree=継承ツリー doclet.DefinedIn=定義先 doclet.ReferencedIn=参照 doclet.External_Specifications=外部仕様 -doclet.External_Specifications.All_Specifications=すべての仕様 +doclet.External_Specifications.by-host=ホスト名別に仕様を表示: +doclet.External_Specifications.all-hosts=すべてのホスト名 doclet.External_Specifications.no-host=ローカル doclet.Specification=仕様 doclet.Summary_Page=サマリー・ページ @@ -388,7 +389,7 @@ doclet.usage.version.description=@versionパラグラフを含めます doclet.usage.author.description=@authorパラグラフを含めます -doclet.usage.docfilessubdirs.description='doc-files'ディレクトリのディープ・コピーを有効にします。\n宛先には、サブディレクトリとそのすべて内容が再帰的にコピーされます +doclet.usage.docfilessubdirs.description=-docfilessubdirsオプションは必須ではなくなり、\n将来のリリースで削除される可能性があります。 doclet.usage.splitindex.description=1字ごとに1ファイルに索引を分割します @@ -432,7 +433,7 @@ doclet.usage.link-platform-properties.parameters= doclet.usage.link-platform-properties.description=にあるプロパティ・ファイルで宣言されているプラットフォーム・ドキュメントのURLにリンクします doclet.usage.excludedocfilessubdir.parameters=,,... -doclet.usage.excludedocfilessubdir.description=指定された名前の'doc-files'サブディレクトリをすべて除外します。\n':'も、セパレータとして引数の任意の場所に使用できます。 +doclet.usage.excludedocfilessubdir.description=指定された名前の'doc-files'サブディレクトリをすべて除外します。\nすべてのサブディレクトリを除外するには'*'を使用します。':'も、セパレータ\nとして引数の任意の場所に使用できます。 doclet.usage.group.parameters= ,... doclet.usage.group.description=指定するパッケージまたはモジュールを概要ページにおいてグループ化します。\n':'も、セパレータとして引数の任意の場所に使用できます。 @@ -544,4 +545,7 @@ doclet.NoFrames_specified=--no-framesオプションは必須ではなくなり # L10N: do not localize the option name -footer doclet.footer_specified=-footerオプションはサポートされなくなったため、無視されます。\n将来のリリースで削除される可能性があります。 +# L10N: do not localize the option name -docfilessubdirs +doclet.docfilessubdirs_specified=ノート: -docfilessubdirsオプションは必須ではなくなり、\n将来のリリースで削除される可能性があります。 + doclet.selectModule=検索するモジュールを選択します。 diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_zh_CN.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_zh_CN.properties index b3a0a3a1197..f1e14ebedd8 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_zh_CN.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_zh_CN.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2010, 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 @@ -178,7 +178,8 @@ doclet.Inheritance_Tree=继承树 doclet.DefinedIn=定义位置 doclet.ReferencedIn=参考位置 doclet.External_Specifications=外部规范 -doclet.External_Specifications.All_Specifications=所有规范 +doclet.External_Specifications.by-host=按主机名显示规范: +doclet.External_Specifications.all-hosts=所有主机名 doclet.External_Specifications.no-host=本地 doclet.Specification=规范 doclet.Summary_Page=概要页 @@ -388,7 +389,7 @@ doclet.usage.version.description=包含 @version 段 doclet.usage.author.description=包含 @author 段 -doclet.usage.docfilessubdirs.description=启用对 'doc-files' 目录的深层复制。\n子目录和所有内容将递归复制到目标 +doclet.usage.docfilessubdirs.description=-docfilessubdirs 选项不再是必需的,可能\n会在未来发行版中删除此选项。 doclet.usage.splitindex.description=将索引分为每个字母对应一个文件 @@ -432,7 +433,7 @@ doclet.usage.link-platform-properties.parameters= doclet.usage.link-platform-properties.description=链接到位于 的属性文件中声明的平台文档 URL doclet.usage.excludedocfilessubdir.parameters=,,... -doclet.usage.excludedocfilessubdir.description=排除包含给定名称的所有 'doc-files' 子目录。\n还可以将 ':' 作为分隔符用于参数中的任何位置。 +doclet.usage.excludedocfilessubdir.description=排除包含给定名称的所有 'doc-files' 子目录。\n使用 '*' 排除所有子目录。还可以将 ':' 作为\n分隔符用于参数中的任何位置。 doclet.usage.group.parameters= ,... doclet.usage.group.description=在概览页面中将指定程序包或模块归到一组。\n还可以将 ':' 作为分隔符用于参数中的任何位置。 @@ -544,4 +545,7 @@ doclet.NoFrames_specified=--no-frames 选项不再是必需的,可能\n会在 # L10N: do not localize the option name -footer doclet.footer_specified=-footer 选项不再受支持并将被忽略。\n可能会在未来发行版中删除此选项。 +# L10N: do not localize the option name -docfilessubdirs +doclet.docfilessubdirs_specified=注:-docfilessubdirs 选项不再是必需的,可能\n会在未来发行版中删除此选项。 + doclet.selectModule=选择要在其中搜索的模块。 diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_de.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_de.properties index a6dbf050bf3..7f262141961 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_de.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_de.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2010, 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 @@ -318,8 +318,10 @@ doclet.search.many_results={0} Ergebnisse gefunden doclet.search.loading=Suchindex wird geladen... doclet.search.searching=Suche wird ausgeführt... doclet.search.redirecting=Zum ersten Ergebnis wird umgeleitet... -# {0} is a select input containing all_modules message below and module names -doclet.search.in=in {0} +# Used as label for the search input field +doclet.search.for=Suchen nach +# Used as label for the module select control which defaults to doclet.search.all_modules +doclet.search.in_modules=in doclet.search.all_modules=allen Modulen doclet.search.modules=Module doclet.search.packages=Packages @@ -327,7 +329,8 @@ doclet.search.classes_and_interfaces=Klassen und Schnittstellen doclet.search.types=Typen doclet.search.members=Mitglieder doclet.search.search_tags=Tags suchen -doclet.search.linkSearchPageLabel=Gehe zur Suchseite +doclet.search.linkSearchPageLabel=Suchseite +doclet.search.linkSearchHelpLabel=Hilfe durchsuchen doclet.snippet.contents.none=@snippet gibt keinen Inhalt an diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_ja.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_ja.properties index 1970203da38..10282979ee4 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_ja.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_ja.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2010, 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 @@ -318,8 +318,10 @@ doclet.search.many_results={0}の結果が見つかりました doclet.search.loading=検索索引をロード中... doclet.search.searching=検索中... doclet.search.redirecting=最初の結果にリダイレクト中... -# {0} is a select input containing all_modules message below and module names -doclet.search.in={0}内 +# Used as label for the search input field +doclet.search.for=検索対象 +# Used as label for the module select control which defaults to doclet.search.all_modules +doclet.search.in_modules=対象 doclet.search.all_modules=すべてのモジュール doclet.search.modules=モジュール doclet.search.packages=パッケージ @@ -327,7 +329,8 @@ doclet.search.classes_and_interfaces=クラスとインタフェース doclet.search.types=タイプ doclet.search.members=メンバー doclet.search.search_tags=タグの検索 -doclet.search.linkSearchPageLabel=検索ページに移動します +doclet.search.linkSearchPageLabel=検索ページ +doclet.search.linkSearchHelpLabel=検索ヘルプ doclet.snippet.contents.none=@snippetにコンテンツが指定されていません diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_zh_CN.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_zh_CN.properties index 62e51c2c1c4..cde88e840db 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_zh_CN.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_zh_CN.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2010, 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 @@ -318,8 +318,10 @@ doclet.search.many_results=找到 {0} 个结果 doclet.search.loading=正在加载搜索索引... doclet.search.searching=正在搜索... doclet.search.redirecting=正在重定向到第一个结果... -# {0} is a select input containing all_modules message below and module names -doclet.search.in=在 {0} 中 +# Used as label for the search input field +doclet.search.for=搜索 +# Used as label for the module select control which defaults to doclet.search.all_modules +doclet.search.in_modules=位于 doclet.search.all_modules=全部模块 doclet.search.modules=模块 doclet.search.packages=程序包 @@ -327,7 +329,8 @@ doclet.search.classes_and_interfaces=类和接口 doclet.search.types=类型 doclet.search.members=成员 doclet.search.search_tags=搜索标记 -doclet.search.linkSearchPageLabel=转至搜索页 +doclet.search.linkSearchPageLabel=搜索页 +doclet.search.linkSearchHelpLabel=搜索帮助 doclet.snippet.contents.none=@snippet 未指定内容 diff --git a/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage_de.properties b/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage_de.properties index 62bfd36181f..547bc591bbd 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage_de.properties +++ b/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage_de.properties @@ -54,15 +54,18 @@ main.command.files=\ @ Liest Optionen aus der D main.opt.footer=\nBei Optionen, die eine erfordern, ist der Wert eine durch Komma getrennte\nListe von Elementen, die jeweils eines der folgenden Formate verwenden:\n \n glob:\n regex: - - err.not.a.task=Aufgabe muss einen der folgenden Werte aufweisen: : {0} err.missing.arg=kein Wert angegeben für {0} err.ambiguous.arg=Wert für Option {0} beginnt mit "--", aber muss das Format {0}= verwenden err.not.a.dir=Kein Verzeichnis: {0} err.not.a.jimage=Keine jimage-Datei: {0} -err.invalid.jimage={0} kann nicht geöffnet werden: {1} err.no.jimage=Kein jimage angegeben err.option.unsupported={0} nicht unterstützt: {1} err.unknown.option=unbekannte Option: {0} err.cannot.create.dir=Verzeichnis {0} kann nicht erstellt werden + +# General failure to open a jimage file. +# {0} = path of jimage file, {1} = underlying error message +err.invalid.jimage={0} kann nicht geöffnet werden: {1} +# More specific alternative for cases of version mismatch +err.wrong.version={0} kann nicht geöffnet werden: Nicht übereinstimmende Datei- und Toolversion\nVerwenden Sie "/bin/jimage" für das JDK, das mit der jimage-Datei verknüpft ist:\n{1} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage_ja.properties b/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage_ja.properties index 7ab6d7f655f..461154b9cc2 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage_ja.properties +++ b/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage_ja.properties @@ -54,15 +54,18 @@ main.command.files=\ @ ファイルからオプ main.opt.footer=\nを必要とするオプションの場合、値は、次の形式のいずれかを使用する、\n要素のカンマ区切りリストになります:\n \n glob:\n regex: - - err.not.a.task=タスクはのいずれかである必要があります: {0} err.missing.arg={0}に値が指定されていません err.ambiguous.arg=オプション{0}の値が"--"で始まっています。{0}=形式を使用する必要があります err.not.a.dir=ディレクトリではありません: {0} err.not.a.jimage=jimageファイルではありません: {0} -err.invalid.jimage={0}を開けません: {1} err.no.jimage=jimageが提供されていません err.option.unsupported={0}はサポートされていません: {1} err.unknown.option=不明なオプション: {0} err.cannot.create.dir=ディレクトリ{0}を作成できません + +# General failure to open a jimage file. +# {0} = path of jimage file, {1} = underlying error message +err.invalid.jimage={0}を開けません: {1} +# More specific alternative for cases of version mismatch +err.wrong.version={0}を開けません: ファイルとツールのバージョンが一致していません\njimageファイルに関連付けられたJDKの''/bin/jimage''を使用してください:\n{1} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage_zh_CN.properties b/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage_zh_CN.properties index 37954b9b743..19ca8617a0a 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage_zh_CN.properties +++ b/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage_zh_CN.properties @@ -54,15 +54,18 @@ main.command.files=\ @<文件名> 从文件中 main.opt.footer=\n对于需要 的选项,值将为逗号分隔的元素列表,\n每个元素使用以下格式之一:\n \n glob:\n regex: - - err.not.a.task=任务必须是 之一:{0} err.missing.arg=没有为{0}指定值 err.ambiguous.arg=选项 {0} 的值以 "--" 开头,应使用 {0}= 格式 err.not.a.dir=不是目录:{0} err.not.a.jimage=不是 jimage 文件:{0} -err.invalid.jimage=无法打开 {0}: {1} err.no.jimage=未提供 jimage err.option.unsupported=不支持{0}: {1} err.unknown.option=未知选项: {0} err.cannot.create.dir=无法创建目录 {0} + +# General failure to open a jimage file. +# {0} = path of jimage file, {1} = underlying error message +err.invalid.jimage=无法打开 {0}: {1} +# More specific alternative for cases of version mismatch +err.wrong.version=无法打开 {0}:文件和工具版本不匹配\n请为与 jimage 文件关联的 JDK 使用 ''/bin/jimage'':\n{1} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_ja.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_ja.properties index a5dc70061f6..54620d1ff26 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_ja.properties +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_ja.properties @@ -45,7 +45,7 @@ compress.argument=[:filter=] compress.description= リソースの圧縮に使用する圧縮。 -compress.usage=\ --compress リソースの圧縮に使用する圧縮:\n 使用可能な値は\n zip-'{0-9}'です。zip-0では圧縮は行われず、\n zip-9では最適な圧縮が行われます。\n デフォルトはzip-6です。 +compress.usage=\ --compress <圧縮> リソースの圧縮に使用する圧縮:\n 使用可能な値は\n zip-'{0-9}'です。zip-0では圧縮は行われず、\n zip-9では最適な圧縮が行われます。\n デフォルトはzip-6です。 compress.warn.argumentdeprecated=警告: --compressの{0}引数は非推奨であり、今後のリリースで削除される可能性があります diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_de.properties b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_de.properties index 345ed36b7be..5a0d6df3950 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_de.properties +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_de.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, 2025, 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 @@ -36,6 +36,9 @@ resource.menu-icon=Menüsymbol resource.rpm-spec-file=RPM-Spezifikationsdatei resource.systemd-unit-file=systemd-Einheitsdatei +summary.property.linux-package-name=Packagename +summary.property.linux-required-packages-search=Suche nach erforderlichen Packages + error.tool-not-found.advice=Installieren Sie die erforderlichen Packages error.tool-old-version.advice=Installieren Sie die erforderlichen Packages @@ -49,19 +52,12 @@ error.rpm-arch-not-detected="RPM-Architektur konnte nicht erkannt werden" message.icon-not-png=Das angegebene Symbol "{0}" ist keine PNG-Datei und wird nicht verwendet. Stattdessen wird das Standardsymbol verwendet. message.test-for-tool=Test für [{0}]. Ergebnis: {1} -message.outputting-to-location=DEB für Installationsprogramm wird generiert in: {0}. -message.output-to-location=Package (.deb) gespeichert in: {0}. message.debs-like-licenses=Debian-Packages müssen eine Lizenz angeben. Bei fehlender Lizenz geben einige Linux-Distributionen eine Meldung über eine Beeinträchtigung der Anwendungsqualität aus. -message.outputting-bundle-location=RPM für Installationsprogramm wird generiert in: {0}. -message.output-bundle-location=Package (.rpm) gespeichert in: {0}. message.ldd-not-available=ldd-Befehl nicht gefunden. Packageabhängigkeiten werden nicht generiert. message.deb-ldd-not-available.advice=Installieren Sie das DEB-Package "libc-bin", um ldd abzurufen. message.rpm-ldd-not-available.advice=Installieren Sie das RPM-Package "glibc-common", um ldd abzurufen. -warning.foreign-app-image=Warnung: app-image-Verzeichnis wurde von jpackage nicht generiert. -message.not-default-bundler-no-dependencies-lookup={0} ist nicht der Standardpackagetyp. Packageabhängigkeiten werden nicht generiert. - -error.unexpected-package-property=Erwarteter Wert der Eigenschaft "{0}": [{1}]. Tatsächlicher Wert in Ausgabepackage: [{2}]. Anscheinend enthielt die benutzerdefinierte Datei "{3}" aus dem Ressourcenverzeichnis einen hartcodierten Wert der Eigenschaft "{0}" +error.unexpected-package-property=Der erwartete Wert der Eigenschaft "{0}" ist [{1}]. Der tatsächliche Wert im Ausgabepackage ist [{2}]. Anscheinend ist der Wert der Eigenschaft "{0}" in der Datei "{3}" im Ressourcenverzeichnis hartcodiert error.unexpected-package-property.advice=Verwenden Sie die Musterzeichenfolge [{0}] anstelle des hartcodierten Wertes [{1}] der {2}-Eigenschaft in der benutzerdefinierten Datei "{3}" -error.unexpected-default-package-property.advice=Legen Sie den Wert der {0}-Eigenschaft in der benutzerdefinierten Datei "{1}" nicht explizit fest +error.unexpected-default-package-property.advice=Legen Sie den Wert der Eigenschaft "{0}" in der benutzerdefinierten Datei "{1}" nicht explizit fest diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_ja.properties b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_ja.properties index d0bc4f73407..472c575ba1f 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_ja.properties +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_ja.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, 2025, 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 @@ -36,6 +36,9 @@ resource.menu-icon=メニュー・アイコン resource.rpm-spec-file=RPM仕様ファイル resource.systemd-unit-file=systemdユニット・ファイル +summary.property.linux-package-name=パッケージ名 +summary.property.linux-required-packages-search=必要なパッケージの検索 + error.tool-not-found.advice=必要なパッケージをインストールしてください error.tool-old-version.advice=必要なパッケージをインストールしてください @@ -49,19 +52,12 @@ error.rpm-arch-not-detected="RPM archの検出に失敗しました" message.icon-not-png=指定したアイコン"{0}"はPNGファイルではなく、使用されません。デフォルト・アイコンがその位置に使用されます。 message.test-for-tool=[{0}]のテスト。結果: {1} -message.outputting-to-location=インストーラのDEBを次に生成しています: {0} -message.output-to-location=パッケージ(.deb)は次に保存されました: {0} message.debs-like-licenses=Debianパッケージではライセンスを指定する必要があります。ライセンスがない場合、一部のLinuxディストリビューションでアプリケーションの品質に問題が発生する場合があります。 -message.outputting-bundle-location=インストーラのRPMを次に生成しています: {0} -message.output-bundle-location=パッケージ(.rpm)は次に保存されました: {0} message.ldd-not-available=lddコマンドが見つかりませんでした。パッケージ依存性は生成されません。 message.deb-ldd-not-available.advice="libc-bin" DEBパッケージをインストールしてlddを取得します。 message.rpm-ldd-not-available.advice="glibc-common" RPMパッケージをインストールしてlddを取得します。 -warning.foreign-app-image=警告: app-imageディレクトリはjpackageで生成されません。 -message.not-default-bundler-no-dependencies-lookup={0}はデフォルトのパッケージ・タイプではありません。パッケージの依存性は生成されません。 - -error.unexpected-package-property="{0}"プロパティに必要な値は[{1}]です。出力パッケージの実際の値は[{2}]です。リソース・ディレクトリのカスタム"{3}"ファイルには、"{0}"プロパティのハードコードされた値が含まれているようです +error.unexpected-package-property="{0}"プロパティの予期される値は[{1}]です。出力パッケージの実際の値は[{2}]です。"{0}"プロパティの値が、リソース・ディレクトリの"{3}"ファイルにハードコードされているようです error.unexpected-package-property.advice=カスタム"{3}"ファイルで{2}プロパティのハードコードされた値[{1}]ではなく、[{0}]パターン文字列を使用してください -error.unexpected-default-package-property.advice=カスタム"{1}"ファイルで{0}プロパティの値を明示的に設定しないでください +error.unexpected-default-package-property.advice=カスタム"{1}"ファイルで"{0}"プロパティの値を明示的に設定しないでください diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_zh_CN.properties b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_zh_CN.properties index f3d62675c4d..00efdc378af 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_zh_CN.properties +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_zh_CN.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, 2025, 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 @@ -36,6 +36,9 @@ resource.menu-icon=菜单图标 resource.rpm-spec-file=RPM 规范文件 resource.systemd-unit-file=systemd 单元文件 +summary.property.linux-package-name=程序包名称 +summary.property.linux-required-packages-search=所需的程序包搜索 + error.tool-not-found.advice=请安装所需的程序包 error.tool-old-version.advice=请安装所需的程序包 @@ -49,19 +52,12 @@ error.rpm-arch-not-detected="无法检测 RPM 体系结构" message.icon-not-png=指定的图标 "{0}" 不是 PNG 文件, 不会使用。将使用默认图标代替。 message.test-for-tool=[{0}] 的测试。结果: {1} -message.outputting-to-location=正在为安装程序生成 DEB, 位置: {0}。 -message.output-to-location=程序包 (.deb) 已保存到: {0}。 message.debs-like-licenses=Debian 程序包应指定许可证。缺少许可证将导致某些 Linux 分发投诉应用程序质量。 -message.outputting-bundle-location=正在为安装程序生成 RPM, 位置: {0}。 -message.output-bundle-location=程序包 (.rpm) 已保存到: {0}。 message.ldd-not-available=未找到 ldd 命令。将不生成程序包被依赖对象。 message.deb-ldd-not-available.advice=安装 "libc-bin" DEB 程序包以获取 ldd。 message.rpm-ldd-not-available.advice=安装 "glibc-common" RPM 程序包以获取 ldd。 -warning.foreign-app-image=警告:jpackage 未生成 app-image 目录。 -message.not-default-bundler-no-dependencies-lookup={0} 不是默认程序包类型。将不生成程序包被依赖对象。 - -error.unexpected-package-property="{0}" 属性的预期值为 [{1}]。输出程序包中的实际值为 [{2}]。与定制的 "{3}" 文件相似,该文件所在的资源目录中包含 "{0}" 属性的硬编码值 +error.unexpected-package-property="{0}" 属性的预期值为 [{1}]。输出程序包中的实际值为 [{2}]。"{0}" 属性的值似乎是在资源目录的 "{3}" 文件中进行了硬编码 error.unexpected-package-property.advice=在定制的 "{3}" 文件中使用 [{0}] 模式字符串,而非 {2} 属性的硬编码值 [{1}] -error.unexpected-default-package-property.advice=请勿在定制的 "{1}" 文件中显式设置 {0} 属性的值 +error.unexpected-default-package-property.advice=请勿在定制的 "{1}" 文件中显式设置 "{0}" 属性的值 diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_de.properties b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_de.properties index 02e8c029ec6..973249c7016 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_de.properties +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_de.properties @@ -23,17 +23,19 @@ # questions. # # -error.invalid-cfbundle-version.advice=Legen Sie einen kompatiblen Wert für "app-version" fest. Gültige Versionsnummern sind ein bis drei durch Punkte getrennte Ganzzahlen. -error.explicit-sign-no-cert=Signatur wurde explizit angefordert, doch es wurde kein Signaturzertifikat gefunden -error.explicit-sign-no-cert.advice=Geben Sie gültige Werte für mac-signing-key-user-name und mac-signing-keychain an -error.certificate.expired=Zertifikat abgelaufen {0} +error.invalid-cfbundle-version.advice=Legen Sie einen kompatiblen Wert für "app-version" fest. Die gültige Version ist eine Zeichenfolge, die aus nicht negativen, durch Punkte getrennten Ganzzahlen besteht. +error.certificate.outside-validity-period=Das Zertifikat "{0}" liegt außerhalb seines Gültigkeitszeitraums error.cert.not.found=Kein Zertifikat gefunden, das [{0}] mit Schlüsselbund [{1}] entspricht error.multiple.certs.found=Mehrere Zertifikate mit Namen [{0}] in Schlüsselbund [{1}] gefunden error.app-image.mac-sign.required=Die Option --mac-sign ist mit einem vordefinierten Anwendungsimage und Typ [app-image] erforderlich -error.tool.failed.with.output="{0}" war mit folgender Ausgabe nicht erfolgreich: error.invalid-runtime-image-missing-file=Im Laufzeitimage "{0}" fehlt die Datei "{1}" +error.invalid-app-image-runtime-image-bin-dir=Laufzeitverzeichnis {0} im vordefinierten Anwendungsimage [{1}] darf nicht den Ordner "bin" enthalten error.invalid-runtime-image-bin-dir=Laufzeitimage "{0}" darf keinen Ordner "bin" enthalten error.invalid-runtime-image-bin-dir.advice=Verwenden Sie die jlink-Option --strip-native-commands, wenn das Laufzeitimage mit Option {0} generiert wird +error.invalid-app-image-plist-file=Ungültige Datei "{0}" im vordefinierten Anwendungsimage +error.invalid-derived-bundle-identifier=Es kann keine gültige Bundle-ID von den Eingabedaten abgeleitet werden +error.invalid-derived-bundle-identifier.advice=Geben Sie die Bundle-ID mit der Option "--mac-package-identifier" an + resource.app-info-plist=Info.plist der Anwendung resource.app-runtime-info-plist=Eingebettete Info.plist von Java Runtime resource.runtime-info-plist=Info.plist von Java Runtime @@ -51,30 +53,27 @@ resource.pkg-background-image=PKG-Hintergrundbild resource.pkg-pdf=Projektdefinitionsdatei resource.launchd-plist-file=launchd-PLIST-Datei -message.bundle-name-too-long-warning={0} ist auf "{1}" gesetzt. Dies ist länger als 16 Zeichen. Kürzen Sie den Wert, um die Mac-Nutzungserfahrung zu verbessern. +summary.property.mac-bundle-identifier=CFBundleIdentifier +summary.property.mac-bundle-name=CFBundleName +summary.property.mac-sign-app-image.format={0} im Verzeichnis "{1}" signieren + +warning.bundle-name-too-long-warning=Bundle-Name "{0}" ist länger als 16 Zeichen. Kürzen Sie den Wert, um die Mac-Nutzungserfahrung zu verbessern. message.preparing-info-plist=Info.plist wird vorbereitet: {0}. message.icon-not-icns= Das angegebene Symbol "{0}" ist keine ICNS-Datei und wird nicht verwendet. Stattdessen wird das Standardsymbol verwendet. message.keychain.error=Schlüsselbundliste kann nicht abgerufen werden. -message.invalid-identifier=Ungültige Mac-Bundle-ID [{0}]. -message.invalid-identifier.advice=Geben Sie die ID mit "--mac-package-identifier" an. -message.building-dmg=DMG-Package für {0} wird erstellt. message.preparing-dmg-setup=DMG-Setup wird vorbereitet: {0}. -message.creating-dmg-file=DMG-Datei wird erstellt: {0}. -message.dmg-cannot-be-overwritten=DMG-Datei [{0}] ist vorhanden und kann nicht entfernt werden. -message.output-to-location=Ergebnis von DMG-Installationsprogramm für {0}: {1}. -message.building-pkg=PKG-Package für {0} wird erstellt. message.preparing-scripts=Packageskripte werden vorbereitet. message.preparing-distribution-dist=distribution.dist wird vorbereitet: {0}. -message.signing.pkg=Warnung: Zum Signieren von PKG müssen Sie möglicherweise mit dem Schlüsselbundverwaltungstool die Option "Immer vertrauen" für Ihr Zertifikat festlegen. message.setfile.dmg=Das Festlegen des benutzerdefinierten Symbols für die DMG-Datei wurde übersprungen, weil das Utility "SetFile" nicht gefunden wurde. Durch Installieren von Xcode mit Befehlszeilentools sollte dieses Problem behoben werden. message.codesign.failed.reason.app.content="codesign" war nicht erfolgreich, und zusätzlicher Anwendungsinhalt wurde über den Parameter "--app-content" angegeben. Wahrscheinlich hat der zusätzliche Inhalt die Integrität des Anwendungs-Bundles beeinträchtigt und den Fehler verursacht. Stellen Sie sicher, das der über den Parameter "--app-content" angegebene Inhalt nicht die Integrität des Anwendungs-Bundles beeinträchtigt, oder fügen Sie ihn im Nachverarbeitungsschritt hinzu. message.codesign.failed.reason.xcode.tools=Möglicher Grund für "codesign"-Fehler ist fehlender Xcode mit Befehlszeilen-Entwicklertools. Installieren Sie Xcode mit Befehlszeilen-Entwicklertools, und prüfen Sie, ob das Problem dadurch beseitigt wird. -message.dmg.license.button.agree=Akzeptieren +message.dmg.license.button.agree=Zustimmen message.dmg.license.button.disagree=Ablehnen message.dmg.license.button.print=Drucken -message.dmg.license.button.save=Sichern... -message.dmg.license.message=Klicken Sie in “Akzeptieren”, wenn Sie mit den Bestimmungen des Software-Lizenzvertrags einverstanden sind. Falls nicht, bitte “Ablehnen” anklicken. Sie können die Software nur installieren, wenn Sie “Akzeptieren” angeklickt haben. -warning.unsigned.app.image=Warnung: Nicht signiertes app-image wird zum Erstellen von signiertem {0} verwendet. -warning.per.user.app.image.signed=Warnung: Konfiguration der installierten Anwendung pro Benutzer wird nicht unterstützt, da "{0}" im vordefinierten signierten Anwendungsimage fehlt. -warning.non.standard.contents.sub.dir=Warnung: Der Dateiname des Verzeichnisses "{0}", das für die Option --app-content angegeben wurde, ist kein Standardunterverzeichnisname im Verzeichnis "Contents" des Anwendungs-Bundles. Möglicherweise verläuft die Codesignierung und/oder Notarisierung im Ergebnisanwendungs-Bundle nicht erfolgreich. -warning.app.content.is.not.dir=Warnung: Der Wert "{0}" der Option --app-content ist kein Verzeichnis. Möglicherweise verläuft die Codesignierung und/oder Notarisierung im Ergebnisanwendungs-Bundle nicht erfolgreich. +message.dmg.license.button.save=Speichern... +message.dmg.license.message=Wenn Sie mit den Bestimmungen dieser Lizenz einverstanden sind, wählen Sie "Zustimmen" aus, um die Software zu installieren. Wenn Sie nicht zustimmen, wählen Sie "Ablehnen" aus. +warning.unsigned.app.image=Nicht signiertes vordefiniertes Anwendungsimage mit signiertem Ausgabepackage +warning.per.user.app.image.signed=Konfiguration der installierten Anwendung pro Benutzer wird nicht unterstützt, da die Datei "{0}" im signierten vordefinierten Anwendungsimage fehlt +warning.non-standard-app-content=Der Wert der Option "--app-content" kann dazu führen, dass das Ergebnisanwendungs-Bundle nicht erfolgreich signiert und/oder notarisiert werden kann +warning.non-standard-app-content.not-dir="{0}" ist kein Verzeichnis. +warning.non-standard-app-content.non-standard-dir-name=Der Name "{0}" von Verzeichnis "{1}" ist kein Standardunterverzeichnisname im Verzeichnis "Contents" eines macOS-Bundle diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties index c9de142d796..a0698c2266f 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties @@ -23,17 +23,19 @@ # questions. # # -error.invalid-cfbundle-version.advice=互換性のある'app-version'値を設定します。有効なバージョンは、ドットで区切られた1から3つの整数です。 -error.explicit-sign-no-cert=署名が明示的に要求されましたが、署名証明書が見つかりません -error.explicit-sign-no-cert.advice=有効なmac-signing-key-user-nameおよびmac-signing-keychainを指定してください -error.certificate.expired=証明書が期限切れです{0} +error.invalid-cfbundle-version.advice=互換性のある'app-version'値を設定します。有効なバージョンは、負でないピリオド区切りの整数で構成される文字列です。 +error.certificate.outside-validity-period=証明書"{0}"は有効期間の範囲外です error.cert.not.found=キーチェーン[{1}]を使用する[{0}]と一致する証明書が見つかりません error.multiple.certs.found=名前[{0}]に一致する複数の証明書がキーチェーン[{1}]で見つかりました error.app-image.mac-sign.required=--mac-signオプションは、事前定義済アプリケーション・イメージおよびタイプ[app-image]で必要です -error.tool.failed.with.output="{0}"は次の出力で失敗しました: error.invalid-runtime-image-missing-file=ランタイム・イメージ"{0}"に"{1}"ファイルがありません +error.invalid-app-image-runtime-image-bin-dir=事前定義済アプリケーション・イメージ[{1}]のランタイム・ディレクトリ{0}に"bin"フォルダを含めることはできません error.invalid-runtime-image-bin-dir=ランタイム・イメージ"{0}"に"bin"フォルダを含めることはできません error.invalid-runtime-image-bin-dir.advice={0}オプションとともに使用されるランタイム・イメージを生成する場合は、--strip-native-commands jlinkオプションを使用します +error.invalid-app-image-plist-file=事前定義済アプリケーション・イメージの"{0}"ファイルが無効です +error.invalid-derived-bundle-identifier=入力データから有効なバンドル識別子を導出できません +error.invalid-derived-bundle-identifier.advice=--mac-package-identifierオプションを使用したバンドル識別子を指定してください + resource.app-info-plist=アプリケーションのInfo.plist resource.app-runtime-info-plist=埋込みJavaランタイムのInfo.plist resource.runtime-info-plist=JavaランタイムのInfo.plist @@ -51,30 +53,27 @@ resource.pkg-background-image=pkg背景イメージ resource.pkg-pdf=プロジェクト定義ファイル resource.launchd-plist-file=launchd plistファイル -message.bundle-name-too-long-warning={0}が16文字を超える''{1}''に設定されています。Macでの操作性をより良くするために短くすることを検討してください。 +summary.property.mac-bundle-identifier=CFBundleIdentifier +summary.property.mac-bundle-name=CFBundleName +summary.property.mac-sign-app-image.format="{1}"ディレクトリの{0}に署名します + +warning.bundle-name-too-long-warning=バンドル名"{0}"が16文字を超えています。Macでの操作性をより良くするために短くすることを検討してください。 message.preparing-info-plist=Info.plistを準備しています: {0}。 message.icon-not-icns= 指定したアイコン"{0}"はICNSファイルではなく、使用されません。デフォルト・アイコンがその位置に使用されます。 message.keychain.error=キーチェーン・リストを取得できません。 -message.invalid-identifier=macバンドル識別子[{0}]が無効です。 -message.invalid-identifier.advice="--mac-package-identifier"で識別子を指定してください。 -message.building-dmg={0}のDMGパッケージを作成しています message.preparing-dmg-setup=dmgの設定を準備しています: {0} -message.creating-dmg-file=DMGファイルを作成しています: {0} -message.dmg-cannot-be-overwritten=Dmgファイルは存在し[{0}]、削除できません。 -message.output-to-location={0}の結果のDMGインストーラ: {1} -message.building-pkg={0}のPKGパッケージを作成しています message.preparing-scripts=パッケージ・スクリプトを準備しています message.preparing-distribution-dist=distribution.distを準備しています: {0} -message.signing.pkg=警告: PKGへの署名の場合、「キーチェーン・アクセス」ツールを使用して証明書に「常に信頼する」を設定する必要があります。 message.setfile.dmg='SetFile'ユーティリティが見つからないため、DMGファイルでのカスタム・アイコンの設定がスキップされました。Xcodeとコマンド・ライン・ツールをインストールすると、この問題は解決されます。 message.codesign.failed.reason.app.content="codesign"が失敗したため、追加のアプリケーション・コンテンツが、"--app-content"パラメータを介して提供されました。追加のコンテンツにより、アプリケーション・バンドルの整合性が損われ、失敗の原因になった可能性があります。"--app-content"パラメータを介して提供されたコンテンツによって、アプリケーション・バンドルの整合性が損われていないことを確認するか、処理後のステップで追加してください。 message.codesign.failed.reason.xcode.tools="codesign"失敗の考えられる理由は、Xcodeとコマンドライン・デベロッパ・ツールの欠落です。Xcodeとコマンドライン・デベロッパ・ツールをインストールして、問題が解決されるかを確認してください。 -message.dmg.license.button.agree=同意します -message.dmg.license.button.disagree=同意しません -message.dmg.license.button.print=印刷する +message.dmg.license.button.agree=同意する +message.dmg.license.button.disagree=同意しない +message.dmg.license.button.print=印刷 message.dmg.license.button.save=保存... -message.dmg.license.message=本ソフトウエア使用許諾契約の条件に同意される場合には、ソフトウエアをインストールするために「同意します」を押してください。 同意されない場合には、「同意しません」を押してください。 -warning.unsigned.app.image=警告: 署名されていないapp-imageを使用して署名された{0}を作成します。 -warning.per.user.app.image.signed=警告: 事前定義済の署名付きアプリケーション・イメージに"{0}"がないため、インストール済アプリケーションのユーザーごとの構成はサポートされません。 -warning.non.standard.contents.sub.dir=警告: --app-contentオプションに指定されたディレクトリ"{0}"のファイル名が、アプリケーション・バンドルの"Contents"ディレクトリ内の標準サブディレクトリ名ではありません。結果アプリケーション・バンドルは、コード署名および/または公証に失敗することがあります。 -warning.app.content.is.not.dir=警告: --app-contentオプションの値"{0}"はディレクトリではありません。結果アプリケーション・バンドルは、コード署名または公証(あるいはその両方)に失敗することがあります。 +message.dmg.license.message=このライセンスの条件に同意する場合は、「同意する」を押してソフトウェアをインストールします。同意しない場合は、「同意しない」を押してください。 +warning.unsigned.app.image=署名なしの事前定義済アプリケーション・イメージと署名付きの出力パッケージ +warning.per.user.app.image.signed=署名付きの事前定義済アプリケーション・イメージに"{0}"ファイルがないため、インストール済アプリケーションのユーザーごとの構成はサポートされません +warning.non-standard-app-content=--app-contentオプションの値によっては、結果アプリケーション・バンドルの署名または公証(あるいはその両方)に失敗することがあります +warning.non-standard-app-content.not-dir="{0}"はディレクトリではありません +warning.non-standard-app-content.non-standard-dir-name=ディレクトリ"{1}"の名前"{0}"は、macOSバンドルの"Contents"ディレクトリ内の標準サブディレクトリ名ではありません diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties index 9a925859af5..9c51fed5eae 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties @@ -23,17 +23,19 @@ # questions. # # -error.invalid-cfbundle-version.advice=设置兼容的 'app-version' 值。有效版本包含一到三个用点分隔的整数。 -error.explicit-sign-no-cert=已明确请求签名,但找不到签名证书 -error.explicit-sign-no-cert.advice=指定有效的 mac-signing-key-user-name 和 mac-signing-keychain -error.certificate.expired=证书已到期 {0} +error.invalid-cfbundle-version.advice=设置兼容的 'app-version' 值。有效版本是由非负、以句点分隔的整数组成的字符串。 +error.certificate.outside-validity-period=证书 "{0}" 超出其有效期 error.cert.not.found=使用密钥链 [{1}] 找不到与 [{0}] 匹配的证书 error.multiple.certs.found=在密钥链 [{1}] 中找到多个与名称 [{0}] 匹配的证书 error.app-image.mac-sign.required=预定义的应用程序映像和类型 [app-image] 需要 --mac-sign 选项 -error.tool.failed.with.output="{0}" 失败,显示以下输出: error.invalid-runtime-image-missing-file=运行时映像 "{0}" 缺少 "{1}" 文件 +error.invalid-app-image-runtime-image-bin-dir=预定义的应用程序映像 [{1}] 中的运行时目录 {0} 不应包含 "bin" 文件夹 error.invalid-runtime-image-bin-dir=运行时映像 "{0}" 不应包含 "bin" 文件夹 error.invalid-runtime-image-bin-dir.advice=生成与 {0} 选项一起使用的运行时映像时,使用 --strip-native-commands jlink 选项 +error.invalid-app-image-plist-file=预定义的应用程序映像中的 "{0}" 文件无效 +error.invalid-derived-bundle-identifier=无法根据输入数据推导有效的包标识符 +error.invalid-derived-bundle-identifier.advice=使用 --mac-package-identifier 选项指定包标识符 + resource.app-info-plist=应用程序 Info.plist resource.app-runtime-info-plist=嵌入式 Java 运行时 Info.plist resource.runtime-info-plist=Java 运行时 Info.plist @@ -51,30 +53,27 @@ resource.pkg-background-image=pkg 背景图像 resource.pkg-pdf=项目定义文件 resource.launchd-plist-file=launchd plist 文件 -message.bundle-name-too-long-warning={0}已设置为 ''{1}'', 其长度超过了 16 个字符。为了获得更好的 Mac 体验, 请考虑将其缩短。 +summary.property.mac-bundle-identifier=CFBundleIdentifier +summary.property.mac-bundle-name=CFBundleName +summary.property.mac-sign-app-image.format=对 "{1}" 目录中的 {0} 签名 + +warning.bundle-name-too-long-warning=包名称 "{0}" 的长度超过了 16 个字符。为了获得更好的 Mac 体验,请考虑将其缩短。 message.preparing-info-plist=正在准备 Info.plist: {0}。 message.icon-not-icns= 指定的图标 "{0}" 不是 ICNS 文件, 不会使用。将使用默认图标代替。 message.keychain.error=无法获取密钥链列表。 -message.invalid-identifier=mac 包标识符 [{0}] 无效。 -message.invalid-identifier.advice=请使用 "--mac-package-identifier" 指定标识符。 -message.building-dmg=正在为 {0} 构建 DMG 程序包。 message.preparing-dmg-setup=正在准备 dmg 设置: {0}。 -message.creating-dmg-file=正在创建 DMG 文件: {0}。 -message.dmg-cannot-be-overwritten=Dmg 文件已存在 [{0}] 且无法删除。 -message.output-to-location=为 {0} 生成的 DMG 安装程序: {1}。 -message.building-pkg=正在为 {0} 构建 PKG 程序包。 message.preparing-scripts=正在准备程序包脚本。 message.preparing-distribution-dist=正在准备 distribution.dist: {0}。 -message.signing.pkg=警告:要对 PKG 进行签名,可能需要使用“密钥链访问”工具为证书设置“始终信任”。 message.setfile.dmg=由于未找到 'SetFile' 实用程序,跳过了针对 DMG 文件设置定制图标的操作。安装带命令行工具的 Xcode 应能解决此问题。 message.codesign.failed.reason.app.content="codesign" 失败,并通过 "--app-content" 参数提供了附加应用程序内容。可能是附加内容破坏了应用程序包的完整性,导致了故障。请确保通过 "--app-content" 参数提供的内容不会破坏应用程序包的完整性,或者在后处理步骤中添加该内容。 message.codesign.failed.reason.xcode.tools="codesign" 失败可能是因为缺少带命令行开发人员工具的 Xcode。请安装带命令行开发人员工具的 Xcode,看看是否可以解决问题。 message.dmg.license.button.agree=同意 message.dmg.license.button.disagree=不同意 message.dmg.license.button.print=打印 -message.dmg.license.button.save=存储... -message.dmg.license.message=如果您同意本许可协议的条款,请按“同意”来安装此软件。如果您不同意,请按“不同意”。 -warning.unsigned.app.image=警告:使用未签名的 app-image 生成已签名的 {0}。 -warning.per.user.app.image.signed=警告:由于预定义的已签名应用程序映像中缺少 "{0}",不支持对已安装应用程序的每用户配置提供支持。 -warning.non.standard.contents.sub.dir=警告:为 --app-content 选项指定的目录 "{0}" 的文件名不是应用程序包的 "Contents" 目录中的标准子目录名称。结果应用程序包可能会使代码签名和/或公证失败。 -warning.app.content.is.not.dir=警告:--app-content 选项的值 "{0}" 不是目录。结果应用程序包可能会使代码签名和/或公证失败。 +message.dmg.license.button.save=保存... +message.dmg.license.message=如果您同意本许可证条款,请按“同意”以安装本软件。如果您不同意,请按“不同意”。 +warning.unsigned.app.image=带已签名输出程序包的未签名的预定义应用程序映像 +warning.per.user.app.image.signed=由于签名的预定义应用程序映像中缺少 "{0}" 文件,将不支持已安装应用程序的每用户配置 +warning.non-standard-app-content=--app-content 选项的值可能会导致结果应用程序包的签名和/或公证失败 +warning.non-standard-app-content.not-dir="{0}" 不是目录 +warning.non-standard-app-content.non-standard-dir-name=目录 "{1}" 的名称 "{0}" 不是 macOS 包的 "Contents" 目录中的标准子目录名称 diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_de.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_de.properties index 5b9a5728912..d9e4dc11f0a 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_de.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_de.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, 2025, 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 @@ -160,8 +160,7 @@ help.option.type.mac=\ Der zu erstellende Packagetyp\n Gültige help.option.vendor=\ Anbieter der Anwendung -help.option.verbose=\ Aktiviert Ausgabe im Verbose-Modus - +help.option.verbose=\ Konfiguriert die Verbose-Ausgabe. Dabei ist "category" einer der folgenden Werte\n "all"\n "console"\n "log"\n "errors"\n "progress"\n "resources"\n "summary"\n "tools"\n "trace"\n "warnings"\n\n Gesamte Konsolenausgabe unterdrücken, Logging über System.Logger-API aktivieren:\n --verbose log\n Alle Nachrichtenkategorien in der Konsole aktivieren:\n --verbose console\n Alle Nachrichtenkategorien außer "trace" und "tools" in der Konsole aktivieren:\n --verbose console,-trace,-tools\n Die Nachrichtenkategorien "trace" und "tools" in der Konsole aktivieren:\n --verbose trace,tools\n Die Nachrichtenkategorien "trace" und "tools" in der Konsole aktivieren und\n Logging über die System.Logger-API aktivieren:\n --verbose log,trace,tools\n\n Wenn die Option ohne den Wert angegeben wird, entspricht sie\n --verbose console,-trace\n Wenn die Option nicht angegeben wird, entspricht sie\n --verbose errors,warnings\n help.option.version=\ Gibt die Produktversion an den Outputstream aus und beendet den Vorgang. help.option.win-console=\ Erstellt einen Konsolenlauncher für die Anwendung. Sollte für\n Anwendungen angegeben werden, die Konsoleninteraktionen erfordern @@ -170,17 +169,18 @@ help.option.win-dir-chooser=\ Fügt ein Dialogfeld hinzu, in dem der Be help.option.win-help-url=\ URL, unter der der Benutzer weitere Informationen oder technische Unterstützung erhält -help.option.win-menu=\ Anforderung zum Hinzufügen einer Startmenüverknüpfung für diese Anwendung +help.option.win-menu=\ Fügt eine Verknüpfung im Startmenü für diese Anwendung hinzu oder fordert diese an,\n wenn "--win-shortcut-prompt" angegeben wird help.option.win-menu-group=\ Startmenügruppe, in der diese Anwendung abgelegt wird -help.option.win-per-user-install=\ Anforderung zum Ausführen einer Installation pro Benutzer +help.option.win-per-user-install=\ Installiert die Anwendung pro Benutzer. \n Ohne diese Option wird sie pro Rechner installiert -help.option.win-shortcut=\ Anforderung zum Hinzufügen einer Desktopverknüpfung für diese Anwendung +help.option.win-shortcut=\ Fügt eine Desktopverknüpfung für diese Anwendung hinzu oder fordert diese an,\n wenn "--win-shortcut-prompt" angegeben wird -help.option.win-shortcut-prompt=\ Fügt ein Dialogfeld hinzu, in dem der Benutzer auswählen kann, ob Verknüpfungen\n vom Installationsprogramm erstellt werden. +help.option.win-shortcut-prompt=\ Fügt ein Dialogfeld hinzu, wenn mindestens "--win-menu" oder "--win-shortcut" angegeben wird,\n in dem der Benutzer auswählen kann, ob diese Verknüpfungen\n vom Installationsprogramm erstellt werden help.option.win-update-url=\ URL der verfügbaren Anwendungsaktualisierungsinformationen help.option.win-upgrade-uuid=\ UUID für Upgrades für dieses Package +help.option.win-with-ui=\ Setzt eine UI für das Installationsprogramm durch diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_ja.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_ja.properties index ca606dda9f8..0b3ae36be46 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_ja.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_ja.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, 2025, 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 @@ -160,8 +160,7 @@ help.option.type.mac=\ 作成するパッケージのタイプ\n help.option.vendor=\ アプリケーションのベンダー -help.option.verbose=\ 詳細な出力を有効にします - +help.option.verbose=\ 詳細出力を構成します。"category"は次のいずれかです\n "all"\n "console"\n "log"\n "errors"\n "progress"\n "resources"\n "summary"\n "tools"\n "trace"\n "warnings"\n\n すべてのコンソール出力を抑止して、System.Logger APIを使用したロギングを有効にします:\n --verbose log\n コンソールですべてのメッセージ・カテゴリを有効にします:\n --verbose console\n コンソールで"trace"と"tools"を除くすべてのメッセージ・カテゴリを有効にします:\n --verbose console,-trace,-tools\n コンソールで"trace"と"tools"のメッセージ・カテゴリを有効にします:\n --verbose trace,tools\n コンソールで"trace"と"tools"のメッセージ・カテゴリを有効にして、\n System.Logger APIを使用したロギングを有効にします:\n --verbose log,trace,tools\n\n 値なしでオプションを指定した場合、次と等しくなります\n --verbose console,-trace\n オプションを指定しない場合、次と等しくなります\n --verbose errors,warnings\n help.option.version=\ 製品バージョンを出力ストリームに出力して終了します help.option.win-console=\ アプリケーションのコンソール・ランチャを作成します。コンソール・\n インタラクションが必要なアプリケーションに指定する必要があります @@ -170,17 +169,18 @@ help.option.win-dir-chooser=\ 製品をインストールするディ help.option.win-help-url=\ ユーザーが詳細情報または技術サポートを取得できるURL -help.option.win-menu=\ このアプリケーションのスタート・メニューのショートカットを追加するリクエスト +help.option.win-menu=\ このアプリケーションのスタート・メニュー・ショートカットを追加するか、\n --win-shortcut-promptが指定されている場合は、追加するかどうかを尋ねます help.option.win-menu-group=\ このアプリケーションを配置するスタート・メニュー・グループ -help.option.win-per-user-install=\ ユーザーごとにインストールを実行するリクエスト +help.option.win-per-user-install=\ ユーザーごとにアプリケーションをインストールします。\n このオプションがない場合、マシンごとにインストールします -help.option.win-shortcut=\ このアプリケーションのデスクトップのショートカットを追加するリクエスト +help.option.win-shortcut=\ このアプリケーションのデスクトップ・ショートカットを追加するか、\n --win-shortcut-promptが指定されている場合は、追加するかどうかを尋ねます -help.option.win-shortcut-prompt=\ ショートカットをインストーラで作成するかどうかをユーザーが\n 選択できるダイアログを追加します。 +help.option.win-shortcut-prompt=\ --win-menuまたは--win-shortcutが少なくとも1つ\n 指定されている場合、これらのショートカットをインストーラで作成するかどうかをユーザーが\n 選択できるダイアログを追加します help.option.win-update-url=\ 使用可能なアプリケーション更新情報のURL help.option.win-upgrade-uuid=\ このパッケージのアップグレードに関連付けられているUUID +help.option.win-with-ui=\ インストーラにUIを強制的に表示します diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_zh_CN.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_zh_CN.properties index ac72c67ee2f..99d1915d936 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_zh_CN.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_zh_CN.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, 2025, 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 @@ -160,8 +160,7 @@ help.option.type.mac=\ 要创建的程序包的类型\n 有效 help.option.vendor=\ 应用程序的供应商 -help.option.verbose=\ 启用详细的输出 - +help.option.verbose=\ 配置详细输出。其中 "category" 为以下项之一\n "all"\n "console"\n "log"\n "errors"\n "progress"\n "resources"\n "summary"\n "tools"\n "trace"\n "warnings"\n\n 隐藏所有控制台输出,通过 System.Logger API 启用日志记录:\n --verbose log\n 在控制台中启用所有消息类别:\n --verbose console\n 在控制台中启用除 "trace" 和 "tools" 之外的所有消息类别:\n --verbose console,-trace,-tools\n 在控制台中启用 "trace" 和 "tools" 消息类别:\n --verbose trace,tools\n 在控制台中启用 "trace" 和 "tools" 消息类别,并\n 通过 System.Logger API 启用日志记录:\n --verbose log,trace,tools\n\n 如果指定了不带值的该选项,则等效于\n --verbose console,-trace\n 如果未指定该选项,则等效于\n --verbose errors,warnings\n help.option.version=\ 将产品版本输出到输出流并退出。 help.option.win-console=\ 为应用程序创建控制台启动程序,应当为\n 需要控制台交互的应用程序指定 @@ -170,17 +169,18 @@ help.option.win-dir-chooser=\ 添加一个对话框以允许用户选 help.option.win-help-url=\ 用户可以从中获取更多信息或技术支持的 URL -help.option.win-menu=\ 请求为此应用程序添加“开始”菜单快捷方式 +help.option.win-menu=\ 为此应用程序添加“开始”菜单快捷方式,或者\n 如果指定了 --win-shortcut-prompt,则请求执行此操作 help.option.win-menu-group=\ 此应用程序所在的“开始”菜单组 -help.option.win-per-user-install=\ 请求基于每个用户执行安装 +help.option.win-per-user-install=\ 按用户安装应用程序。\n 如果没有此选项,则按计算机安装 -help.option.win-shortcut=\ 请求为此应用程序添加桌面快捷方式 +help.option.win-shortcut=\ 为此应用程序添加桌面快捷方式,或者\n 如果指定了 --win-shortcut-prompt,则请求执行此操作 -help.option.win-shortcut-prompt=\ 添加一个对话框以允许用户选择是否将由安装程序\n 创建快捷方式。 +help.option.win-shortcut-prompt=\ 如果至少指定了 --win-menu 或 --win-shortcut 中的一个,则添加一个\n 对话框,使用户可以选择是否将由安装程序创建\n 这些快捷方式 help.option.win-update-url=\ 可用应用程序更新信息的 URL help.option.win-upgrade-uuid=\ 与此程序包的升级关联的 UUID +help.option.win-with-ui=\ 强制安装程序具有 UI diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_de.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_de.properties index 7816f8ee71a..f27d7fa0c01 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_de.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_de.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, 2025, 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 @@ -28,14 +28,24 @@ param.copyright.default=Copyright (C) {0,date,YYYY} param.vendor.default=Unbekannt bundle-type.win-app=Windows-Anwendungsimage -bundle-type.win-exe=EXE-Installationsprogrammpackage -bundle-type.win-msi=MSI-Installationsprogrammpackage +bundle-type.win-exe=Windows-EXE-Installationsprogramm +bundle-type.win-msi=Windows-MSI-Installationsprogramm bundle-type.mac-app=Mac-Anwendungsimage bundle-type.mac-dmg=Mac-DMG-Package bundle-type.mac-pkg=Mac-PKG-Package bundle-type.linux-app=Linux-Anwendungsimage -bundle-type.linux-deb=DEB-Bundle -bundle-type.linux-rpm=RPM-Bundle +bundle-type.linux-deb=Linux-DEB-Package +bundle-type.linux-rpm=Linux-RPM-Package + +summary.property.operation=Vorgang +summary.property.operation.format={0} erstellen +summary.property.output-bundle=Ausgabe-Bundle +summary.property.version=Version +summary.warning=WARNING: {0} +summary.multi-line-warning=WARNING: {0}: + +summary.value.disabled=Deaktiviert +summary.value.enabled=Aktiviert resource.post-app-image-script=Auszuführendes Skript nach dem Auffüllen des Anwendungsimages @@ -43,13 +53,28 @@ message.using-default-resource=Standardpackageressource {0} {1} wird verwendet ( message.no-default-resource=Keine Standardpackageressource {0} (durch Hinzufügen von {1} zu resource-dir ist eine Anpassung möglich). message.using-custom-resource-from-file=Benutzerdefinierte Packageressource {0} wird verwendet (aus Datei {1} geladen). message.using-custom-resource=Benutzerdefinierte Packageressource {0} wird verwendet (aus {1} geladen). -message.creating-app-bundle=Anwendungspackage {0} wird in {1} erstellt + +message.create-package=Ausgabepackagedatei wird erstellt... +message.create-app-image=Ausgabeverzeichnis für Anwendungsimage wird erstellt... +message.package-created=Ausgabepackagedatei wurde erfolgreich erstellt +message.app-image-created=Ausgabeverzeichnis für Anwendungsimage wurde erfolgreich erstellt + message.debug-working-directory=Arbeitsverzeichnis für Debug beibehalten: {0} -message.bundle-created={0}-Package wurde erfolgreich erstellt + message.module-version=Version "{0}" aus Modul "{1}" wird als Anwendungsversion verwendet +message.release-version=Version "{0}" aus "release"-Datei der vordefinierten Laufzeit wird als Packageversion verwendet +message.version-normalized=Version "{0}" wird mit Normalisierung auf das von der Plattform unterstützte Format von "{1}" verwendet -message.error-header={0} +message.error-header=Fehler: {0} message.advice-header=Empfehlung zur Behebung: {0} +message.failed-command-output-header=Befehlsausgabe: + +progress.warning-header=WARNING: {0} +progress.warning-header2=WARNING: {0}: {1} + +error.command-failed-unexpected-output=Unerwartete Ausgabe bei Ausführung des Befehls {0} +error.command-failed-unexpected-exit-code=Unerwarteter Exitcode {0} bei Ausführung des Befehls {1} +error.command-failed-timed-out=Timeout bei Befehl {0} error.version-string-empty=Version darf keine leere Zeichenfolge sein error.version-string-zero-length-component=Version [{0}] enthält eine Komponente mit Nulllänge @@ -75,13 +100,19 @@ error.parameter-not-directory=Der für Parameter {1} angegebene Wert "{0}" ist k error.parameter-not-empty-directory=Der für Parameter {1} angegebene Wert "{0}" ist kein leeres Verzeichnis oder kein vorhandener Pfad error.parameter-not-url=Der für Parameter {1} angegebene Wert "{0}" ist keine gültige URL error.parameter-not-launcher-shortcut-dir=Der für Parameter {1} angegebene Wert "{0}" ist kein gültiges Verknüpfungsstartverzeichnis +error.parameter-not-mac-bundle=Der für Parameter {1} angegebene Wert "{0}" ist kein gültiges macOS-Bundle +error.parameter-not-mac-bundle-identifier=Der für Parameter {1} angegebene Wert "{0}" ist keine gültige macOS-Bundle-ID. +error.parameter-not-mac-bundle-identifier.advice=Die Bundle-ID darf keine leere Zeichenfolge sein und nur alphanumerische Zeichen (A-Z, a-z und 0-9), Bindestriche (-) und Punkte (.) enthalten error.path-parameter-ioexception=I/O-Fehler beim Zugriff auf Pfadwert "{0}" von Parameter {1} +error.parameter-invalid-value=Ungültiger Wert "{0}" für Parameter {1} angegeben error.parameter-add-launcher-malformed=Der für Parameter {1} angegebene Wert "{0}" stimmt nicht mit dem Muster = überein error.parameter-add-launcher-not-file=Der Wert des Pfades zu einer Eigenschaftendatei "{0}", der für den zusätzlichen Launcher "{1}" bereitgestellt wird, ist kein gültiger Dateipfad error.properties-parameter-not-path=Der für Eigenschaft "{1}" in Datei "{2}" angegebene Wert "{0}" ist kein gültiger Pfad error.properties-parameter-not-file=Der für Eigenschaft "{1}" in Datei "{2}" angegebene Wert "{0}" ist keine Datei +error.properties-parameter-not-directory=Der für Eigenschaft "{1}" in Datei "{2}" angegebene Wert "{0}" ist kein Verzeichnis error.properties-parameter-not-launcher-shortcut-dir=Der für Eigenschaft "{1}" in Datei "{2}" angegebene Wert "{0}" ist kein gültiges Verknüpfungsstartverzeichnis +error.no-extensions-for-file-association=Für Dateiverknüpfungsnummer {0} wurden keine Erweiterungen angegeben error.no-content-types-for-file-association=Für Dateiverknüpfungsnummer {0} wurden keine MIME-Typen angegeben error.no-content-types-for-file-association.advice=Geben Sie einen MIME-Typ für Dateiverknüpfungsnummer {0} an error.too-many-content-types-for-file-association=Für Dateiverknüpfungsnummer {0} wurde mehr als ein MIME-Typ angegeben @@ -96,7 +127,11 @@ error.tool-not-found.advice=Installieren Sie "{0}" error.tool-old-version="{0}" {1} oder eine neuere Version kann nicht gefunden werden error.tool-old-version.advice=Installieren Sie "{0}" {1} oder eine neuere Version -error.jlink.failed=jlink nicht erfolgreich mit: {0} +warning.tempdir.cleanup-failed=Temporäres Verzeichnis {0} konnte nicht bereinigt werden +warning.tempdir.cleanup-file-failed=Datei "{0}" im temporären Verzeichnis konnte nicht gelöscht werden + +error.output-bundle-cannot-be-overwritten=Ausgabepackagedatei "{0}" ist vorhanden und kann nicht entfernt werden. + error.blocked.option=jlink-Option [{0}] ist in --jlink-options nicht zulässig error.no.name=Name nicht mit --name angegeben. Es kann auch kein Name aus app-image abgeleitet werden error.no.name.advice=Geben Sie den Namen mit --name an diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties index 5db5ead7577..37d043861cb 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, 2025, 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 @@ -28,14 +28,24 @@ param.copyright.default=Copyright (C) {0,date,YYYY} param.vendor.default=不明 bundle-type.win-app=Windowsアプリケーション・イメージ -bundle-type.win-exe=EXEインストーラ・パッケージ -bundle-type.win-msi=MSIインストーラ・パッケージ +bundle-type.win-exe=Windows EXEインストーラ +bundle-type.win-msi=Windows MSIインストーラ bundle-type.mac-app=Macアプリケーション・イメージ bundle-type.mac-dmg=Mac DMGパッケージ bundle-type.mac-pkg=Mac PKGパッケージ bundle-type.linux-app=Linuxアプリケーション・イメージ -bundle-type.linux-deb=DEBバンドル -bundle-type.linux-rpm=RPMバンドル +bundle-type.linux-deb=Linux DEBパッケージ +bundle-type.linux-rpm=Linux RPMパッケージ + +summary.property.operation=操作 +summary.property.operation.format={0}の作成 +summary.property.output-bundle=出力バンドル +summary.property.version=バージョン +summary.warning=WARNING: {0} +summary.multi-line-warning=WARNING: {0}: + +summary.value.disabled=無効 +summary.value.enabled=有効 resource.post-app-image-script=アプリケーション・イメージを移入した後に実行するスクリプト @@ -43,13 +53,28 @@ message.using-default-resource=デフォルトのパッケージ・リソース{ message.no-default-resource=デフォルトのパッケージ・リソース{0}なし({1}をresource-dirに追加してカスタマイズ)。 message.using-custom-resource-from-file=カスタム・パッケージ・リソース{0}の使用(ファイル{1}からロード済) message.using-custom-resource=カスタム・パッケージ・リソース{0}の使用({1}からロード済) -message.creating-app-bundle=アプリケーション・パッケージを作成しています: {1}内の{0} + +message.create-package=出力パッケージ・ファイルを作成しています... +message.create-app-image=出力アプリケーション・イメージ・ディレクトリを作成しています... +message.package-created=出力パッケージ・ファイルの作成に成功しました +message.app-image-created=出力アプリケーション・イメージ・ディレクトリの作成に成功しました + message.debug-working-directory=デバッグの作業ディレクトリが保持されました: {0} -message.bundle-created={0}パッケージの作成に成功しました + message.module-version=モジュール"{1}"のバージョン"{0}"をアプリケーション・バージョンとして使用 +message.release-version=事前定義済ランタイムの"release"ファイルのバージョン"{0}"をパッケージ・バージョンとして使用 +message.version-normalized="{1}"からプラットフォームでサポートされる形式に正規化されたバージョン"{0}"を使用 -message.error-header={0} +message.error-header=エラー: {0} message.advice-header=修正のアドバイス: {0} +message.failed-command-output-header=コマンド出力: + +progress.warning-header=WARNING: {0} +progress.warning-header2=WARNING: {0}: {1} + +error.command-failed-unexpected-output=コマンド{0}を実行中の予期しない出力 +error.command-failed-unexpected-exit-code=コマンド{1}を実行中の予期しない終了コード{0} +error.command-failed-timed-out=コマンド{0}がタイムアウトしました error.version-string-empty=バージョンを空の文字列にすることはできません error.version-string-zero-length-component=バージョン[{0}]に長さゼロのコンポーネントが含まれます @@ -75,13 +100,19 @@ error.parameter-not-directory=パラメータ{1}に指定された値"{0}"はデ error.parameter-not-empty-directory=パラメータ{1}に指定された値"{0}"が空のディレクトリでないか、存在しないパスです error.parameter-not-url=パラメータ{1}に指定された値"{0}"は有効なURLではありません error.parameter-not-launcher-shortcut-dir=パラメータ{1}に指定された値"{0}"は、有効なショートカット起動ディレクトリではありません +error.parameter-not-mac-bundle=パラメータ{1}に指定された値"{0}"は有効なmacOSバンドルではありません +error.parameter-not-mac-bundle-identifier=パラメータ{1}に指定された値"{0}"は有効なmacOSバンドル識別子ではありません。 +error.parameter-not-mac-bundle-identifier.advice=バンドル識別子は、英数字(A-Z、a-z、0-9)、ハイフン(-)およびピリオド(.)のみを含む空でない文字列である必要があります error.path-parameter-ioexception=パラメータ{1}のパス値"{0}"へのアクセス中にI/Oエラーが発生しました +error.parameter-invalid-value=パラメータ{1}に指定された値"{0}"は無効です error.parameter-add-launcher-malformed=パラメータ{1}に指定された値"{0}"がパターン=と一致しません error.parameter-add-launcher-not-file=追加のランチャ"{1}"に指定されたプロパティ・ファイル"{0}"へのパスの値は有効なファイル・パスではありません error.properties-parameter-not-path="{2}"ファイルのプロパティ"{1}"に指定された値"{0}"は有効なパスではありません error.properties-parameter-not-file="{2}"ファイルのプロパティ"{1}"に指定された値"{0}"はファイルではありません +error.properties-parameter-not-directory="{2}"ファイルのプロパティ"{1}"に指定された値"{0}"はディレクトリではありません error.properties-parameter-not-launcher-shortcut-dir="{2}"ファイルのプロパティ"{1}"に指定された値"{0}"は、有効なショートカット起動ディレクトリではありません +error.no-extensions-for-file-association=ファイル・アソシエーション番号{0}に拡張子が指定されませんでした error.no-content-types-for-file-association=ファイル・アソシエーション番号{0}にMIMEタイプが指定されませんでした error.no-content-types-for-file-association.advice=ファイル・アソシエーション番号{0}にMIMEタイプを指定してください error.too-many-content-types-for-file-association=ファイル・アソシエーション番号{0}に複数のMIMEタイプが指定されました @@ -96,7 +127,11 @@ error.tool-not-found.advice="{0}"をインストールしてください error.tool-old-version="{0}" {1}以降が見つかりません error.tool-old-version.advice="{0}" {1}以降をインストールしてください -error.jlink.failed=jlinkが次で失敗しました: {0} +warning.tempdir.cleanup-failed=一時ディレクトリ{0}のクリーンアップに失敗しました +warning.tempdir.cleanup-file-failed=一時ディレクトリの"{0}"ファイルの削除に失敗しました + +error.output-bundle-cannot-be-overwritten=出力パッケージ・ファイル"{0}"は存在しており、削除できません。 + error.blocked.option=jlinkオプション[{0}]は--jlink-optionsでは許可されません error.no.name=名前が--nameで指定されておらず、app-imageから推論できません error.no.name.advice=--nameで名前を指定します diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties index 23540af7db2..559a7f93d2c 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, 2025, 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 @@ -28,14 +28,24 @@ param.copyright.default=版权所有 (C) {0,date,YYYY} param.vendor.default=未知 bundle-type.win-app=Windows 应用程序映像 -bundle-type.win-exe=EXE 安装程序包 -bundle-type.win-msi=MSI 安装程序包 +bundle-type.win-exe=Windows EXE 安装程序 +bundle-type.win-msi=Windows MSI 安装程序 bundle-type.mac-app=Mac 应用程序映像 bundle-type.mac-dmg=Mac DMG 程序包 bundle-type.mac-pkg=Mac PKG 程序包 bundle-type.linux-app=Linux 应用程序映像 -bundle-type.linux-deb=DEB 包 -bundle-type.linux-rpm=RPM 包 +bundle-type.linux-deb=Linux DEB 程序包 +bundle-type.linux-rpm=Linux RPM 程序包 + +summary.property.operation=操作 +summary.property.operation.format=创建 {0} +summary.property.output-bundle=输出包 +summary.property.version=版本 +summary.warning=WARNING: {0} +summary.multi-line-warning=WARNING: {0}: + +summary.value.disabled=已禁用 +summary.value.enabled=已启用 resource.post-app-image-script=要在填充应用程序映像之后运行的脚本 @@ -43,13 +53,28 @@ message.using-default-resource=使用默认程序包资源 {0} {1}(将 {2} 添 message.no-default-resource=无默认程序包资源 {0}(将 {1} 添加到 resource-dir 中以进行定制)。 message.using-custom-resource-from-file=使用定制程序包资源 {0} (从文件 {1} 加载)。 message.using-custom-resource=使用定制程序包资源 {0} (从 {1} 加载)。 -message.creating-app-bundle=正在 {1} 中创建应用程序包 {0} + +message.create-package=正在构建输出程序包文件... +message.create-app-image=正在构建输出应用程序映像目录... +message.package-created=已成功构建输出程序包文件 +message.app-image-created=已成功构建输出应用程序映像目录 + message.debug-working-directory=用于调试的已保留工作目录: {0} -message.bundle-created=已成功地构建 {0} 程序包 + message.module-version=正在将模块 "{1}" 中的版本 "{0}" 用作应用程序版本 +message.release-version=将预定义运行时的 "release" 文件中的版本 "{0}" 用作程序包版本 +message.version-normalized=使用版本 "{0}",该版本已从 "{1}" 规范化为平台支持的格式 -message.error-header={0} +message.error-header=错误:{0} message.advice-header=修复建议:{0} +message.failed-command-output-header=命令输出: + +progress.warning-header=WARNING: {0} +progress.warning-header2=WARNING: {0}: {1} + +error.command-failed-unexpected-output=执行命令 {0} 时出现意外的输出 +error.command-failed-unexpected-exit-code=执行命令 {1} 时出现意外的退出代码 {0} +error.command-failed-timed-out=超时命令 {0} error.version-string-empty=版本不能为空字符串 error.version-string-zero-length-component=版本 [{0}] 包含长度为零的组件 @@ -75,13 +100,19 @@ error.parameter-not-directory=为参数 {1} 提供的值 "{0}" 不是目录 error.parameter-not-empty-directory=为参数 {1} 提供的值 "{0}" 不是空目录或是不存在的路径 error.parameter-not-url=为参数 {1} 提供的值 "{0}" 不是有效的 URL error.parameter-not-launcher-shortcut-dir=为参数 {1} 提供的值 "{0}" 不是有效的快捷方式启动目录 +error.parameter-not-mac-bundle=为参数 {1} 提供的值 "{0}" 不是有效的 macOS 包 +error.parameter-not-mac-bundle-identifier=为参数 {1} 提供的值 "{0}" 不是有效的 macOS 包标识符。 +error.parameter-not-mac-bundle-identifier.advice=包标识符必须是仅包含字母数字字符(A-Z、a-z 和 0-9)、连字符 (-) 和句点 (.) 的非空字符串 error.path-parameter-ioexception=访问参数 {1} 的路径值 "{0}" 时出现 I/O 错误 +error.parameter-invalid-value=为参数 {1} 提供的值 "{0}" 无效 error.parameter-add-launcher-malformed=为参数 {1} 提供的值 "{0}" 与模式 = 不匹配 error.parameter-add-launcher-not-file=为其他启动程序 "{1}" 提供的属性文件 "{0}" 的路径值不是有效的文件路径 error.properties-parameter-not-path=为 "{2}" 文件中的属性 "{1}" 提供的值 "{0}" 不是有效路径 error.properties-parameter-not-file=为 "{2}" 文件中的属性 "{1}" 提供的值 "{0}" 不是文件 +error.properties-parameter-not-directory=为 "{2}" 文件中的属性 "{1}" 提供的值 "{0}" 不是目录 error.properties-parameter-not-launcher-shortcut-dir=为 "{2}" 文件中的属性 "{1}" 提供的值 "{0}" 不是有效的快捷方式启动目录 +error.no-extensions-for-file-association=没有为文件关联号 {0} 指定扩展名 error.no-content-types-for-file-association=没有为文件关联号{0}指定 MIME 类型 error.no-content-types-for-file-association.advice=为文件关联号 {0} 指定 MIME 类型 error.too-many-content-types-for-file-association=为文件关联号{0}指定了多个 MIME 类型 @@ -96,7 +127,11 @@ error.tool-not-found.advice=请安装 "{0}" error.tool-old-version=找不到 "{0}" {1} 或更新版本 error.tool-old-version.advice=请安装 "{0}" {1} 或更新版本 -error.jlink.failed=jlink 失败,出现 {0} +warning.tempdir.cleanup-failed=无法清除临时目录 {0} +warning.tempdir.cleanup-file-failed=无法删除临时目录中的 "{0}" 文件 + +error.output-bundle-cannot-be-overwritten=输出程序包文件 "{0}" 已存在且无法删除。 + error.blocked.option=不允许在 --jlink-options 中使用 jlink 选项 [{0}] error.no.name=未使用 --name 指定名称,无法从 app-image 推断名称 error.no.name.advice=使用 --name 指定名称 diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_de.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_de.properties index baaba64b398..a615f87841b 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_de.properties +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_de.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, 2025, 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 @@ -36,8 +36,12 @@ resource.launcher-as-service-wix-file=WiX-Projektdatei für Serviceinstallations resource.wix-src-conv=XSLT-Stylesheet zum Konvertieren von WiX-Quellen vom Format WiX v3 in WiX v4 resource.installer-exe=Ausführbares Installationsprogramm -error.no-wix-tools=WiX-Tools nicht gefunden. Gesucht wurden WiX v3 light.exe und candle.exe oder WiX v4/v5 wix.exe, aber keine der Dateien wurde gefunden -error.no-wix-tools.advice=Laden Sie WiX 3.0 oder höher von https://wixtoolset.org herunter, und fügen Sie es zu PATH hinzu. +summary.property.win-product-code=MSI-ProductCode +summary.property.win-upgrade-code=MSI-UpgradeCode +summary.property.win-wix-version=WiX-Toolkitversion + +error.no-wix-tools=Keine verwendbare WiX Toolset-Installation gefunden +error.no-wix-tools.advice=Installieren Sie die neueste WiX-Version v3 von https://github.com/wixtoolset/wix3/releases oder WiX v4+ von https://github.com/wixtoolset/wix/releases error.version-string-wrong-format.advice=Setzen Sie den Wert des --app-version-Parameters auf eine gültige ProductVersion des Windows-Installationsprogramms. error.msi-product-version-components=Versionszeichenfolge [{0}] muss zwischen 2 und 4 Komponenten aufweisen. error.msi-product-version-major-out-of-range=Hauptversion muss im Bereich [0, 255] liegen @@ -45,7 +49,6 @@ error.msi-product-version-build-out-of-range=Build-Teil der Version muss im Bere error.msi-product-version-minor-out-of-range=Nebenversion muss im Bereich [0, 255] liegen error.version-swap=Versionsinformationen für {0} konnten nicht aktualisiert werden error.icon-swap=Symbol für {0} konnte nicht aktualisiert werden -error.invalid-envvar=Ungültiger Wert der {0}-Umgebungsvariable error.lock-resource=Sperren nicht erfolgreich: {0} error.unlock-resource=Aufheben der Sperre nicht erfolgreich: {0} error.read-wix-l10n-file=Datei {0} konnte nicht geparst werden @@ -55,14 +58,5 @@ error.missing-service-installer=Serviceinstallationsprogramm "service-installer. error.missing-service-installer.advice=Fügen Sie das Serviceinstallationsprogramm "service-installer.exe" zum Ressourcenverzeichnis hinzu message.icon-not-ico=Das angegebene Symbol "{0}" ist keine ICO-Datei und wird nicht verwendet. Stattdessen wird das Standardsymbol verwendet. -message.potential.windows.defender.issue=Warnung: Windows Defender verhindert eventuell die korrekte Ausführung von jpackage. Wenn ein Problem auftritt, deaktivieren Sie das Echtzeitmonitoring, oder fügen Sie einen Ausschluss für das Verzeichnis "{0}" hinzu. -message.outputting-to-location=EXE für Installationsprogramm wird generiert in: {0}. -message.output-location=Installationsprogramm (.exe) gespeichert in: {0} message.tool-version=[{0}]-Version [{1}] erkannt. -message.wrong-tool-version=[{0}]-Version {1} wurde erkannt. Erforderlich ist jedoch Version {2}. -message.use-wix36-features=WiX {0} erkannt. Erweiterte Bereinigungsaktion wird aktiviert. -message.product-code=MSI-ProductCode: {0}. -message.upgrade-code=MSI-UpgradeCode: {0}. message.preparing-msi-config=MSI-Konfiguration wird vorbereitet: {0}. -message.generating-msi=MSI wird generiert: {0}. - diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties index 119c7532b1f..2cd43d436e8 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, 2025, 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 @@ -36,8 +36,12 @@ resource.launcher-as-service-wix-file=サービス・インストーラWiXプロ resource.wix-src-conv=WiXソースをWiX v3からWiX v4フォーマットに変換するXSLTスタイルシート resource.installer-exe=インストーラ実行可能ファイル -error.no-wix-tools=WiXツールが見つかりません。WiX v3 light.exeとcandle.exeまたはWiX v4/v5 wix.exeを探しましたが、いずれも見つかりませんでした -error.no-wix-tools.advice=WiX 3.0以降をhttps://wixtoolset.orgからダウンロードし、PATHに追加します。 +summary.property.win-product-code=MSI ProductCode +summary.property.win-upgrade-code=MSI UpgradeCode +summary.property.win-wix-version=WiX Toolkitバージョン + +error.no-wix-tools=使用可能なWiX Toolsetインストールが見つかりません +error.no-wix-tools.advice=最新のWiX v3をhttps://github.com/wixtoolset/wix3/releasesからインストールするか、WiX v4+をhttps://github.com/wixtoolset/wix/releasesからインストールしてください error.version-string-wrong-format.advice=--app-versionパラメータの値を有効なWindows Installer ProductVersionに設定します。 error.msi-product-version-components=バージョン文字列[{0}]には、2から4つのコンポーネントが含まれている必要があります。 error.msi-product-version-major-out-of-range=メジャー・バージョンは範囲[0, 255]内である必要があります @@ -45,7 +49,6 @@ error.msi-product-version-build-out-of-range=バージョンのビルド部分 error.msi-product-version-minor-out-of-range=マイナー・バージョンは範囲[0, 255]内である必要があります error.version-swap={0}のバージョン情報の更新に失敗しました error.icon-swap={0}のアイコンの更新に失敗しました -error.invalid-envvar={0}環境変数の値が無効です error.lock-resource=ロックに失敗しました: {0} error.unlock-resource=ロック解除に失敗しました: {0} error.read-wix-l10n-file={0}ファイルの解析に失敗しました @@ -55,14 +58,5 @@ error.missing-service-installer=リソース・ディレクトリに'service-ins error.missing-service-installer.advice=リソース・ディレクトリに'service-installer.exe'サービス・インストーラを追加します message.icon-not-ico=指定したアイコン"{0}"はICOファイルではなく、使用されません。デフォルト・アイコンがその位置に使用されます。 -message.potential.windows.defender.issue=警告: Windows Defenderが原因でjpackageが機能しないことがあります。問題が発生した場合は、リアルタイム・モニタリングを無効にするか、ディレクトリ"{0}"の除外を追加することにより、問題に対処できます。 -message.outputting-to-location=インストーラのEXEを次に生成しています: {0} -message.output-location=インストーラ(.exe)は次に保存されました: {0} message.tool-version=[{0}]バージョン[{1}]が検出されました。 -message.wrong-tool-version=[{0}]バージョン{1}が検出されましたが、バージョン{2}が必要です。 -message.use-wix36-features=WiX {0}が検出されました。拡張クリーンアップ・アクションを有効化しています。 -message.product-code=MSI ProductCode: {0}。 -message.upgrade-code=MSI UpgradeCode: {0}。 message.preparing-msi-config=MSI構成を準備しています: {0} -message.generating-msi=MSIを生成しています: {0}。 - diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties index 66d8a9d8b96..4c0c2e826ba 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, 2025, 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 @@ -36,8 +36,12 @@ resource.launcher-as-service-wix-file=服务安装程序 WiX 项目文件 resource.wix-src-conv=将 WiX 源码从 WiX v3 格式转换为 WiX v4 格式的 XSLT 样式表 resource.installer-exe=安装程序可执行文件 -error.no-wix-tools=找不到 WiX 工具。已查找 WiX v3 light.exe 和 candle.exe 或 WiX v4/v5 wix.exe,但都未找到 -error.no-wix-tools.advice=从 https://wixtoolset.org 下载 WiX 3.0 或更高版本,然后将其添加到 PATH。 +summary.property.win-product-code=MSI ProductCode +summary.property.win-upgrade-code=MSI UpgradeCode +summary.property.win-wix-version=WiX 工具包版本 + +error.no-wix-tools=未找到可用的 WiX 工具集安装 +error.no-wix-tools.advice=从 https://github.com/wixtoolset/wix3/releases 安装最新的 WiX v3,或从 https://github.com/wixtoolset/wix/releases 安装 WiX v4+ error.version-string-wrong-format.advice=将 --app-version 参数的值设置为有效的 Windows Installer ProductVersion。 error.msi-product-version-components=版本字符串 [{0}] 必须包含 2 到 4 个组成部分。 error.msi-product-version-major-out-of-range=主版本必须位于 [0, 255] 范围中 @@ -45,7 +49,6 @@ error.msi-product-version-build-out-of-range=版本的工作版本部分必须 error.msi-product-version-minor-out-of-range=次版本必须位于 [0, 255] 范围中 error.version-swap=无法更新 {0} 的版本信息 error.icon-swap=无法更新 {0} 的图标 -error.invalid-envvar={0} 环境变量的值无效 error.lock-resource=无法锁定:{0} error.unlock-resource=无法解锁:{0} error.read-wix-l10n-file=无法解析 {0} 文件 @@ -55,14 +58,5 @@ error.missing-service-installer=在资源目录中找不到 'service-installer.e error.missing-service-installer.advice=将 'service-installer.exe' 服务安装程序添加到资源目录 message.icon-not-ico=指定的图标 "{0}" 不是 ICO 文件, 不会使用。将使用默认图标代替。 -message.potential.windows.defender.issue=警告:Windows Defender 可能会阻止 jpackage 正常工作。如果存在问题,可以通过禁用实时监视或者为目录 "{0}" 添加排除项来解决。 -message.outputting-to-location=正在为安装程序生成 EXE, 位置: {0}。 -message.output-location=安装程序 (.exe) 已保存到: {0} message.tool-version=检测到 [{0}] 版本 [{1}]。 -message.wrong-tool-version=检测到 [{0}] 版本 {1}, 但需要版本 {2}。 -message.use-wix36-features=检测到 WiX {0}。正在启用高级清除操作。 -message.product-code=MSI ProductCode:{0}。 -message.upgrade-code=MSI UpgradeCode:{0}。 message.preparing-msi-config=正在准备 MSI 配置: {0}。 -message.generating-msi=正在生成 MSI: {0}。 - From 9c3a662c07084298c80f823ddab2730ade4661c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Walln=C3=B6fer?= Date: Mon, 22 Jun 2026 19:53:11 +0000 Subject: [PATCH 032/707] 8386865: Fix links in JDK 27 JavaDoc API documentation Reviewed-by: iris, naoto, jlu --- .../share/classes/java/util/spi/LocaleNameProvider.java | 8 ++++---- .../sun/util/locale/provider/LocaleNameProviderImpl.java | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/java.base/share/classes/java/util/spi/LocaleNameProvider.java b/src/java.base/share/classes/java/util/spi/LocaleNameProvider.java index 2109a4cead1..eebfc93ac76 100644 --- a/src/java.base/share/classes/java/util/spi/LocaleNameProvider.java +++ b/src/java.base/share/classes/java/util/spi/LocaleNameProvider.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 @@ -45,7 +45,7 @@ protected LocaleNameProvider() { } /** - * Returns a localized name for the given + * Returns a localized name for the given * IETF BCP47 language code and the given locale that is appropriate for * display to the user. * For example, if {@code languageCode} is "fr" and {@code locale} @@ -70,7 +70,7 @@ protected LocaleNameProvider() { public abstract String getDisplayLanguage(String languageCode, Locale locale); /** - * Returns a localized name for the given + * Returns a localized name for the given * IETF BCP47 script code and the given locale that is appropriate for * display to the user. * For example, if {@code scriptCode} is "Latn" and {@code locale} @@ -100,7 +100,7 @@ public String getDisplayScript(String scriptCode, Locale locale) { } /** - * Returns a localized name for the given + * Returns a localized name for the given * IETF BCP47 region code (either ISO 3166 country code or UN M.49 area * codes) and the given locale that is appropriate for display to the user. * For example, if {@code countryCode} is "FR" and {@code locale} diff --git a/src/java.base/share/classes/sun/util/locale/provider/LocaleNameProviderImpl.java b/src/java.base/share/classes/sun/util/locale/provider/LocaleNameProviderImpl.java index a0a0b6f5785..fc879282e20 100644 --- a/src/java.base/share/classes/sun/util/locale/provider/LocaleNameProviderImpl.java +++ b/src/java.base/share/classes/sun/util/locale/provider/LocaleNameProviderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -91,7 +91,7 @@ public String getDisplayLanguage(String lang, Locale locale) { } /** - * Returns a localized name for the given + * Returns a localized name for the given * IETF BCP47 script code and the given locale that is appropriate for * display to the user. * For example, if scriptCode is "Latn" and locale From db008b3396628199168463ea44de5402b936736e Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Mon, 22 Jun 2026 21:24:42 +0000 Subject: [PATCH 033/707] 8386562: JVM crashes when StackMapTable attribute is too long Reviewed-by: stuefe, coleenp --- src/hotspot/share/memory/metaspace.cpp | 8 +- .../classFileParserBug/StackMapTooLong.java | 88 +++++++++++++++++++ 2 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/classFileParserBug/StackMapTooLong.java diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp index b97ae9ab540..8b8b80cd893 100644 --- a/src/hotspot/share/memory/metaspace.cpp +++ b/src/hotspot/share/memory/metaspace.cpp @@ -867,8 +867,10 @@ size_t Metaspace::max_allocation_word_size() { // Callers are responsible for checking null. MetaWord* Metaspace::allocate(ClassLoaderData* loader_data, size_t word_size, MetaspaceObj::Type type) { - assert(word_size <= Metaspace::max_allocation_word_size(), - "allocation size too large (%zu)", word_size); + if (word_size > Metaspace::max_allocation_word_size()) { + log_warning(gc, metaspace)("allocation size too large (%zu words)", word_size); + return nullptr; + } assert(loader_data != nullptr, "Should never pass around a null loader_data. " "ClassLoaderData::the_null_class_loader_data() should have been used."); @@ -913,7 +915,7 @@ MetaWord* Metaspace::allocate(ClassLoaderData* loader_data, size_t word_size, tracer()->report_metaspace_allocation_failure(loader_data, word_size, type, mdtype); // Allocation failed. - if (is_init_completed()) { + if (is_init_completed() && word_size <= Metaspace::max_allocation_word_size()) { // Only start a GC if the bootstrapping has completed. // Try to clean out some heap memory and retry. This can prevent premature // expansion of the metaspace. diff --git a/test/hotspot/jtreg/runtime/classFileParserBug/StackMapTooLong.java b/test/hotspot/jtreg/runtime/classFileParserBug/StackMapTooLong.java new file mode 100644 index 00000000000..130b7f539ab --- /dev/null +++ b/test/hotspot/jtreg/runtime/classFileParserBug/StackMapTooLong.java @@ -0,0 +1,88 @@ +/* + * 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 Very large StackMapTable should cause OutOfMemoryError and not VM crash. + * @bug 8386562 + * @library /test/lib /test/hotspot/jtreg/testlibrary/asm + * @run main StackMapTooLong + */ + +import java.lang.invoke.MethodHandles; +import static org.objectweb.asm.Opcodes.*; +import org.objectweb.asm.*; + +public class StackMapTooLong { + private static String BadClass = "BadClass"; + + public static void main(String[] args) throws Exception { + byte[] classFile = dumpBadClass(); + + try { + MethodHandles.lookup().defineClass(classFile); + throw new RuntimeException("OutOfMemoryError expected but not thrown!"); + } catch (OutOfMemoryError expected) {} + } + + static class LargeStackMapTable extends Attribute { + LargeStackMapTable() { + super("StackMapTable"); + } + + @Override + public boolean isCodeAttribute() { + return true; + } + + @Override + protected ByteVector write(ClassWriter cw, byte[] code, + int codeLength, int maxStack, int maxLocals) { + int len = 16 * 1024 * 1024 + 1; // Too large to be allocated by Metaspace::allocate() + ByteVector bv = new ByteVector(); + bv.putByteArray(new byte[len], 0, len); + return bv; + } + } + + private static byte[] dumpBadClass() throws Exception { + ClassWriter classWriter = new ClassWriter(0); + MethodVisitor methodVisitor; + + classWriter.visit(51, ACC_SUPER, BadClass, null, "java/lang/Object", + null); + + { + methodVisitor = + classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, "main", + "([Ljava/lang/String;)V", null, null); + methodVisitor.visitCode(); + methodVisitor.visitInsn(RETURN); + methodVisitor.visitAttribute(new LargeStackMapTable()); + methodVisitor.visitMaxs(0, 1); + methodVisitor.visitEnd(); + } + classWriter.visitEnd(); + + return classWriter.toByteArray(); + } +} From ee9616d4a43006f932774f9a5d421dc46a91d5d6 Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Tue, 23 Jun 2026 04:10:28 +0000 Subject: [PATCH 034/707] 8386155: C2 Vector API: missing truncation in VectorNode::push_through_replicate Reviewed-by: mhaessig, epeter --- src/hotspot/share/opto/vectornode.cpp | 7 + .../TestTruncationAfterReassociation.java | 447 ++++++++++++++++++ 2 files changed, 454 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/vectorapi/TestTruncationAfterReassociation.java diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index d1b8b89b0b3..a0454a41044 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -1416,6 +1416,13 @@ Node* VectorNode::push_through_replicate(PhaseGVN* phase) { sop = phase->transform(sop); + // For subword types, the scalar operation computes at int width and may + // produce values outside the subword range. Narrow the result unconditionally + // before feeding it to Replicate. + if (is_subword_type(bt)) { + sop = Compile::narrow_value(bt, sop, Type::get_const_basic_type(bt), phase, true); + } + return new ReplicateNode(sop, vect_type()); } diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestTruncationAfterReassociation.java b/test/hotspot/jtreg/compiler/vectorapi/TestTruncationAfterReassociation.java new file mode 100644 index 00000000000..2c4b10e7e2b --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestTruncationAfterReassociation.java @@ -0,0 +1,447 @@ +/* + * 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 8386155 + * @summary Test missing trunctation after subword vector operation reassociation + * @modules jdk.incubator.vector + * @library /test/lib / + * @run driver compiler.vectorapi.TestTruncationAfterReassociation + */ + +package compiler.vectorapi; + +import compiler.lib.generators.Generator; +import compiler.lib.generators.Generators; +import compiler.lib.ir_framework.*; +import compiler.lib.verify.Verify; +import jdk.incubator.vector.*; + +public class TestTruncationAfterReassociation { + + public static void main(String[] args) { + TestFramework.runWithFlags("--add-modules=jdk.incubator.vector"); + } + + static final VectorSpecies BSP = ByteVector.SPECIES_PREFERRED; + static final VectorSpecies SSP = ShortVector.SPECIES_PREFERRED; + + // Random value source (covers the full integer range, biased towards + // interesting/special values such as 0, MIN, MAX and powers of two). + static final Generator INT_GEN = Generators.G.ints(); + + static final int RAND_ITERS = 2048; + + static byte B_127 = (byte) 127; + static byte B_N16 = (byte) -16; + static byte B_N7 = (byte) -7; + static byte B_100 = (byte) 100; + static byte B_4 = (byte) 4; + static byte B_5 = (byte) 5; + static byte B_10 = (byte) 10; + static byte B_N128 = (byte) -128; + static byte B_1 = (byte) 1; + + static short S_32767 = (short) 32767; + static short S_N16 = (short) -16; + static short S_N7 = (short) -7; + static short S_200 = (short) 200; + static short S_5 = (short) 5; + static short S_10 = (short) 10; + static short S_N32768 = (short) -32768; + static short S_1 = (short) 1; + + static byte bmul(byte x, byte y) { return (byte) (x * y); } + static byte badd(byte x, byte y) { return (byte) (x + y); } + static byte bsub(byte x, byte y) { return (byte) (x - y); } + static byte bmax(byte x, byte y) { return (byte) Math.max(x, y); } + static byte bmin(byte x, byte y) { return (byte) Math.min(x, y); } + + static short smul(short x, short y) { return (short) (x * y); } + static short sadd(short x, short y) { return (short) (x + y); } + static short ssub(short x, short y) { return (short) (x - y); } + static short smax(short x, short y) { return (short) Math.max(x, y); } + static short smin(short x, short y) { return (short) Math.min(x, y); } + + @Test + static byte bug_8386155_reproducer() { + return ByteVector.broadcast(ByteVector.SPECIES_64, (byte) 127) + // Expected: mul is truncated to signed byte: 127 * -16 = (byte)-2032 = 16 + .mul((byte) -16) + // Expected: max(16, -7) = 16 + .max((byte) -7) + .lane(0); + } + + @Run(test = "bug_8386155_reproducer") + static void run_bug_8386155_reproducer() { + Verify.checkEQ(bug_8386155_reproducer(), (byte) 16); + } + + /* ========================================================= + * BYTE: then . + * ========================================================= */ + + @Test + @IR(failOn = { IRNode.MUL_VB, IRNode.MAX_VB }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.MUL_I, ">= 1", IRNode.MAX_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static byte byte_mul_then_max() { + return ByteVector.broadcast(BSP, B_127) + .mul(ByteVector.broadcast(BSP, B_N16)) + .max(ByteVector.broadcast(BSP, B_N7)) + .lane(0); + } + + @Run(test = "byte_mul_then_max") + static void run_byte_mul_then_max() { + Verify.checkEQ(byte_mul_then_max(), bmax(bmul(B_127, B_N16), B_N7)); + } + + @Test + @IR(failOn = { IRNode.MUL_VB, IRNode.MIN_VB }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.MUL_I, ">= 1", IRNode.MIN_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static byte byte_mul_then_min() { + return ByteVector.broadcast(BSP, B_100) + .mul(ByteVector.broadcast(BSP, B_4)) + .min(ByteVector.broadcast(BSP, B_5)) + .lane(0); + } + + @Run(test = "byte_mul_then_min") + static void run_byte_mul_then_min() { + Verify.checkEQ(byte_mul_then_min(), bmin(bmul(B_100, B_4), B_5)); + } + + @Test + @IR(failOn = { IRNode.ADD_VB, IRNode.MAX_VB }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.ADD_I, ">= 1", IRNode.MAX_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static byte byte_add_then_max() { + return ByteVector.broadcast(BSP, B_127) + .add(ByteVector.broadcast(BSP, B_127)) + .max(ByteVector.broadcast(BSP, B_5)) + .lane(0); + } + + @Run(test = "byte_add_then_max") + static void run_byte_add_then_max() { + Verify.checkEQ(byte_add_then_max(), bmax(badd(B_127, B_127), B_5)); + } + + @Test + @IR(failOn = { IRNode.ADD_VB, IRNode.MIN_VB }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.ADD_I, ">= 1", IRNode.MIN_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static byte byte_add_then_min() { + return ByteVector.broadcast(BSP, B_127) + .add(ByteVector.broadcast(BSP, B_127)) + .min(ByteVector.broadcast(BSP, B_10)) + .lane(0); + } + + @Run(test = "byte_add_then_min") + static void run_byte_add_then_min() { + Verify.checkEQ(byte_add_then_min(), bmin(badd(B_127, B_127), B_10)); + } + + @Test + @IR(failOn = { IRNode.SUB_VB, IRNode.MAX_VB }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.SUB_I, ">= 1", IRNode.MAX_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static byte byte_sub_then_max() { + return ByteVector.broadcast(BSP, B_N128) + .sub(ByteVector.broadcast(BSP, B_1)) + .max(ByteVector.broadcast(BSP, B_10)) + .lane(0); + } + + @Run(test = "byte_sub_then_max") + static void run_byte_sub_then_max() { + Verify.checkEQ(byte_sub_then_max(), bmax(bsub(B_N128, B_1), B_10)); + } + + @Test + @IR(failOn = { IRNode.SUB_VB, IRNode.MIN_VB }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.SUB_I, ">= 1", IRNode.MIN_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static byte byte_sub_then_min() { + return ByteVector.broadcast(BSP, B_N128) + .sub(ByteVector.broadcast(BSP, B_1)) + .min(ByteVector.broadcast(BSP, B_10)) + .lane(0); + } + + @Run(test = "byte_sub_then_min") + static void run_byte_sub_then_min() { + Verify.checkEQ(byte_sub_then_min(), bmin(bsub(B_N128, B_1), B_10)); + } + + /* ========================================================= + * SHORT: then . + * ========================================================= */ + + @Test + @IR(failOn = { IRNode.MUL_VS, IRNode.MAX_VS }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.MUL_I, ">= 1", IRNode.MAX_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static short short_mul_then_max() { + return ShortVector.broadcast(SSP, S_32767) + .mul(ShortVector.broadcast(SSP, S_N16)) + .max(ShortVector.broadcast(SSP, S_N7)) + .lane(0); + } + + @Run(test = "short_mul_then_max") + static void run_short_mul_then_max() { + Verify.checkEQ(short_mul_then_max(), smax(smul(S_32767, S_N16), S_N7)); + } + + @Test + @IR(failOn = { IRNode.MUL_VS, IRNode.MIN_VS }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.MUL_I, ">= 1", IRNode.MIN_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static short short_mul_then_min() { + return ShortVector.broadcast(SSP, S_200) + .mul(ShortVector.broadcast(SSP, S_200)) + .min(ShortVector.broadcast(SSP, S_5)) + .lane(0); + } + + @Run(test = "short_mul_then_min") + static void run_short_mul_then_min() { + Verify.checkEQ(short_mul_then_min(), smin(smul(S_200, S_200), S_5)); + } + + @Test + @IR(failOn = { IRNode.ADD_VS, IRNode.MAX_VS }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.ADD_I, ">= 1", IRNode.MAX_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static short short_add_then_max() { + return ShortVector.broadcast(SSP, S_32767) + .add(ShortVector.broadcast(SSP, S_32767)) + .max(ShortVector.broadcast(SSP, S_5)) + .lane(0); + } + + @Run(test = "short_add_then_max") + static void run_short_add_then_max() { + Verify.checkEQ(short_add_then_max(), smax(sadd(S_32767, S_32767), S_5)); + } + + @Test + @IR(failOn = { IRNode.SUB_VS, IRNode.MIN_VS }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.SUB_I, ">= 1", IRNode.MIN_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static short short_sub_then_min() { + return ShortVector.broadcast(SSP, S_N32768) + .sub(ShortVector.broadcast(SSP, S_1)) + .min(ShortVector.broadcast(SSP, S_10)) + .lane(0); + } + + @Run(test = "short_sub_then_min") + static void run_short_sub_then_min() { + Verify.checkEQ(short_sub_then_min(), smin(ssub(S_N32768, S_1), S_10)); + } + + // Two independent overflowing products feeding a single max + @Test + @IR(failOn = { IRNode.MUL_VB, IRNode.MAX_VB }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.MUL_I, ">= 1", IRNode.MAX_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static byte byte_two_muls_then_max() { + return ByteVector.broadcast(BSP, B_127).mul(ByteVector.broadcast(BSP, B_N16)) + .max(ByteVector.broadcast(BSP, B_100).mul(ByteVector.broadcast(BSP, B_4))) + .lane(0); + } + + @Run(test = "byte_two_muls_then_max") + static void run_byte_two_muls_then_max() { + Verify.checkEQ(byte_two_muls_then_max(), + bmax(bmul(B_127, B_N16), bmul(B_100, B_4))); + } + + // Chained (reassociated) adds whose running value overflows, then a max + @Test + @IR(failOn = { IRNode.ADD_VB, IRNode.MAX_VB }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.ADD_I, ">= 1", IRNode.MAX_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static byte byte_chain_add_then_max() { + return ByteVector.broadcast(BSP, B_127) + .add(ByteVector.broadcast(BSP, B_127)) + .add(ByteVector.broadcast(BSP, B_127)) + .max(ByteVector.broadcast(BSP, B_127)) + .lane(0); + } + + @Run(test = "byte_chain_add_then_max") + static void run_byte_chain_add_then_max() { + Verify.checkEQ(byte_chain_add_then_max(), + bmax(badd(badd(B_127, B_127), B_127), B_127)); + } + + // Overflowing product feeding max then min: + @Test + @IR(failOn = { IRNode.MUL_VB, IRNode.MAX_VB, IRNode.MIN_VB }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.MUL_I, ">= 1", IRNode.MAX_I, ">= 1", IRNode.MIN_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static byte byte_mul_max_then_min() { + return ByteVector.broadcast(BSP, B_127) + .mul(ByteVector.broadcast(BSP, B_N16)) + .max(ByteVector.broadcast(BSP, B_N7)) + .min(ByteVector.broadcast(BSP, B_10)) + .lane(0); + } + + @Run(test = "byte_mul_max_then_min") + static void run_byte_mul_max_then_min() { + Verify.checkEQ(byte_mul_max_then_min(), + bmin(bmax(bmul(B_127, B_N16), B_N7), B_10)); + } + + // Two overflowing short products feeding a single min + @Test + @IR(failOn = { IRNode.MUL_VS, IRNode.MIN_VS }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.MUL_I, ">= 1", IRNode.MIN_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static short short_two_muls_then_min() { + return ShortVector.broadcast(SSP, S_200).mul(ShortVector.broadcast(SSP, S_200)) + .min(ShortVector.broadcast(SSP, S_32767).mul(ShortVector.broadcast(SSP, S_N16))) + .lane(0); + } + + @Run(test = "short_two_muls_then_min") + static void run_short_two_muls_then_min() { + Verify.checkEQ(short_two_muls_then_min(), + smin(smul(S_200, S_200), smul(S_32767, S_N16))); + } + + /* ========================================================= + * Randomized coverage (Generators). + * ========================================================= */ + + @Test + @IR(failOn = { IRNode.MUL_VB, IRNode.MAX_VB }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.MUL_I, ">= 1", IRNode.MAX_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static byte rand_byte_mul_then_max(byte a, byte b, byte c) { + return ByteVector.broadcast(BSP, a) + .mul(ByteVector.broadcast(BSP, b)) + .max(ByteVector.broadcast(BSP, c)) + .lane(0); + } + + @Run(test = "rand_byte_mul_then_max") + static void run_rand_byte_mul_then_max() { + for (int i = 0; i < RAND_ITERS; i++) { + byte a = INT_GEN.next().byteValue(); + byte b = INT_GEN.next().byteValue(); + byte c = INT_GEN.next().byteValue(); + Verify.checkEQ(rand_byte_mul_then_max(a, b, c), bmax(bmul(a, b), c)); + } + } + + @Test + @IR(failOn = { IRNode.MUL_VB, IRNode.MIN_VB }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.MUL_I, ">= 1", IRNode.MIN_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static byte rand_byte_mul_then_min(byte a, byte b, byte c) { + return ByteVector.broadcast(BSP, a) + .mul(ByteVector.broadcast(BSP, b)) + .min(ByteVector.broadcast(BSP, c)) + .lane(0); + } + + @Run(test = "rand_byte_mul_then_min") + static void run_rand_byte_mul_then_min() { + for (int i = 0; i < RAND_ITERS; i++) { + byte a = INT_GEN.next().byteValue(); + byte b = INT_GEN.next().byteValue(); + byte c = INT_GEN.next().byteValue(); + Verify.checkEQ(rand_byte_mul_then_min(a, b, c), bmin(bmul(a, b), c)); + } + } + + @Test + @IR(failOn = { IRNode.MUL_VS, IRNode.MAX_VS }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.MUL_I, ">= 1", IRNode.MAX_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static short rand_short_mul_then_max(short a, short b, short c) { + return ShortVector.broadcast(SSP, a) + .mul(ShortVector.broadcast(SSP, b)) + .max(ShortVector.broadcast(SSP, c)) + .lane(0); + } + + @Run(test = "rand_short_mul_then_max") + static void run_rand_short_mul_then_max() { + for (int i = 0; i < RAND_ITERS; i++) { + short a = INT_GEN.next().shortValue(); + short b = INT_GEN.next().shortValue(); + short c = INT_GEN.next().shortValue(); + Verify.checkEQ(rand_short_mul_then_max(a, b, c), smax(smul(a, b), c)); + } + } + + @Test + @IR(failOn = { IRNode.MUL_VS, IRNode.MIN_VS }, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + counts = { IRNode.MUL_I, ">= 1", IRNode.MIN_I, ">= 1", + IRNode.LSHIFT_I, ">= 1", IRNode.RSHIFT_I, ">= 1" }) + static short rand_short_mul_then_min(short a, short b, short c) { + return ShortVector.broadcast(SSP, a) + .mul(ShortVector.broadcast(SSP, b)) + .min(ShortVector.broadcast(SSP, c)) + .lane(0); + } + + @Run(test = "rand_short_mul_then_min") + static void run_rand_short_mul_then_min() { + for (int i = 0; i < RAND_ITERS; i++) { + short a = INT_GEN.next().shortValue(); + short b = INT_GEN.next().shortValue(); + short c = INT_GEN.next().shortValue(); + Verify.checkEQ(rand_short_mul_then_min(a, b, c), smin(smul(a, b), c)); + } + } +} From c60dc06d513ba92ae0442327a2eb921400071261 Mon Sep 17 00:00:00 2001 From: Serguei Spitsyn Date: Tue, 23 Jun 2026 04:37:55 +0000 Subject: [PATCH 035/707] 8386325: The AttachListener does not do proper exception handling Reviewed-by: kevinw, cjplummer --- src/hotspot/share/services/attachListener.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/services/attachListener.cpp b/src/hotspot/share/services/attachListener.cpp index 92d3c302ded..b4e9bd88c1b 100644 --- a/src/hotspot/share/services/attachListener.cpp +++ b/src/hotspot/share/services/attachListener.cpp @@ -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 @@ -180,7 +180,12 @@ static bool get_bool_sys_prop(const char* name, bool default_value, TRAPS) { HandleMark hm(THREAD); // setup the arguments to getProperty - Handle key_str = java_lang_String::create_from_str(name, CHECK_(default_value)); + Handle key_str = java_lang_String::create_from_str(name, THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + return default_value; + } + // return value JavaValue result(T_OBJECT); // public static String getProperty(String key, String def); @@ -189,7 +194,12 @@ static bool get_bool_sys_prop(const char* name, bool default_value, TRAPS) { vmSymbols::getProperty_name(), vmSymbols::string_string_signature(), key_str, - CHECK_(default_value)); + THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + return default_value; + } + oop value_oop = result.get_oop(); if (value_oop != nullptr) { // convert Java String to utf8 string From ad075c96bfb35e02eed3ff55d2805dd55a9995d0 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 23 Jun 2026 06:17:53 +0000 Subject: [PATCH 036/707] =?UTF-8?q?8386597:=20C2:=20TestTruncationWrapFuzz?= =?UTF-8?q?er.java=E2=80=8E=20for=20CountedLoop=20detection=20of=20subword?= =?UTF-8?q?=20truncated=20iv?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: mhaessig, qamai --- .../loopopts/TestTruncationWrapFuzzer.java | 535 ++++++++++++++++++ 1 file changed, 535 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/loopopts/TestTruncationWrapFuzzer.java diff --git a/test/hotspot/jtreg/compiler/loopopts/TestTruncationWrapFuzzer.java b/test/hotspot/jtreg/compiler/loopopts/TestTruncationWrapFuzzer.java new file mode 100644 index 00000000000..915808d07cb --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestTruncationWrapFuzzer.java @@ -0,0 +1,535 @@ +/* + * 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 8386597 8385855 8386482 8386591 8386830 + * @summary Fuzz patterns for CountedLoopConverter::has_truncation_wrap + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @compile ../lib/ir_framework/TestFramework.java + * @compile ../lib/generators/Generators.java + * @run driver ${test.main.class} + */ + +package compiler.loopopts; + +import java.util.List; +import java.util.ArrayList; +import java.util.Random; +import java.util.Set; + +import jdk.test.lib.Utils; + +import compiler.lib.compile_framework.*; +import compiler.lib.generators.*; +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateToken; +import static compiler.lib.template_framework.Template.scope; +import static compiler.lib.template_framework.Template.let; +import static compiler.lib.template_framework.Template.$; + +import compiler.lib.template_framework.library.TestFrameworkClass; + +/** + * For more basic examples, see: + * - TestHasTruncationWrap.java + * - TestTruncationWrapEmptyType.java + * - TestTruncationWrapPhiTypeUnion.java + * - TestTruncationWrapBadCharWrap.java + * + * So far, this test does not have IR verification, only result verification. + * + * This test generates a wide range of patterns, and will require a lot of + * runs to find a specific code shape. + * + * Features: + * - Truncation patterns, see TRUNCATIONS and randomIVMutation. + * - Stride: positive, negative, small and large, see ivMutationWithRandomStride. + * - Reference (not compiled) vs test (compiled), and result verification. + * - Loop Shapes: for, while, do-while, see LOOP_SHAPES. + * - Exit checks: random Comparison, see Comparator and Comparison (signed and unsigned). + * - For endless loops / loops that would take too long: early exit via opaqueCheck, + * Note: it is verified that this does not hinder optimization, see: + * TestHasTruncationWrap.java -> testIRShort7. + * - Interesting loop bounds: init/limit + * - constant + * - variable, sampled (see getInputTemplate), and modified (no-op, truncate, clamp). + * - Extra check dominating the loop: compare against constant of limit. + * Note: has_truncation_wrap can use such checks to constrain the entry type. + * Note2: We've had bugs around this, confusing CmpI/CmpU, see JDK-8385855. + */ +public class TestTruncationWrapFuzzer { + private static final Random RANDOM = Utils.getRandomInstance(); + private static final RestrictableGenerator INT_GEN = Generators.G.ints(); + + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + long t0 = System.nanoTime(); + // Add a java source file. + comp.addJavaSourceCode("compiler.loopopts.templated.Generated", generate(comp)); + + long t1 = System.nanoTime(); + // Compile the source file. + comp.compile(); + + long t2 = System.nanoTime(); + + // Run the tests without any additional VM flags. + comp.invoke("compiler.loopopts.templated.Generated", "main", new Object[] {new String[] {}}); + long t3 = System.nanoTime(); + + System.out.println("Code Generation: " + (t1-t0) * 1e-9f); + System.out.println("Code Compilation: " + (t2-t1) * 1e-9f); + System.out.println("Running Tests: " + (t3-t2) * 1e-9f); + } + + public static String generate(CompileFramework comp) { + // Create a list to collect all tests. + List testTemplateTokens = new ArrayList<>(); + + // Some utilities, to help us get an additional exit, in case the + // generated loops spin too long, or are infinite loops. + Template.ZeroArgs utilsTemplate = Template.make(() -> scope( + """ + private static final Random RANDOM = Utils.getRandomInstance(); + + public static int opaqueCounter; + public static int opaqueCounterMax; + + @DontInline + public static void opaqueReset() { + opaqueCounter = 0; + } + + @DontInline + public static boolean opaqueCheck() { + return (opaqueCounter++) > opaqueCounterMax; + } + + @DontInline + public static int opaqueSum(int i, int j) { + return i + j + 1; + } + """ + )); + testTemplateTokens.add(utilsTemplate.asToken()); + + for (int i = 0; i < 100; i++) { + testTemplateTokens.add(generateTest(/* no warmup, like -Xcomp */ 0)); + } + for (int i = 0; i < 5; i++) { + testTemplateTokens.add(generateTest(/* with warmup, slower */ 100)); + } + + // Create the test class, which runs all testTemplateTokens. + return TestFrameworkClass.render( + // package and class name. + "compiler.loopopts.templated", "Generated", + // List of imports. + Set.of("compiler.lib.generators.*", + "java.util.Random", + "jdk.test.lib.Utils"), + // classpath, so the Test VM has access to the compiled class files. + comp.getEscapedClassPathOfCompiledClasses(), + // The list of tests. + testTemplateTokens); + } + + // This is copied from TestFoldComparesFuzzer.java, and we should + // refactor this out into the template framework library, in a + // future RFE. + enum Comparator { + ULT(" < 0", false), + ULE(" <= 0", false), + UGT(" > 0", false), + UGE(" >= 0", false), + UEQ(" == 0", false), + UNE(" != 0", false), + LT(" < ", true), + LE(" <= ", true), + GT(" > ", true), + GE(" >= ", true), + EQ(" == ", true), + NE(" != ", true); + + private final String token; + private final boolean signed; + + Comparator(String token, boolean signed) { + this.token = token; + this.signed = signed; + } + + public String getToken() { + return token; + } + + public boolean isSigned() { + return signed; + } + + public Comparator negate() { + return switch(this) { + case ULT -> UGE; + case ULE -> UGT; + case UGT -> ULE; + case UGE -> ULT; + case UEQ -> UNE; + case UNE -> UEQ; + case LT -> GE; + case LE -> GT; + case GT -> LE; + case GE -> LT; + case EQ -> NE; + case NE -> EQ; + }; + } + + public Comparator flip() { + return switch(this) { + case ULT -> UGT; + case ULE -> UGE; + case UGT -> ULT; + case UGE -> ULE; + case UEQ -> UEQ; + case UNE -> UNE; + case LT -> GT; + case LE -> GE; + case GT -> LT; + case GE -> LE; + case EQ -> EQ; + case NE -> NE; + }; + } + + static Comparator random() { + return values()[RANDOM.nextInt(values().length)]; + } + } + + record Comparison(String lhs, Comparator cmp, String rhs, boolean negated) { + public Comparison(String lhs, Comparator cmp, String rhs) { + this(lhs, cmp, rhs, false); + } + + public String toString() { + return cmp.isSigned() + ? ((negated ? "!" : "") + "(" + lhs + " "+ cmp.getToken() + " " + rhs + ")") + : ((negated ? "!" : "") + "(Integer.compareUnsigned(" + lhs + ", " + rhs + ")" + cmp.getToken() + ")"); + } + + // Keep the same semantics of the test, but change its form. + Comparison permuteRandom() { + return flipRandom().complementRandom(); + } + + Comparison flipRandom() { + return RANDOM.nextBoolean() ? this : new Comparison(rhs, cmp.flip(), lhs); + } + + Comparison complementRandom() { + return RANDOM.nextBoolean() ? this : new Comparison(lhs, cmp.negate(), rhs, true); + } + } + + interface TestMethodGenerator { + Template.OneArg getTestTemplate(); + + default Template.ZeroArgs getInputTemplate() { + return Template.make(() -> scope( + switch (RANDOM.nextInt(5)) { + case 0 -> """ + RestrictableGenerator gen = Generators.G.ints(); + int init = gen.next(); + int limit = gen.next(); + """; + case 1 -> """ + int init = (byte)RANDOM.nextInt(); + int limit = (byte)RANDOM.nextInt(); + """; + case 2 -> """ + int init = (short)RANDOM.nextInt(); + int limit = (short)RANDOM.nextInt(); + """; + case 3 -> """ + int init = (char)RANDOM.nextInt(); + int limit = (char)RANDOM.nextInt(); + """; + case 4 -> """ + int e0 = RANDOM.nextInt(32); + int e1 = RANDOM.nextInt(32); + int r0 = RANDOM.nextInt(32); + int r1 = RANDOM.nextInt(32); + int init = (1 << e0) + r0; + int limit = (1 << e1) + r1; + """; + default -> throw new RuntimeException("not expected"); + } + )); + }; + } + + private static record Truncation(String s0, String s1) { + public String ivMutationWithRandomStride() { + int stride = switch(RANDOM.nextInt(3)) { + case 0 -> INT_GEN.next(); + case 1 -> RANDOM.nextInt(9) - 4; + case 2 -> RANDOM.nextInt(129) - 64; + default -> throw new RuntimeException("not expected"); + }; + + return "i = " + s0 + "i + " + stride + s1; + } + + public String truncate(String val) { + return val + " = " + s0 + val + s1; + } + } + + // Different patterns relevant for triggering truncation/wrap. + private static final Truncation[] TRUNCATIONS = new Truncation[] { + new Truncation("", ""), + new Truncation("(byte)(", ")"), + new Truncation("(short)(", ")"), + new Truncation("(char)(", ")"), + new Truncation("((", ") << 8) >> 8"), + new Truncation("((", ") << 16) >> 16"), + new Truncation("((", ") << 24) >> 24"), + new Truncation("((", ") & 0x7f)"), + new Truncation("((", ") & 0xff)"), + new Truncation("((", ") & 0x7fff)"), + new Truncation("((", ") & 0xffff)") + }; + + private static Truncation randomTruncation() { + return TRUNCATIONS[RANDOM.nextInt(TRUNCATIONS.length)]; + } + + private static String randomIVMutation() { + return randomTruncation().ivMutationWithRandomStride(); + } + + private static String randomTruncation(String val) { + return randomTruncation().truncate(val); + } + + private static final String[] LOOP_SHAPES = new String[] { + """ + // Loop Shape: For + int i; + for (i = init; #exitCheck; #ivMutation) { + sum = opaqueSum(sum, #addValue); + if (opaqueCheck()) { break; } + } + """, + """ + // Loop Shape: While: + int i = init; + while (#exitCheck) { + sum = opaqueSum(sum, #addValue); + if (opaqueCheck()) { break; } + #ivMutation; + } + """, + """ + // Loop Shape: Do-While: + int i = init; + do { + sum = opaqueSum(sum, #addValue); + if (opaqueCheck()) { break; } + #ivMutation; + } while (#exitCheck); + """, + """ + // Loop Shape: Do-While + pre-loop check. + int i = init; + if (!(#exitCheck)) { return sum; } + do { + sum = opaqueSum(sum, #addValue); + if (opaqueCheck()) { break; } + #ivMutation; + } while (#exitCheck); + """ + }; + + private static String randomLoopShape() { + return LOOP_SHAPES[RANDOM.nextInt(LOOP_SHAPES.length)]; + } + + // Loop init/limit are constants. + static class TestMethodGeneratorConst implements TestMethodGenerator { + private final int init = INT_GEN.next(); + private final int limit = INT_GEN.next(); + + private final String ivMutation = randomIVMutation(); + private final String loopShape = randomLoopShape(); + private final String addValue = RANDOM.nextBoolean() ? "0" : "i"; + + private final Comparison exitCheck = new Comparison("i", Comparator.random(), "limit").permuteRandom(); + + private final Template.OneArg testTemplate = Template.make("methodName", (String methodName) -> scope( + let("init", init), + let("limit", limit), + let("ivMutation", ivMutation), + let("exitCheck", exitCheck), + let("addValue", addValue), + """ + static int #methodName(int unused0, int unused1) { + opaqueReset(); + int init = #init; + int limit = #limit; + int sum = 0; + """, + loopShape, + """ + return sum + #addValue; + } + """ + )); + + public Template.OneArg getTestTemplate() { return testTemplate; } + } + + // Clamp randomly, but not always on both sides. + private static String randomClamping(String value) { + String clamp = value; + if (RANDOM.nextBoolean()) { + clamp = "Math.max(" + clamp + ", " + INT_GEN.next() + ")"; + } + if (RANDOM.nextBoolean()) { + clamp = "Math.min(" + clamp + ", " + INT_GEN.next() + ")"; + } + return value + " = " + clamp; + } + + // We want to be able to modify the incoming init/limit. + // - nothing + // - truncate + // - clamp with min/max, maybe even only one-sided + private static String randomModifyValue(String value) { + return switch(RANDOM.nextInt(3)) { + case 0 -> "// Don't modify " + value + "\n"; + case 1 -> randomTruncation(value) + ";\n"; + case 2 -> randomClamping(value) + ";\n"; + default -> throw new RuntimeException("not expected"); + }; + } + + private static String randomExtraCheck() { + // We can constrain the init value with limit or a constant. + String other = RANDOM.nextBoolean() ? "limit" : INT_GEN.next().toString(); + Comparison check = new Comparison("init", Comparator.random(), other).permuteRandom(); + return RANDOM.nextBoolean() + ? "// No extra check.\n" + : "if (" + check + ") { return -1; }\n"; + } + + // Loop init/limit are variables. + static class TestMethodGeneratorVars implements TestMethodGenerator { + private final String ivMutation = randomIVMutation(); + private final String loopShape = randomLoopShape(); + private final String addValue = RANDOM.nextBoolean() ? "0" : "i"; + private final String modifyInit = randomModifyValue("init"); + private final String modifyLimit = randomModifyValue("limit"); + private final String extraCheck = randomExtraCheck(); + + private final Comparison exitCheck = new Comparison("i", Comparator.random(), "limit").permuteRandom(); + + private final Template.OneArg testTemplate = Template.make("methodName", (String methodName) -> scope( + let("ivMutation", ivMutation), + let("exitCheck", exitCheck), + let("addValue", addValue), + """ + static int #methodName(int init, int limit) { + opaqueReset(); + int sum = 0; + """, + modifyInit, // modify type of init + modifyLimit, // modify type of limit + extraCheck, // extra CmpI/CmpU dominating the loop, might constrain entry value. + loopShape, + """ + return sum + #addValue; + } + """ + )); + + public Template.OneArg getTestTemplate() { return testTemplate; } + } + public static TemplateToken generateTest(int warmup) { + TestMethodGenerator tg = switch(RANDOM.nextInt(2)) { + case 0 -> new TestMethodGeneratorConst(); + case 1 -> new TestMethodGeneratorVars(); + default -> throw new RuntimeException("not expected"); + }; + Template.ZeroArgs testInputTemplate = tg.getInputTemplate(); + Template.OneArg testMethodTemplate = tg.getTestTemplate(); + + var testTemplate = Template.make(() -> scope( + let("warmup", warmup), + """ + // --- $test start --- + @Run(test = "$test") + @Warmup(#warmup) + public static void $run(RunInfo info) { + int reps = info.isWarmUp() ? 1 : 100; + for (int i = 0; i < reps; i++) { + // Generate random values for init and limit. + """, + testInputTemplate.asToken(), + """ + + // Limit how long we can spin in the loop: + opaqueCounterMax = 10_000 + RANDOM.nextInt(1000); + + // Run test and compare with interpreter results. + var result = $test(init, limit); + var expected = $reference(init, limit); + if (result != expected) { + throw new RuntimeException("wrong result: " + result + " vs " + expected + + "\\ninit: " + init + + "\\nlimit: " + limit + + "\\nopaqueCounterMax: " + opaqueCounterMax); + } + } + } + + @Test + """, + testMethodTemplate.asToken($("test")), + """ + + @DontCompile + """, + testMethodTemplate.asToken($("reference")), + """ + // --- $test end --- + """ + )); + return testTemplate.asToken(); + } +} From 9556c417b345d47a4786eda9fc2203b00b924bcd Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Tue, 23 Jun 2026 07:31:52 +0000 Subject: [PATCH 037/707] 8386878: [make] BUILD_LIBZIP_EXCLUDES seems to be unused Reviewed-by: erikj --- make/modules/java.base/lib/CoreLibraries.gmk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/make/modules/java.base/lib/CoreLibraries.gmk b/make/modules/java.base/lib/CoreLibraries.gmk index 316103be4cd..164bf3704a8 100644 --- a/make/modules/java.base/lib/CoreLibraries.gmk +++ b/make/modules/java.base/lib/CoreLibraries.gmk @@ -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 @@ -77,7 +77,7 @@ TARGETS += $(BUILD_LIBJAVA) ## Build libzip ################################################################################ -BUILD_LIBZIP_EXCLUDES := +LIBZIP_EXCLUDES := ifeq ($(USE_EXTERNAL_LIBZ), true) LIBZIP_EXCLUDES += zlib endif From 6f81f7aa7ee05f67aa6ce790430c2780e767d404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Du=EF=BF=BDan=20B=EF=BF=BDlek?= Date: Tue, 23 Jun 2026 07:48:52 +0000 Subject: [PATCH 038/707] 8173155: JavacTask should have close() method Reviewed-by: jlahoda, cstein --- .../com/sun/source/util/JavacTask.java | 17 +- .../sun/tools/javac/api/BasicJavacTask.java | 7 +- .../sun/tools/javac/api/JavacTaskImpl.java | 7 +- .../tools/javac/api/TestJavacTask_Close.java | 171 ++++++++++++++++++ 4 files changed, 198 insertions(+), 4 deletions(-) create mode 100644 test/langtools/tools/javac/api/TestJavacTask_Close.java diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/JavacTask.java b/src/jdk.compiler/share/classes/com/sun/source/util/JavacTask.java index af47f9073f9..29018cfe00d 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/util/JavacTask.java +++ b/src/jdk.compiler/share/classes/com/sun/source/util/JavacTask.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2020, 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 @@ -49,7 +49,7 @@ * @author Jonathan Gibbons * @since 1.6 */ -public abstract class JavacTask implements CompilationTask { +public abstract class JavacTask implements CompilationTask, AutoCloseable { /** * Constructor for subclasses to call. */ @@ -101,6 +101,19 @@ public abstract Iterable parse() */ public abstract Iterable generate() throws IOException; + /** + * Releases any resources opened by this task, either directly or + * indirectly. After this method is called, the task becomes unusable, + * and subsequent calls to its methods may throw an {@code IllegalStateException}. + * Closing a task that has already been closed has no effect. + * + * @throws IOException if an error occurs while releasing resources. + * @throws IllegalStateException if the operation cannot be performed at this time. + * @since 28 + */ + @Override + public abstract void close() throws IOException; + /** * Sets a specified listener to receive notification of events * describing the progress of this compilation task. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/BasicJavacTask.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/BasicJavacTask.java index 8bad3f64c38..c334d491174 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/BasicJavacTask.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/BasicJavacTask.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2019, 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 @@ -109,6 +109,11 @@ public Iterable generate() { throw new IllegalStateException(); } + @Override @DefinedBy(Api.COMPILER_TREE) + public void close() { + throw new IllegalStateException(); + } + @Override @DefinedBy(Api.COMPILER_TREE) public void setTaskListener(TaskListener tl) { MultiTaskListener mtl = MultiTaskListener.instance(context); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java index bc597876778..32fe6bd98ce 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.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 @@ -495,6 +495,11 @@ public void process(Env env) { return results; } + @Override @DefinedBy(Api.COMPILER_TREE) + public void close() { + cleanup(); + } + public void ensureEntered() { args.allowEmpty(); enter(null); diff --git a/test/langtools/tools/javac/api/TestJavacTask_Close.java b/test/langtools/tools/javac/api/TestJavacTask_Close.java new file mode 100644 index 00000000000..0b331498d0a --- /dev/null +++ b/test/langtools/tools/javac/api/TestJavacTask_Close.java @@ -0,0 +1,171 @@ +/* + * 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 8173155 + * @summary Cannot release resources after partial compilation + * @library /tools/lib + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.JarTask toolbox.JavacTask toolbox.ToolBox + * @run junit ${test.main.class} + */ + +import java.io.Closeable; +import java.io.IOException; +import java.net.URI; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import javax.tools.ForwardingJavaFileManager; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.SimpleJavaFileObject; +import javax.tools.ToolProvider; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import toolbox.ToolBox; +import toolbox.JarTask; +import toolbox.JavacTask; + + +public class TestJavacTask_Close { + + private Path base; + + @Test + void testClose() throws Exception { + Path jar = createJar(); + + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + JavaFileObject compilationUnit = SimpleJavaFileObject.forSource(URI.create("string:///Test.java"), + """ + public class Test { + private Lib lib; + } + """); + + boolean[] state = new boolean[] {false, false}; + try (FM fm = new FM(compiler.getStandardFileManager(null, null, null), state)) { + com.sun.source.util.JavacTask task = (com.sun.source.util.JavacTask) compiler.getTask( + null, fm, null, List.of("-classpath", jar.toString()), null, List.of(compilationUnit)); + Assertions.assertNotNull(task.parse(), "parse() failed"); + task.close(); + Assertions.assertThrows(IllegalStateException.class, () -> task.analyze(), "analyze() on closed task"); + } + + Assertions.assertTrue(state[0], "URLClassLoader not created"); + Assertions.assertTrue(state[1], "URLClassLoader not closed"); + } + + @Test + void testRepeatedClose() throws Exception { + Path jar = createJar(); + + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + JavaFileObject compilationUnit = SimpleJavaFileObject.forSource(URI.create("string:///Test.java"), + """ + public class Test { + private Lib lib; + } + """); + + boolean[] state = new boolean[] {false, false}; + try (FM fm = new FM(compiler.getStandardFileManager(null, null, null), state); + com.sun.source.util.JavacTask task = (com.sun.source.util.JavacTask) compiler.getTask( + null, fm, null, List.of("-classpath", jar.toString()), null, List.of(compilationUnit))) { + Assertions.assertEquals(true, task.call(), "Compilation task failed"); + } + + Assertions.assertTrue(state[0], "URLClassLoader not created"); + Assertions.assertTrue(state[1], "URLClassLoader not closed"); + } + + @BeforeEach + public void setUp(TestInfo info) { + base = Paths.get(".") + .resolve(info.getTestMethod() + .orElseThrow() + .getName()); + } + + private Path createJar() throws IOException { + Path jarSrc = base.resolve("jarSrc"); + Path jarClasses = base.resolve("jarClasses"); + Path jar = base.resolve("jar.jar"); + Files.createDirectories(jarClasses); + + ToolBox tb = new ToolBox(); + tb.writeJavaFiles(jarSrc, "public class Lib { }"); + + new JavacTask(tb) + .outdir(jarClasses) + .files(tb.findJavaFiles(jarSrc)) + .run() + .writeAll(); + new JarTask(tb) + .run("cf", jar.toString(), "-C", jarClasses.toString(), "."); + + return jar; + } + + private static class FM extends ForwardingJavaFileManager { + + private final boolean[] state; + + private FM(JavaFileManager fileManager, boolean[] state) { + super(fileManager); + this.state = state; + } + + @Override + public ClassLoader getClassLoader(Location location) { + ClassLoader cl = super.getClassLoader(location); + return cl instanceof URLClassLoader urlCl ? new CL(urlCl, state) : cl; + } + } + + private static class CL extends ClassLoader implements Closeable { + + private final URLClassLoader urlCl; + private final boolean[] state; + + private CL(URLClassLoader urlCl, boolean[] state) { + this.urlCl = urlCl; + this.state = state; + this.state[0] = true; + } + + @Override + public void close() throws IOException { + state[1] = true; + urlCl.close(); + } + } +} From 96a1a9d2f50f27d9b97e7b04c70b7b2bccf2e0cd Mon Sep 17 00:00:00 2001 From: Oli Gillespie Date: Tue, 23 Jun 2026 09:24:30 +0000 Subject: [PATCH 039/707] 8386372: Add ConcurrentSkipListMap to map stress test Reviewed-by: vklang --- .../jdk/java/util/concurrent/ConcurrentHashMap/MapLoops.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/jdk/java/util/concurrent/ConcurrentHashMap/MapLoops.java b/test/jdk/java/util/concurrent/ConcurrentHashMap/MapLoops.java index 0f52bc67f08..4a96530e026 100644 --- a/test/jdk/java/util/concurrent/ConcurrentHashMap/MapLoops.java +++ b/test/jdk/java/util/concurrent/ConcurrentHashMap/MapLoops.java @@ -34,7 +34,7 @@ /* * @test * @bug 4486658 - * @summary Exercise multithreaded maps, by default ConcurrentHashMap. + * @summary Exercise multithreaded maps. * Multithreaded hash table test. Each thread does a random walk * though elements of "key" array. On each iteration, it checks if * table includes key. If absent, with probability pinsert it @@ -42,7 +42,8 @@ * it. (pinsert and premove are expressed as percentages to simplify * parsing from command line.) * @library /test/lib - * @run main/timeout=1600 MapLoops + * @run main/timeout=1600 MapLoops java.util.concurrent.ConcurrentHashMap + * @run main/timeout=1600 MapLoops java.util.concurrent.ConcurrentSkipListMap */ import static java.util.concurrent.TimeUnit.MILLISECONDS; From 7ca9ae1dfe0ae7d786b8cf0957cd1f777da5642c Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Tue, 23 Jun 2026 11:58:09 +0000 Subject: [PATCH 040/707] 8385891: Introduce a test for GZIPInputStream whose underlying stream is a blocking InputStream Reviewed-by: lancea --- .../zip/GZIP/GZIPOverBlockingStreams.java | 406 ++++++++++++++++++ 1 file changed, 406 insertions(+) create mode 100644 test/jdk/java/util/zip/GZIP/GZIPOverBlockingStreams.java diff --git a/test/jdk/java/util/zip/GZIP/GZIPOverBlockingStreams.java b/test/jdk/java/util/zip/GZIP/GZIPOverBlockingStreams.java new file mode 100644 index 00000000000..81f55f2f0dd --- /dev/null +++ b/test/jdk/java/util/zip/GZIP/GZIPOverBlockingStreams.java @@ -0,0 +1,406 @@ +/* + * 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. + */ + +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import jdk.test.lib.RandomFactory; +import jdk.test.lib.net.URIBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import static java.nio.charset.StandardCharsets.US_ASCII; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +/* + * @test + * @summary Verifies that the GZIPInputStream works as expected when the underlying + * InputStream is a blocking stream + * @key randomness + * @library /test/lib + * @build jdk.test.lib.net.URIBuilder jdk.test.lib.RandomFactory + * @run junit GZIPOverBlockingStreams + */ +class GZIPOverBlockingStreams { + + private static final Random random = RandomFactory.getRandom(); + private static final String MEMBER_CONTENT_FORMAT = "Hello member %d, foo bar hello world\n"; + private static final ExecutorService httpServerExecutor = Executors.newCachedThreadPool(); + + private static Server nonHttpServer; + private static HttpServer httpServer; + + + @BeforeAll + static void beforeAll() throws Exception { + // create a socket based (non-HTTP) server + nonHttpServer = new Server(); + nonHttpServer.start(); + System.err.println("(non-HTTP) server started at " + nonHttpServer.getAddress()); + + // create a HTTP server + final InetAddress loopback = InetAddress.getLoopbackAddress(); + final InetSocketAddress serverAddr = new InetSocketAddress(loopback, 0); + httpServer = HttpServer.create(serverAddr, 0); + httpServer.setExecutor(httpServerExecutor); + httpServer.createContext("/", new HttpReqHandler()); + httpServer.start(); + System.err.println("started HTTP server at " + httpServer.getAddress()); + + } + + @AfterAll + static void afterAll() throws Exception { + if (nonHttpServer != null) { + System.err.println("stopping server " + nonHttpServer.getAddress()); + nonHttpServer.close(); + } + if (httpServer != null) { + System.err.println("stopping HTTP server " + httpServer.getAddress()); + httpServer.stop(0); + } + httpServerExecutor.shutdownNow(); + } + + static List numGZIPMembers() { + return List.of(1, + 13, + 42, + random.nextInt(2, 101) // a reasonable number of members, not too many + ); + } + + static List socketStreamTestArgs() { + final List args = new ArrayList<>(); + final List numMembers = numGZIPMembers(); + for (boolean shouldCloseSocket : new boolean[]{true, false}) { + for (int n : numMembers) { + args.add(Arguments.of(n, shouldCloseSocket)); + } + } + return args; + } + + /* + * Verifies that when the GZIPInputStream is used to read GZIP content + * over a socket stream, it does not block when reading past a member trailer to determine + * the presence of a subsequent member. + */ + @ParameterizedTest + @MethodSource("socketStreamTestArgs") + void testSocketStream(final int numMembers, final boolean shouldCloseSocket) throws Exception { + final InetSocketAddress serverAddr = nonHttpServer.getAddress(); + try (final Socket socket = new Socket(serverAddr.getAddress(), serverAddr.getPort())) { + System.err.println("connect established " + socket); + try (final OutputStream os = socket.getOutputStream(); + final DataOutputStream dos = new DataOutputStream(os)) { + // instruct the server side the number of GZIP members we want in the response + dos.writeInt(numMembers); + // instruct the server side whether to close the socket after writing out the + // response + dos.writeBoolean(shouldCloseSocket); + System.err.println("sent request for GZIP stream with " + numMembers + " members"); + // read the response + try (final InputStream in = socket.getInputStream(); + final GZIPInputStream gzipInputStream = new GZIPInputStream(in)) { + final byte[] decompressed = gzipInputStream.readAllBytes(); + System.err.println("read " + decompressed.length + + " bytes of decompressed response"); + // verify it's the expected content + assertDecompressedContent(numMembers, decompressed); + } + } + } + final Throwable serverFailure = nonHttpServer.failure; + if (serverFailure != null) { + fail("Server ran into an error", serverFailure); + } + } + + static List httpTestArgs() { + final List args = new ArrayList<>(); + final List numMembers = numGZIPMembers(); + for (boolean chunkedOrNot : new boolean[]{true, false}) { + for (int n : numMembers) { + args.add(Arguments.of(n, chunkedOrNot)); + } + } + return args; + } + + /* + * Verifies that when the GZIPInputStream is used to read GZIP content, + * over a stream obtained from a HTTP response, it does not block when reading past a member + * trailer to determine the presence of a subsequent member. + */ + @ParameterizedTest + @MethodSource("httpTestArgs") + void testHttpStream(final int numMembers, final boolean httpResponseChunked) throws Exception { + final URI reqURI = URIBuilder.newBuilder() + .scheme("http") + .loopback() + .port(httpServer.getAddress().getPort()) + .path("/") + .build(); + final HttpURLConnection conn = (HttpURLConnection) reqURI.toURL().openConnection(); + conn.setRequestProperty("numMembers", String.valueOf(numMembers)); + conn.setRequestProperty("chunkedResponse", String.valueOf(httpResponseChunked)); + System.err.println("issuing request " + reqURI); + try (final InputStream in = conn.getInputStream(); + final GZIPInputStream gzipInputStream = new GZIPInputStream(in)) { + final byte[] decompressed = gzipInputStream.readAllBytes(); + System.err.println("read " + decompressed.length + + " bytes of decompressed response"); + assertDecompressedContent(numMembers, decompressed); + } + } + + /* + * Creates and returns bytes representing a GZIP stream consisting of the given number of + * members. + */ + private static byte[] createGZIPStream(final int numMembers) throws IOException { + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + for (int i = 1; i <= numMembers; i++) { + final ByteArrayOutputStream member = new ByteArrayOutputStream(); + try (final OutputStream gzip = new GZIPOutputStream(member)) { + final String memberContent = String.format(MEMBER_CONTENT_FORMAT, i); + gzip.write(memberContent.getBytes(US_ASCII)); + } + // write out the GZIP member to the stream which accumulates all the members + baos.write(member.toByteArray()); + } + return baos.toByteArray(); + } + + /* + * Verifies that the given decompressed bytes, representing the given number of + * GZIP members, do match the expected content. + */ + private static void assertDecompressedContent(final int numMembers, + final byte[] decompressed) { + final String actual = new String(decompressed, US_ASCII); + final StringBuilder sb = new StringBuilder(); + for (int i = 1; i <= numMembers; i++) { + sb.append(String.format(MEMBER_CONTENT_FORMAT, i)); + } + final String expected = sb.toString(); + assertEquals(expected, actual, "unexpected decompressed content"); + } + + /* + * A server which communicates over a socket to receive a request consisting of an integer + * representing the number of GZIP members to respond with. The server then responds back + * on the socket's OutputStream with GZIP content representing those many members. + */ + private static final class Server implements AutoCloseable, Runnable { + private final ServerSocket serverSocket; + private volatile boolean stop; + private volatile Throwable failure; + + private Server() throws IOException { + this.serverSocket = new ServerSocket(0, 0, InetAddress.getLoopbackAddress()); + } + + private InetSocketAddress getAddress() { + return (InetSocketAddress) this.serverSocket.getLocalSocketAddress(); + } + + @Override + public void close() throws IOException { + this.stop = true; + System.err.println("closing server: " + this.serverSocket); + this.serverSocket.close(); + } + + private void start() { + final Thread t = new Thread(this); + t.setName("server"); + t.setDaemon(true); + t.start(); + } + + private synchronized void recordServerFailure(final Throwable t) { + Throwable previous = this.failure; + if (previous != null && previous != t) { + previous.addSuppressed(t); + return; + } + this.failure = t; + } + + @Override + public void run() { + System.err.println("server started accepting requests at " + this.serverSocket); + try { + doRun(); + } catch (Throwable t) { + if (!stop) { // ignore failures if the server is stopped + recordServerFailure(t); + System.err.println("server ran into error: " + t); + t.printStackTrace(); + } + } finally { + try { + this.close(); + } catch (IOException ioe) { + System.err.println("ignoring excpetion " + + "that happened during closing server: " + ioe); + ioe.printStackTrace(); + } + } + } + + private void doRun() throws Exception { + while (!this.stop) { + // we intentionally do not close the Socket. It's upto the + // sendGZIPResponse(...) method to do that only if the test + // request has instructed it to do so. This allows the test + // method to exercise the case where the socket is open + // but doesn't have any more data to send (and thus read() blocks) + final Socket socket = this.serverSocket.accept(); + System.err.println("accepted connection from " + socket); + // handle the request on a separate thread + final Thread handler = new Thread(() -> { + try { + handleRequest(socket); + } catch (Throwable t) { + // keep track of the failure + recordServerFailure(t); + System.err.println("failure when handling request on socket " + + socket + ", exception: " + t); + t.printStackTrace(); + } + }); + handler.setName("request-handler-" + socket.getRemoteSocketAddress()); + handler.setDaemon(true); + handler.start(); + } + } + + private static void handleRequest(final Socket socket) throws IOException { + final int numMembers; + final boolean shouldCloseSocket; + try { + final InputStream in = socket.getInputStream(); + final DataInputStream dis = new DataInputStream(in); + // read the socket's inputstream to determine how many GZIP members are + // expected in the response stream, by the client + numMembers = dis.readInt(); + // whether the socket should be closed after writing out the response + shouldCloseSocket = dis.readBoolean(); + } catch (IOException ioe) { + // could be a socket connection from an unexpected client, so ignore any + // failure when reading the request + System.err.println("Ignoring exception that happened when reading" + + " request from client socket " + socket + ", exception: " + ioe); + ioe.printStackTrace(); + // close the unexpected client connection + socket.close(); + return; + } + // valid request, respond to it with a GZIP response + sendGZIPResponse(socket, numMembers, shouldCloseSocket); + } + + /* + * Sends GZIP content over the socket's OutputStream. This method closes the socket + * only if the test request (read over the socket's InputStream) instructs it to do so. + */ + private static void sendGZIPResponse(final Socket socket, final int numMembers, + final boolean shouldCloseSocket) throws IOException { + // respond back with a GZIP output, containing the expected number of members + final byte[] gzipResponse = createGZIPStream(numMembers); + System.err.println("responding to " + socket + " with a GZIP stream of size " + + gzipResponse.length + " with " + numMembers + " members"); + final OutputStream os = socket.getOutputStream(); + os.write(gzipResponse); + System.err.println("done responding to " + socket); + // close the socket only if the test request wants us to + if (shouldCloseSocket) { + System.err.println("closing " + socket); + socket.close(); + } + } + } + + /* + * A HTTP request handler which responds back with chunked or non-chunked + * response containing GZIP content. + */ + private static final class HttpReqHandler implements HttpHandler { + + @Override + public void handle(final HttpExchange exchange) throws IOException { + final URI reqURI = exchange.getRequestURI(); + System.err.println("handling request: " + reqURI + " from " + + exchange.getRemoteAddress()); + + final String val = exchange.getRequestHeaders().getFirst("numMembers"); + final int numMembers = Integer.parseInt(val); + final boolean respChunked = Boolean.parseBoolean( + exchange.getRequestHeaders().getFirst("chunkedResponse")); + + final byte[] gzipResponse = createGZIPStream(numMembers); + System.err.println("responding to " + reqURI + + " with a GZIP stream of size " + gzipResponse.length + + " with " + numMembers + " members" + + " with chunked response = " + respChunked); + + // drain the inputstream and write out the response + exchange.getRequestBody().readAllBytes(); + if (respChunked) { + exchange.sendResponseHeaders(200, 0); // 0 = Chunked response + } else { + exchange.sendResponseHeaders(200, gzipResponse.length); + } + try (final OutputStream os = exchange.getResponseBody()) { + os.write(gzipResponse); + } + System.err.println("done responding to " + reqURI); + } + } +} From 16d0f161ef23d3025b467030fda667ecf13c80dd Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Tue, 23 Jun 2026 12:08:06 +0000 Subject: [PATCH 041/707] 8386960: BUILD_LIBVERIFY remove special warning settings Reviewed-by: erikj, clanger --- make/modules/java.base/lib/CoreLibraries.gmk | 2 -- src/java.base/share/native/libverify/check_code.c | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/make/modules/java.base/lib/CoreLibraries.gmk b/make/modules/java.base/lib/CoreLibraries.gmk index 164bf3704a8..8e3891a344c 100644 --- a/make/modules/java.base/lib/CoreLibraries.gmk +++ b/make/modules/java.base/lib/CoreLibraries.gmk @@ -35,8 +35,6 @@ ifeq ($(INCLUDE), true) $(eval $(call SetupJdkLibrary, BUILD_LIBVERIFY, \ NAME := verify, \ OPTIMIZATION := HIGH, \ - DISABLED_WARNINGS_gcc_check_code.c := unused-variable, \ - DISABLED_WARNINGS_clang_check_code.c := unused-variable, \ EXTRA_HEADER_DIRS := libjava, \ JDK_LIBS := libjvm, \ )) diff --git a/src/java.base/share/native/libverify/check_code.c b/src/java.base/share/native/libverify/check_code.c index e6aebead212..c0cc4ee33e2 100644 --- a/src/java.base/share/native/libverify/check_code.c +++ b/src/java.base/share/native/libverify/check_code.c @@ -3705,7 +3705,7 @@ CCerror (context_type *context, char *format, ...) static void CCout_of_memory(context_type *context) { - int n = print_CCerror_info(context); + print_CCerror_info(context); context->err_code = CC_OutOfMemory; longjmp(context->jump_buffer, 1); } From acbedab7198b4c1de2edd8960bcd6da644a15828 Mon Sep 17 00:00:00 2001 From: Jayathirth D V Date: Tue, 23 Jun 2026 12:33:57 +0000 Subject: [PATCH 042/707] 8384512: BMPImageWriter uses integer division before Math.ceil causing incorrect calculation Reviewed-by: azvegint, prr --- .../imageio/plugins/bmp/BMPImageWriter.java | 6 +- .../imageio/plugins/bmp/RLE4PaddingTest.java | 113 ++++++++++++++++++ 2 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 test/jdk/javax/imageio/plugins/bmp/RLE4PaddingTest.java diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageWriter.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageWriter.java index ea47d9d35e6..962a47aa922 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageWriter.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -1111,7 +1111,7 @@ private void encodeRLE4(byte[] bipixels, int scanlineBytes) incCompImageSize(1); } // Padding to word align absolute encoding - if ( !isEven((int)Math.ceil((absVal-1)/2)) ) { + if (!isEven((absVal - 1) / 2)) { stream.writeByte(0); incCompImageSize(1); } @@ -1247,7 +1247,7 @@ private void encodeRLE4(byte[] bipixels, int scanlineBytes) } // Padding - if ( !isEven((int)Math.ceil((absVal+1)/2)) ) { + if (!isEven((absVal + 2) / 2)) { stream.writeByte(0); incCompImageSize(1); } diff --git a/test/jdk/javax/imageio/plugins/bmp/RLE4PaddingTest.java b/test/jdk/javax/imageio/plugins/bmp/RLE4PaddingTest.java new file mode 100644 index 00000000000..e1e341fabe0 --- /dev/null +++ b/test/jdk/javax/imageio/plugins/bmp/RLE4PaddingTest.java @@ -0,0 +1,113 @@ +/* + * 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 8384512 + * @summary Test verifies that BMP images are encoded correctly with RLE4 + * compression and odd number of distinct pixels at the end of + * scanline. + */ + +import java.awt.image.BufferedImage; +import java.awt.image.IndexColorModel; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import javax.imageio.IIOImage; +import javax.imageio.ImageIO; +import javax.imageio.ImageWriteParam; +import javax.imageio.ImageWriter; +import javax.imageio.stream.ImageOutputStream; + +public class RLE4PaddingTest { + private static final int width = 5; + private static final int height = 2; + private static BufferedImage getTestImage() { + // create BufferedImage with width 5 and all distinct pixels, + // so that it uses absolute mode for RLE. + // If we don't add appropriate padding at end of the scanline, + // the encoded data of next scanline will be corrupt. + int bpp = 4; + int size = 16; + byte[] r = new byte[16]; + byte[] g = new byte[16]; + byte[] b = new byte[16]; + + for (int i = 0; i < 16; i++) { + r[i] = g[i] = b[i] = (byte)(i * 16); + } + IndexColorModel icm = new IndexColorModel(bpp, size, r, g, b); + BufferedImage src = new BufferedImage(width, height, + BufferedImage.TYPE_BYTE_INDEXED, icm); + + int[][] rows = { + {1, 2, 3, 4, 5}, + {6, 7, 8, 9, 10} + }; + + for (int y = 0; y < src.getHeight(); y++) { + for (int x = 0; x < src.getWidth(); x++) { + src.getRaster().setSample(x, y, 0, rows[y][x]); + } + } + return src; + } + + public static void main(String[] args) throws IOException { + BufferedImage src = getTestImage(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageOutputStream ios = ImageIO.createImageOutputStream(baos); + ImageWriter writer = ImageIO.getImageWritersByFormatName("BMP").next(); + writer.setOutput(ios); + ImageWriteParam param = writer.getDefaultWriteParam(); + param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); + param.setCompressionType("BI_RLE4"); + writer.write(null, new IIOImage(src, null, null), param); + ios.close(); + baos.close(); + + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + BufferedImage dst = ImageIO.read(bais); + + checkResult(src, dst); + } + + private static void checkResult(BufferedImage src, BufferedImage dst) { + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + int srcRgb = src.getRGB(x, y); + int dstRgb = dst.getRGB(x, y); + + if (srcRgb != dstRgb) { + throw new RuntimeException("Test failed due to color" + + " difference: " + Integer.toHexString(dstRgb) + + " instead of " + Integer.toHexString(srcRgb) + + " at [" + x + ", " + y + "]"); + } + } + } + } +} From e356cbb3958dcd3329765716d8b5376c6a213e89 Mon Sep 17 00:00:00 2001 From: Ivan Bereziuk Date: Tue, 23 Jun 2026 13:08:15 +0000 Subject: [PATCH 043/707] 8384847: Fix documentation typos around ML-KEM and ML-DSA intrinsic code for aarch64 Reviewed-by: adinn, aph --- .../cpu/aarch64/stubGenerator_aarch64.cpp | 77 ++++++++++--------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index 8e9af2b7b8a..f41a54e9d26 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -4977,7 +4977,7 @@ class StubGenerator: public StubCodeGenerator { return start; } // Implements the double_keccak() method of the - // sun.secyrity.provider.SHA3Parallel class + // sun.security.provider.SHA3Parallel class __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); start = __ pc(); @@ -5045,7 +5045,8 @@ class StubGenerator: public StubCodeGenerator { __ ldpd(v8, v9, __ post(sp, 64)); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // record the stub entry and end @@ -5458,7 +5459,7 @@ class StubGenerator: public StubCodeGenerator { // load N/2 pairs of quadword values from memory into N vector // registers via the address supplied in base with each pair indexed - // using the the start offset plus the corresponding entry in the + // using the start offset plus the corresponding entry in the // offsets array template void vs_ldpq_indexed(const VSeq& v, Register base, int start, int (&offsets)[N/2]) { @@ -5469,7 +5470,7 @@ class StubGenerator: public StubCodeGenerator { // store N vector registers into N/2 pairs of quadword memory // locations via the address supplied in base with each pair indexed - // using the the start offset plus the corresponding entry in the + // using the start offset plus the corresponding entry in the // offsets array template void vs_stpq_indexed(const VSeq& v, Register base, int start, int offsets[N/2]) { @@ -5480,7 +5481,7 @@ class StubGenerator: public StubCodeGenerator { // load N single quadword values from memory into N vector registers // via the address supplied in base with each value indexed using - // the the start offset plus the corresponding entry in the offsets + // the start offset plus the corresponding entry in the offsets // array template void vs_ldr_indexed(const VSeq& v, Assembler::SIMD_RegVariant T, Register base, @@ -5492,7 +5493,7 @@ class StubGenerator: public StubCodeGenerator { // store N vector registers into N single quadword memory locations // via the address supplied in base with each value indexed using - // the the start offset plus the corresponding entry in the offsets + // the start offset plus the corresponding entry in the offsets // array template void vs_str_indexed(const VSeq& v, Assembler::SIMD_RegVariant T, Register base, @@ -5504,7 +5505,7 @@ class StubGenerator: public StubCodeGenerator { // load N/2 pairs of quadword values from memory de-interleaved into // N vector registers 2 at a time via the address supplied in base - // with each pair indexed using the the start offset plus the + // with each pair indexed using the start offset plus the // corresponding entry in the offsets array template void vs_ld2_indexed(const VSeq& v, Assembler::SIMD_Arrangement T, Register base, @@ -5517,7 +5518,7 @@ class StubGenerator: public StubCodeGenerator { // store N vector registers 2 at a time interleaved into N/2 pairs // of quadword memory locations via the address supplied in base - // with each pair indexed using the the start offset plus the + // with each pair indexed using the start offset plus the // corresponding entry in the offsets array template void vs_st2_indexed(const VSeq& v, Assembler::SIMD_Arrangement T, Register base, @@ -5776,7 +5777,7 @@ class StubGenerator: public StubCodeGenerator { // registers. // 3. In the seilerNTT() method we use R = 2^20 for the Montgomery // multiplications (this is because that way there should not be any - // overflow during the inverse NTT computation), here we usr R = 2^16 so + // overflow during the inverse NTT computation), here we use R = 2^16 so // that we can use the 16-bit arithmetic in the vector unit. // // On each level, we fill up the vector registers in such a way that the @@ -5898,7 +5899,7 @@ class StubGenerator: public StubCodeGenerator { // level 4 // At level 4 coefficients occur in 8 discrete blocks of size 16 - // so they are loaded using employing an ldr at 8 distinct offsets. + // so they are loaded by employing an ldr at 8 distinct offsets. vs_ldpq(vq, kyberConsts); int offsets3[8] = { 0, 32, 64, 96, 128, 160, 192, 224 }; @@ -5954,7 +5955,6 @@ class StubGenerator: public StubCodeGenerator { kyber_montmul32_sub_add(vs_even(vs1), vs_odd(vs1), vs_front(vs2), vtmp, vq); vs_st2_indexed(vs1, __ T4S, coeffs, tmpAddr, 0, offsets4); vs_ld2_indexed(vs1, __ T4S, coeffs, tmpAddr, 128, offsets4); - // __ ldpq(v18, v19, __ post(zetas, 32)); load32shorts(vs_front(vs2), zetas); kyber_montmul32_sub_add(vs_even(vs1), vs_odd(vs1), vs_front(vs2), vtmp, vq); vs_st2_indexed(vs1, __ T4S, coeffs, tmpAddr, 128, offsets4); @@ -5970,7 +5970,7 @@ class StubGenerator: public StubCodeGenerator { vs_st2_indexed(vs1, __ T4S, coeffs, tmpAddr, 384, offsets4); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // record the stub entry and end @@ -6067,7 +6067,7 @@ class StubGenerator: public StubCodeGenerator { // level 2 // At level 2 coefficients occur in 8 discrete blocks of size 16 - // so they are loaded using employing an ldr at 8 distinct offsets. + // so they are loaded by employing an ldr at 8 distinct offsets. int offsets3[8] = { 0, 32, 64, 96, 128, 160, 192, 224 }; vs_ldr_indexed(vs1, __ Q, coeffs, 0, offsets3); @@ -6262,7 +6262,7 @@ class StubGenerator: public StubCodeGenerator { store64shorts(vs2, tmpAddr); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // record the stub entry and end @@ -6407,7 +6407,7 @@ class StubGenerator: public StubCodeGenerator { __ br(Assembler::NE, kyberNttMult_loop); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // record the stub entry and end @@ -6499,7 +6499,7 @@ class StubGenerator: public StubCodeGenerator { } __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // record the stub entry and end @@ -6606,7 +6606,7 @@ class StubGenerator: public StubCodeGenerator { } __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // record the stub entry and end @@ -6692,8 +6692,8 @@ class StubGenerator: public StubCodeGenerator { // twice, one copy manipulated to provide the lower 4 bits // belonging to the first short in a pair and another copy // manipulated to provide the higher 4 bits belonging to the - // second short in a pair. This is why the the vector sequences va - // and vb used to hold the expanded 8H elements are of length 8. + // second short in a pair. This is why the vector sequences va + // and vb are used to hold the expanded 8H elements are of length 8. // Expand vin[0] into va[0:1], and vin[1] into va[2:3] and va[4:5] // n.b. target elements 2 and 3 duplicate elements 4 and 5 @@ -6763,7 +6763,7 @@ class StubGenerator: public StubCodeGenerator { __ br(Assembler::GT, L_loop); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // bind label and generate constant data used by this stub @@ -6869,7 +6869,7 @@ class StubGenerator: public StubCodeGenerator { } __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // record the stub entry and end @@ -6943,7 +6943,7 @@ class StubGenerator: public StubCodeGenerator { vs_addv(va0, __ T4S, va0, vc); } - // Perform combined add/sub then montul on 4x4S vectors. + // Perform combined add/sub then montmul on 4x4S vectors. void dilithium_sub_add_montmul16( const VSeq<4>& va0, const VSeq<4>& va1, const VSeq<4>& vb, const VSeq<4>& vtmp1, const VSeq<4>& vtmp2, const VSeq<2>& vq) { @@ -7079,7 +7079,7 @@ class StubGenerator: public StubCodeGenerator { // coefficients we load 4 adjacent values at 8 different offsets // using an indexed ldr with register variant Q and multiply them // in sequence order by the next set of inputs. Likewise we store - // the resuls using an indexed str with register variant Q. + // the results using an indexed str with register variant Q. for (int i = 0; i < 1024; i += 256) { // reload constants q, qinv each iteration as they get clobbered later vs_ldpq(vq, dilithiumConsts); // qInv, q @@ -7129,11 +7129,11 @@ class StubGenerator: public StubCodeGenerator { // level 7 // At level 7 the coefficients we need to combine with the zetas - // occur singly with montmul inputs alterating with add/sub + // occur singly with montmul inputs alternating with add/sub // inputs. Once again we can use 4-way parallelism to combine 16 // zetas at a time. However, we have to load 8 adjacent values at // 4 different offsets using an ld2 load with arrangement 4S. That - // interleaves the the odd words of each pair into one + // interleaves the odd words of each pair into one // coefficients vector register and the even words of the pair // into the next register. We then need to montmul the 4 even // elements of the coefficients register sequence by the zetas in @@ -7155,7 +7155,7 @@ class StubGenerator: public StubCodeGenerator { vs_st2_indexed(vs1, __ T4S, coeffs, tmpAddr, i, offsets); } __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // record the stub entry and end @@ -7334,7 +7334,7 @@ class StubGenerator: public StubCodeGenerator { // c0 load 32 (8x4S) coefficients via first offsets vs_ldr_indexed(vs1, __ Q, coeffs, i, offsets1); // c1 load 32 (8x4S) coefficients via second offsets - vs_ldr_indexed(vs2, __ Q,coeffs, i, offsets2); + vs_ldr_indexed(vs2, __ Q, coeffs, i, offsets2); // a0 = c0 + c1 n.b. clobbers vq which overlaps vs3 vs_addv(vs3, __ T4S, vs1, vs2); // c = c0 - c1 @@ -7355,7 +7355,7 @@ class StubGenerator: public StubCodeGenerator { dilithiumInverseNttLevel3_7(dilithiumConsts, coeffs, zetas); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // record the stub entry and end @@ -7367,8 +7367,8 @@ class StubGenerator: public StubCodeGenerator { // Dilithium multiply polynomials in the NTT domain. // Straightforward implementation of the method // static int implDilithiumNttMult( - // int[] result, int[] ntta, int[] nttb {} of - // the sun.security.provider.ML_DSA class. + // int[] product, int[] coeffs1, int[] coeffs2) {} + // of the sun.security.provider.ML_DSA class. // // result (int[256]) = c_rarg0 // poly1 (int[256]) = c_rarg1 @@ -7429,7 +7429,7 @@ class StubGenerator: public StubCodeGenerator { __ br(Assembler::GE, L_loop); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // record the stub entry and end @@ -7438,10 +7438,10 @@ class StubGenerator: public StubCodeGenerator { return start; } - // Dilithium Motgomery multiply an array by a constant. + // Dilithium Montgomery multiply an array by a constant. // A straightforward implementation of the method // static int implDilithiumMontMulByConstant(int[] coeffs, int constant) {} - // of the sun.security.provider.MLDSA class + // of the sun.security.provider.ML_DSA class // // coeffs (int[256]) = c_rarg0 // constant (int) = c_rarg1 @@ -7498,7 +7498,7 @@ class StubGenerator: public StubCodeGenerator { __ br(Assembler::GE, L_loop); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // record the stub entry and end @@ -7509,7 +7509,8 @@ class StubGenerator: public StubCodeGenerator { // Dilithium decompose poly. // Implements the method - // static int implDilithiumDecomposePoly(int[] coeffs, int constant) {} + // static int implDilithiumDecomposePoly(int[] input, int[] lowPart, int[] highPart, + // int twoGamma2, int multiplier) { // of the sun.security.provider.ML_DSA class // // input (int[256]) = c_rarg0 @@ -7613,7 +7614,7 @@ class StubGenerator: public StubCodeGenerator { vs_andr(vtmp, vs4, twog2); vs_subv(vs3, __ T4S, vs3, vtmp); - // quotient += (mask & 1); + // quotient += (mask & 1); vs_andr(vtmp, vs4, one); vs_addv(vs2, __ T4S, vs2, vtmp); @@ -7647,7 +7648,7 @@ class StubGenerator: public StubCodeGenerator { // r1 = r1 & quotient; vs_andr(vs1, vs2, vs1); - // store results inteleaved + // store results interleaved // lowPart[m] = r0; // highPart[m] = r1; __ st4(vs3[0], vs3[1], vs3[2], vs3[3], __ T4S, __ post(lowPart, 64)); @@ -7664,7 +7665,7 @@ class StubGenerator: public StubCodeGenerator { __ ldpd(v8, v9, __ post(sp, 64)); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov(r0, zr); // return 0 + __ mov(r0, zr); // return 0 (Java callees return 1. Caller ignores the return value) __ ret(lr); // record the stub entry and end From fe46d6b1e2e62b9143267827729b0ff9abd80563 Mon Sep 17 00:00:00 2001 From: Naoto Sato Date: Tue, 23 Jun 2026 16:09:55 +0000 Subject: [PATCH 044/707] 8387041: Add a URL link to BCP 47 in the Locale class Reviewed-by: jlu, iris --- .../share/classes/java/util/Locale.java | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/java.base/share/classes/java/util/Locale.java b/src/java.base/share/classes/java/util/Locale.java index 2bab271a489..f600afbb007 100644 --- a/src/java.base/share/classes/java/util/Locale.java +++ b/src/java.base/share/classes/java/util/Locale.java @@ -79,9 +79,10 @@ * the number should be formatted according to the customs and conventions of the * user's native country, region, or culture. * - *

The {@code Locale} class implements IETF BCP 47 which is composed of - * RFC 4647 "Matching of Language - * Tags" and RFC 5646 "Tags + *

The {@code Locale} class implements + * IETF BCP 47 which contains + * RFC 4647 "Matching of Language + * Tags" and RFC 5646 "Tags * for Identifying Languages" with support for the LDML (UTS#35, "Unicode * Locale Data Markup Language") BCP 47-compatible extensions for locale data * exchange. Each {@code Locale} is associated with locale data which is provided @@ -101,7 +102,7 @@ *

* {@code Locale} implements IETF BCP 47 and any deviations should be observed * by the comments prefixed by "BCP 47 deviation:". - * RFC 5646 + * RFC 5646 * combines subtags from various ISO (639, 3166, 15924) standards which are also * included in the composition of {@code Locale}. * Additionally, the full list of valid codes for each field can be found in the @@ -120,7 +121,7 @@ * *

Syntax: Well-formed {@code language} values have the form {@code [a-zA-Z]{2,8}}.
*
BCP 47 deviation: this is not the full BCP 47 language production, since it excludes - * extlang + * extlang * (as modern three-letter language codes are preferred).
* *
Example: "en" (English), "ja" (Japanese), "kok" (Konkani)
@@ -208,7 +209,7 @@ * * * BCP 47 deviation: BCP47 defines the following two levels of - * conformance, + * conformance, * "valid" and "well-formed". A valid tag requires that it is well-formed, its * subtag values are registered in the IANA Language Subtag Registry, and it does not * contain duplicate variant or extension singleton subtags. The {@code Locale} @@ -222,8 +223,10 @@ * *

Unicode BCP 47 U Extension

* - *

UTS#35, "Unicode Locale Data Markup Language" defines optional - * attributes and keywords to override or refine the default behavior + *

UTS#35, "Unicode Locale Data Markup Language" defines the + * Unicode BCP 47 U Extension, + * an extension based on RFC 6067, + * which describes optional attributes and keywords to override or refine the default behavior * associated with a locale. A keyword is represented by a pair of * key and type. For example, "nu-thai" indicates that Thai local * digits (value:"thai") should be used for formatting numbers @@ -410,7 +413,7 @@ * with "locale" in the following locale matching documentation. * *

In order to match a user's preferred locales to a set of language - * tags, RFC 4647 Matching of + * tags, RFC 4647 Matching of * Language Tags defines two mechanisms: filtering and lookup. * Filtering is used to get all matching locales, whereas * lookup is to select the best matching locale. @@ -546,6 +549,8 @@ * this mapping, so that resources can be named using either convention, * see {@link ResourceBundle.Control}. * + * @spec https://www.rfc-editor.org/info/bcp47 + * IETF BCP 47 * @spec https://www.rfc-editor.org/info/rfc4647 * RFC 4647: Matching of Language Tags * @spec https://www.rfc-editor.org/info/rfc5646 @@ -2985,7 +2990,7 @@ public Locale build() { /** * This enum provides constants to select a filtering mode for locale - * matching. Refer to RFC 4647 + * matching. Refer to RFC 4647 * Matching of Language Tags for details. * *

As an example, think of two Language Priority Lists each of which @@ -3122,7 +3127,7 @@ public static enum FilteringMode { /** * This class expresses a Language Range defined in - * RFC 4647 Matching of + * RFC 4647 Matching of * Language Tags. A language range is an identifier which is used to * select language tag(s) meeting specific requirements by using the * mechanisms described in {@linkplain Locale##LocaleMatching Locale From d3d560f756082aec24fbbcb7a44d94346fcf9e23 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Tue, 23 Jun 2026 17:32:34 +0000 Subject: [PATCH 045/707] 8387002: Test ManualTests/JPackage/JPKG001/JPKG001_004: CommonLicenseTest fails on Windows because the license agreement text is not displayed Reviewed-by: almatvee --- .../jdk/jpackage/internal/RtfConverter.java | 2 +- .../jpackage/internal/RtfConverterTest.java | 82 +++++++++++++++++++ .../tools/jpackage/junit/windows/junit.java | 8 ++ 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 test/jdk/tools/jpackage/junit/windows/jdk.jpackage/jdk/jpackage/internal/RtfConverterTest.java diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/RtfConverter.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/RtfConverter.java index 8886afc7918..a0ff70066b9 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/RtfConverter.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/RtfConverter.java @@ -58,7 +58,7 @@ static boolean isRtfFile(Path path) throws IOException { } static Optional createSimple(Path path) throws IOException { - if (isRtfFile(path)) { + if (!Files.isDirectory(path) && !isRtfFile(path)) { return Optional.of(Details.Simple.VALUE); } else { return Optional.empty(); diff --git a/test/jdk/tools/jpackage/junit/windows/jdk.jpackage/jdk/jpackage/internal/RtfConverterTest.java b/test/jdk/tools/jpackage/junit/windows/jdk.jpackage/jdk/jpackage/internal/RtfConverterTest.java new file mode 100644 index 00000000000..0943f6c9733 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/windows/jdk.jpackage/jdk/jpackage/internal/RtfConverterTest.java @@ -0,0 +1,82 @@ +/* + * 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 jdk.jpackage.internal; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Optional; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + + +class RtfConverterTest { + + @Test + void test_createSimple_dir(@TempDir Path workDir) throws IOException { + + assertEquals(Optional.empty(), RtfConverter.createSimple(workDir)); + } + + @ParameterizedTest + @ValueSource(strings = { + // Empty value to exercise the case when the file's content is shorter than the RTF's header + "", + // Value to exercise the case when the file's content is shorter than the RTF's header + "Hello", + "Hello Duke!", + }) + void test_createSimple_text_file(String text, @TempDir Path workDir) throws IOException { + + final var licenseFile = workDir.resolve("license"); + + Files.writeString(licenseFile, text); + + final var conv = RtfConverter.createSimple(licenseFile); + + assertTrue(conv.isPresent()); + + conv.orElseThrow().convert(licenseFile); + + assertEquals(Optional.empty(), RtfConverter.createSimple(licenseFile)); + } + + @ParameterizedTest + @ValueSource(strings = { + "{\\rtf1\\ansi\\deff0{\\fonttbl{\\f0 Arial;}}\\f0\\fs24 Hello, Duke!}", + }) + void test_createSimple_rtf_file(String text, @TempDir Path workDir) throws IOException { + + final var licenseFile = workDir.resolve("license"); + + Files.writeString(licenseFile, text); + + assertEquals(Optional.empty(), RtfConverter.createSimple(workDir)); + } +} diff --git a/test/jdk/tools/jpackage/junit/windows/junit.java b/test/jdk/tools/jpackage/junit/windows/junit.java index 8c290c2c87f..c046589a363 100644 --- a/test/jdk/tools/jpackage/junit/windows/junit.java +++ b/test/jdk/tools/jpackage/junit/windows/junit.java @@ -57,3 +57,11 @@ * jdk/jpackage/internal/WixToolTest.java * @run junit jdk.jpackage/jdk.jpackage.internal.WixToolTest */ + +/* @test + * @summary RtfConverter unit tests + * @requires (os.family == "windows") + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/RtfConverterTest.java + * @run junit jdk.jpackage/jdk.jpackage.internal.RtfConverterTest + */ From b06aa89c60cfad9621af227f42c993c6d96beecf Mon Sep 17 00:00:00 2001 From: Xiaolong Peng Date: Tue, 23 Jun 2026 17:40:17 +0000 Subject: [PATCH 046/707] 8386992: Shenandoah: Pad hot atomic counters to avoid false sharing on the allocation path Reviewed-by: shade, ruili --- src/hotspot/share/gc/shenandoah/shenandoahAllocRate.hpp | 3 +++ src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAllocRate.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAllocRate.hpp index 4d75c5d6794..24221e504fd 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAllocRate.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAllocRate.hpp @@ -25,6 +25,7 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHALLOCRATE_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHALLOCRATE_HPP +#include "gc/shenandoah/shenandoahPadding.hpp" #include "gc/shenandoah/shenandoahWeightedSeq.hpp" #include "runtime/atomic.hpp" #include "runtime/mutex.hpp" @@ -110,7 +111,9 @@ class ShenandoahAllocRate { static constexpr size_t ALLOC_SAMPLE_MAX = G; PaddedMonitor _sample_lock; + shenandoah_padding(0); Atomic _allocated_bytes_since_last_sample; + shenandoah_padding(1); Atomic _minimum_sample_size; // bytes, read by mutator, updated by gc jlong _last_sample_time; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp index f7f0a0ae0ba..43151af4c87 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp @@ -29,6 +29,7 @@ #include "gc/shenandoah/shenandoahAllocRequest.hpp" #include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahGenerationalHeap.hpp" +#include "gc/shenandoah/shenandoahPadding.hpp" #include "gc/shenandoah/shenandoahScanRemembered.hpp" #include "gc/shenandoah/shenandoahSharedVariables.hpp" @@ -64,7 +65,9 @@ class ShenandoahOldGeneration : public ShenandoahGeneration { // is therefore always accessed through atomic operations. This is increased when a // PLAB is allocated for promotions. The value is decreased by the amount of memory // remaining in a PLAB when it is retired. + shenandoah_padding(0); Atomic _promoted_expended; + shenandoah_padding(1); // Represents the quantity of live bytes we expect to promote during the next GC cycle, either by // evacuation or by promote-in-place. This value is used by the young heuristic to trigger mixed collections. From 02c82240ebe87fefcba094c707e164d85fca0a94 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Tue, 23 Jun 2026 18:12:35 +0000 Subject: [PATCH 047/707] 8387042: Shenandoah: Build time regression with LBE Reviewed-by: wkemper, xpeng --- .../gc/shenandoah/shenandoahBarrierSet.hpp | 4 ++-- .../shenandoah/shenandoahBarrierSet.inline.hpp | 18 +++++------------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp index 8f8dbc9ac83..4b5620ff0bf 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp @@ -134,8 +134,8 @@ class ShenandoahBarrierSet: public BarrierSet { template inline void arraycopy_update(T* src, size_t count); - inline void clone_evacuation(oop src); - inline void clone_update(oop src); + template + inline void clone_work(oop src); template inline void arraycopy_work(T* src, size_t count); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp index e7ddfdb0f6a..1bdeb01618b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp @@ -469,18 +469,10 @@ class ShenandoahUpdateEvacForCloneOopClosure : public BasicOopIterateClosure { virtual void do_oop(narrowOop* p) { do_oop_work(p); } }; -void ShenandoahBarrierSet::clone_evacuation(oop obj) { - assert(_heap->is_evacuation_in_progress(), "only during evacuation"); - if (need_bulk_update(cast_from_oop(obj))) { - ShenandoahUpdateEvacForCloneOopClosure cl; - obj->oop_iterate(&cl); - } -} - -void ShenandoahBarrierSet::clone_update(oop obj) { - assert(_heap->is_update_refs_in_progress(), "only during update-refs"); +template +void ShenandoahBarrierSet::clone_work(oop obj) { if (need_bulk_update(cast_from_oop(obj))) { - ShenandoahUpdateEvacForCloneOopClosure cl; + ShenandoahUpdateEvacForCloneOopClosure cl; obj->oop_iterate(&cl); } } @@ -494,9 +486,9 @@ void ShenandoahBarrierSet::AccessBarrier::clone_in_heap if (gc_state != 0 && ShenandoahCloneBarrier) { ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set(); if ((gc_state & ShenandoahHeap::EVACUATION) != 0) { - bs->clone_evacuation(src); + bs->clone_work(src); } else if ((gc_state & ShenandoahHeap::UPDATE_REFS) != 0) { - bs->clone_update(src); + bs->clone_work(src); } } From 41c33dafd51c89a2e28f276213d2289e0405fd3c Mon Sep 17 00:00:00 2001 From: Man Cao Date: Tue, 23 Jun 2026 18:12:57 +0000 Subject: [PATCH 048/707] 8386965: Data race on java.lang.Class.reflectionFactory field Reviewed-by: liach, alanb, iklam --- src/hotspot/share/cds/aotMetaspace.cpp | 14 ---- .../share/classes/java/lang/Class.java | 65 +++++++------------ .../vm/annotation/AOTRuntimeSetup.java | 7 +- 3 files changed, 26 insertions(+), 60 deletions(-) diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index 76634fc3fba..fac320c3ed7 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -1176,20 +1176,6 @@ void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS AOTReferenceObjSupport::initialize(CHECK); AOTReferenceObjSupport::stabilize_cached_reference_objects(CHECK); - - if (CDSConfig::is_dumping_aot_linked_classes()) { - // java.lang.Class::reflectionFactory cannot be archived yet. We set this field - // to null, and it will be initialized again at runtime. - log_debug(aot)("Resetting Class::reflectionFactory"); - TempNewSymbol method_name = SymbolTable::new_symbol("resetArchivedStates"); - Symbol* method_sig = vmSymbols::void_method_signature(); - JavaValue result(T_VOID); - JavaCalls::call_static(&result, vmClasses::Class_klass(), - method_name, method_sig, CHECK); - - // Perhaps there is a way to avoid hard-coding these names here. - // See discussion in JDK-8342481. - } } else { log_info(aot)("Not dumping heap, reset CDSConfig::_is_using_optimized_module_handling"); CDSConfig::stop_using_optimized_module_handling(); diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index b08b9fe4d2c..b8bc97a1f0d 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -233,8 +233,7 @@ public final class Class implements java.io.Serializable, runtimeSetup(); } - /// No significant static final fields; [#resetArchivedStates()] handles - /// prevents storing [#reflectionFactory] into AOT image. + /// No significant static final fields @AOTRuntimeSetup private static void runtimeSetup() { registerNatives(); @@ -710,7 +709,7 @@ public T newInstance() } try { Class[] empty = {}; - final Constructor c = getReflectionFactory().copyConstructor( + final Constructor c = ReflectionFactory.getReflectionFactory().copyConstructor( getConstructor0(empty, Member.DECLARED)); // Disable accessibility checks on the constructor // access check is done with the true caller @@ -724,7 +723,8 @@ public T newInstance() try { Class caller = Reflection.getCallerClass(); - return getReflectionFactory().newInstance(tmpConstructor, null, caller); + return ReflectionFactory.getReflectionFactory().newInstance(tmpConstructor, + null, caller); } catch (InvocationTargetException e) { Unsafe.getUnsafe().throwException(e.getTargetException()); // Not reached @@ -1396,8 +1396,9 @@ public Set accessFlags() { isAnonymousClass() || isArray()) ? AccessFlag.Location.INNER_CLASS : AccessFlag.Location.CLASS; - return getReflectionFactory().parseAccessFlags((location == AccessFlag.Location.CLASS) ? - getClassFileAccessFlags() : getModifiers(), location, this); + return ReflectionFactory.getReflectionFactory().parseAccessFlags( + (location == AccessFlag.Location.CLASS) ? getClassFileAccessFlags() : getModifiers(), + location, this); } /** @@ -1460,7 +1461,7 @@ public Method getEnclosingMethod() { * type. Matching return type is also necessary * because of covariant returns, etc. */ - ReflectionFactory fact = getReflectionFactory(); + ReflectionFactory fact = ReflectionFactory.getReflectionFactory(); for (Method m : candidates) { if (m.getName().equals(enclosingInfo.getName()) && arrayContentsEq(parameterClasses, @@ -1586,7 +1587,7 @@ public Constructor getEnclosingConstructor() { * Loop over all declared constructors; match number * of and type of parameters. */ - ReflectionFactory fact = getReflectionFactory(); + ReflectionFactory fact = ReflectionFactory.getReflectionFactory(); for (Constructor c : candidates) { if (arrayContentsEq(parameterClasses, fact.getExecutableSharedParameterTypes(c))) { @@ -2069,7 +2070,7 @@ public Field getField(String name) throws NoSuchFieldException { if (field == null) { throw new NoSuchFieldException(name); } - return getReflectionFactory().copyField(field); + return ReflectionFactory.getReflectionFactory().copyField(field); } @@ -2167,7 +2168,7 @@ public Method getMethod(String name, Class... parameterTypes) if (method == null) { throw new NoSuchMethodException(methodToString(name, parameterTypes)); } - return getReflectionFactory().copyMethod(method); + return ReflectionFactory.getReflectionFactory().copyMethod(method); } /** @@ -2198,7 +2199,7 @@ public Method getMethod(String name, Class... parameterTypes) */ public Constructor getConstructor(Class... parameterTypes) throws NoSuchMethodException { - return getReflectionFactory().copyConstructor( + return ReflectionFactory.getReflectionFactory().copyConstructor( getConstructor0(parameterTypes, Member.PUBLIC)); } @@ -2383,7 +2384,7 @@ public Field getDeclaredField(String name) throws NoSuchFieldException { if (field == null) { throw new NoSuchFieldException(name); } - return getReflectionFactory().copyField(field); + return ReflectionFactory.getReflectionFactory().copyField(field); } @@ -2425,7 +2426,7 @@ public Method getDeclaredMethod(String name, Class... parameterTypes) if (method == null) { throw new NoSuchMethodException(methodToString(name, parameterTypes)); } - return getReflectionFactory().copyMethod(method); + return ReflectionFactory.getReflectionFactory().copyMethod(method); } /** @@ -2440,7 +2441,7 @@ public Method getDeclaredMethod(String name, Class... parameterTypes) */ List getDeclaredPublicMethods(String name, Class... parameterTypes) { Method[] methods = privateGetDeclaredMethods(/* publicOnly */ true); - ReflectionFactory factory = getReflectionFactory(); + ReflectionFactory factory = ReflectionFactory.getReflectionFactory(); List result = new ArrayList<>(); for (Method method : methods) { if (method.getName().equals(name) @@ -2465,7 +2466,8 @@ List getDeclaredPublicMethods(String name, Class... parameterTypes) { */ Method findMethod(boolean publicOnly, String name, Class... parameterTypes) { PublicMethods.MethodList res = getMethodsRecursive(name, parameterTypes, true, publicOnly); - return res == null ? null : getReflectionFactory().copyMethod(res.getMostSpecific()); + return res == null ? null : ReflectionFactory.getReflectionFactory().copyMethod( + res.getMostSpecific()); } /** @@ -2492,7 +2494,7 @@ Method findMethod(boolean publicOnly, String name, Class... parameterTypes) { */ public Constructor getDeclaredConstructor(Class... parameterTypes) throws NoSuchMethodException { - return getReflectionFactory().copyConstructor( + return ReflectionFactory.getReflectionFactory().copyConstructor( getConstructor0(parameterTypes, Member.DECLARED)); } @@ -2897,7 +2899,7 @@ private ClassRepository getGenericInfo() { // Since 1.8 native byte[] getRawTypeAnnotations(); static byte[] getExecutableTypeAnnotationBytes(Executable ex) { - return getReflectionFactory().getExecutableTypeAnnotationBytes(ex); + return ReflectionFactory.getReflectionFactory().getExecutableTypeAnnotationBytes(ex); } native ConstantPool getConstantPool(); @@ -3111,7 +3113,7 @@ private static Method searchMethods(Method[] methods, String name, Class[] parameterTypes) { - ReflectionFactory fact = getReflectionFactory(); + ReflectionFactory fact = ReflectionFactory.getReflectionFactory(); Method res = null; for (Method m : methods) { if (m.getName().equals(name) @@ -3179,7 +3181,7 @@ private PublicMethods.MethodList getMethodsRecursive(String name, private Constructor getConstructor0(Class[] parameterTypes, int which) throws NoSuchMethodException { - ReflectionFactory fact = getReflectionFactory(); + ReflectionFactory fact = ReflectionFactory.getReflectionFactory(); Constructor[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC)); for (Constructor constructor : constructors) { if (arrayContentsEq(parameterTypes, @@ -3218,7 +3220,7 @@ private static boolean arrayContentsEq(Object[] a1, Object[] a2) { private static Field[] copyFields(Field[] arg) { Field[] out = new Field[arg.length]; - ReflectionFactory fact = getReflectionFactory(); + ReflectionFactory fact = ReflectionFactory.getReflectionFactory(); for (int i = 0; i < arg.length; i++) { out[i] = fact.copyField(arg[i]); } @@ -3227,7 +3229,7 @@ private static Field[] copyFields(Field[] arg) { private static Method[] copyMethods(Method[] arg) { Method[] out = new Method[arg.length]; - ReflectionFactory fact = getReflectionFactory(); + ReflectionFactory fact = ReflectionFactory.getReflectionFactory(); for (int i = 0; i < arg.length; i++) { out[i] = fact.copyMethod(arg[i]); } @@ -3236,7 +3238,7 @@ private static Method[] copyMethods(Method[] arg) { private static Constructor[] copyConstructors(Constructor[] arg) { Constructor[] out = arg.clone(); - ReflectionFactory fact = getReflectionFactory(); + ReflectionFactory fact = ReflectionFactory.getReflectionFactory(); for (int i = 0; i < out.length; i++) { out[i] = fact.copyConstructor(out[i]); } @@ -3390,25 +3392,6 @@ public boolean isRecord() { isRecord0(); } - // Fetches the factory for reflective objects - private static ReflectionFactory getReflectionFactory() { - var factory = reflectionFactory; - if (factory != null) { - return factory; - } - return reflectionFactory = ReflectionFactory.getReflectionFactory(); - } - private static ReflectionFactory reflectionFactory; - - /** - * When CDS is enabled, the Class class may be aot-initialized. However, - * we can't archive reflectionFactory, so we reset it to null, so it - * will be allocated again at runtime. - */ - private static void resetArchivedStates() { - reflectionFactory = null; - } - /** * Returns the elements of this enum class or null if this * Class object does not represent an enum class. diff --git a/src/java.base/share/classes/jdk/internal/vm/annotation/AOTRuntimeSetup.java b/src/java.base/share/classes/jdk/internal/vm/annotation/AOTRuntimeSetup.java index c3a0c283dc3..05f582b9a54 100644 --- a/src/java.base/share/classes/jdk/internal/vm/annotation/AOTRuntimeSetup.java +++ b/src/java.base/share/classes/jdk/internal/vm/annotation/AOTRuntimeSetup.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 @@ -64,10 +64,7 @@ /// an AOT-initialized class, at the end of the assembly phase run which builds /// an AOT cache. The `resetArchivedStates` may "tear down" state that should /// not be stored in the AOT cache, which the `runtimeSetup` method may then -/// "build up again" as the production run begins. This additional method is -/// currently only used by [Class] to reset a cache field, but it may be -/// expanded to other classes and interfaces later on, using more -/// annotation-driven logic. +/// "build up again" as the production run begins. /// /// The logic in `classFileParser.cpp` performs checks on the annotated method: If the /// annotated method's signature differs from that described above, or if (during the From ce93858acd423e1fa1011358cff9fc495182aca6 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Tue, 23 Jun 2026 20:11:04 +0000 Subject: [PATCH 049/707] 8386852: Lower peak throughput with AOTCache Reviewed-by: kvn, iveresov, shade --- src/hotspot/share/compiler/compilationPolicy.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/compiler/compilationPolicy.cpp b/src/hotspot/share/compiler/compilationPolicy.cpp index 94e734aaad5..81c03587416 100644 --- a/src/hotspot/share/compiler/compilationPolicy.cpp +++ b/src/hotspot/share/compiler/compilationPolicy.cpp @@ -1377,7 +1377,7 @@ CompLevel CompilationPolicy::transition_from_limited_profile(const methodHandle& // Determine if a method should be compiled with a normal entry point at a different level. CompLevel CompilationPolicy::call_event(const methodHandle& method, CompLevel cur_level, JavaThread* THREAD) { CompLevel osr_level = MIN2((CompLevel) method->highest_osr_comp_level(), common(method, cur_level, THREAD, true)); - CompLevel next_level = common(method, cur_level, THREAD, !TrainingData::have_data() && is_old(method)); + CompLevel next_level = common(method, cur_level, THREAD, is_old(method)); // If OSR method level is greater than the regular method level, the levels should be // equalized by raising the regular method level in order to avoid OSRs during each From 316065c828861f32a949bfa5f965010e782bebb7 Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Wed, 24 Jun 2026 06:22:41 +0000 Subject: [PATCH 050/707] 8387083: G1: Remove redundant NMT tagging from G1RegionToSpaceMapper Reviewed-by: stefank, tschatzl --- src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp b/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp index 5e37c7fa5a1..6ecaaf78e5e 100644 --- a/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp +++ b/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp @@ -28,7 +28,6 @@ #include "gc/shared/gc_globals.hpp" #include "memory/allocation.inline.hpp" #include "memory/reservedSpace.hpp" -#include "nmt/memTracker.hpp" #include "runtime/mutexLocker.hpp" #include "utilities/align.hpp" #include "utilities/bitMap.inline.hpp" @@ -46,8 +45,6 @@ G1RegionToSpaceMapper::G1RegionToSpaceMapper(ReservedSpace rs, _memory_tag(mem_tag) { guarantee(is_power_of_2(page_size), "must be"); guarantee(is_power_of_2(region_granularity), "must be"); - - MemTracker::record_virtual_memory_tag(rs, mem_tag); } // Used to manually signal a mapper to handle a set of regions as committed. From c8ba683939348eab8bdae1a21ba47813101e7b16 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Wed, 24 Jun 2026 06:57:03 +0000 Subject: [PATCH 051/707] 8387047: Shenandoah: Purge SBS::resolve_forwarded Reviewed-by: kdnilsen, xpeng, ruili --- .../share/gc/shenandoah/shenandoahBarrierSet.hpp | 3 --- .../shenandoah/shenandoahBarrierSet.inline.hpp | 16 ++-------------- .../gc/shenandoah/shenandoahClosures.inline.hpp | 5 +++-- .../gc/shenandoah/shenandoahConcurrentGC.cpp | 3 ++- .../gc/shenandoah/shenandoahGenerationalHeap.cpp | 3 ++- .../gc/shenandoah/shenandoahHeap.inline.hpp | 4 ++-- 6 files changed, 11 insertions(+), 23 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp index 4b5620ff0bf..8989c5f2028 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp @@ -93,9 +93,6 @@ class ShenandoahBarrierSet: public BarrierSet { void on_thread_attach(Thread* thread) override; void on_thread_detach(Thread* thread) override; - static inline oop resolve_forwarded_not_null(oop p); - static inline oop resolve_forwarded(oop p); - template inline void satb_barrier(T* field); inline void satb_enqueue(oop value); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp index 1bdeb01618b..e8eb4ee4180 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp @@ -43,18 +43,6 @@ #include "memory/iterator.inline.hpp" #include "oops/oop.inline.hpp" -inline oop ShenandoahBarrierSet::resolve_forwarded_not_null(oop p) { - return ShenandoahForwarding::get_forwardee(p); -} - -inline oop ShenandoahBarrierSet::resolve_forwarded(oop p) { - if (p != nullptr) { - return resolve_forwarded_not_null(p); - } else { - return p; - } -} - template inline oop ShenandoahBarrierSet::load_reference_barrier_mutator(oop obj, T* load_addr) { assert(ShenandoahLoadRefBarrier, "Should be enabled"); @@ -119,7 +107,7 @@ inline oop ShenandoahBarrierSet::load_reference_barrier(oop obj) { if (_heap->has_forwarded_objects() && _heap->in_collection_set(obj)) { // Subsumes null-check assert(obj != nullptr, "cset check must have subsumed null-check"); - oop fwd = resolve_forwarded_not_null(obj); + oop fwd = ShenandoahForwarding::get_forwardee(obj); if (obj == fwd && _heap->is_evacuation_in_progress()) { Thread* t = Thread::current(); return _heap->evacuate_object(obj, t); @@ -536,7 +524,7 @@ void ShenandoahBarrierSet::arraycopy_work(T* src, size_t count) { if (!CompressedOops::is_null(o)) { oop obj = CompressedOops::decode_not_null(o); if (HAS_FWD && cset->is_in(obj)) { - oop fwd = resolve_forwarded_not_null(obj); + oop fwd = ShenandoahForwarding::get_forwardee(obj); if (EVAC && obj == fwd) { fwd = _heap->evacuate_object(obj, thread); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp index 7580b8d1015..83aede5b7d9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp @@ -30,6 +30,7 @@ #include "gc/shared/barrierSetNMethod.hpp" #include "gc/shenandoah/shenandoahAsserts.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" +#include "gc/shenandoah/shenandoahForwarding.inline.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahMark.inline.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" @@ -87,7 +88,7 @@ bool ShenandoahForwardedIsAliveClosure::do_object_b(oop obj) { if (CompressedOops::is_null(obj)) { return false; } - obj = ShenandoahBarrierSet::resolve_forwarded_not_null(obj); + obj = ShenandoahForwarding::get_forwardee(obj); shenandoah_assert_not_forwarded_if(nullptr, obj, ShenandoahHeap::heap()->is_concurrent_mark_in_progress()); return _mark_context->is_marked_or_old(obj); } @@ -152,7 +153,7 @@ void ShenandoahEvacuateUpdateRootClosureBase::do_oop_ if (_heap->in_collection_set(obj)) { assert(_heap->is_evacuation_in_progress(), "Only do this when evacuation is in progress"); shenandoah_assert_marked(p, obj); - oop resolved = ShenandoahBarrierSet::resolve_forwarded_not_null(obj); + oop resolved = ShenandoahForwarding::get_forwardee(obj); if (resolved == obj) { Thread* thr = STABLE_THREAD ? _thread : Thread::current(); assert(thr == Thread::current(), "Wrong thread"); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index 08032b224d0..24748bdaab3 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -32,6 +32,7 @@ #include "gc/shenandoah/shenandoahClosures.inline.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahConcurrentGC.hpp" +#include "gc/shenandoah/shenandoahForwarding.inline.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahGenerationalHeap.hpp" @@ -910,7 +911,7 @@ void ShenandoahEvacUpdateCleanupOopStorageRootsClosure::do_oop(oop* p) { ShenandoahHeap::atomic_clear_oop(p, obj); } } else if (_evac_in_progress && _heap->in_collection_set(obj)) { - oop resolved = ShenandoahBarrierSet::resolve_forwarded_not_null(obj); + oop resolved = ShenandoahForwarding::get_forwardee(obj); if (resolved == obj) { resolved = _heap->evacuate_object(obj, _thread); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp index 7170c88cd43..e7638ed15c7 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp @@ -26,6 +26,7 @@ #include "gc/shenandoah/shenandoahAgeCensus.hpp" #include "gc/shenandoah/shenandoahClosures.inline.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" +#include "gc/shenandoah/shenandoahForwarding.inline.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahGenerationalControlThread.hpp" @@ -207,7 +208,7 @@ oop ShenandoahGenerationalHeap::evacuate_object(oop p, Thread* thread) { markWord mark = p->mark(); if (mark.is_marked()) { // Already forwarded. - return ShenandoahBarrierSet::resolve_forwarded(p); + return ShenandoahForwarding::get_forwardee(p); } if (mark.has_displaced_mark_helper()) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp index b8db14e5ee7..69eaf1589d2 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp @@ -110,7 +110,7 @@ inline void ShenandoahHeap::non_conc_update_with_forwarded(T* p) { // set that are not really forwarded. We can still go and try and update them // (uselessly) to simplify the common path. shenandoah_assert_forwarded_except(p, obj, cancelled_gc()); - oop fwd = ShenandoahBarrierSet::resolve_forwarded_not_null(obj); + oop fwd = ShenandoahForwarding::get_forwardee(obj); shenandoah_assert_not_in_cset_except(p, fwd, cancelled_gc()); // Unconditionally store the update: no concurrent updates expected. @@ -129,7 +129,7 @@ inline void ShenandoahHeap::conc_update_with_forwarded(T* p) { // set that are not really forwarded. We can still go and try CAS-update them // (uselessly) to simplify the common path. shenandoah_assert_forwarded_except(p, obj, cancelled_gc()); - oop fwd = ShenandoahBarrierSet::resolve_forwarded_not_null(obj); + oop fwd = ShenandoahForwarding::get_forwardee(obj); shenandoah_assert_not_in_cset_except(p, fwd, cancelled_gc()); // Sanity check: we should not be updating the cset regions themselves, From 35ff862a547deb137bbb0a073ded0d3729d4dad3 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Wed, 24 Jun 2026 08:21:35 +0000 Subject: [PATCH 052/707] 8387012: C2: PhaseVector::expand_vunbox_node should not inject the payload type into the load Co-authored-by: Emanuel Peter Reviewed-by: vlivanov, chagedorn --- src/hotspot/share/opto/vector.cpp | 4 +- src/hotspot/share/opto/vectorIntrinsics.cpp | 5 +- src/hotspot/share/opto/vectornode.hpp | 11 ++-- .../vectorapi/TestTypeUnsafeLoad.java | 51 +++++++++++++++++++ 4 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/vectorapi/TestTypeUnsafeLoad.java diff --git a/src/hotspot/share/opto/vector.cpp b/src/hotspot/share/opto/vector.cpp index f9fa02317bc..8e0f6f5bf43 100644 --- a/src/hotspot/share/opto/vector.cpp +++ b/src/hotspot/share/opto/vector.cpp @@ -29,6 +29,7 @@ #include "opto/phaseX.hpp" #include "opto/rootnode.hpp" #include "opto/vector.hpp" +#include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" static bool is_vector_mask(ciKlass* klass) { @@ -455,11 +456,12 @@ void PhaseVector::expand_vunbox_node(VectorUnboxNode* vec_unbox) { gvn.record_for_igvn(local_mem); BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); C2OptAccess access(gvn, ctrl, local_mem, decorators, T_OBJECT, obj, addr); + vec_field_ld = bs->load_at(access, Type::get_const_basic_type(T_OBJECT)); // For proper aliasing, attach concrete payload type. ciKlass* payload_klass = ciTypeArrayKlass::make(bt); const Type* payload_type = TypeAryPtr::make_from_klass(payload_klass)->cast_to_ptr_type(TypePtr::NotNull); - vec_field_ld = bs->load_at(access, payload_type); + vec_field_ld = gvn.transform(new CheckCastPPNode(ctrl, vec_field_ld, payload_type, ConstraintCastNode::DependencyType::NonFloatingNarrowing)); } Node* adr = kit.array_element_address(vec_field_ld, gvn.intcon(0), bt); diff --git a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp index 96b717fe56c..d04eda60b81 100644 --- a/src/hotspot/share/opto/vectorIntrinsics.cpp +++ b/src/hotspot/share/opto/vectorIntrinsics.cpp @@ -174,7 +174,10 @@ Node* GraphKit::unbox_vector(Node* v, const TypeInstPtr* vbox_type, BasicType el } assert(check_vbox(vbox_type), ""); const TypeVect* vt = TypeVect::make(elem_bt, num_elem, is_vector_mask(vbox_type->instance_klass())); - Node* unbox = gvn().transform(new VectorUnboxNode(C, vt, v, merged_memory())); + Node* ctrl = control(); + Node* mem = reset_memory(); + set_all_memory(mem); + Node* unbox = gvn().transform(new VectorUnboxNode(C, vt, ctrl, v, mem)); if (gvn().type(unbox)->isa_vect() == nullptr) { assert(gvn().type(unbox) == Type::TOP, "sanity"); return nullptr; // not a vector diff --git a/src/hotspot/share/opto/vectornode.hpp b/src/hotspot/share/opto/vectornode.hpp index 73181bce256..d013bbc25d6 100644 --- a/src/hotspot/share/opto/vectornode.hpp +++ b/src/hotspot/share/opto/vectornode.hpp @@ -2156,11 +2156,10 @@ class VectorBoxAllocateNode : public CallStaticJavaNode { // vector value. This is a macro node expanded during vector optimization // phase. class VectorUnboxNode : public VectorNode { - protected: - uint size_of() const { return sizeof(*this); } - public: - VectorUnboxNode(Compile* C, const TypeVect* vec_type, Node* obj, Node* mem) +public: + VectorUnboxNode(Compile* C, const TypeVect* vec_type, Node* ctrl, Node* obj, Node* mem) : VectorNode(mem, obj, vec_type) { + init_req(0, ctrl); init_class_id(Class_VectorUnbox); init_flags(Flag_is_macro); C->add_macro_node(this); @@ -2171,6 +2170,10 @@ class VectorUnboxNode : public VectorNode { Node* mem() const { return in(1); } virtual Node* Identity(PhaseGVN* phase); Node* Ideal(PhaseGVN* phase, bool can_reshape); + +private: + uint size_of() const { return sizeof(*this); } + bool depends_only_on_test_impl() const { return false; } }; // Lane-wise right rotation of the first input by the second input. diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestTypeUnsafeLoad.java b/test/hotspot/jtreg/compiler/vectorapi/TestTypeUnsafeLoad.java new file mode 100644 index 00000000000..8d4a580efb8 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestTypeUnsafeLoad.java @@ -0,0 +1,51 @@ +/* + * 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.vectorapi; + +import jdk.incubator.vector.ByteVector; +import jdk.incubator.vector.VectorOperators; +import jdk.incubator.vector.VectorShuffle; + +/* + * @test + * @bug 8387012 + * @summary Expansion of a VectorUnboxNode should not create a type-unsafe load. + * @modules jdk.incubator.vector + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -Xbatch -XX:-TieredCompilation + * -XX:+StressGCM -XX:+StressIGVN -XX:+StressCCP ${test.main.class} + */ +public class TestTypeUnsafeLoad { + public static void main(String[] args) { + for (int i = 0; i < 20_000; i++) { + test(); + } + } + + public static ByteVector test() { + var v0 = ByteVector.broadcast(ByteVector.SPECIES_128, (byte) 0); + var v2 = v0.rearrange(VectorShuffle.makeUnzip(ByteVector.SPECIES_128, 1)); + var v3 = v0.lanewise(VectorOperators.MIN, v2); + var v5 = v3.lanewise(VectorOperators.MAX, v0); + return v5; + } +} From 210a6429623ace1f595fad82f16b71ca8fd2698a Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Wed, 24 Jun 2026 08:48:40 +0000 Subject: [PATCH 053/707] 8385893: G1: G1CollectedHeap::_old_marking_cycles_completed should be an Atomic Reviewed-by: iwalulya, ayang --- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 36 ++++++++++----------- src/hotspot/share/gc/g1/g1CollectedHeap.hpp | 8 ++--- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 8ea880c820f..233f993bb16 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -1799,12 +1799,12 @@ bool G1CollectedHeap::should_do_concurrent_full_gc(GCCause::Cause cause) { } void G1CollectedHeap::increment_old_marking_cycles_started() { - assert(_old_marking_cycles_started == _old_marking_cycles_completed || - _old_marking_cycles_started == _old_marking_cycles_completed + 1, - "Wrong marking cycle count (started: %d, completed: %d)", - _old_marking_cycles_started, _old_marking_cycles_completed); + assert(old_marking_cycles_started() == old_marking_cycles_completed() || + old_marking_cycles_started() == old_marking_cycles_completed() + 1, + "Wrong marking cycle count (started: %u, completed: %u)", + old_marking_cycles_started(), old_marking_cycles_completed()); - _old_marking_cycles_started++; + _old_marking_cycles_started.add_then_fetch(1u, memory_order_relaxed); } void G1CollectedHeap::increment_old_marking_cycles_completed(bool concurrent, @@ -1825,21 +1825,21 @@ void G1CollectedHeap::increment_old_marking_cycles_completed(bool concurrent, // This is the case for the inner caller, i.e. a Full GC. assert(concurrent || - (_old_marking_cycles_started == _old_marking_cycles_completed + 1) || - (_old_marking_cycles_started == _old_marking_cycles_completed + 2), - "for inner caller (Full GC): _old_marking_cycles_started = %u " - "is inconsistent with _old_marking_cycles_completed = %u", - _old_marking_cycles_started, _old_marking_cycles_completed); + (old_marking_cycles_started() == old_marking_cycles_completed() + 1) || + (old_marking_cycles_started() == old_marking_cycles_completed() + 2), + "for inner caller (Full GC): old_marking_cycles_started = %u " + "is inconsistent with old_marking_cycles_completed = %u", + old_marking_cycles_started(), old_marking_cycles_completed()); // This is the case for the outer caller, i.e. the concurrent cycle. assert(!concurrent || - (_old_marking_cycles_started == _old_marking_cycles_completed + 1), + (old_marking_cycles_started() == old_marking_cycles_completed() + 1), "for outer caller (concurrent cycle): " - "_old_marking_cycles_started = %u " - "is inconsistent with _old_marking_cycles_completed = %u", - _old_marking_cycles_started, _old_marking_cycles_completed); + "old_marking_cycles_started = %u " + "is inconsistent with old_marking_cycles_completed = %u", + old_marking_cycles_started(), old_marking_cycles_completed()); - _old_marking_cycles_completed += 1; + _old_marking_cycles_completed.add_then_fetch(1u, memory_order_relaxed); if (whole_heap_examined) { // Signal that we have completed a visit to all live objects. record_whole_heap_examined_timestamp(); @@ -1904,7 +1904,7 @@ bool G1CollectedHeap::wait_full_mark_finished(GCCause::Cause cause, // while completed_now < started_after. LOG_COLLECT_CONCURRENTLY(cause, "wait"); MonitorLocker ml(G1OldGCCount_lock); - while (gc_counter_less_than(_old_marking_cycles_completed, + while (gc_counter_less_than(old_marking_cycles_completed(), old_marking_started_after)) { ml.wait(); } @@ -1986,8 +1986,8 @@ bool G1CollectedHeap::try_collect_concurrently(size_t allocation_word_size, // more recent collection. That's what we want, rather than having // our retry possibly perform an unnecessary collection. gc_counter = total_collections(); - old_marking_started_after = _old_marking_cycles_started; - old_marking_completed_after = _old_marking_cycles_completed; + old_marking_started_after = old_marking_cycles_started(); + old_marking_completed_after = old_marking_cycles_completed(); } if (cause == GCCause::_wb_breakpoint) { diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index a60596e67ae..a0767d8fd0a 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -313,11 +313,11 @@ class G1CollectedHeap : public CollectedHeap { // Keeps track of how many "old marking cycles" (i.e., Full GCs or // concurrent cycles) we have started. - volatile uint _old_marking_cycles_started; + Atomic _old_marking_cycles_started; // Keeps track of how many "old marking cycles" (i.e., Full GCs or // concurrent cycles) we have completed. - volatile uint _old_marking_cycles_completed; + Atomic _old_marking_cycles_completed; // Create a memory mapper for auxiliary data structures of the given size and // translation factor. @@ -691,11 +691,11 @@ class G1CollectedHeap : public CollectedHeap { void increment_old_marking_cycles_completed(bool concurrent, bool whole_heap_examined); uint old_marking_cycles_started() const { - return _old_marking_cycles_started; + return _old_marking_cycles_started.load_relaxed(); } uint old_marking_cycles_completed() const { - return _old_marking_cycles_completed; + return _old_marking_cycles_completed.load_relaxed(); } // Allocates a new heap region instance. From 05cd2d948c7fca1315a3f0e2d2646a30869cff23 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Wed, 24 Jun 2026 08:50:49 +0000 Subject: [PATCH 054/707] 8381128: G1: Tighten accesses to TAMS/TARS Co-authored-by: Ivan Walulya Reviewed-by: iwalulya, ayang --- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 12 +- src/hotspot/share/gc/g1/g1CollectedHeap.hpp | 2 +- src/hotspot/share/gc/g1/g1CollectionSet.cpp | 2 +- .../share/gc/g1/g1CollectorState.inline.hpp | 11 +- src/hotspot/share/gc/g1/g1ConcurrentMark.cpp | 129 +++++++++++++----- src/hotspot/share/gc/g1/g1ConcurrentMark.hpp | 69 +++++++--- .../share/gc/g1/g1ConcurrentMark.inline.hpp | 74 +++++++++- .../gc/g1/g1ConcurrentMarkRemarkTasks.cpp | 2 +- .../share/gc/g1/g1ConcurrentMarkThread.hpp | 2 +- .../gc/g1/g1ConcurrentMarkThread.inline.hpp | 5 +- .../gc/g1/g1ConcurrentRebuildAndScrub.cpp | 7 +- src/hotspot/share/gc/g1/g1FullCollector.cpp | 2 +- .../share/gc/g1/g1FullGCResetMetadataTask.cpp | 3 +- src/hotspot/share/gc/g1/g1HeapRegion.cpp | 6 +- .../share/gc/g1/g1HeapRegion.inline.hpp | 4 - src/hotspot/share/gc/g1/g1HeapVerifier.cpp | 20 ++- src/hotspot/share/gc/g1/g1HeapVerifier.hpp | 5 +- src/hotspot/share/gc/g1/g1Policy.cpp | 4 +- src/hotspot/share/gc/g1/g1Policy.hpp | 5 +- .../share/gc/g1/g1RegionMarkStatsCache.hpp | 12 ++ src/hotspot/share/gc/g1/g1RemSet.cpp | 3 +- .../share/gc/g1/g1SATBMarkQueueSet.cpp | 11 +- src/hotspot/share/gc/g1/g1YoungCollector.cpp | 24 ++-- .../gc/g1/g1YoungGCPostEvacuateTasks.cpp | 23 ++-- src/hotspot/share/gc/shared/satbMarkQueue.cpp | 6 +- src/hotspot/share/gc/shared/satbMarkQueue.hpp | 6 +- .../g1/TestEagerReclaimHumongousRegions.java | 5 +- .../g1/TestVerificationInConcurrentCycle.java | 29 +++- .../pinnedobjs/TestDroppedRetainedTAMS.java | 9 +- 29 files changed, 368 insertions(+), 124 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 233f993bb16..ebf7a1086fa 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -846,7 +846,7 @@ void G1CollectedHeap::prepare_heap_for_full_collection() { _hrm.remove_all_free_regions(); } -void G1CollectedHeap::verify_before_full_collection() { +void G1CollectedHeap::verify_before_full_collection(bool concurrent_cycle_aborted) { assert_used_and_recalculate_used_equal(this); if (!VerifyBeforeGC) { return; @@ -856,7 +856,7 @@ void G1CollectedHeap::verify_before_full_collection() { } _verifier->verify_region_sets_optional(); _verifier->verify_before_gc(); - _verifier->verify_bitmap_clear(true /* above_tams_only */); + _verifier->verify_bitmap_clear(true /* above_tams_only */, concurrent_cycle_aborted); } void G1CollectedHeap::prepare_for_mutator_after_full_collection(size_t allocation_word_size) { @@ -2880,6 +2880,7 @@ void G1CollectedHeap::free_region(G1HeapRegion* hr, G1FreeRegionList* free_list) // Reset region metadata to allow reuse. hr->hr_clear(true /* clear_space */); + concurrent_mark()->reset_region_marking_state(hr); _policy->remset_tracker()->update_at_free(hr); if (free_list != nullptr) { @@ -3192,6 +3193,9 @@ G1HeapRegion* G1CollectedHeap::new_gc_alloc_region(size_t word_size, G1HeapRegio // Synchronize with region attribute table. update_region_attr(new_alloc_region); } + + _cm->notify_new_region(new_alloc_region); + G1HeapRegionPrinter::alloc(new_alloc_region); return new_alloc_region; } @@ -3209,8 +3213,8 @@ void G1CollectedHeap::retire_gc_alloc_region(G1HeapRegion* alloc_region, _survivor.add_used_bytes(allocated_bytes); } - bool const during_im = collector_state()->is_in_concurrent_start_gc(); - if (during_im && allocated_bytes > 0) { + bool in_concurrent_start_gc = collector_state()->is_in_concurrent_start_gc(); + if (in_concurrent_start_gc && allocated_bytes > 0) { _cm->add_root_region(alloc_region); } G1HeapRegionPrinter::retire(alloc_region); diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index a0767d8fd0a..fc31878097b 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -524,7 +524,7 @@ class G1CollectedHeap : public CollectedHeap { // Internal helpers used during full GC to split it up to // increase readability. bool abort_concurrent_cycle(); - void verify_before_full_collection(); + void verify_before_full_collection(bool concurrent_cycle_aborted); void prepare_heap_for_full_collection(); void prepare_for_mutator_after_full_collection(size_t allocation_word_size); void abort_refinement(); diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.cpp b/src/hotspot/share/gc/g1/g1CollectionSet.cpp index 14b5e321585..b0eb493120b 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.cpp @@ -299,7 +299,7 @@ class G1PrintCollectionSetDetailClosure : public G1HeapRegionClosure { G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); _st->print_cr(" " HR_FORMAT ", TAMS: " PTR_FORMAT " PB: " PTR_FORMAT ", age: %4d", HR_FORMAT_PARAMS(r), - p2i(cm->top_at_mark_start(r)), + p2i(cm->top_at_mark_start_or_bottom(r)), p2i(r->parsable_bottom()), r->has_surv_rate_group() ? checked_cast(r->age_in_surv_rate_group()) : -1); return false; diff --git a/src/hotspot/share/gc/g1/g1CollectorState.inline.hpp b/src/hotspot/share/gc/g1/g1CollectorState.inline.hpp index b2d3dfcc489..b63d683bb63 100644 --- a/src/hotspot/share/gc/g1/g1CollectorState.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectorState.inline.hpp @@ -77,19 +77,22 @@ inline bool G1CollectorState::initiate_conc_mark_if_possible() const { inline bool G1CollectorState::is_in_concurrent_cycle() const { G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); - return cm->is_in_concurrent_cycle(); + return cm->is_fully_initialized() && cm->is_in_concurrent_cycle(); } + inline bool G1CollectorState::is_in_marking() const { G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); - return cm->is_in_marking(); + return cm->is_fully_initialized() && cm->is_in_marking(); } + inline bool G1CollectorState::is_in_mark_or_rebuild() const { G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); - return is_in_marking() || cm->is_in_rebuild_or_scrub(); + return cm->is_fully_initialized() && cm->is_in_marking_or_rebuild(); } + inline bool G1CollectorState::is_in_reset_for_next_cycle() const { G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); - return cm->is_in_reset_for_next_cycle(); + return cm->is_fully_initialized() && cm->is_in_reset_for_next_cycle(); } inline void G1CollectorState::assert_is_young_pause(Pause type) { diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 4afc7fa8ff1..11c93b092b1 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -449,7 +449,7 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, _worker_id_offset(G1ConcRefinementThreads), // The refinement control thread does not refine cards, so it's just the worker threads. _max_num_tasks(MAX2(ConcGCThreads, ParallelGCThreads)), _num_active_tasks(0), // _num_active_tasks set in set_non_marking_state() - _tasks(nullptr), // _tasks set inside late_init() + _tasks(nullptr), _task_queues(new G1CMTaskQueueSet(_max_num_tasks)), _terminator(_max_num_tasks, _task_queues), _partial_array_state_manager(new PartialArrayStateManager(_max_num_tasks)), @@ -476,9 +476,10 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, _num_concurrent_workers(0), _max_concurrent_workers(0), - _region_mark_stats(NEW_C_HEAP_ARRAY(G1RegionMarkStats, _g1h->max_num_regions(), mtGC)), - _top_at_mark_starts(NEW_C_HEAP_ARRAY(Atomic, _g1h->max_num_regions(), mtGC)), - _top_at_rebuild_starts(NEW_C_HEAP_ARRAY(Atomic, _g1h->max_num_regions(), mtGC)), + _is_region_mark_stats_cache_in_use(false), + _region_mark_stats(nullptr), + _top_at_mark_starts(nullptr), + _top_at_rebuild_starts(nullptr), _needs_remembered_set_rebuild(false) { assert(G1CGC_lock != nullptr, "CGC_lock must be initialized"); @@ -487,6 +488,8 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, } void G1ConcurrentMark::fully_initialize() { + assert_at_safepoint(); + if (is_fully_initialized()) { return; } @@ -510,6 +513,10 @@ void G1ConcurrentMark::fully_initialize() { vm_exit_during_initialization("Failed to allocate initial concurrent mark overflow mark stack."); } + _region_mark_stats = NEW_C_HEAP_ARRAY(G1RegionMarkStats, _g1h->max_num_regions(), mtGC); + _top_at_mark_starts = NEW_C_HEAP_ARRAY(Atomic, _g1h->max_num_regions(), mtGC); + _top_at_rebuild_starts = NEW_C_HEAP_ARRAY(Atomic, _g1h->max_num_regions(), mtGC); + _tasks = NEW_C_HEAP_ARRAY(G1CMTask*, _max_num_tasks, mtGC); // so that the assertion in MarkingTaskQueue::task_queue doesn't fail @@ -527,22 +534,21 @@ void G1ConcurrentMark::fully_initialize() { for (uint i = 0; i < max_num_regions; i++) { ::new (&_top_at_mark_starts[i]) Atomic(_g1h->bottom_addr_for_region(i)); } - // Contrary to TAMS, the default value of _top_at_rebuild_starts needs to be null. ::new (_top_at_rebuild_starts) Atomic[max_num_regions]{}; reset_at_marking_complete(); } bool G1ConcurrentMark::is_in_concurrent_cycle() const { - return is_fully_initialized() ? _cm_thread->is_in_progress() : false; + return _cm_thread->is_in_progress(); } bool G1ConcurrentMark::is_in_marking() const { - return is_fully_initialized() ? cm_thread()->is_in_marking() : false; + return _cm_thread->is_in_marking(); } -bool G1ConcurrentMark::is_in_rebuild_or_scrub() const { - return cm_thread()->is_in_rebuild_or_scrub(); +bool G1ConcurrentMark::is_in_marking_or_rebuild() const { + return _cm_thread->is_in_marking_or_rebuild(); } bool G1ConcurrentMark::is_in_reset_for_next_cycle() const { @@ -559,8 +565,11 @@ G1ConcurrentMarkThread* G1ConcurrentMark::cm_thread() const { } void G1ConcurrentMark::reset() { + assert_fully_initialized(); + _has_aborted.store_relaxed(false); + _is_region_mark_stats_cache_in_use = true; reset_marking_for_restart(); // Reset all tasks, since different phases will use different number of active @@ -572,6 +581,9 @@ void G1ConcurrentMark::reset() { uint max_num_regions = _g1h->max_num_regions(); ::new (_top_at_rebuild_starts) Atomic[max_num_regions]{}; for (uint i = 0; i < max_num_regions; i++) { + // Do not update TAMS here. NoteStartOfMarkTask updates this in parallel in + // the pre-concurrent-start WorkerTask. + _top_at_rebuild_starts[i].store_relaxed(nullptr); _region_mark_stats[i].clear(); } @@ -579,11 +591,61 @@ void G1ConcurrentMark::reset() { _root_regions.reset(); } -void G1ConcurrentMark::clear_statistics(G1HeapRegion* r) { +void G1ConcurrentMark::assert_statistics_clear(G1HeapRegion* r) { + assert_fully_initialized(); +#ifdef ASSERT uint region_idx = r->hrm_index(); for (uint j = 0; j < _max_num_tasks; ++j) { - _tasks[j]->clear_mark_stats_cache(region_idx); + _tasks[j]->verify_no_mark_stats_for(r->hrm_index()); + } + + assert(_top_at_rebuild_starts[region_idx].load_relaxed() == nullptr, "must be"); + + G1RegionMarkStats* s = &_region_mark_stats[region_idx]; + assert(s->incoming_refs() == 0, "must be"); + assert(s->live_words() == 0, "must be"); +#endif +} + +void G1ConcurrentMark::note_start_of_mark_for_region(G1HeapRegion* r) { + assert_at_safepoint(); + assert_fully_initialized(); + if (r->is_old_or_humongous() && !r->is_collection_set_candidate() && !r->in_collection_set()) { + update_top_at_mark_start(r); + } else { + set_top_at_mark_start_to_bottom(r); + } +} + +void G1ConcurrentMark::notify_new_region(G1HeapRegion* r, size_t marked_live_bytes_below_tams) { + assert_at_safepoint(); + if (!is_fully_initialized()) { + return; + } + G1CollectorState* state = _g1h->collector_state(); + if (state->is_in_concurrent_start_gc()) { + update_top_at_mark_start(r); + set_live_bytes(r->hrm_index(), marked_live_bytes_below_tams); + } +} + +void G1ConcurrentMark::reset_region_marking_state(G1HeapRegion* r) { + assert_at_safepoint(); + if (!is_fully_initialized()) { + return; + } + uint region_idx = r->hrm_index(); + // Only need to clear the stats cache for the given region if we are using the cache. + if (_is_region_mark_stats_cache_in_use) { + for (uint j = 0; j < _max_num_tasks; ++j) { + _tasks[j]->clear_mark_stats_cache(region_idx); + } + } else { + for (uint j = 0; j < _max_num_tasks; ++j) { + _tasks[j]->verify_no_mark_stats_for(region_idx); + } } + set_top_at_mark_start_to_bottom(r); _top_at_rebuild_starts[region_idx].store_relaxed(nullptr); _region_mark_stats[region_idx].clear(); } @@ -594,19 +656,11 @@ void G1ConcurrentMark::humongous_object_eagerly_reclaimed(G1HeapRegion* r) { // Need to clear mark bit of the humongous object. Doing this unconditionally is fine. mark_bitmap()->clear(r->bottom()); - - if (!_g1h->collector_state()->is_in_mark_or_rebuild()) { - return; - } - - // Clear any statistics about the region gathered so far. - _g1h->humongous_obj_regions_iterate(r, - [&] (G1HeapRegion* r) { - clear_statistics(r); - }); } void G1ConcurrentMark::reset_marking_for_restart() { + assert_fully_initialized(); + _global_mark_stack.set_empty(); // Expand the marking stack, if we have to and if we can. @@ -729,7 +783,6 @@ class G1ClearBitMapTask : public WorkerTask { assert(_bitmap->get_next_marked_addr(r->bottom(), r->end()) == r->end(), "Should not have marked bits"); return r->bottom(); } - assert(_bitmap->get_next_marked_addr(_cm->top_at_mark_start(r), r->end()) == r->end(), "Should not have marked bits above tams"); } return r->end(); } @@ -776,8 +829,6 @@ class G1ClearBitMapTask : public WorkerTask { } assert(cur >= end, "Must have completed iteration over the bitmap for region %u.", r->hrm_index()); - _cm->reset_top_at_mark_start(r); - return false; } }; @@ -877,11 +928,7 @@ class G1PreConcurrentStartTask::NoteStartOfMarkTask : public G1AbstractSubTask { NoteStartOfMarkHRClosure() : G1HeapRegionClosure(), _cm(G1CollectedHeap::heap()->concurrent_mark()) { } bool do_heap_region(G1HeapRegion* r) override { - if (r->is_old_or_humongous() && !r->is_collection_set_candidate() && !r->in_collection_set()) { - _cm->update_top_at_mark_start(r); - } else { - _cm->reset_top_at_mark_start(r); - } + _cm->note_start_of_mark_for_region(r); return false; } } _region_cl; @@ -1189,6 +1236,11 @@ void G1ConcurrentMark::add_root_region(G1HeapRegion* r) { root_regions()->add(top_at_mark_start(r), r->top()); } +void G1ConcurrentMark::add_root_region_set_bottom(G1HeapRegion* r) { + set_top_at_mark_start_to_bottom(r); + root_regions()->add(r->bottom(), r->top()); +} + bool G1ConcurrentMark::is_root_region(G1HeapRegion* r) { return root_regions()->contains(MemRegion(top_at_mark_start(r), r->top())); } @@ -1822,7 +1874,7 @@ void G1ConcurrentMark::finalize_marking() { print_stats(); } -void G1ConcurrentMark::flush_all_task_caches() { +void G1ConcurrentMark::flush_all_task_caches(bool ends_use_of_mark_cache) { size_t hits = 0; size_t misses = 0; for (uint i = 0; i < _max_num_tasks; i++) { @@ -1833,6 +1885,9 @@ void G1ConcurrentMark::flush_all_task_caches() { size_t sum = hits + misses; log_debug(gc, stats)("Mark stats cache hits %zu misses %zu ratio %1.3lf", hits, misses, percent_of(hits, sum)); + if (ends_use_of_mark_cache) { + _is_region_mark_stats_cache_in_use = false; + } } void G1ConcurrentMark::clear_bitmap_for_region(G1HeapRegion* hr) { @@ -1870,7 +1925,8 @@ G1HeapRegion* G1ConcurrentMark::claim_region(uint worker_id) { return curr_region; } else { assert(limit == bottom, - "The region limit should be at bottom"); + "The scan limit for region %u (%s) should be bottom but is " PTR_FORMAT, + curr_region->hrm_index(), curr_region->get_short_type_str(), p2i(limit)); // We return null and the caller should try calling // claim_region() again. return nullptr; @@ -1878,7 +1934,9 @@ G1HeapRegion* G1ConcurrentMark::claim_region(uint worker_id) { } else { // Read the finger again. HeapWord* next_finger = finger(); - assert(next_finger > local_finger, "The finger should have moved forward " PTR_FORMAT " " PTR_FORMAT, p2i(local_finger), p2i(next_finger)); + assert(next_finger > local_finger, + "The finger should have moved forward " PTR_FORMAT " " PTR_FORMAT, + p2i(local_finger), p2i(next_finger)); local_finger = next_finger; } } @@ -2003,6 +2061,7 @@ bool G1ConcurrentMark::concurrent_cycle_abort() { return false; } + flush_all_task_caches(); reset_marking_for_restart(); abort_marking_threads(); @@ -2457,6 +2516,12 @@ void G1CMTask::drain_satb_buffers() { decrease_limits(); } +#ifndef PRODUCT +void G1CMTask::verify_no_mark_stats_for(uint region_idx) { + _mark_stats_cache.verify_no_mark_stats_for(region_idx); +} +#endif + void G1CMTask::clear_mark_stats_cache(uint region_idx) { _mark_stats_cache.reset(region_idx); } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index 2040916b8e7..f0071286e04 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -494,15 +494,18 @@ class G1ConcurrentMark : public CHeapObj { // true, periodically insert checks to see if this method should exit prematurely. void clear_bitmap(WorkerThreads* workers, bool may_yield); + // Records whether the region mark stats cache may contain entries due to marking activity, + // and the cache for freed regions needs to be cleared for those. + bool _is_region_mark_stats_cache_in_use; // Region statistics gathered during marking. G1RegionMarkStats* _region_mark_stats; - // Top pointer for each region at the start of marking. Must be valid for all committed - // regions. + // Top pointer for each region at the start of marking. Must be valid, i.e. be within + // [bottom, end] of a region for all committed regions. Atomic* _top_at_mark_starts; // Top pointer for each region at the start of the rebuild remembered set process // for regions which remembered sets need to be rebuilt. A null for a given region - // means that this region does not be scanned during the rebuilding remembered - // set phase at all. + // means that this region does not need to be scanned during the remembered set rebuild + // phase at all. Atomic* _top_at_rebuild_starts; // True when Remark pause selected regions for rebuilding. bool _needs_remembered_set_rebuild; @@ -512,27 +515,47 @@ class G1ConcurrentMark : public CHeapObj { // Concurrent cycle state queries. bool is_in_concurrent_cycle() const; bool is_in_marking() const; - bool is_in_rebuild_or_scrub() const; + bool is_in_marking_or_rebuild() const; bool is_in_reset_for_next_cycle() const; + void assert_fully_initialized() const { assert(is_fully_initialized(), "must be"); } + // The TAMS may be read and returns useful values related to the current concurrent marking. + // This is the case only during the concurrent cycle or the Concurrent Start pause. + inline bool tams_may_be_read() const; + // Update the TAMS for the given region to the current top. + inline void update_top_at_mark_start(G1HeapRegion* r); + // Reset the TAMS for the given region to bottom. + inline void set_top_at_mark_start_to_bottom(G1HeapRegion* r); + public: // To be called when an object is marked the first time, e.g. after a successful // mark_in_bitmap call. Updates various statistics data. void add_to_liveness(uint worker_id, oop const obj, size_t size); // Did the last marking find a live object between bottom and TAMS? - bool contains_live_object(uint region) const { return _region_mark_stats[region].live_words() != 0; } + bool contains_live_object(uint region) const; // Live bytes in the given region as determined by concurrent marking, i.e. the amount of // live bytes between bottom and TAMS. - size_t live_bytes(uint region) const { return _region_mark_stats[region].live_words() * HeapWordSize; } + size_t live_bytes(uint region) const; // Set live bytes for concurrent marking. - void set_live_bytes(uint region, size_t live_bytes) { _region_mark_stats[region]._live_words.store_relaxed(live_bytes / HeapWordSize); } + void set_live_bytes(uint region, size_t live_bytes); // Approximate number of incoming references found during marking. - size_t incoming_refs(uint region) const { return _region_mark_stats[region].incoming_refs(); } - - // Update the TAMS for the given region to the current top. - inline void update_top_at_mark_start(G1HeapRegion* r); - // Reset the TAMS for the given region to bottom of that region. - inline void reset_top_at_mark_start(G1HeapRegion* r); + size_t incoming_refs(uint region) const; + + void note_start_of_mark_for_region(G1HeapRegion* r); + inline void assert_top_at_mark_start_is_bottom(G1HeapRegion* r); + + // Returns the TAMS for the given region; outside of the concurrent cycle or Concurrent Start + // pause, always returns r->bottom(). + // Intended to be used for queries that are not allowed to fail at any time, but give a + // reasonable value, e.g. for logging to avoid having to do lots of check at every call site. + // Do not use for logic. + inline HeapWord* top_at_mark_start_or_bottom(const G1HeapRegion* r) const; + // Special method to return TAMS for verification purposes. During verification, if Full GC + // aborted a concurrent cycle, we need to use the TAMS data because the bitmap < TAMS may + // legitimately contain marks, however since we are in a Full GC tams_may_be_read() returns + // false. The other methods would return bottom(), which is wrong for verification. + inline HeapWord* top_at_mark_start_for_verification(const G1HeapRegion* r, + bool concurrent_cycle_aborted) const; inline HeapWord* top_at_mark_start(const G1HeapRegion* r) const; inline HeapWord* top_at_mark_start(uint region) const; @@ -551,10 +574,16 @@ class G1ConcurrentMark : public CHeapObj { uint max_num_tasks() const {return _max_num_tasks; } - // Clear statistics gathered during the concurrent cycle for the given region after - // it has been reclaimed. - void clear_statistics(G1HeapRegion* r); - // Notification for eagerly reclaimed regions to clean up. + void assert_statistics_clear(G1HeapRegion* r); + + // Notification for marking that a new region has been added to the heap. Updates the TAMS and + // live bytes for this region during a Concurrent Start pause. + void notify_new_region(G1HeapRegion* r, size_t marked_live_bytes_below_tams = 0); + + // Resets region marking state for the given region, i.e. TAMS, statistics, task metadata, + // etc. to initial state. + void reset_region_marking_state(G1HeapRegion* r); + // Notification for eagerly reclaimed regions to do extra clean up. void humongous_object_eagerly_reclaimed(G1HeapRegion* r); // Manipulation of the global mark stack. // The push and pop operations are used by tasks for transfers @@ -605,7 +634,7 @@ class G1ConcurrentMark : public CHeapObj { void reset(); // Moves all per-task cached data into global state. - void flush_all_task_caches(); + void flush_all_task_caches(bool ends_use_of_mark_cache = true); // Prepare internal data structures for the next mark cycle. This includes clearing // the next mark bitmap and some internal data structures. This method is intended // to be called concurrently to the mutator. It will yield to safepoint requests. @@ -632,6 +661,7 @@ class G1ConcurrentMark : public CHeapObj { void stop(); void add_root_region(G1HeapRegion* r); + void add_root_region_set_bottom(G1HeapRegion* r); bool is_root_region(G1HeapRegion* r); // Scan all the root regions concurrently and mark everything reachable from @@ -958,6 +988,7 @@ class G1CMTask : public TerminatorTerminator { inline void inc_incoming_refs(oop const obj); + void verify_no_mark_stats_for(uint region_idx) PRODUCT_RETURN; // Clear (without flushing) the mark cache entry for the given region. void clear_mark_stats_cache(uint region_idx); // Evict the whole statistics cache into the global statistics. Returns the diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp index 094f4dca994..ec6a486dc02 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp @@ -28,6 +28,7 @@ #include "gc/g1/g1ConcurrentMark.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1ConcurrentMarkBitMap.inline.hpp" #include "gc/g1/g1HeapRegion.hpp" #include "gc/g1/g1HeapRegionRemSet.inline.hpp" @@ -189,20 +190,69 @@ inline void G1CMTask::process_array_chunk(objArrayOop obj, size_t start, size_t } inline void G1ConcurrentMark::update_top_at_mark_start(G1HeapRegion* r) { + assert_fully_initialized(); + assert(_g1h->collector_state()->is_in_concurrent_start_gc(), "must be"); uint const region = r->hrm_index(); assert(region < _g1h->max_num_regions(), "Tried to access TAMS for region %u out of bounds", region); _top_at_mark_starts[region].store_relaxed(r->top()); } -inline void G1ConcurrentMark::reset_top_at_mark_start(G1HeapRegion* r) { +inline void G1ConcurrentMark::set_top_at_mark_start_to_bottom(G1HeapRegion* r) { + assert_fully_initialized(); _top_at_mark_starts[r->hrm_index()].store_relaxed(r->bottom()); } +inline void G1ConcurrentMark::assert_top_at_mark_start_is_bottom(G1HeapRegion* r) { + // Can not assert anything if not initialized. + if (!tams_may_be_read()) { + return; + } + HeapWord* local_top_at_mark_start = top_at_mark_start(r); + assert(local_top_at_mark_start == r->bottom(), + "must be, but tams for r %u (%s) is" PTR_FORMAT, + r->hrm_index(), r->get_short_type_str(), p2i(local_top_at_mark_start)); +} + +inline HeapWord* G1ConcurrentMark::top_at_mark_start_or_bottom(const G1HeapRegion* r) const { + if (!tams_may_be_read()) { + return r->bottom(); + } + return top_at_mark_start(r); +} + +inline HeapWord* G1ConcurrentMark::top_at_mark_start_for_verification(const G1HeapRegion* r, + bool concurrent_cycle_aborted) const { + if (!is_fully_initialized()) { + // We do not have TAMS data yet. + return r->bottom(); + } + if (tams_may_be_read()) { + // Normal case, we can read TAMS data and it is valid. + return top_at_mark_start(r); + } + if (concurrent_cycle_aborted) { + assert(_g1h->collector_state()->is_in_full_gc(), "Must be in Full GC if concurrent cycle has aborted"); + assert(r->hrm_index() < _g1h->max_num_regions(), + "Tried to access TAMS for region %u out of bounds", r->hrm_index()); + return _top_at_mark_starts[r->hrm_index()].load_relaxed(); + } + return r->bottom(); +} + +inline bool G1ConcurrentMark::tams_may_be_read() const { + // We need the TAMS to be valid even outside of actual marking for e.g. clearing the bitmap. + G1CollectorState* state = _g1h->collector_state(); + return is_fully_initialized() && + (state->is_in_concurrent_cycle() || state->is_in_concurrent_start_gc()); +} + inline HeapWord* G1ConcurrentMark::top_at_mark_start(const G1HeapRegion* r) const { return top_at_mark_start(r->hrm_index()); } inline HeapWord* G1ConcurrentMark::top_at_mark_start(uint region) const { + assert_fully_initialized(); + assert(tams_may_be_read(), "must be"); assert(region < _g1h->max_num_regions(), "Tried to access TARS for region %u out of bounds", region); return _top_at_mark_starts[region].load_relaxed(); } @@ -214,10 +264,12 @@ inline bool G1ConcurrentMark::obj_allocated_since_mark_start(oop obj) const { } inline HeapWord* G1ConcurrentMark::top_at_rebuild_start(G1HeapRegion* r) const { + assert_fully_initialized(); return _top_at_rebuild_starts[r->hrm_index()].load_relaxed(); } inline void G1ConcurrentMark::update_top_at_rebuild_start(G1HeapRegion* r) { + assert_fully_initialized(); assert(r->is_old() || r->is_humongous(), "precondition"); uint const region = r->hrm_index(); @@ -240,6 +292,26 @@ inline void G1ConcurrentMark::add_to_liveness(uint worker_id, oop const obj, siz task(worker_id)->update_liveness(obj, size); } +inline bool G1ConcurrentMark::contains_live_object(uint region) const { + assert_fully_initialized(); + return _region_mark_stats[region].live_words() != 0; +} + +inline size_t G1ConcurrentMark::live_bytes(uint region) const { + assert_fully_initialized(); + return _region_mark_stats[region].live_words() * HeapWordSize; +} + +inline void G1ConcurrentMark::set_live_bytes(uint region, size_t live_bytes) { + assert_fully_initialized(); + _region_mark_stats[region]._live_words.store_relaxed(live_bytes / HeapWordSize); +} + +inline size_t G1ConcurrentMark::incoming_refs(uint region) const { + assert_fully_initialized(); + return _region_mark_stats[region].incoming_refs(); +} + inline void G1CMTask::abort_marking_if_regular_check_fail() { if (!regular_clock_call()) { set_has_aborted(); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.cpp index 3eda7200e25..02c1d2bf0d3 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.cpp @@ -65,7 +65,7 @@ struct G1UpdateRegionLivenessAndSelectForRebuildTask::G1OnRegionClosure : public _freed_bytes += hr->used(); hr->set_containing_set(nullptr); hr->clear_both_card_tables(); - _cm->clear_statistics(hr); + _cm->assert_statistics_clear(hr); G1HeapRegionPrinter::mark_reclaim(hr); _g1h->concurrent_refine()->notify_region_reclaimed(hr); } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp index ad6062d0239..a22442c2b7f 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp @@ -115,7 +115,7 @@ class G1ConcurrentMarkThread: public ConcurrentGCThread { bool is_in_progress() const; bool is_in_marking() const; - bool is_in_rebuild_or_scrub() const; + bool is_in_marking_or_rebuild() const; bool is_in_reset_for_next_cycle() const; bool is_in_undo_cycle() const; diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp index be2bc8e9e7a..bea6fe4e451 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp @@ -79,8 +79,9 @@ inline bool G1ConcurrentMarkThread::is_in_marking() const { return state() == FullCycleMarking; } -inline bool G1ConcurrentMarkThread::is_in_rebuild_or_scrub() const { - return state() == FullCycleRebuildOrScrub; +inline bool G1ConcurrentMarkThread::is_in_marking_or_rebuild() const { + ServiceState st = state(); + return st == FullCycleMarking || st == FullCycleRebuildOrScrub; } inline bool G1ConcurrentMarkThread::is_in_reset_for_next_cycle() const { diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRebuildAndScrub.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRebuildAndScrub.cpp index cd560a41333..5b652f096a7 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRebuildAndScrub.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRebuildAndScrub.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -180,7 +180,10 @@ class G1RebuildRSAndScrubTask : public WorkerTask { assert(should_rebuild_or_scrub(hr), "must be"); log_trace(gc, marking)("Scrub and rebuild region: " HR_FORMAT " pb: " PTR_FORMAT " TARS: " PTR_FORMAT " TAMS: " PTR_FORMAT, - HR_FORMAT_PARAMS(hr), p2i(pb), p2i(_cm->top_at_rebuild_start(hr)), p2i(_cm->top_at_mark_start(hr))); + HR_FORMAT_PARAMS(hr), + p2i(pb), + p2i(_cm->top_at_rebuild_start(hr)), + p2i(_cm->top_at_mark_start_or_bottom(hr))); { // Step 1: Scan the given region from bottom to parsable_bottom. diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp index cf153226920..c5af4a8220b 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.cpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp @@ -192,7 +192,7 @@ void G1FullCollector::prepare_collection() { // Verification needs the bitmap, so we should clear the bitmap only later. bool in_concurrent_cycle = _heap->abort_concurrent_cycle(); - _heap->verify_before_full_collection(); + _heap->verify_before_full_collection(in_concurrent_cycle); if (in_concurrent_cycle) { GCTraceTime(Debug, gc) debug("Clear Bitmap"); _heap->concurrent_mark()->clear_bitmap(_heap->workers()); diff --git a/src/hotspot/share/gc/g1/g1FullGCResetMetadataTask.cpp b/src/hotspot/share/gc/g1/g1FullGCResetMetadataTask.cpp index c8fba3459aa..310cc4297c6 100644 --- a/src/hotspot/share/gc/g1/g1FullGCResetMetadataTask.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCResetMetadataTask.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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 @@ -35,6 +35,7 @@ void G1FullGCResetMetadataTask::G1ResetMetadataClosure::reset_region_metadata(G1 "Non-humongous regions must not have cset group"); hr->rem_set()->clear(); hr->clear_both_card_tables(); + _g1h->concurrent_mark()->reset_region_marking_state(hr); } bool G1FullGCResetMetadataTask::G1ResetMetadataClosure::do_heap_region(G1HeapRegion* hr) { diff --git a/src/hotspot/share/gc/g1/g1HeapRegion.cpp b/src/hotspot/share/gc/g1/g1HeapRegion.cpp index 2052a3ce156..810bd4df2ee 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegion.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegion.cpp @@ -129,8 +129,6 @@ void G1HeapRegion::hr_clear(bool clear_space) { rem_set()->clear(); - G1CollectedHeap::heap()->concurrent_mark()->reset_top_at_mark_start(this); - _parsable_bottom.store_relaxed(bottom()); _garbage_bytes.store_relaxed(0); _incoming_refs = 0; @@ -439,7 +437,9 @@ void G1HeapRegion::print_on(outputStream* st) const { } G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); st->print("|TAMS " PTR_FORMAT "| PB " PTR_FORMAT "| %-9s ", - p2i(cm->top_at_mark_start(this)), p2i(parsable_bottom_acquire()), rem_set()->get_state_str()); + p2i(cm->top_at_mark_start_or_bottom(this)), + p2i(parsable_bottom_acquire()), + rem_set()->get_state_str()); if (UseNUMA) { G1NUMA* numa = G1NUMA::numa(); if (node_index() < numa->num_active_nodes()) { diff --git a/src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp b/src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp index 619aef35a9a..87597d450cb 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp @@ -152,10 +152,6 @@ inline void G1HeapRegion::reset_skip_compacting_after_full_gc() { } inline void G1HeapRegion::reset_after_full_gc_common() { - // After a full gc the mark information in a movable region is invalid. Reset marking - // information. - G1CollectedHeap::heap()->concurrent_mark()->reset_top_at_mark_start(this); - // Everything above bottom() is parsable and live. reset_parsable_bottom(); diff --git a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp index 304722c13a1..7b518379a73 100644 --- a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp +++ b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp @@ -499,12 +499,14 @@ void G1HeapVerifier::verify_marking_state() { // Verify TAMSes, bitmaps and liveness statistics. // // - if part of marking: TAMS != bottom, liveness == 0, bitmap clear - // - if evacuation failed + part of marking: TAMS != bottom, liveness != 0, bitmap has at least on object set (corresponding to liveness) + // - if evacuation failed + part of marking: TAMS != bottom, liveness != 0, bitmap has at least one + // object set (corresponding to liveness) // - if not part of marking: TAMS == bottom, liveness == 0, bitmap clear; must be in root region // To compare liveness recorded in G1ConcurrentMark and actual we need to flush the - // cache. - G1CollectedHeap::heap()->concurrent_mark()->flush_all_task_caches(); + // cache. Do not signal end of use of the mark stats cache as this flush is only to + // make verification work. Further concurrent marking continues to need these values. + G1CollectedHeap::heap()->concurrent_mark()->flush_all_task_caches(false /* ends_use_of_mark_cache */); G1VerifyRegionMarkingStateClosure cl; _g1h->heap_region_iterate(&cl); @@ -532,28 +534,32 @@ void G1HeapVerifier::verify_after_gc() { verify_card_tables_in_sync(); } -void G1HeapVerifier::verify_bitmap_clear(bool from_tams) { +void G1HeapVerifier::verify_bitmap_clear(bool from_tams, bool concurrent_cycle_aborted) { if (!G1VerifyBitmaps) { return; } class G1VerifyBitmapClear : public G1HeapRegionClosure { bool _from_tams; + bool _concurrent_cycle_aborted; public: - G1VerifyBitmapClear(bool from_tams) : _from_tams(from_tams) { } + G1VerifyBitmapClear(bool from_tams, bool concurrent_cycle_aborted) : + _from_tams(from_tams), _concurrent_cycle_aborted(concurrent_cycle_aborted) { } virtual bool do_heap_region(G1HeapRegion* r) { G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); G1CMBitMap* bitmap = cm->mark_bitmap(); - HeapWord* start = _from_tams ? cm->top_at_mark_start(r) : r->bottom(); + HeapWord* start = _from_tams + ? cm->top_at_mark_start_for_verification(r, _concurrent_cycle_aborted) + : r->bottom(); HeapWord* mark = bitmap->get_next_marked_addr(start, r->end()); guarantee(mark == r->end(), "Found mark at " PTR_FORMAT " in region %u from start " PTR_FORMAT, p2i(mark), r->hrm_index(), p2i(start)); return false; } - } cl(from_tams); + } cl(from_tams, concurrent_cycle_aborted); G1CollectedHeap::heap()->heap_region_iterate(&cl); } diff --git a/src/hotspot/share/gc/g1/g1HeapVerifier.hpp b/src/hotspot/share/gc/g1/g1HeapVerifier.hpp index 55f6646563f..05a89a524d6 100644 --- a/src/hotspot/share/gc/g1/g1HeapVerifier.hpp +++ b/src/hotspot/share/gc/g1/g1HeapVerifier.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -73,7 +73,8 @@ class G1HeapVerifier : public CHeapObj { // Verify that marking state is set up correctly after a concurrent start pause. void verify_marking_state(); - void verify_bitmap_clear(bool above_tams_only); + void verify_bitmap_clear(bool above_tams_only, + bool concurrent_cycle_aborted = false); // Do sanity check on the contents of the in-cset fast test table. bool check_region_attr_table() PRODUCT_RETURN_( return true; ); diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 04afd262dd4..6aeabb9ac9b 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -672,8 +672,8 @@ void G1Policy::record_dirtying_stats(double last_mutator_start_dirty_ms, _to_collection_set_cards = next_to_collection_set_cards; } -bool G1Policy::should_retain_evac_failed_region(uint index) const { - size_t live_bytes = _g1h->region_at(index)->live_bytes(); +bool G1Policy::should_retain_evac_failed_region(G1HeapRegion* r) const { + size_t live_bytes = r->live_bytes(); size_t threshold = G1RetainRegionLiveThresholdPercent * G1HeapRegion::GrainBytes / 100; return live_bytes < threshold; } diff --git a/src/hotspot/share/gc/g1/g1Policy.hpp b/src/hotspot/share/gc/g1/g1Policy.hpp index 3cfd54c8c94..b661daa9a3e 100644 --- a/src/hotspot/share/gc/g1/g1Policy.hpp +++ b/src/hotspot/share/gc/g1/g1Policy.hpp @@ -389,10 +389,7 @@ class G1Policy: public CHeapObj { size_t next_pending_cards_from_gc, size_t next_to_collection_set_cards); - bool should_retain_evac_failed_region(G1HeapRegion* r) const { - return should_retain_evac_failed_region(r->hrm_index()); - } - bool should_retain_evac_failed_region(uint index) const; + bool should_retain_evac_failed_region(G1HeapRegion* r) const; private: // diff --git a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp index df76147f4b1..4db2dbd2287 100644 --- a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp +++ b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp @@ -44,6 +44,8 @@ struct G1RegionMarkStats { Atomic _live_words; Atomic _incoming_refs; + G1RegionMarkStats() : _live_words(0), _incoming_refs(0) { } + // Clear all members. void clear() { _live_words.store_relaxed(0); @@ -121,6 +123,16 @@ class G1RegionMarkStatsCache { cur->_stats._live_words.store_relaxed(cur->_stats.live_words() + live_words); } + void verify_no_mark_stats_for(uint region_idx) { + uint const cache_idx = hash(region_idx); + G1RegionMarkStatsCacheEntry* const cur = &_cache[cache_idx]; + if (cur->_region_idx != region_idx) { + return; + } + assert(cur->_stats.incoming_refs() == 0, "must be"); + assert(cur->_stats.live_words() == 0, "must be"); + } + void inc_incoming_refs(uint region_idx) { G1RegionMarkStatsCacheEntry* const cur = find_for_add(region_idx); // This method is only ever called single-threaded, so we do not need atomic diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp index 608f12e3859..bcb50dcc98f 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.cpp +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp @@ -1061,11 +1061,10 @@ class G1MergeHeapRootsTask : public WorkerTask { // so the bitmap for the regions in the collection set must be cleared if not already. if (should_clear_region(hr)) { _g1h->clear_bitmap_for_region(hr); - _g1h->concurrent_mark()->reset_top_at_mark_start(hr); } else { assert_bitmap_clear(hr, _g1h->concurrent_mark()->mark_bitmap()); } - _g1h->concurrent_mark()->clear_statistics(hr); + _g1h->concurrent_mark()->reset_region_marking_state(hr); _scan_state->add_all_dirty_region(hr->hrm_index()); return false; } diff --git a/src/hotspot/share/gc/g1/g1SATBMarkQueueSet.cpp b/src/hotspot/share/gc/g1/g1SATBMarkQueueSet.cpp index 7d197c37158..7553936bb26 100644 --- a/src/hotspot/share/gc/g1/g1SATBMarkQueueSet.cpp +++ b/src/hotspot/share/gc/g1/g1SATBMarkQueueSet.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -114,5 +114,12 @@ class G1SATBMarkQueueFilterFn { }; void G1SATBMarkQueueSet::filter(SATBMarkQueue& queue) { - apply_filter(G1SATBMarkQueueFilterFn(), queue); + G1CollectedHeap* g1h = G1CollectedHeap::heap(); + if (g1h->collector_state()->is_in_marking()) { + apply_filter(G1SATBMarkQueueFilterFn(), queue); + } else { + // is_in_marking() covers both the concurrent marking and the Remark pause. Outside + // of that, there can be no entry that requires SATB marking. + queue.set_empty(); + } } diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.cpp b/src/hotspot/share/gc/g1/g1YoungCollector.cpp index edfe97d04d6..ec83d8a27d3 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.cpp @@ -362,13 +362,14 @@ class G1PrepareEvacuationTask : public WorkerTask { // There is no difference between scanning cards covering an effectively // dead humongous object vs. some other objects in reallocated regions. // - // TAMSes are only reset after completing the entire mark cycle, during - // bitmap clearing. It is worth to not wait until then, and allow reclamation - // outside of actual (concurrent) SATB marking. + // TAMSes are only reset in the Concurrent Start pause and when they are + // reclaimed/freed. It is worth to not wait for TAMS updates until either + // of these conditions applies and allow reclamation as much as possible. // This also applies to the concurrent start pause - we only set - // mark_in_progress() at the end of that GC: no mutator is running that can + // is_in_marking() at the end of that GC: no mutator is running that can // sneakily install a new reference to the potentially reclaimed humongous // object. + // // During the concurrent start pause the situation described above where we // miss a reference can not happen. No mutator is modifying the object // graph to install such an overlooked reference. @@ -376,12 +377,15 @@ class G1PrepareEvacuationTask : public WorkerTask { // After the pause, having reclaimed h, obviously the mutator can't fetch // the reference from h any more. if (!obj->is_typeArray()) { - // All regions that were allocated before marking have a TAMS != bottom. - bool allocated_before_mark_start = region->bottom() != _g1h->concurrent_mark()->top_at_mark_start(region); bool mark_in_progress = _g1h->collector_state()->is_in_marking(); - - if (allocated_before_mark_start && mark_in_progress) { - return false; + // top_at_mark_start() will assert outside of marking, so check first. + if (mark_in_progress) { + // All regions that were allocated before marking have a TAMS != bottom. + G1ConcurrentMark* cm = _g1h->concurrent_mark(); + bool allocated_before_mark_start = region->bottom() != cm->top_at_mark_start(region); + if (allocated_before_mark_start) { + return false; + } } } return _g1h->is_potential_eager_reclaim_candidate(region); @@ -1028,7 +1032,7 @@ void G1YoungCollector::enqueue_candidates_as_root_regions() { G1CollectionSetCandidates* candidates = collection_set()->candidates(); candidates->iterate_regions([&] (G1HeapRegion* r) { - _g1h->concurrent_mark()->add_root_region(r); + _g1h->concurrent_mark()->add_root_region_set_bottom(r); }); } diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp index cc9c4b10202..97378d0542e 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp @@ -394,8 +394,13 @@ class G1FreeHumongousRegionClosure : public G1HeapRegionIndexClosure { oop obj = cast_to_oop(r->bottom()); { ResourceMark rm; - bool allocated_after_mark_start = r->bottom() == _g1h->concurrent_mark()->top_at_mark_start(r); bool mark_in_progress = _g1h->collector_state()->is_in_marking(); + bool allocated_after_mark_start = false; + if (mark_in_progress) { + // top_at_mark_start() will assert if we are not in marking, so check first. + allocated_after_mark_start = r->bottom() == _g1h->concurrent_mark()->top_at_mark_start(r); + } + guarantee(obj->is_typeArray() || (allocated_after_mark_start || !mark_in_progress), "Only eagerly reclaiming primitive arrays is supported, other humongous objects only if allocated after mark start, but the object " PTR_FORMAT " (%s) is not (mark %d allocated after mark: %d).", @@ -498,20 +503,22 @@ class G1PostEvacuateCollectionSetCleanupTask2::ProcessEvacuationFailedRegionsTas G1CollectedHeap* g1h = G1CollectedHeap::heap(); G1ConcurrentMark* cm = g1h->concurrent_mark(); - // Concurrent mark does not mark through regions that we retain (they are root - // regions wrt to marking), so we must clear their mark data (tams, bitmap, ...) - // set eagerly or during evacuation failure. + // Retained regions are root regions for marking, so we must clear their mark data + // (tams, bitmap, ...). Outside of Concurrent Start GC we must always clear the mark data + // for the next GC. bool clear_mark_data = !g1h->collector_state()->is_in_concurrent_start_gc() || g1h->policy()->should_retain_evac_failed_region(r); if (clear_mark_data) { g1h->clear_bitmap_for_region(r); + // Must be because this is a region that should not have been selected to + // be marked through. + cm->assert_top_at_mark_start_is_bottom(r); } else { // This evacuation failed region is going to be marked through. Update mark data. - cm->update_top_at_mark_start(r); - cm->set_live_bytes(r->hrm_index(), r->live_bytes()); - assert(cm->mark_bitmap()->get_next_marked_addr(r->bottom(), cm->top_at_mark_start(r)) != cm->top_at_mark_start(r), - "Marks must be on bitmap for region %u", r->hrm_index()); + // Since we have some marked live data information, pass that too. + cm->assert_statistics_clear(r); + cm->notify_new_region(r, r->live_bytes()); } return false; } diff --git a/src/hotspot/share/gc/shared/satbMarkQueue.cpp b/src/hotspot/share/gc/shared/satbMarkQueue.cpp index 63496f2eb25..a1bd4e5a9c8 100644 --- a/src/hotspot/share/gc/shared/satbMarkQueue.cpp +++ b/src/hotspot/share/gc/shared/satbMarkQueue.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -199,7 +199,7 @@ void SATBMarkQueueSet::set_active_all_threads(bool active, bool expected_active) if (_active) { assert(queue.is_empty(), "queues should be empty when activated"); } else { - queue.set_index(queue.current_capacity()); + queue.set_empty(); } queue.set_active(_active); } @@ -363,7 +363,7 @@ size_t SATBMarkQueue::current_capacity() const { } void SATBMarkQueueSet::reset_queue(SATBMarkQueue& queue) { - queue.set_index(queue.current_capacity()); + queue.set_empty(); } void SATBMarkQueueSet::flush_queue(SATBMarkQueue& queue) { diff --git a/src/hotspot/share/gc/shared/satbMarkQueue.hpp b/src/hotspot/share/gc/shared/satbMarkQueue.hpp index f1577c004de..36c287cd64d 100644 --- a/src/hotspot/share/gc/shared/satbMarkQueue.hpp +++ b/src/hotspot/share/gc/shared/satbMarkQueue.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -99,6 +99,10 @@ class SATBMarkQueue { _index = index_to_byte_index(new_index); } + void set_empty() { + set_index(current_capacity()); + } + // Returns the capacity of the buffer, or 0 if the queue doesn't currently // have a buffer. size_t current_capacity() const; diff --git a/test/hotspot/jtreg/gc/g1/TestEagerReclaimHumongousRegions.java b/test/hotspot/jtreg/gc/g1/TestEagerReclaimHumongousRegions.java index 5637e578e8f..37902cad906 100644 --- a/test/hotspot/jtreg/gc/g1/TestEagerReclaimHumongousRegions.java +++ b/test/hotspot/jtreg/gc/g1/TestEagerReclaimHumongousRegions.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 @@ -109,7 +109,10 @@ private static List testArgs() throws Exception { "-Xmx20M", "-Xms20m", "-XX:+UnlockDiagnosticVMOptions", + "-XX:+VerifyBeforeGC", "-XX:+VerifyAfterGC", + "-XX:+VerifyDuringGC", + "-XX:+G1VerifyBitmaps", "-Xbootclasspath/a:.", "-Xlog:gc=debug,gc+humongous=debug", "-XX:+UnlockDiagnosticVMOptions", diff --git a/test/hotspot/jtreg/gc/g1/TestVerificationInConcurrentCycle.java b/test/hotspot/jtreg/gc/g1/TestVerificationInConcurrentCycle.java index 5a69a6e5288..b5cf4c333a6 100644 --- a/test/hotspot/jtreg/gc/g1/TestVerificationInConcurrentCycle.java +++ b/test/hotspot/jtreg/gc/g1/TestVerificationInConcurrentCycle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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,6 +23,8 @@ package gc.g1; +import java.lang.ref.Reference; + /* * @test TestVerificationInConcurrentCycle * @requires vm.gc.G1 @@ -34,6 +36,7 @@ * @run main/othervm * -Xbootclasspath/a:. * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:G1HeapRegionSize=2m * -XX:+VerifyBeforeGC -XX:+VerifyDuringGC -XX:+VerifyAfterGC * -XX:+UseG1GC -XX:+G1VerifyHeapRegionCodeRoots * -XX:+G1VerifyBitmaps @@ -52,6 +55,7 @@ * @run main/othervm * -Xbootclasspath/a:. * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:G1HeapRegionSize=2m * -XX:+VerifyBeforeGC -XX:+VerifyDuringGC -XX:+VerifyAfterGC * -XX:+UseG1GC -XX:+G1VerifyHeapRegionCodeRoots * gc.g1.TestVerificationInConcurrentCycle @@ -64,27 +68,50 @@ public class TestVerificationInConcurrentCycle { private static final WhiteBox WB = WhiteBox.getWhiteBox(); + private static Object[] allocateHumongous() { + Object[] result = new Object[7]; + for (int i = 0; i < result.length; i++) { + result[i] = new byte[1024 * 1024]; // Is humongous. + } + return result; + } + + private static void dropHalf(Object[] array) { + for (int i = 0; i < array.length; i++) { + if (i % 2 == 0) { + array[i] = null; + } + } + } // All testN() assume initial state is idle, and restore that state. private static void testFullGCAt(String at) throws Exception { System.out.println("testSimpleCycle"); + + Object[] objects = allocateHumongous(); try { // Run one cycle. WB.concurrentGCRunTo(at); + dropHalf(objects); WB.fullGC(); } finally { WB.concurrentGCRunToIdle(); + Reference.reachabilityFence(objects); } } private static void testYoungGCAt(String at) throws Exception { System.out.println("testSimpleCycle"); + + Object[] objects = allocateHumongous(); try { // Run one cycle. WB.concurrentGCRunTo(at); + dropHalf(objects); WB.youngGC(); } finally { WB.concurrentGCRunToIdle(); + Reference.reachabilityFence(objects); } } diff --git a/test/hotspot/jtreg/gc/g1/pinnedobjs/TestDroppedRetainedTAMS.java b/test/hotspot/jtreg/gc/g1/pinnedobjs/TestDroppedRetainedTAMS.java index f650e53a25f..c4dd2900c86 100644 --- a/test/hotspot/jtreg/gc/g1/pinnedobjs/TestDroppedRetainedTAMS.java +++ b/test/hotspot/jtreg/gc/g1/pinnedobjs/TestDroppedRetainedTAMS.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -30,9 +30,10 @@ * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - -XX:+WhiteBoxAPI -Xbootclasspath/a:. -Xmx32m -XX:G1NumCollectionsKeepPinned=1 - -XX:+VerifyBeforeGC -XX:+VerifyAfterGC -XX:G1MixedGCLiveThresholdPercent=100 - -XX:G1HeapWastePercent=0 -Xlog:gc,gc+ergo+cset=trace gc.g1.pinnedobjs.TestDroppedRetainedTAMS + * -XX:+WhiteBoxAPI -Xbootclasspath/a:. -Xmx32m -XX:G1NumCollectionsKeepPinned=1 + * -XX:+VerifyBeforeGC -XX:+VerifyAfterGC -XX:+VerifyDuringGC -XX:+G1VerifyBitmaps + * -XX:G1MixedGCLiveThresholdPercent=100 -XX:G1HeapWastePercent=0 + * -Xlog:gc,gc+ergo+cset=trace gc.g1.pinnedobjs.TestDroppedRetainedTAMS */ package gc.g1.pinnedobjs; From f1cd7f6ab9c162736ea3fc8f1523294ec004776e Mon Sep 17 00:00:00 2001 From: Ferenc Rakoczi Date: Wed, 24 Jun 2026 10:21:22 +0000 Subject: [PATCH 055/707] 8355216: Accelerate P-256 arithmetic on aarch64 Reviewed-by: adinn, aph --- src/hotspot/cpu/aarch64/assembler_aarch64.hpp | 28 + src/hotspot/cpu/aarch64/register_aarch64.hpp | 11 + .../cpu/aarch64/stubDeclarations_aarch64.hpp | 2 +- .../cpu/aarch64/stubGenerator_aarch64.cpp | 988 +++++++++++++++++- .../cpu/aarch64/vm_version_aarch64.cpp | 4 + src/hotspot/share/code/aotCodeCache.hpp | 2 +- 6 files changed, 1032 insertions(+), 3 deletions(-) diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp index 4eb2f6010c0..ae2b9ac9bf7 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp @@ -3151,6 +3151,34 @@ template _pmull(Vd, Ta, Vn, Vm, Tb); } + //Vector by element variant of UMULL + void _umullv(FloatRegister Vd, SIMD_Arrangement Ta, FloatRegister Vn, + SIMD_Arrangement Tb, FloatRegister Vm, SIMD_RegVariant Ts, int lane) { + starti; + int size = (Ta == T4S) ? 0b01 : 0b10; + int q = (Tb == T4H || Tb == T2S) ? 0 : 1; + int h = (size == 0b01) ? ((lane >> 2) & 1) : ((lane >> 1) & 1); + int l = (size == 0b01) ? ((lane >> 1) & 1) : (lane & 1); + assert(Ta == T4S || Ta == T2D, "umull{2}v destination register must have arrangement T4S or T2D"); + assert(size == 0b10 ? lane < 4 : lane < 8, "umull{2}v assumes lane < 4 when using half-words and lane < 8 otherwise"); + assert(Ts == H ? Vm->encoding() < 16 : Vm->encoding() < 32, "umull{2}v requires Vm to be in range V0..V15 when Ts is H"); + f(0, 31), f(q, 30), f(0b101111, 29, 24), f(size, 23, 22), f(l, 21); //f(m, 20); + rf(Vm, 16), f(0b1010, 15, 12), f(h, 11), f(0, 10), rf(Vn, 5), rf(Vd, 0); + } + + //Vector by element variant of UMULL + void umullv(FloatRegister Vd, SIMD_Arrangement Ta, FloatRegister Vn, + SIMD_Arrangement Tb, FloatRegister Vm, SIMD_RegVariant Ts, int lane) { + assert(Ta == T4S ? (Tb == T4H && Ts == H) : (Tb == T2S && Ts == S), "umullv register arrangements must adhere to spec"); + _umullv(Vd, Ta, Vn, Tb, Vm, Ts, lane); + } + + void umull2v(FloatRegister Vd, SIMD_Arrangement Ta, FloatRegister Vn, + SIMD_Arrangement Tb, FloatRegister Vm, SIMD_RegVariant Ts, int lane) { + assert(Ta == T4S ? (Tb == T8H && Ts == H) : (Tb == T4S && Ts == S), "umull2v register arrangements must adhere to spec"); + _umullv(Vd, Ta, Vn, Tb, Vm, Ts, lane); + } + void uqxtn(FloatRegister Vd, SIMD_Arrangement Tb, FloatRegister Vn, SIMD_Arrangement Ta) { starti; int size_b = (int)Tb >> 1; diff --git a/src/hotspot/cpu/aarch64/register_aarch64.hpp b/src/hotspot/cpu/aarch64/register_aarch64.hpp index d1e0632c80b..ab83307d526 100644 --- a/src/hotspot/cpu/aarch64/register_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/register_aarch64.hpp @@ -535,6 +535,17 @@ VSeq vs_odd(const VSeq& v) { return VSeq(v.base() + v.delta(), v.delta() * 2); } +template +FloatRegister vs_head(const VSeq& v) { + static_assert(N > 1, "sequence length must be greater than 1"); + return v.base(); +} + +template +VSeq vs_tail(const VSeq& v) { + return VSeq(v.base() + v.delta(), v.delta()); +} + // convenience method to construct a vector register sequence that // indexes its elements in reverse order to the original diff --git a/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp b/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp index d1f59e479db..d1e0621f6a9 100644 --- a/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp @@ -57,7 +57,7 @@ do_arch_entry, \ do_arch_entry_init, \ do_arch_entry_array) \ - do_arch_blob(compiler, 70000) \ + do_arch_blob(compiler, 75000) \ do_stub(compiler, vector_iota_indices) \ do_arch_entry_array(aarch64, compiler, vector_iota_indices, \ vector_iota_indices, vector_iota_indices, \ diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index f41a54e9d26..f89b6e2d579 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -152,6 +152,12 @@ static const uint64_t _double_keccak_round_consts[24] = { 0x8000000000008080L, 0x0000000080000001L, 0x8000000080008008L }; +//Omit 3rd limb of modulus since it is 0 +static const int64_t _modulus_P256[5] = { + 0x000fffffffffffffL, 0x00000fffffffffffL, + 0x0000001000000000L, 0x0000ffffffff0000L +}; + static const char _encodeBlock_toBase64[64] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', @@ -5311,6 +5317,32 @@ class StubGenerator: public StubCodeGenerator { } } + template + void vs_shl(const VSeq& v, Assembler::SIMD_Arrangement T, + const VSeq& v1, int shift) { + // output must not be constant + assert(N == 1 || !v.is_constant(), "cannot output multiple values to a constant vector"); + // output cannot overwrite pending inputs + assert(!vs_write_before_read(v, v1), "output overwrites input"); + + for (int i = 0; i < N; i++) { + __ shl(v[i], T, v1[i], shift); + } + } + + template + void vs_ushr(const VSeq& v, Assembler::SIMD_Arrangement T, + const VSeq& v1, int shift) { + // output must not be constant + assert(N == 1 || !v.is_constant(), "cannot output multiple values to a constant vector"); + // output cannot overwrite pending inputs + assert(!vs_write_before_read(v, v1), "output overwrites input"); + + for (int i = 0; i < N; i++) { + __ ushr(v[i], T, v1[i], shift); + } + } + template void vs_sshr(const VSeq& v, Assembler::SIMD_Arrangement T, const VSeq& v1, int shift) { @@ -5335,6 +5367,29 @@ class StubGenerator: public StubCodeGenerator { } } + template + void vs_andr(const VSeq& v, const VSeq& v1, const FloatRegister v2) { + // output must not be constant + assert(N == 1 || !v.is_constant(), "cannot output multiple values to a constant vector"); + // output cannot overwrite pending inputs + assert(!vs_write_before_read(v, v1), "output overwrites input"); + for (int i = 0; i < N; i++) { + __ andr(v[i], __ T16B, v1[i], v2); + } + } + + template + void vs_eor(const VSeq& v, const VSeq& v1, const VSeq& v2) { + // output must not be constant + assert(N == 1 || !v.is_constant(), "cannot output multiple values to a constant vector"); + // output cannot overwrite pending inputs + assert(!vs_write_before_read(v, v1), "output overwrites input"); + assert(!vs_write_before_read(v, v2), "output overwrites input"); + for (int i = 0; i < N; i++) { + __ eor(v[i], __ T16B, v1[i], v2[i]); + } + } + template void vs_orr(const VSeq& v, const VSeq& v1, const VSeq& v2) { // output must not be constant @@ -5388,7 +5443,7 @@ class StubGenerator: public StubCodeGenerator { template void vs_ldpq(const VSeq& v, Register base) { for (int i = 0; i < N; i += 2) { - __ ldpq(v[i], v[i+1], Address(base, 32 * i)); + __ ldpq(v[i], v[i+1], Address(base, 16 * i)); } } @@ -5436,6 +5491,18 @@ class StubGenerator: public StubCodeGenerator { } } + // store two vector register sequences of length N + // interleaved into N pairs of quadword memory locations + // starting at the address supplied in dest using + // post-increment addressing. + template + void vs_st1_interleaved(VSeq A, VSeq B, Register dest) { + for (int i = 0; i < N; i++) { + __ st1(A[i], __ T2D, __ post(dest, 16)); + __ st1(B[i], __ T2D, __ post(dest, 16)); + } + } + // load N quadword values from memory de-interleaved into N vector // registers 3 elements at a time via the address supplied in base. template @@ -7674,6 +7741,919 @@ class StubGenerator: public StubCodeGenerator { return start; } + static constexpr int montMulP256Shift1 = 12; // 64 - bits per limb + static constexpr int montMulP256Shift2 = 52; // bits per limb + // stack space needed for carry computation + static constexpr int cDataSize = 6 * BytesPerLong; + // stack space needed for data computed by the neon side + static constexpr int mulDataSize = 16 * BytesPerLong; + + + // Subroutine used by the 52 x 52 bit multiplication algorithm in + // generate_intpoly_montgomeryMult_P256(). + // This function computes partial results of eight 52 x 52 bit multiplications, + // where the multiplicands are stored as 64-bit values, specifically + // (b_0, b_1, b_2, b_3) * (a_3, a_4). (The 4 calls to this function + // together provide the results of these limb-multiplications.) + // Calls to this function accept either the low 32 bits or high 20 bits + // of each b_i packed into bs in ascending order. a_3 and a_4 are packed + // into successive 64 bit elements of as. lane selects the low 32 or high + // 20 bits of each a_j value. So four calls with the appropriate parameters + // will produce the 64-bit low32 * low32, low32 * high20, high20 * low32, + // high20 * high20 values in the output register sequences vs. The + // 64-bit partial products are returned in vs in ascending order: + // vs[0] = (b_0*a_3, b_1*a_3) . . . vs[3] = (b_2*a_4, b_3*a_4) + + void neon_partial_mult_64(const VSeq<4>& vs, FloatRegister bs, FloatRegister as, int lane_lo) { + __ umullv(vs[0], __ T2D, bs, __ T2S, as, __ S, lane_lo); + __ umull2v(vs[1], __ T2D, bs, __ T4S, as, __ S, lane_lo); + __ umullv(vs[2], __ T2D, bs, __ T2S, as, __ S, lane_lo + 2); + __ umull2v(vs[3], __ T2D, bs, __ T4S, as, __ S, lane_lo + 2); + } + + // Subroutine used by the generate_intpoly_montgomeryMult_P256() function + // to compute the result of a 52 x 52 bit multiplications where the + // multiplicands, a and b are available as 64-bit values. + // The result is going to two 64-bit registers lo (least significant 52 bits) + // and hi (most significant 52 bits). + void gpr_partial_mult_52(Register a, Register b, Register hi, Register lo, + Register mask) { + // compute 104-bit (40 + 64) full product + __ umulh(hi, a, b); + __ mul(lo, a, b); + // combine 40 + 12 bits into hi result + // on certain implementations of aarch64 (e.g. apple M1) replacing extr() + // with the following equivalent instruction sequence the performance + // improves slightly (despite it is two instructions longer and needs + // an additional register) + // __ lsl(hi, hi, montMulP256Shift1); + // __ lsr(tmp, lo, montMulP256Shift2); + // __ orr(hi, hi, tmp); + __ extr(hi, hi, lo, montMulP256Shift2); + // mask off 52 bits of lo result + __ andr(lo, lo, mask); + } + + // This assembly follows the Java code in MontgomeryIntegerPolynomial256.mult() + // quite closely. The main difference is that the computations done with the + // last two limbs of `a` are done using Neon registers. This allows us to take + // advantage of both the Neon registers and GPRs simultaneously. + // It is also worth noting that since Neon does not support 64 bit + // multiplication, we split each 64 bit value into lower and upper halves + // and use the "schoolbook" multiplication algorithm. + address generate_intpoly_montgomeryMult_P256() { + assert(UseIntPolyIntrinsics, "what are we doing here?"); + StubId stub_id = StubId::stubgen_intpoly_montgomeryMult_P256_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); + StubCodeMark mark(this, stub_id); + start = __ pc(); + __ enter(); + + // Registers that are used throughout entire routine + const Register a = c_rarg0; + const Register b = c_rarg1; + const Register result = c_rarg2; + + RegSet regs = RegSet::range(r0, r28) - rscratch1 - rscratch2 + - r16 - r17 - r18_tls - a - b - result; + + auto common_regs = regs.begin(); + Register limb_mask = *common_regs++, + c_ptr = *common_regs++, + mod_0 = *common_regs++, + mod_1 = *common_regs++, + mod_3 = *common_regs++, + mod_4 = *common_regs++, + b_0 = *common_regs++, + b_1 = *common_regs++, + b_2 = *common_regs++, + b_3 = *common_regs++, + b_4 = *common_regs++; + + FloatRegSet floatRegs = FloatRegSet::range(v0, v31) + - FloatRegSet::range(v8, v15) // Caller saved vectors + - FloatRegSet::range(v16, v31); // Manually-allocated vectors + + auto common_vectors = floatRegs.begin(); + FloatRegister limb_mask_vec = *common_vectors++, + b_lows = *common_vectors++, + b_highs = *common_vectors++, + a_vals = *common_vectors++; + + // Push callee saved registers on to the stack + RegSet callee_saved = RegSet::range(r19, r28); + __ push(callee_saved, sp); + + // Allocate space on the stack for carry values + __ sub(sp, sp, cDataSize); + __ mov(c_ptr, sp); + + // Calculate (52-bit) limb masks for both gpr and vector registers + __ mov(limb_mask, -UCONST64(1) >> montMulP256Shift1); + __ dup(limb_mask_vec, __ T2D, limb_mask); + + //Load input arrays and modulus + Register a_ptr = *common_regs++, mod_ptr = *common_regs++; + // skip 3 limbs so a_ptr addresses trailing pair {a3, a4} + __ add(a_ptr, a, 3 * BytesPerLong); + __ lea(mod_ptr, ExternalAddress((address)_modulus_P256)); + __ ldr(b_0, Address(b)); + __ ldr(b_1, Address(b, BytesPerLong)); + __ ldr(b_2, Address(b, 2 * BytesPerLong)); + __ ldr(b_3, Address(b, 3 * BytesPerLong)); + __ ldr(b_4, Address(b, 4 * BytesPerLong)); + __ ldr(mod_0, __ post(mod_ptr, BytesPerLong)); + __ ldr(mod_1, __ post(mod_ptr, BytesPerLong)); + __ ldr(mod_3, __ post(mod_ptr, BytesPerLong)); + __ ldr(mod_4, mod_ptr); + __ ld1(a_vals, __ T2D, a_ptr); + // use an interleaved load to group low 32 bits and high 20 bits + // of 4 successive b values into two vector registers + // n.b. these are the same inputs as the ones in b_0 ... b4 + __ ld2(b_lows, b_highs, __ T4S, b); + common_regs = common_regs.remaining() + + a_ptr + mod_ptr; + a_ptr = mod_ptr = noreg; + + //Regs used throughout the main "loop", which is partially unrolled here + Register high = *common_regs++, + low = *common_regs++, + mul_ptr = *common_regs++, + mod_high = *common_regs++, + mod_low = *common_regs++, + a_i = *common_regs++, + c_i = *common_regs++, + tmp = *common_regs++, + n = *common_regs++; + + // vector sequences used to compute and combine partial products of + // b_i * a_j for i = {0,1,2,3} j = {3,4} + VSeq<4> A(16); + VSeq<4> B(20); + VSeq<4> C(24); + VSeq<4> D(28); + + + // neon and gpr computations are interleaved to maximize parallelism + + // allocate stack space for the neon results + __ sub(sp, sp, mulDataSize); + __ mov(mul_ptr, sp); + + // cross-multiply low * low for limbs b0-b3 and a3-a4 in parallel + neon_partial_mult_64(A, b_lows, a_vals, 0); + + // Limb 0 + __ ldr(a_i, __ post(a, BytesPerLong)); + gpr_partial_mult_52(a_i, b_0, high, low, limb_mask); + __ mov(n, low); + // __ andr(n, low, limb_mask); + + // cross-multiply high * low for limbs b0-b3 and a3-a4 in parallel + neon_partial_mult_64(B, b_highs, a_vals, 0); + + // Limb 0 modulus computation + // n.b. modulus computation requires multiplying successive + // limbs of the product by corresponding limbs of the p256 + // prime adding the result to the limb and folding this + // partial result into a running 256-bit sum in c_i. Limbs + // of c_i are stored via c_ptr once carries are included. + // n.b. the mul + add is omitted for limb 2 since the + // corresponding prime bits are zero. + gpr_partial_mult_52(n, mod_0, mod_high, mod_low, limb_mask); + __ add(low, low, mod_low); + __ add(high, high, mod_high); + __ lsr(c_i, low, montMulP256Shift2); + __ add(c_i, c_i, high); + + // cross-multiply low * high for limbs b0-b3 and a3-a4 in parallel + neon_partial_mult_64(C, b_lows, a_vals, 1); + + // Limb 1 + gpr_partial_mult_52(a_i, b_1, high, low, limb_mask); + + // cross-multiply high * high for limbs b0-b3 and a3-a4 in parallel + neon_partial_mult_64(D, b_highs, a_vals, 1); + + gpr_partial_mult_52(n, mod_1, mod_high, mod_low, limb_mask); + __ add(low, low, mod_low); + __ add(high, high, mod_high); + __ add(c_i, c_i, low); + __ str(c_i, c_ptr); + __ mov(c_i, high); + + // combine neon 32-bit partial products, regrouping to produce + // 8*52-bit low products in A and 8*52-bit high products in D + + // add low*high/high*low intermediate products before regrouping + vs_addv(B, __ T2D, B, C); // Store (B+C) in B + + // Limb 2 + gpr_partial_mult_52(a_i, b_2, high, low, limb_mask); + __ add(c_i, c_i, low); + __ str(c_i, Address(c_ptr, 8)); + __ mov(c_i, high); + + // shift high*high (40-bit) product up into 52-bits of output + vs_shl(D, __ T2D, D, montMulP256Shift1); + + // Limb 3 + gpr_partial_mult_52(a_i, b_3, high, low, limb_mask); + + // shift high 32 (or 33) bits of intermediate products for addition to D + vs_ushr(C, __ T2D, B, 32 - montMulP256Shift1); // Use C for ((B+C) >>> 20) + + gpr_partial_mult_52(n, mod_3, mod_high, mod_low, limb_mask); + __ add(low, low, mod_low); + __ add(high, high, mod_high); + __ add(c_i, c_i, low); + __ str(c_i, Address(c_ptr, 2 * BytesPerLong)); + __ mov(c_i, high); + + // shift low 32 bits of intermediate product up for masking and addition to A + vs_shl(B, __ T2D, B, 32); + + // Limb 4 + gpr_partial_mult_52(a_i, b_4, high, low, limb_mask); + + // add high bits of intermediate product into D + vs_addv(D, __ T2D, D, C); + + gpr_partial_mult_52(n, mod_4, mod_high, mod_low, limb_mask); + __ add(low, low, mod_low); + __ add(high, high, mod_high); + __ add(c_i, c_i, low); + __ str(c_i, Address(c_ptr, 3 * BytesPerLong)); + __ str(high, Address(c_ptr, 4 * BytesPerLong)); + + // top 12 bits of 32*32 bit product in A need adding into high 52-bit output + vs_ushr(C, __ T2D, A, 52); // C now holds (A >>> 52) + // Only 20 of the 32 bits now in the top of B should be added into A + vs_andr(B, B, limb_mask_vec); + // reduce original 64-bit product to 52-bits + vs_andr(A, A, limb_mask_vec); + // add intermediate products to high 52-bit result in D + vs_addv(D, __ T2D, D, C); + // add 20/21 bits of intermediate product in top of B into low 52-bit result + vs_addv(A, __ T2D, A, B); + // save and then mask off any overflow bit from computing low 52-bit result + vs_ushr(B, __ T2D, A, montMulP256Shift2); + vs_andr(A, A, limb_mask_vec); + // add any remaining carry into the high 52-bit result + vs_addv(D, __ T2D, D, B); + + // the write interleaves the 4 successive pairs of low and + // high results: (l0, l1), (h0, h1), ... (l6, l7), (h6, h7) + vs_st1_interleaved(A, D, mul_ptr); + + // Free mul_ptr + common_regs = common_regs.remaining() + mul_ptr; + mul_ptr = noreg; + + ///////////////////////// + // Loop 2 & 3 + ///////////////////////// + + for (int i = 0; i < 2; i++) { + // Load a_i and increment by 8 bytes + __ ldr(a_i, __ post(a, BytesPerLong)); + __ ldr(c_i, c_ptr); //Load prior c_i + + // Limb 0 + gpr_partial_mult_52(a_i, b_0, high, low, limb_mask); + __ add(low, low, c_i); + __ ldr(c_i, Address(c_ptr, BytesPerLong)); + __ andr(n, low, limb_mask); + gpr_partial_mult_52(n, mod_0, mod_high, mod_low, limb_mask); + __ add(low, low, mod_low); + __ add(high, high, mod_high); + __ lsr(tmp, low, montMulP256Shift2); + __ add(c_i, c_i, tmp); + __ add(c_i, c_i, high); + + // Limb 1 + gpr_partial_mult_52(a_i, b_1, high, low, limb_mask); + gpr_partial_mult_52(n, mod_1, mod_high, mod_low, limb_mask); + __ ldr(tmp, Address(c_ptr, 2 * BytesPerLong)); + __ add(low, low, mod_low); + __ add(high, high, mod_high); + __ add(c_i, c_i, low); + __ str(c_i, c_ptr); + __ add(c_i, tmp, high); + + // Limb 2 + gpr_partial_mult_52(a_i, b_2, high, low, limb_mask); + __ ldr(tmp, Address(c_ptr, 3 * BytesPerLong)); + __ add(c_i, c_i, low); + __ str(c_i, Address(c_ptr, BytesPerLong)); + __ add(c_i, tmp, high); + + // Limb 3 + gpr_partial_mult_52(a_i, b_3, high, low, limb_mask); + gpr_partial_mult_52(n, mod_3, mod_high, mod_low, limb_mask); + __ ldr(tmp, Address(c_ptr, 4 * BytesPerLong)); + __ add(low, low, mod_low); + __ add(high, high, mod_high); + __ add(c_i, c_i, low); + __ str(c_i, Address(c_ptr, 2 * BytesPerLong)); + __ add(c_i, tmp, high); + + // Limb 4 + gpr_partial_mult_52(a_i, b_4, high, low, limb_mask); + gpr_partial_mult_52(n, mod_4, mod_high, mod_low, limb_mask); + __ add(low, low, mod_low); + __ add(high, high, mod_high); + __ add(c_i, c_i, low); + __ str(c_i, Address(c_ptr, 3 * BytesPerLong)); + __ str(high, Address(c_ptr, 4 * BytesPerLong)); + } + // Reallocate regs b_0, b_1, b_2 and b_3 + common_regs = common_regs.remaining() + + b_0 + b_1 + b_2 + b_3; + b_0 = b_1 = b_2 = b_3 = noreg; + + Register low_1 = *common_regs++; + Register high_1 = *common_regs++; + + ////////////////////////////// + // a[3] + ////////////////////////////// + + // For a_3 and a_4 we have already computed the cross-products + // with b_0 ... b_3 and stored them on the stack relative to + // `mul_ptr` i.e. the current `sp`in the order + // l(a_3 * b_0), l(a_3 * b_1), h(a_3 * b_0), h(a_3 * b_1), + // l(a_3 * b_2), l(a_3 * b_3), h(a_3 * b_2), h(a_3 * b_3), + // l(a_4 * b_0), l(a_4 * b_1), h(a_4 * b_0), h(a_4 * b_1), + // l(a_4 * b_2), l(a_4 * b_3), h(a_4 * b_2), h(a_4 * b_3), + // where l(x) is the low 52 bits of x and h(x) is the high 52 bits + + __ ldr(low_1, Address(sp)); + __ ldr(high_1, Address(sp, 2 * BytesPerLong)); + + __ ldr(low, Address(sp, BytesPerLong)); + __ ldr(high, Address(sp, 3 * BytesPerLong)); + __ ldr(a_i, __ post(a, BytesPerLong)); + __ ldr(c_i, c_ptr); + + // Limb 0 + __ add(low_1, low_1, c_i); + __ ldr(c_i, Address(c_ptr, BytesPerLong)); + __ andr(n, low_1, limb_mask); + gpr_partial_mult_52(n, mod_0, mod_high, mod_low, limb_mask); + __ add(low_1, low_1, mod_low); + __ add(high_1, high_1, mod_high); + __ lsr(tmp, low_1, montMulP256Shift2); + __ add(c_i, c_i, tmp); + __ add(c_i, c_i, high_1); + + // Limb 1 + __ ldr(low_1, Address(sp, 4 * BytesPerLong)); + __ ldr(high_1, Address(sp, 6 * BytesPerLong)); + gpr_partial_mult_52(n, mod_1, mod_high, mod_low, limb_mask); + __ ldr(tmp, Address(c_ptr, 2 * BytesPerLong)); + __ andr(mod_low, mod_low, limb_mask); + __ add(low, low, mod_low); + __ add(high, high, mod_high); + __ add(c_i, c_i, low); + __ str(c_i, c_ptr); + __ add(c_i, tmp, high); + + // Limb 2 + __ ldr(low, Address(sp, 5 * BytesPerLong)); + __ ldr(high, Address(sp, 7 * BytesPerLong)); + __ ldr(tmp, Address(c_ptr, 3 * BytesPerLong)); + __ add(c_i, c_i, low_1); + __ str(c_i, Address(c_ptr, BytesPerLong)); + __ add(c_i, tmp, high_1); + + // Limb 3 + gpr_partial_mult_52(n, mod_3, mod_high, mod_low, limb_mask); + __ ldr(tmp, Address(c_ptr, 4 * BytesPerLong)); + __ add(low, low, mod_low); + __ add(high, high, mod_high); + __ add(c_i, c_i, low); + __ str(c_i, Address(c_ptr, 2 * BytesPerLong)); + __ add(c_i, tmp, high); + + // Limb 4 + __ ldr(low, Address(sp, 8 * BytesPerLong)); + __ ldr(high, Address(sp, 10 * BytesPerLong)); + gpr_partial_mult_52(a_i, b_4, high_1, low_1, limb_mask); + gpr_partial_mult_52(n, mod_4, mod_high, mod_low, limb_mask); + __ add(low_1, low_1, mod_low); + __ add(high_1, high_1, mod_high); + __ add(c_i, c_i, low_1); + __ str(c_i, Address(c_ptr, 3 * BytesPerLong)); + __ str(high_1, Address(c_ptr, 4 * BytesPerLong)); + + ////////////////////////////// + // a[4] + ////////////////////////////// + + Register c5 = *common_regs++, + c6 = *common_regs++, + c7 = *common_regs++; + + __ ldr(a_i, a); + __ ldr(c_i, c_ptr); + + // Limb 0 + __ ldr(low_1, Address(sp, 9 * BytesPerLong)); + __ ldr(high_1, Address(sp, 11 * BytesPerLong)); + + __ add(low, low, c_i); + __ ldr(c_i, Address(c_ptr, BytesPerLong)); + __ andr(n, low, limb_mask); + gpr_partial_mult_52(n, mod_0, mod_high, mod_low, limb_mask); + __ add(low, low, mod_low); + __ add(high, high, mod_high); + __ lsr(tmp, low, montMulP256Shift2); + __ add(c_i, c_i, tmp); + __ add(c_i, c_i, high); + + __ ldr(low, Address(sp, 12 * BytesPerLong)); + __ ldr(high, Address(sp, 14 * BytesPerLong)); + gpr_partial_mult_52(n, mod_1, mod_high, mod_low, limb_mask); + __ add(low_1, low_1, mod_low); + __ add(high_1, high_1, mod_high); + __ add(c5, c_i, low_1); + __ ldr(c_i, Address(c_ptr, 2 * BytesPerLong)); + __ lsr(tmp, c5, montMulP256Shift2); + __ add(c_i, c_i, tmp); + __ add(c_i, c_i, high_1); + + // Limb 2 + __ ldr(low_1, Address(sp, 13 * BytesPerLong)); + __ ldr(high_1, Address(sp, 15 * BytesPerLong)); + __ add(c6, c_i, low); + __ ldr(c_i, Address(c_ptr, 3 * BytesPerLong)); + __ lsr(tmp, c6, montMulP256Shift2); + __ add(c_i, c_i, tmp); + __ add(c_i, c_i, high); + + // Limb 3 + gpr_partial_mult_52(n, mod_3, mod_high, mod_low, limb_mask); + __ add(low_1, low_1, mod_low); + __ add(high_1, high_1, mod_high); + __ add(c7, c_i, low_1); + __ ldr(c_i, Address(c_ptr, 4 * BytesPerLong)); + __ lsr(tmp, c7, montMulP256Shift2); + __ add(c_i, c_i, tmp); + __ add(c_i, c_i, high_1); + + // Limb 4 + gpr_partial_mult_52(a_i, b_4, high, low, limb_mask); + gpr_partial_mult_52(n, mod_4, mod_high, mod_low, limb_mask); + __ add(low, low, mod_low); + __ add(high, high, mod_high); + + // Reallocate b_4 + common_regs = common_regs.remaining() + b_4; + b_4 = noreg; + + Register c8 = *common_regs++, + c9 = *common_regs++; + + __ add(c8, c_i, low); + __ lsr(c9, c8, montMulP256Shift2); + __ add(c9, c9, high); + + __ andr(c5, c5, limb_mask); + __ andr(c6, c6, limb_mask); + __ andr(c7, c7, limb_mask); + __ andr(c8, c8, limb_mask); + + ///////////////////////////// + // Final carry propagate + ///////////////////////////// + + // c0 = c5 - modulus[0]; + // c1 = c6 - modulus[1] + (c0 >> BITS_PER_LIMB); + // c0 &= LIMB_MASK; + // c2 = c7 + (c1 >> BITS_PER_LIMB); + // c1 &= LIMB_MASK; + // c3 = c8 - modulus[3] + (c2 >> BITS_PER_LIMB); + // c2 &= LIMB_MASK; + // c4 = c9 - modulus4] + (c3 >> BITS_PER_LIMB); + // c3 &= LIMB_MASK; + + // Free up all unused regs + common_regs = common_regs.remaining() + + c_ptr + low + high + mod_high + + mod_low + a_i + c_i + n + low_1 + high_1; + c_ptr = low = high = mod_high + = mod_low = a_i = c_i = n = low_1 = high_1 = noreg; + + Register c0 = *common_regs++, + c1 = *common_regs++, + c2 = *common_regs++, + c3 = *common_regs++, + c4 = *common_regs++; + + __ sub(c0, c5, mod_0); + __ sub(c1, c6, mod_1); + __ sub(c3, c8, mod_3); + __ sub(c4, c9, mod_4); + __ add(c1, c1, c0, Assembler::ASR, montMulP256Shift2); + __ andr(c0, c0, limb_mask); + __ add(c2, c7, c1, Assembler::ASR, montMulP256Shift2); + __ andr(c1, c1, limb_mask); + __ add(c3, c3, c2, Assembler::ASR, montMulP256Shift2); + __ andr(c2, c2, limb_mask); + __ add(c4, c4, c3, Assembler::ASR, montMulP256Shift2); + __ andr(c3, c3, limb_mask); + + // Final write back + // mask = c4 >> 63 + // r[0] = ((c5 & mask) | (c0 & ~mask)); + // r[1] = ((c6 & mask) | (c1 & ~mask)); + // r[2] = ((c7 & mask) | (c2 & ~mask)); + // r[3] = ((c8 & mask) | (c3 & ~mask)); + // r[4] = ((c9 & mask) | (c4 & ~mask)); + + common_regs = common_regs.remaining() + + mod_0 + mod_1 + mod_3 + mod_4; + mod_0 = mod_1 = mod_3 = mod_4 = noreg; + + Register mask = *common_regs++; + Register nmask = *common_regs++; + + __ asr(mask, c4, 63); + __ mvn(nmask, mask); + __ andr(c5, c5, mask); + __ andr(tmp, c0, nmask); + __ orr(c5, c5, tmp); + __ andr(c6, c6, mask); + __ andr(tmp, c1, nmask); + __ orr(c6, c6, tmp); + __ andr(c7, c7, mask); + __ andr(tmp, c2, nmask); + __ orr(c7, c7, tmp); + __ andr(c8, c8, mask); + __ andr(tmp, c3, nmask); + __ orr(c8, c8, tmp); + __ andr(c9, c9, mask); + __ andr(tmp, c4, nmask); + __ orr(c9, c9, tmp); + + __ str(c5, result); + __ str(c6, Address(result, BytesPerLong)); + __ str(c7, Address(result, 2 * BytesPerLong)); + __ str(c8, Address(result, 3 * BytesPerLong)); + __ str(c9, Address(result, 4 * BytesPerLong)); + + // End intrinsic call + __ add(sp, sp, cDataSize + mulDataSize); + __ pop(callee_saved, sp); + __ leave(); + __ mov(r0, zr); // return 0 + __ ret(lr); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + + return start; + } + + address generate_intpoly_assign() { + // KNOWN Lengths: + // MontgomeryIntPolynP256: 5 = 4 + 1 + // IntegerPolynomial1305: 5 = 4 + 1 + // IntegerPolynomial25519: 10 = 8 + 2 + // IntegerPolynomialP256: 10 = 8 + 2 + // Curve25519OrderField: 10 = 8 + 2 + // Curve25519OrderField: 10 = 8 + 2 + // P256OrderField: 10 = 8 + 2 + // IntegerPolynomialP384: 14 = 8 + 4 + 2 + // P384OrderField: 14 = 8 + 4 + 2 + // IntegerPolynomial448: 16 = 8 + 8 + // Curve448OrderField: 16 = 8 + 8 + // Curve448OrderField: 16 = 8 + 8 + // IntegerPolynomialP521: 19 = 8 + 8 + 2 + 1 + // P521OrderField: 19 = 8 + 8 + 2 + 1 + // Special Cases 5, 10, 14, 16, 19 + assert(UseIntPolyIntrinsics, "what are we doing here?"); + StubId stub_id = StubId::stubgen_intpoly_assign_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + + __ align(CodeEntryAlignment); + StubCodeMark mark(this, stub_id); + start = __ pc(); + __ enter(); + + // Inputs + const Register set = c_rarg0; + const Register aLimbs = c_rarg1; + const Register bLimbs = c_rarg2; + const Register length = c_rarg3; + + Label L_Length5, L_Length10, L_Length14, L_Length16, L_Length19, L_Default, L_Done; + + /* + int maskValue = -set; + for (int i = 0; i < a.length; i++) { + long dummyLimbs = maskValue & (a[i] ^ b[i]); + a[i] = dummyLimbs ^ a[i]; + } + */ + Register mask_scalar = r4; + FloatRegister mask_vec = v0; + + __ neg(mask_scalar, set); + __ dup(mask_vec, __ T2D, mask_scalar); + + __ cmp(length, (u1)5); + __ br(Assembler::EQ, L_Length5); + __ cmp(length, (u1)10); + __ br(Assembler::EQ, L_Length10); + __ cmp(length, (u1)14); + __ br(Assembler::EQ, L_Length14); + __ cmp(length, (u1)16); + __ br(Assembler::EQ, L_Length16); + __ cmp(length, (u1)19); + __ br(Assembler::EQ, L_Length19); + __ b(L_Default); + + + // Length = 5 + // Use 5 GPRs (neon not faster with this few limbs) + __ BIND(L_Length5); + { + Register a0 = r5; + Register a1 = r6; + Register a2 = r7; + Register a3 = r10; + Register a4 = r11; + Register b0 = r12; + Register b1 = r13; + Register b2 = r14; + Register b3 = r15; + Register b4 = r19; + + __ push(r19, sp); + + __ ldr(a0, aLimbs); + __ ldr(a1, Address(aLimbs, 1 * BytesPerLong)); + __ ldr(a2, Address(aLimbs, 2 * BytesPerLong)); + __ ldr(a3, Address(aLimbs, 3 * BytesPerLong)); + __ ldr(a4, Address(aLimbs, 4 * BytesPerLong)); + + __ ldr(b0, bLimbs); + __ ldr(b1, Address(bLimbs, 1 * BytesPerLong)); + __ ldr(b2, Address(bLimbs, 2 * BytesPerLong)); + __ ldr(b3, Address(bLimbs, 3 * BytesPerLong)); + __ ldr(b4, Address(bLimbs, 4 * BytesPerLong)); + + __ eor(b0, b0, a0); + __ eor(b1, b1, a1); + __ eor(b2, b2, a2); + __ eor(b3, b3, a3); + __ eor(b4, b4, a4); + + __ andr(b0, b0, mask_scalar); + __ andr(b1, b1, mask_scalar); + __ andr(b2, b2, mask_scalar); + __ andr(b3, b3, mask_scalar); + __ andr(b4, b4, mask_scalar); + + __ eor(a0, a0, b0); + __ eor(a1, a1, b1); + __ eor(a2, a2, b2); + __ eor(a3, a3, b3); + __ eor(a4, a4, b4); + + __ str(a0, aLimbs); + __ str(a1, Address(aLimbs, 1 * BytesPerLong)); + __ str(a2, Address(aLimbs, 2 * BytesPerLong)); + __ str(a3, Address(aLimbs, 3 * BytesPerLong)); + __ str(a4, Address(aLimbs, 4 * BytesPerLong)); + + __ pop(r19, sp); + __ b(L_Done); + } + + // Length = 10 + // Split into 4 neon regs and 2 GPRs + __ BIND(L_Length10); + { + Register a9 = r10; + Register a10 = r11; + Register b9 = r12; + Register b10 = r13; + + VSeq<4> a_vec(16); + VSeq<4> b_vec(20); + + __ ldr(a9, Address(aLimbs, 8 * BytesPerLong)); + __ ldr(a10, Address(aLimbs, 9 * BytesPerLong)); + __ ldr(b9, Address(bLimbs, 8 * BytesPerLong)); + __ ldr(b10, Address(bLimbs, 9 * BytesPerLong)); + + vs_ldpq(a_vec, aLimbs); + + __ eor(b9, b9, a9); + __ eor(b10, b10, a10); + + vs_ldpq(b_vec, bLimbs); + + __ andr(b9, b9, mask_scalar); + __ andr(b10, b10, mask_scalar); + + vs_eor(b_vec, b_vec, a_vec); + + __ eor(a9, a9, b9); + __ eor(a10, a10, b10); + + vs_andr(b_vec, b_vec, mask_vec); + + __ str(a9, Address(aLimbs, 8 * BytesPerLong)); + __ str(a10, Address(aLimbs, 9 * BytesPerLong)); + + vs_eor(a_vec, a_vec, b_vec); + vs_stpq_post(a_vec, aLimbs); + + __ b(L_Done); + } + + // Length = 14 + // Split into 5 neon regs and 4 GPRs + __ BIND(L_Length14); + { + Register a10 = r5; + Register a11 = r6; + Register a12 = r7; + Register a13 = r8; + Register b10 = r9; + Register b11 = r10; + Register b12 = r11; + Register b13 = r12; + + VSeq<5> a_vec(16); + VSeq<5> b_vec(22); + + int offsets[2] = { 0, 32 }; + + __ ldr(a10, Address(aLimbs, 10 * BytesPerLong)); + __ ldr(a11, Address(aLimbs, 11 * BytesPerLong)); + __ ldr(a12, Address(aLimbs, 12 * BytesPerLong)); + __ ldr(a13, Address(aLimbs, 13 * BytesPerLong)); + + __ ldr(b10, Address(bLimbs, 10 * BytesPerLong)); + __ ldr(b11, Address(bLimbs, 11 * BytesPerLong)); + __ ldr(b12, Address(bLimbs, 12 * BytesPerLong)); + __ ldr(b13, Address(bLimbs, 13 * BytesPerLong)); + + __ ld1(a_vec[0], __ T2D, aLimbs); + vs_ldpq_indexed(vs_tail(a_vec), aLimbs, 16, offsets); + + __ eor(b10, b10, a10); + __ eor(b11, b11, a11); + __ eor(b12, b12, a12); + __ eor(b13, b13, a13); + + __ ld1(b_vec[0], __ T2D, bLimbs); + vs_ldpq_indexed(vs_tail(b_vec), bLimbs, 16, offsets); + + __ andr(b10, b10, mask_scalar); + __ andr(b11, b11, mask_scalar); + __ andr(b12, b12, mask_scalar); + __ andr(b13, b13, mask_scalar); + + vs_eor(b_vec, b_vec, a_vec); + + __ eor(a10, a10, b10); + __ eor(a11, a11, b11); + __ eor(a12, a12, b12); + __ eor(a13, a13, b13); + + vs_andr(b_vec, b_vec, mask_vec); + + __ str(a10, Address(aLimbs, 10 * BytesPerLong)); + __ str(a11, Address(aLimbs, 11 * BytesPerLong)); + __ str(a12, Address(aLimbs, 12 * BytesPerLong)); + __ str(a13, Address(aLimbs, 13 * BytesPerLong)); + + vs_eor(a_vec, a_vec, b_vec); + + __ st1(a_vec[0], __ T2D, aLimbs); + vs_stpq_indexed(vs_tail(a_vec), aLimbs, 16, offsets); + + __ b(L_Done); + } + + // Length = 16 + // Use 8 neon regs + __ BIND(L_Length16); + { + VSeq<8> a_vec(16); + VSeq<8> b_vec(24); + + vs_ldpq(a_vec, aLimbs); + vs_ldpq(b_vec, bLimbs); + vs_eor(b_vec, b_vec, a_vec); + vs_andr(b_vec, b_vec, mask_vec); + vs_eor(a_vec, a_vec, b_vec); + vs_stpq_post(a_vec, aLimbs); + + __ b(L_Done); + } + + // Length = 19 + // Split into 8 neon regs and 3 GPRs + __ BIND(L_Length19); + { + Register a17 = r10; + Register a18 = r11; + Register a19 = r12; + Register b17 = r13; + Register b18 = r14; + Register b19 = r15; + + VSeq<8> a_vec(16); + VSeq<8> b_vec(24); + + __ ldr(a17, Address(aLimbs, 16 * BytesPerLong)); + __ ldr(a18, Address(aLimbs, 17 * BytesPerLong)); + __ ldr(a19, Address(aLimbs, 18 * BytesPerLong)); + __ ldr(b17, Address(bLimbs, 16 * BytesPerLong)); + __ ldr(b18, Address(bLimbs, 17 * BytesPerLong)); + __ ldr(b19, Address(bLimbs, 18 * BytesPerLong)); + + vs_ldpq(a_vec, aLimbs); + + __ eor(b17, b17, a17); + __ eor(b18, b18, a18); + __ eor(b19, b19, a19); + + vs_ldpq(b_vec, bLimbs); + + __ andr(b17, b17, mask_scalar); + __ andr(b18, b18, mask_scalar); + __ andr(b19, b19, mask_scalar); + + vs_eor(b_vec, b_vec, a_vec); + + __ eor(a17, a17, b17); + __ eor(a18, a18, b18); + __ eor(a19, a19, b19); + + vs_andr(b_vec, b_vec, mask_vec); + + __ str(a17, Address(aLimbs, 16 * BytesPerLong)); + __ str(a18, Address(aLimbs, 17 * BytesPerLong)); + __ str(a19, Address(aLimbs, 18 * BytesPerLong)); + + vs_eor(a_vec, a_vec, b_vec); + vs_stpq_post(a_vec, aLimbs); + + __ b(L_Done); + } + + __ BIND(L_Default); + { + Register ctr = r5; + Register a_val = r6; + Register b_val = r7; + + __ mov(ctr, length); // length (the number of limbs) is never 0 + + Label default_loop; + __ BIND(default_loop); + + __ ldr(a_val, aLimbs); + __ ldr(b_val, __ post(bLimbs, 8)); + __ eor(b_val, b_val, a_val); + __ andr(b_val, b_val, mask_scalar); + __ eor(a_val, a_val, b_val); + __ str(a_val, __ post(aLimbs, 8)); + __ sub(ctr, ctr, 1); + __ cmp(ctr, (u1)0); + __ br(Assembler::NE, default_loop); + } + + __ BIND(L_Done); + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ mov(r0, zr); // return 0 + __ ret(lr); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + + return start; + } + void bcax5(Register a0, Register a1, Register a2, Register a3, Register a4, Register tmp0, Register tmp1, Register tmp2) { __ bic(tmp0, a2, a1); // for a0 @@ -12734,6 +13714,11 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_chacha20Block = generate_chacha20Block_blockpar(); } + if (UseIntPolyIntrinsics) { + StubRoutines::_intpoly_montgomeryMult_P256 = generate_intpoly_montgomeryMult_P256(); + StubRoutines::_intpoly_assign = generate_intpoly_assign(); + } + if (UseKyberIntrinsics) { StubRoutines::_kyberNtt = generate_kyberNtt(); StubRoutines::_kyberInverseNtt = generate_kyberInverseNtt(); @@ -12846,6 +13831,7 @@ class StubGenerator: public StubCodeGenerator { ADD(_sha512_round_consts); ADD(_sha3_round_consts); ADD(_double_keccak_round_consts); + ADD(_modulus_P256); ADD(_encodeBlock_toBase64); ADD(_encodeBlock_toBase64URL); ADD(_decodeBlock_fromBase64ForNoSIMD); diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index d1cf8b6feed..e746447e013 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -454,6 +454,10 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseChaCha20Intrinsics, false); } + if (FLAG_IS_DEFAULT(UseIntPolyIntrinsics)) { + UseIntPolyIntrinsics = true; + } + if (supports_feature(CPU_ASIMD)) { if (FLAG_IS_DEFAULT(UseKyberIntrinsics)) { UseKyberIntrinsics = true; diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp index 777ada59a0b..448bab6fbc2 100644 --- a/src/hotspot/share/code/aotCodeCache.hpp +++ b/src/hotspot/share/code/aotCodeCache.hpp @@ -299,6 +299,7 @@ class AOTStubData : public StackObj { do_var(bool, UseSHA256Intrinsics) \ do_var(bool, UseSHA3Intrinsics) \ do_var(bool, UseSHA512Intrinsics) \ + do_var(bool, UseIntPolyIntrinsics) \ do_var(bool, UseVectorizedMismatchIntrinsic) \ do_fun(int, CompressedKlassPointers_shift, CompressedKlassPointers::shift()) \ do_fun(bool, JavaAssertions_systemClassDefault, JavaAssertions::systemClassDefault()) \ @@ -342,7 +343,6 @@ class AOTStubData : public StackObj { do_var(int, AVX3Threshold) /* array copy stubs and nmethods */ \ do_var(bool, EnableX86ECoreOpts) /* nmethods */ \ do_var(bool, UseLibmIntrinsic) \ - do_var(bool, UseIntPolyIntrinsics) \ // END #else #define AOTCODECACHE_CONFIGS_X86_DO(do_var, do_fun) From 24a724f0fe5ca96e4f078244191e88fcea68e86c Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Wed, 24 Jun 2026 10:28:22 +0000 Subject: [PATCH 056/707] 8382338: Various serviceability agent tests fail on Linux x86_64 with LTO enabled Reviewed-by: lucy, erikj --- make/hotspot/lib/JvmFeatures.gmk | 4 ++++ src/hotspot/share/oops/metadata.hpp | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/make/hotspot/lib/JvmFeatures.gmk b/make/hotspot/lib/JvmFeatures.gmk index 9477b0925d2..7dc5fd676a1 100644 --- a/make/hotspot/lib/JvmFeatures.gmk +++ b/make/hotspot/lib/JvmFeatures.gmk @@ -173,6 +173,10 @@ ifeq ($(call check-jvm-feature, link-time-opt), true) ifneq ($(call isCompiler, microsoft), true) JVM_LDFLAGS_FEATURES += $(CXX_O_FLAG_HIGHEST_JVM) endif + # avoid elimination of Metadata vtable when using LTO (important for serviceability agent) + ifeq ($(call isCompiler, gcc), true) + JVM_LDFLAGS_FEATURES += -Wl,--undefined=_ZTV8Metadata + endif else JVM_LTO := false ifeq ($(call isCompiler, gcc), true) diff --git a/src/hotspot/share/oops/metadata.hpp b/src/hotspot/share/oops/metadata.hpp index d88bd9d087d..bd3f17fa3f4 100644 --- a/src/hotspot/share/oops/metadata.hpp +++ b/src/hotspot/share/oops/metadata.hpp @@ -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 @@ -35,6 +35,11 @@ class Metadata : public MetaspaceObj { NOT_PRODUCT(int _valid;) public: NOT_PRODUCT(Metadata() : _valid(0) {}) + + // We have to keep the vtable alive under LTGC dead-section removal/LTO + // for serviceability tests to work. + // This can be done by linker settings or modifications to the Metadata class. + NOT_PRODUCT(bool is_valid() const { return _valid == 0; }) int identity_hash() { return (int)(uintptr_t)this; } From 0da5c1ddab7952b08de588d71341bc3065d02c54 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Wed, 24 Jun 2026 10:39:44 +0000 Subject: [PATCH 057/707] 8387074: Remove duplicate handling of sparc in platform.m4 Reviewed-by: erikj, djelinski --- make/autoconf/platform.m4 | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/make/autoconf/platform.m4 b/make/autoconf/platform.m4 index 90d5d795626..28aea489f7e 100644 --- a/make/autoconf/platform.m4 +++ b/make/autoconf/platform.m4 @@ -174,18 +174,6 @@ AC_DEFUN([PLATFORM_EXTRACT_VARS_FROM_CPU], VAR_CPU_BITS=64 VAR_CPU_ENDIAN=big ;; - sparc) - VAR_CPU=sparc - VAR_CPU_ARCH=sparc - VAR_CPU_BITS=32 - VAR_CPU_ENDIAN=big - ;; - sparcv9|sparc64) - VAR_CPU=sparcv9 - VAR_CPU_ARCH=sparc - VAR_CPU_BITS=64 - VAR_CPU_ENDIAN=big - ;; *) AC_MSG_ERROR([unsupported cpu $1]) ;; From 0c709fdcf2f43719b82f6daffdfba092cce08a73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Sj=C3=B6len?= Date: Wed, 24 Jun 2026 13:19:39 +0000 Subject: [PATCH 058/707] 8378049: test/hotspot/jtreg/runtime/NMT/NMTPrintMallocSiteOfCorruptedMemory.java failing on Windows Reviewed-by: dsimms, syan, cnorrbin --- .../NMT/NMTPrintMallocSiteOfCorruptedMemory.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/runtime/NMT/NMTPrintMallocSiteOfCorruptedMemory.java b/test/hotspot/jtreg/runtime/NMT/NMTPrintMallocSiteOfCorruptedMemory.java index f1d4964d6cf..f9c0d8a1dc3 100644 --- a/test/hotspot/jtreg/runtime/NMT/NMTPrintMallocSiteOfCorruptedMemory.java +++ b/test/hotspot/jtreg/runtime/NMT/NMTPrintMallocSiteOfCorruptedMemory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -34,6 +34,7 @@ */ import jdk.test.lib.Utils; +import jdk.test.lib.Platform; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.whitebox.WhiteBox; @@ -70,7 +71,12 @@ private static void runThisTestWith(String arg) throws Exception { case HEADER_AND_SITE_ARG, FOOTER_AND_SITE_ARG -> output.shouldContain("allocation-site cannot be shown since the marker is also corrupted."); case HEADER_ARG, FOOTER_ARG -> { output.shouldContain("allocated from:"); - output.shouldMatch("\\[.*\\]WB_NMTMalloc\\+0x.*"); + // We will only have this if NMT can determine the name of the symbols in the stack trace. + // This will most likely be true if the platform is Linux and it's a debug build, + // so we only check it for that platform and build. + if (Platform.isLinux() && Platform.isDebugBuild()) { + output.shouldMatch("\\[.*\\]WB_NMTMalloc\\+0x.*"); + } } } } From becdbb1496a1e49f4a23ca9e095d14797180ef2d Mon Sep 17 00:00:00 2001 From: Phil Race Date: Wed, 24 Jun 2026 15:44:37 +0000 Subject: [PATCH 059/707] 8386671: Raster factory methods fail to throw specified exceptions for invalid bandOffsets and bankIndices Reviewed-by: azvegint, kizune, jdv --- .../share/classes/java/awt/image/Raster.java | 30 +++++++++++++++++++ .../Raster/CreateRasterExceptionTest.java | 22 ++++++++++++-- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/java.desktop/share/classes/java/awt/image/Raster.java b/src/java.desktop/share/classes/java/awt/image/Raster.java index 8f35d5819ab..6ae4214b702 100644 --- a/src/java.desktop/share/classes/java/awt/image/Raster.java +++ b/src/java.desktop/share/classes/java/awt/image/Raster.java @@ -308,6 +308,13 @@ public static WritableRaster createInterleavedRaster(int dataType, if (bandOffsets == null) { throw new NullPointerException("bandOffsets is null"); } + for (int i = 0; i < bandOffsets.length; i++) { + int off = bandOffsets[i]; + if ((off > pixelStride) || (off > scanlineStride)) { + throw new IllegalArgumentException("Band offset " + off + " is too large for stride"); + } + } + lsz = (long)w * pixelStride; if (lsz > scanlineStride) { throw new IllegalArgumentException("w * pixelStride is too large"); @@ -803,6 +810,21 @@ public static WritableRaster createInterleavedRaster(DataBuffer dataBuffer, if (dataBuffer == null) { throw new NullPointerException("DataBuffer cannot be null"); } + if (pixelStride < 0) { + throw new IllegalArgumentException("pixelStride is < 0"); + } + if (scanlineStride < 0) { + throw new IllegalArgumentException("scanlineStride is < 0"); + } + if (bandOffsets == null) { + throw new NullPointerException("bandOffsets is null"); + } + for (int i = 0; i < bandOffsets.length; i++) { + int off = bandOffsets[i]; + if ((off > pixelStride) || (off > scanlineStride)) { + throw new IllegalArgumentException("Band offset " + off + " is too large for stride"); + } + } if (location == null) { location = new Point(0, 0); @@ -914,6 +936,14 @@ public static WritableRaster createBandedRaster(DataBuffer dataBuffer, "bankIndices.length != bandOffsets.length"); } + int numBanks = dataBuffer.getNumBanks(); + for (int i = 0; i < bands; i++) { + if (bankIndices[i] >= numBanks) { + throw new ArrayIndexOutOfBoundsException("Bank[" + i + "] == " + bankIndices[i] + + " and there are only " + numBanks + " banks."); + } + } + if (location == null) { location = new Point(0,0); } else { diff --git a/test/jdk/java/awt/image/Raster/CreateRasterExceptionTest.java b/test/jdk/java/awt/image/Raster/CreateRasterExceptionTest.java index 13a88e8c590..aa9d8ff4156 100644 --- a/test/jdk/java/awt/image/Raster/CreateRasterExceptionTest.java +++ b/test/jdk/java/awt/image/Raster/CreateRasterExceptionTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8255800 8369129 8376297 + * @bug 8255800 8369129 8376297 8386671 * @summary verify Raster + SampleModel creation vs spec. */ @@ -931,9 +931,10 @@ static void bandedRasterTests3() { /* @throws ArrayIndexOutOfBoundsException if any element of {@code bankIndices} * is greater or equal to the number of bands in {@code dataBuffer} */ + DataBuffer dBuffer2Bands = new DataBufferByte(15, 2); int[] indices = new int[] { 0, 1, 2 }; int[] offsets = new int[] { 0, 0, 0 }; - Raster.createBandedRaster(dBuffer, 1, 1, 1, + Raster.createBandedRaster(dBuffer2Bands, 1, 1, 1, indices, offsets, null); noException(); } catch (ArrayIndexOutOfBoundsException t) { @@ -1198,6 +1199,21 @@ static void interleavedRasterTests2() { "Got expected exception for bad databuffer type"); System.out.println(t); } + + try { + /* @throws IllegalArgumentException if any element of {@code bandOffsets} is greater + * than {@code pixelStride} or the {@code scanlineStride} + */ + int[] offsets = new int[] {2}; + Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, + 1, 1, 1, 1, offsets, null); + noException(); + } catch (IllegalArgumentException t) { + System.out.println( + "Got expected exception for element too large"); + System.out.println(t); + } + } /* createInterleavedRaster(DataBuffer dBuffer, @@ -1344,7 +1360,7 @@ static void interleavedRasterTests3() { /* @throws IllegalArgumentException if any element of {@code bandOffsets} is greater * than {@code pixelStride} or the {@code scanlineStride} */ - int[] offsets = new int[] { 0, 1, 2}; + int[] offsets = new int[] {2}; Raster.createInterleavedRaster(dBuffer, 1, 1, 1, 1, offsets, null); noException(); From 5cb61efa3caee62502fbb913a756fd16ddecfacc Mon Sep 17 00:00:00 2001 From: Phil Race Date: Wed, 24 Jun 2026 15:59:01 +0000 Subject: [PATCH 060/707] 8041911: media sizes with width > height are not supported by the java printing api Reviewed-by: jdv, azvegint --- .../standard/MediaPrintableArea.java | 4 +- .../print/attribute/standard/MediaSize.java | 22 +++------- .../standard/OrientationRequested.java | 13 +++--- .../sun/print/CustomMediaSizeName.java | 9 +--- .../javax/print/attribute/MediaSizeTest.java | 41 +++++++++++++++++++ 5 files changed, 55 insertions(+), 34 deletions(-) create mode 100644 test/jdk/javax/print/attribute/MediaSizeTest.java diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaPrintableArea.java b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaPrintableArea.java index a9cc2bba195..d03194a19c5 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaPrintableArea.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaPrintableArea.java @@ -66,9 +66,7 @@ *

* The rectangular printable area is defined thus: The (x,y) origin is * positioned at the top-left of the paper in portrait mode regardless of the - * orientation specified in the requesting context. For example a printable area - * for A4 paper in portrait or landscape orientation will have height - * {@literal >} width. + * orientation specified in the requesting context. *

* A printable area attribute's values are stored internally as integers in * units of micrometers (µm), where 1 micrometer = 10-6 meter = diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaSize.java b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaSize.java index 57c0d305809..b5ea11cf4c6 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaSize.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaSize.java @@ -36,7 +36,7 @@ * Class {@code MediaSize} is a two-dimensional size valued printing attribute * class that indicates the dimensions of the medium in a portrait orientation, * with the {@code X} dimension running along the bottom edge and the {@code Y} - * dimension running along the left edge. Thus, the {@code Y} dimension must be + * dimension running along the left edge. For most media, the {@code Y} dimension will be * greater than or equal to the {@code X} dimension. Class {@code MediaSize} * declares many standard media size values, organized into nested classes for * ISO, JIS, North American, engineering, and other media. @@ -77,13 +77,10 @@ public class MediaSize extends Size2DSyntax implements Attribute { * @param units unit conversion factor, e.g. {@code Size2DSyntax.INCH} or * {@code Size2DSyntax.MM} * @throws IllegalArgumentException if {@code x < 0} or {@code y < 0} or - * {@code units < 1} or {@code x > y} + * {@code units < 1} */ public MediaSize(float x, float y,int units) { super (x, y, units); - if (x > y) { - throw new IllegalArgumentException("X dimension > Y dimension"); - } sizeVector.add(this); } @@ -95,13 +92,10 @@ public MediaSize(float x, float y,int units) { * @param units unit conversion factor, e.g. {@code Size2DSyntax.INCH} or * {@code Size2DSyntax.MM} * @throws IllegalArgumentException if {@code x < 0} or {@code y < 0} or - * {@code units < 1} or {@code x > y} + * {@code units < 1} */ public MediaSize(int x, int y,int units) { super (x, y, units); - if (x > y) { - throw new IllegalArgumentException("X dimension > Y dimension"); - } sizeVector.add(this); } @@ -115,13 +109,10 @@ public MediaSize(int x, int y,int units) { * {@code Size2DSyntax.MM} * @param media a media name to associate with this {@code MediaSize} * @throws IllegalArgumentException if {@code x < 0} or {@code y < 0} or - * {@code units < 1} or {@code x > y} + * {@code units < 1} */ public MediaSize(float x, float y,int units, MediaSizeName media) { super (x, y, units); - if (x > y) { - throw new IllegalArgumentException("X dimension > Y dimension"); - } if (media != null && mediaMap.get(media) == null) { mediaName = media; mediaMap.put(mediaName, this); @@ -138,13 +129,10 @@ public MediaSize(float x, float y,int units, MediaSizeName media) { * {@code Size2DSyntax.MM} * @param media a media name to associate with this {@code MediaSize} * @throws IllegalArgumentException if {@code x < 0} or {@code y < 0} or - * {@code units < 1} or {@code x > y} + * {@code units < 1} */ public MediaSize(int x, int y,int units, MediaSizeName media) { super (x, y, units); - if (x > y) { - throw new IllegalArgumentException("X dimension > Y dimension"); - } if (media != null && mediaMap.get(media) == null) { mediaName = media; mediaMap.put(mediaName, this); diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/OrientationRequested.java b/src/java.desktop/share/classes/javax/print/attribute/standard/OrientationRequested.java index 00e3de4a1de..26885fc1a7d 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/OrientationRequested.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/OrientationRequested.java @@ -73,16 +73,17 @@ public final class OrientationRequested extends EnumSyntax private static final long serialVersionUID = -4447437289862822276L; /** - * The content will be imaged across the short edge of the medium. + * The content will be imaged across the default orientation of the medium. + * For almost all media this means across the short edge. */ public static final OrientationRequested PORTRAIT = new OrientationRequested(3); /** - * The content will be imaged across the long edge of the medium. Landscape - * is defined to be a rotation of the print-stream page to be imaged by +90 + * Landscape is defined to be a rotation of the print-stream page to be imaged by +90 * degrees with respect to the medium (i.e. anti-clockwise) from the - * portrait orientation. Note: The +90 direction was chosen because + * portrait orientation. For almost all media this means across the long edge. + * Note: The +90 direction was chosen because * simple finishing on the long edge is the same edge whether portrait or * landscape. */ @@ -90,7 +91,7 @@ public final class OrientationRequested extends EnumSyntax LANDSCAPE = new OrientationRequested(4); /** - * The content will be imaged across the long edge of the medium, but in the + * The content will be imaged similarly, but in the * opposite manner from landscape. Reverse-landscape is defined to be a * rotation of the print-stream page to be imaged by -90 degrees with * respect to the medium (i.e. clockwise) from the portrait orientation. @@ -102,7 +103,7 @@ public final class OrientationRequested extends EnumSyntax REVERSE_LANDSCAPE = new OrientationRequested(5); /** - * The content will be imaged across the short edge of the medium, but in + * The content will be imaged similarly to, but in * the opposite manner from portrait. Reverse-portrait is defined to be a * rotation of the print-stream page to be imaged by 180 degrees with * respect to the medium from the portrait orientation. Note: The diff --git a/src/java.desktop/share/classes/sun/print/CustomMediaSizeName.java b/src/java.desktop/share/classes/sun/print/CustomMediaSizeName.java index 04772f29858..d5173c181c4 100644 --- a/src/java.desktop/share/classes/sun/print/CustomMediaSizeName.java +++ b/src/java.desktop/share/classes/sun/print/CustomMediaSizeName.java @@ -206,14 +206,7 @@ public static CustomMediaSizeName create(String name, String choice, if (value.getStandardMedia() == null) { // add this new custom media size name to MediaSize array if ((width > 0.0) && (length > 0.0)) { - try { - new MediaSize(width, length, Size2DSyntax.INCH, value); - } catch (IllegalArgumentException e) { - /* PDF printer in Linux for Ledger paper causes - "IllegalArgumentException: X dimension > Y dimension". - We rotate based on IPP spec. */ - new MediaSize(length, width, Size2DSyntax.INCH, value); - } + new MediaSize(width, length, Size2DSyntax.INCH, value); } } } diff --git a/test/jdk/javax/print/attribute/MediaSizeTest.java b/test/jdk/javax/print/attribute/MediaSizeTest.java new file mode 100644 index 00000000000..91d8ed19178 --- /dev/null +++ b/test/jdk/javax/print/attribute/MediaSizeTest.java @@ -0,0 +1,41 @@ +/* + * 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 8041911 + * @summary Test that MediaSize with non-standard portrait dimensions is OK +*/ + +import javax.print.attribute.standard.MediaSize; +import static javax.print.attribute.standard.MediaSize.INCH; + +public class MediaSizeTest { + + public static void main(String args[]) { + new MediaSize(0, 0, INCH); + new MediaSize(1, 1, INCH); + new MediaSize(2, 1, INCH); + new MediaSize(1, 2, INCH); + } +} From ae7cced578f20fd973a77122a2b35af88e221136 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Wed, 24 Jun 2026 16:13:39 +0000 Subject: [PATCH 061/707] 8387025: Typo in java man page option --illegal-final-field-mutation=warn "performaed" Reviewed-by: alanb, aivanov, prr --- src/java.base/share/man/java.md | 2 +- test/jdk/java/awt/event/helpers/lwcomponents/LWButton.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/java.base/share/man/java.md b/src/java.base/share/man/java.md index ef99084018d..30f018314e5 100644 --- a/src/java.base/share/man/java.md +++ b/src/java.base/share/man/java.md @@ -483,7 +483,7 @@ the JVM. without any warnings. - `warn`: This mode is identical to `allow` except that a warning message is - issued for the first illegal final field mutation performaed in a module. + issued for the first illegal final field mutation performed in a module. This mode is the default for the current JDK but will change in a future release. diff --git a/test/jdk/java/awt/event/helpers/lwcomponents/LWButton.java b/test/jdk/java/awt/event/helpers/lwcomponents/LWButton.java index 8d132825de0..b4505bbda97 100644 --- a/test/jdk/java/awt/event/helpers/lwcomponents/LWButton.java +++ b/test/jdk/java/awt/event/helpers/lwcomponents/LWButton.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -63,7 +63,7 @@ public class LWButton extends LWComponent { private transient ActionListener actionListener; /* - * The action to be performaed once a button has been + * The action to be performed once a button has been * pressed. * actionCommand can be null. * @serial From 3e17dc9eb1ac0f8b3d04311a614a34c21f4d2b77 Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Wed, 24 Jun 2026 16:55:55 +0000 Subject: [PATCH 062/707] 8386922: Convert TraceRelocator to Unified Logging Reviewed-by: matsaave, jsjolen --- src/hotspot/share/logging/logTag.hpp | 1 + src/hotspot/share/runtime/globals.hpp | 4 - src/hotspot/share/runtime/relocator.cpp | 53 ++++++------ .../jtreg/runtime/logging/RelocatorTest.java | 84 +++++++++++++++++++ 4 files changed, 111 insertions(+), 31 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/logging/RelocatorTest.java diff --git a/src/hotspot/share/logging/logTag.hpp b/src/hotspot/share/logging/logTag.hpp index 15f45d04af3..0e31a371137 100644 --- a/src/hotspot/share/logging/logTag.hpp +++ b/src/hotspot/share/logging/logTag.hpp @@ -175,6 +175,7 @@ class outputStream; LOG_TAG(refine) \ LOG_TAG(region) \ LOG_TAG(reloc) \ + LOG_TAG(relocator) \ LOG_TAG(remset) \ LOG_TAG(resolve) \ LOG_TAG(safepoint) \ diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index ec34305f837..dd89a3847c7 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -253,10 +253,6 @@ const int ObjectAlignmentInBytes = 8; develop(bool, TracePcPatching, false, \ "Trace usage of frame::patch_pc") \ \ - develop(bool, TraceRelocator, false, \ - "Trace the bytecode relocator") \ - \ - \ product(bool, SafepointALot, false, DIAGNOSTIC, \ "Generate a lot of safepoints. This works with " \ "GuaranteedSafepointInterval") \ diff --git a/src/hotspot/share/runtime/relocator.cpp b/src/hotspot/share/runtime/relocator.cpp index ecccea2fbe7..0da545c7ef9 100644 --- a/src/hotspot/share/runtime/relocator.cpp +++ b/src/hotspot/share/runtime/relocator.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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,6 +24,8 @@ #include "classfile/stackMapTableFormat.hpp" #include "interpreter/bytecodes.hpp" +#include "logging/logStream.hpp" +#include "logging/logTag.hpp" #include "memory/metadataFactory.hpp" #include "memory/oopFactory.hpp" #include "oops/method.inline.hpp" @@ -50,13 +52,13 @@ class ChangeItem : public ResourceObj { virtual bool is_switch_pad() { return false; } // accessors - int bci() { return _bci; } + int bci() const { return _bci; } void relocate(int break_bci, int delta) { if (_bci > break_bci) { _bci += delta; } } virtual bool adjust(int bci, int delta) { return false; } // debug - virtual void print() = 0; + virtual void print_on(outputStream* st) const = 0; }; class ChangeWiden : public ChangeItem { @@ -71,7 +73,7 @@ class ChangeWiden : public ChangeItem { // Callback to do instruction bool handle_code_change(Relocator *r) { return r->handle_widen(bci(), _new_ilen, _inst_buffer); }; - void print() { tty->print_cr("ChangeWiden. bci: %d New_ilen: %d", bci(), _new_ilen); } + void print_on(outputStream* st) const { st->print_cr("ChangeWiden. bci: %d New_ilen: %d", bci(), _new_ilen); } }; class ChangeJumpWiden : public ChangeItem { @@ -94,7 +96,7 @@ class ChangeJumpWiden : public ChangeItem { return false; } - void print() { tty->print_cr("ChangeJumpWiden. bci: %d Delta: %d", bci(), _delta); } + void print_on(outputStream* st) const { st->print_cr("ChangeJumpWiden. bci: %d Delta: %d", bci(), _delta); } }; class ChangeSwitchPad : public ChangeItem { @@ -113,7 +115,9 @@ class ChangeSwitchPad : public ChangeItem { int padding() { return _padding; } bool is_lookup_switch() { return _is_lookup_switch; } - void print() { tty->print_cr("ChangeSwitchPad. bci: %d Padding: %d IsLookupSwitch: %d", bci(), _padding, _is_lookup_switch); } + void print_on(outputStream* st) const { + st->print_cr("ChangeSwitchPad. bci: %d Padding: %d IsLookupSwitch: %d", bci(), _padding, _is_lookup_switch); + } }; //----------------------------------------------------------------------------------------------------------- @@ -140,11 +144,10 @@ methodHandle Relocator::insert_space_at(int bci, int size, u_char inst_buffer[], _changes = new GrowableArray (10); _changes->push(new ChangeWiden(bci, size, inst_buffer)); - if (TraceRelocator) { - tty->print_cr("Space at: %d Size: %d", bci, size); - _method->print(); - _method->print_codes(); - tty->print_cr("-------------------------------------------------"); + if (const LogTarget(Debug, relocator) out; out.is_enabled()) { + LogStream ls(out); + ls.print_cr("Space at: %d Size: %d", bci, size); + _method->print_value_on(&ls); } if (!handle_code_changes()) return methodHandle(); @@ -160,13 +163,7 @@ methodHandle Relocator::insert_space_at(int bci, int size, u_char inst_buffer[], ClassLoaderData* loader_data = method()->method_holder()->class_loader_data(); loader_data->add_to_deallocate_list(method()()); - set_method(new_method); - - if (TraceRelocator) { - tty->print_cr("-------------------------------------------------"); - tty->print_cr("new method"); - _method->print_codes(); - } + set_method(new_method); return new_method; } @@ -179,8 +176,9 @@ bool Relocator::handle_code_changes() { // Inv: everything is aligned. ChangeItem* ci = _changes->first(); - if (TraceRelocator) { - ci->print(); + if (const LogTarget(Trace, relocator) out; out.is_enabled()) { + LogStream ls(out); + ci->print_on(&ls); } // Execute operation @@ -407,13 +405,13 @@ void Relocator::adjust_exception_table(int bci, int delta) { } } -static void print_linenumber_table(unsigned char* table) { +static void print_linenumber_table(outputStream* ls, unsigned char* table) { CompressedLineNumberReadStream stream(table); - tty->print_cr("-------------------------------------------------"); + ls->print_cr("-------------------------------------------------"); while (stream.read_pair()) { - tty->print_cr(" - line %d: %d", stream.line(), stream.bci()); + ls->print_cr(" - line %d: %d", stream.line(), stream.bci()); } - tty->print_cr("-------------------------------------------------"); + ls->print_cr("-------------------------------------------------"); } // The width of instruction at "bci" is changing by "delta". Adjust the line number table. @@ -433,9 +431,10 @@ void Relocator::adjust_line_no_table(int bci, int delta) { writer.write_terminator(); set_compressed_line_number_table(writer.buffer()); set_compressed_line_number_table_size(writer.position()); - if (TraceRelocator) { - tty->print_cr("Adjusted line number table"); - print_linenumber_table(compressed_line_number_table()); + if (LogMessage(relocator) out; out.is_trace()) { + NonInterleavingLogStream ls(LogLevelType::Trace, out); + ls.print_cr("Adjusted line number table"); + print_linenumber_table(&ls, compressed_line_number_table()); } } } diff --git a/test/hotspot/jtreg/runtime/logging/RelocatorTest.java b/test/hotspot/jtreg/runtime/logging/RelocatorTest.java new file mode 100644 index 00000000000..edbe71f2638 --- /dev/null +++ b/test/hotspot/jtreg/runtime/logging/RelocatorTest.java @@ -0,0 +1,84 @@ +/* + * 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 8197901 8209758 + * @summary Log relocation in class redefinition. + * @library /test/lib + * @modules java.compiler + * java.instrument + * @requires vm.jvmti + * @run main RedefineClassHelper + * @run driver RelocatorTest + */ + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +// package access top-level class to avoid problem with RedefineClassHelper +// and nested types. +class RelocatorTest_B { + public static void test() { + System.out.println("Old class"); + } +} + +public class RelocatorTest { + public static class InternalClass { + public static String newB = + "class RelocatorTest_B {" + + " public static void test() { " + + " System.out.println(\"New class\");" + + " System.out.println(\"Need more ldc's in this class\");" + + " System.out.println(\"Another ldc\");" + + " }" + + "}"; + + public static void main(String[] args) throws Exception { + RelocatorTest_B.test(); + RedefineClassHelper.redefineClass(RelocatorTest_B.class, newB); + RelocatorTest_B.test(); + } + } + + public static void main(String[] args) throws Exception { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("-javaagent:redefineagent.jar", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+StressLdcRewrite", + "-Xlog:relocator=trace,redefine+class+constantpool=trace", + InternalClass.class.getName()); + OutputAnalyzer output = ProcessTools.executeProcess(pb); + output.shouldContain("Old class"); + output.shouldMatch("\\[debug\\]\\[relocator *\\] Space at: 3 Size: 3"); + output.shouldMatch("\\[debug\\]\\[relocator *\\] \\{method\\} .* 'test' '\\(\\)V' in 'RelocatorTest_B'"); + output.shouldMatch("\\[trace\\]\\[relocator *\\] ChangeWiden. bci: 3 New_ilen: 3"); + output.shouldMatch("\\[debug\\]\\[relocator *\\] Space at: 12 Size: 3"); + output.shouldMatch("\\[debug\\]\\[relocator *\\] \\{method\\} .* 'test' '\\(\\)V' in 'RelocatorTest_B'"); + output.shouldMatch("\\[trace\\]\\[relocator *\\] ChangeWiden. bci: 12 New_ilen: 3"); + output.shouldMatch("\\[debug\\]\\[relocator *\\] Space at: 21 Size: 3"); + output.shouldMatch("\\[debug\\]\\[relocator *\\] \\{method\\} .* 'test' '\\(\\)V' in 'RelocatorTest_B'"); + output.shouldContain("New class"); + output.shouldHaveExitValue(0); + } +} From e8aaa9e59cd35ae6225c065cfea7af834db5fdef Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Wed, 24 Jun 2026 17:37:11 +0000 Subject: [PATCH 063/707] 8387214: TraceJavaAssertions is unused Reviewed-by: jsjolen, shade --- .../share/classfile/javaAssertions.cpp | 20 +------------------ .../share/classfile/javaAssertions.hpp | 9 +-------- src/hotspot/share/runtime/globals.hpp | 3 --- 3 files changed, 2 insertions(+), 30 deletions(-) diff --git a/src/hotspot/share/classfile/javaAssertions.cpp b/src/hotspot/share/classfile/javaAssertions.cpp index 2a8f94d81b9..d2781e6e5e1 100644 --- a/src/hotspot/share/classfile/javaAssertions.cpp +++ b/src/hotspot/share/classfile/javaAssertions.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -83,13 +83,6 @@ void JavaAssertions::addOption(const char* name, bool enable) { if (name_copy[i] == JVM_SIGNATURE_DOT) name_copy[i] = JVM_SIGNATURE_SLASH; } - if (TraceJavaAssertions) { - tty->print_cr("JavaAssertions: adding %s %s=%d", - head == &_classes ? "class" : "package", - name_copy[0] != '\0' ? name_copy : "'default'", - enable); - } - // Prepend a new item to the list. Items added later take precedence, so // prepending allows us to stop searching the list after the first match. *head = new OptionList(name_copy, enable, *head); @@ -183,14 +176,6 @@ JavaAssertions::match_package(const char* classname) { return nullptr; } -inline void JavaAssertions::trace(const char* name, -const char* typefound, const char* namefound, bool enabled) { - if (TraceJavaAssertions) { - tty->print_cr("JavaAssertions: search for %s found %s %s=%d", - name, typefound, namefound[0] != '\0' ? namefound : "'default'", enabled); - } -} - bool JavaAssertions::enabled(const char* classname, bool systemClass) { assert(classname != nullptr, "must have a classname"); @@ -201,18 +186,15 @@ bool JavaAssertions::enabled(const char* classname, bool systemClass) { // First check options that apply to classes. If we find a match we're done. OptionList* p; if ((p = match_class(classname))) { - trace(classname, "class", p->name(), p->enabled()); return p->enabled(); } // Now check packages, from most specific to least. if ((p = match_package(classname))) { - trace(classname, "package", p->name(), p->enabled()); return p->enabled(); } // No match. Return the default status. bool result = systemClass ? systemClassDefault() : userClassDefault(); - trace(classname, systemClass ? "system" : "user", "default", result); return result; } diff --git a/src/hotspot/share/classfile/javaAssertions.hpp b/src/hotspot/share/classfile/javaAssertions.hpp index 58d03eacd48..3477e79c3c5 100644 --- a/src/hotspot/share/classfile/javaAssertions.hpp +++ b/src/hotspot/share/classfile/javaAssertions.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -57,9 +57,6 @@ class JavaAssertions: AllStatic { static void fillJavaArrays(const OptionList* p, int len, objArrayHandle names, typeArrayHandle status, TRAPS); - static inline void trace(const char* name, const char* typefound, - const char* namefound, bool enabled); - static inline OptionList* match_class(const char* classname); static OptionList* match_package(const char* classname); @@ -90,8 +87,6 @@ inline bool JavaAssertions::userClassDefault() { } inline void JavaAssertions::setUserClassDefault(bool enabled) { - if (TraceJavaAssertions) - tty->print_cr("JavaAssertions::setUserClassDefault(%d)", enabled); _userDefault = enabled; } @@ -100,8 +95,6 @@ inline bool JavaAssertions::systemClassDefault() { } inline void JavaAssertions::setSystemClassDefault(bool enabled) { - if (TraceJavaAssertions) - tty->print_cr("JavaAssertions::setSystemClassDefault(%d)", enabled); _sysDefault = enabled; } diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index dd89a3847c7..cd34d874a3c 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -461,9 +461,6 @@ const int ObjectAlignmentInBytes = 8; develop(bool, VerifyStackAtCalls, false, \ "Verify that the stack pointer is unchanged after calls") \ \ - develop(bool, TraceJavaAssertions, false, \ - "Trace java language assertions") \ - \ develop(bool, VerifyCodeCache, false, \ "Verify code cache on memory allocation/deallocation") \ \ From 193de1b1c78ed1cc29a747cc3ca979f219692a5c Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Wed, 24 Jun 2026 18:14:22 +0000 Subject: [PATCH 064/707] 8387185: Locale does not respect numeric singletons Reviewed-by: naoto --- .../classes/sun/util/locale/LanguageTag.java | 4 ++-- .../java/util/Locale/LocaleEnhanceTest.java | 23 +++++++++++++++++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/java.base/share/classes/sun/util/locale/LanguageTag.java b/src/java.base/share/classes/sun/util/locale/LanguageTag.java index 0b2fee7f2cd..bdbbc5eec2d 100644 --- a/src/java.base/share/classes/sun/util/locale/LanguageTag.java +++ b/src/java.base/share/classes/sun/util/locale/LanguageTag.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 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 @@ -601,7 +601,7 @@ public static boolean isExtensionSingleton(String s) { // / %x79-7A ; y - z return (s.length() == 1) - && LocaleUtils.isAlphaString(s) + && LocaleUtils.isAlphaNumericString(s) && !LocaleUtils.caseIgnoreMatch(PRIVATEUSE, s); } diff --git a/test/jdk/java/util/Locale/LocaleEnhanceTest.java b/test/jdk/java/util/Locale/LocaleEnhanceTest.java index 8bcbe20d197..1e38f0b887a 100644 --- a/test/jdk/java/util/Locale/LocaleEnhanceTest.java +++ b/test/jdk/java/util/Locale/LocaleEnhanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 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 @@ -57,7 +57,7 @@ * @test * @bug 6875847 6992272 7002320 7015500 7023613 7032820 7033504 7004603 * 7044019 8008577 8176853 8255086 8263202 8287868 8174269 8369452 - * 8369590 + * 8369590 8387185 * @summary test API changes to Locale * @modules jdk.localedata * @run junit/othervm -esa LocaleEnhanceTest @@ -1377,6 +1377,25 @@ public void testBug7033504() { checkDigit(Locale.forLanguageTag("en-u-nu-thai"), '\u0e50'); } + // Test that numeric singletons are supported + @Test + public void numericSingletonRoundTripTest() { + var tag = "en-0-foo"; + var value = "foo"; + var singleton = '0'; + // test `forLanguageTag` + var locale = Locale.forLanguageTag(tag); + assertEquals(value, locale.getExtension(singleton)); + assertEquals(tag, locale.toLanguageTag()); + // test `Locale.Builder` + locale = new Builder() + .setLanguage("en") + .setExtension(singleton, value) + .build(); + assertEquals(value, locale.getExtension(singleton)); + assertEquals(tag, locale.toLanguageTag()); + } + private void checkCalendar(Locale loc, String expected) { Calendar cal = Calendar.getInstance(loc); assertEquals(expected, cal.getClass().getName(), "Wrong calendar"); From 07a52da1fed10623f66bf424a1c5b10bd07ad25e Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Thu, 25 Jun 2026 07:15:18 +0000 Subject: [PATCH 065/707] 8387015: C2: crash with "named projection 2 not found" from ArrayCopyNode::finish_transform() for clone Reviewed-by: qamai, kvn --- src/hotspot/share/opto/arraycopynode.cpp | 6 ++ .../compiler/arraycopy/TestDeadCloneMem.java | 79 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/arraycopy/TestDeadCloneMem.java diff --git a/src/hotspot/share/opto/arraycopynode.cpp b/src/hotspot/share/opto/arraycopynode.cpp index 2f64482f55b..07b9e907b8f 100644 --- a/src/hotspot/share/opto/arraycopynode.cpp +++ b/src/hotspot/share/opto/arraycopynode.cpp @@ -180,6 +180,12 @@ Node* ArrayCopyNode::try_clone_instance(PhaseGVN *phase, bool can_reshape, int c return nullptr; } + Node* out_mem = proj_out_or_null(TypeFunc::Memory); + if (can_reshape && out_mem == nullptr) { // dead node? + return NodeSentinel; + } + + Node* base_src = in(ArrayCopyNode::Src); Node* base_dest = in(ArrayCopyNode::Dest); Node* ctl = in(TypeFunc::Control); diff --git a/test/hotspot/jtreg/compiler/arraycopy/TestDeadCloneMem.java b/test/hotspot/jtreg/compiler/arraycopy/TestDeadCloneMem.java new file mode 100644 index 00000000000..807f45f11f7 --- /dev/null +++ b/test/hotspot/jtreg/compiler/arraycopy/TestDeadCloneMem.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2026 IBM Corporation. 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 JDK-8387015 + * @summary C2: crash with "named projection 2 not found" from ArrayCopyNode::finish_transform() for clone + * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:CompileOnly=${test.main.class}::test1 + * -XX:CompileCommand=dontinline,${test.main.class}::notInlined -XX:+StressIGVN + * -XX:StressSeed=1324432947 ${test.main.class} + * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:CompileOnly=${test.main.class}::test1 + * -XX:CompileCommand=dontinline,${test.main.class}::notInlined -XX:+StressIGVN + * ${test.main.class} + */ + +package compiler.arraycopy; + +public class TestDeadCloneMem { + private static int field; + + public static void main(String[] args) { + int[] array = new int[10]; + array.clone(); + Object o = new Object(); + test1(42, false); + } + + private static int test1(int flag, boolean flag2) { + int len; + if (flag != 42) { + if (flag2) { + field = 42; + } + int[] array2; + if (flag != 42) { + len = -1; + array2 = new int[4]; + } else { + len = 42; + array2 = new int[100]; + } + int[] array = new int[len]; + int length = array.length; + int i = 0; + do { + synchronized (new Object()) {} + notInlined(); + array2.clone(); + i++; + } while (i < 10); + return length; + } + return 0; + } + + private static void notInlined() { + + } +} From d1905ef91a194f57e81bc2d3a6ad0dec98ca8096 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Thu, 25 Jun 2026 07:36:36 +0000 Subject: [PATCH 066/707] 8386081: Update --release 26 symbol information for JDK 27 build 27 Reviewed-by: darcy, iris --- src/jdk.compiler/share/data/symbols/java.base-R.sym.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/jdk.compiler/share/data/symbols/java.base-R.sym.txt b/src/jdk.compiler/share/data/symbols/java.base-R.sym.txt index 9853f8c70fd..bd9b9177e23 100644 --- a/src/jdk.compiler/share/data/symbols/java.base-R.sym.txt +++ b/src/jdk.compiler/share/data/symbols/java.base-R.sym.txt @@ -27,7 +27,7 @@ # ########################################################## # module name java.base -header exports java/io,java/lang,java/lang/annotation,java/lang/classfile,java/lang/classfile/attribute,java/lang/classfile/constantpool,java/lang/classfile/instruction,java/lang/constant,java/lang/foreign,java/lang/invoke,java/lang/module,java/lang/ref,java/lang/reflect,java/lang/runtime,java/math,java/net,java/net/spi,java/nio,java/nio/channels,java/nio/channels/spi,java/nio/charset,java/nio/charset/spi,java/nio/file,java/nio/file/attribute,java/nio/file/spi,java/security,java/security/cert,java/security/interfaces,java/security/spec,java/text,java/text/spi,java/time,java/time/chrono,java/time/format,java/time/temporal,java/time/zone,java/util,java/util/concurrent,java/util/concurrent/atomic,java/util/concurrent/locks,java/util/function,java/util/jar,java/util/random,java/util/regex,java/util/spi,java/util/stream,java/util/zip,javax/crypto,javax/crypto/interfaces,javax/crypto/spec,javax/net,javax/net/ssl,javax/security/auth,javax/security/auth/callback,javax/security/auth/login,javax/security/auth/spi,javax/security/auth/x500,javax/security/cert,jdk/internal/event[jdk.jfr],jdk/internal/javac[java.compiler\u005C;u002C;jdk.compiler],jdk/internal/vm/vector[jdk.incubator.vector] extraModulePackages jdk/internal/access/foreign,jdk/internal/classfile/impl,jdk/internal/constant,jdk/internal/foreign/abi,jdk/internal/foreign/abi/aarch64/linux,jdk/internal/foreign/abi/aarch64/macos,jdk/internal/foreign/abi/aarch64/windows,jdk/internal/foreign/abi/fallback,jdk/internal/foreign/abi/ppc64/aix,jdk/internal/foreign/abi/ppc64/linux,jdk/internal/foreign/abi/riscv64/linux,jdk/internal/foreign/abi/s390/linux,jdk/internal/foreign/abi/x64/sysv,jdk/internal/foreign/abi/x64/windows,jdk/internal/foreign/layout,jdk/internal/lang,sun/nio,sun/nio/ch,sun/net,jdk/internal/foreign,jdk/internal/foreign,sun/net,sun/nio/ch uses java/lang/System$LoggerFinder,java/net/ContentHandlerFactory,java/net/spi/InetAddressResolverProvider,java/net/spi/URLStreamHandlerProvider,java/nio/channels/spi/AsynchronousChannelProvider,java/nio/channels/spi/SelectorProvider,java/nio/charset/spi/CharsetProvider,java/nio/file/spi/FileSystemProvider,java/nio/file/spi/FileTypeDetector,java/security/Provider,java/text/spi/BreakIteratorProvider,java/text/spi/CollatorProvider,java/text/spi/DateFormatProvider,java/text/spi/DateFormatSymbolsProvider,java/text/spi/DecimalFormatSymbolsProvider,java/text/spi/NumberFormatProvider,java/time/chrono/AbstractChronology,java/time/chrono/Chronology,java/time/format/DateTimeFormatterPatternProvider,java/time/zone/ZoneRulesProvider,java/util/spi/CalendarDataProvider,java/util/spi/CalendarNameProvider,java/util/spi/CurrencyNameProvider,java/util/spi/LocaleNameProvider,java/util/spi/ResourceBundleControlProvider,java/util/spi/ResourceBundleProvider,java/util/spi/TimeZoneNameProvider,java/util/spi/ToolProvider,javax/security/auth/spi/LoginModule,jdk/internal/io/JdkConsoleProvider,jdk/internal/logger/DefaultLoggerFinder,sun/util/locale/provider/LocaleDataMetaInfo,sun/util/resources/LocaleData$LocaleDataResourceBundleProvider,sun/util/spi/CalendarProvider provides interface\u0020;java/nio/file/spi/FileSystemProvider\u0020;impls\u0020;jdk/internal/jrtfs/JrtFileSystemProvider target macos-aarch64 flags 8000 +header exports java/io,java/lang,java/lang/annotation,java/lang/classfile,java/lang/classfile/attribute,java/lang/classfile/constantpool,java/lang/classfile/instruction,java/lang/constant,java/lang/foreign,java/lang/invoke,java/lang/module,java/lang/ref,java/lang/reflect,java/lang/runtime,java/math,java/net,java/net/spi,java/nio,java/nio/channels,java/nio/channels/spi,java/nio/charset,java/nio/charset/spi,java/nio/file,java/nio/file/attribute,java/nio/file/spi,java/security,java/security/cert,java/security/interfaces,java/security/spec,java/text,java/text/spi,java/time,java/time/chrono,java/time/format,java/time/temporal,java/time/zone,java/util,java/util/concurrent,java/util/concurrent/atomic,java/util/concurrent/locks,java/util/function,java/util/jar,java/util/random,java/util/regex,java/util/spi,java/util/stream,java/util/zip,javax/crypto,javax/crypto/interfaces,javax/crypto/spec,javax/net,javax/net/ssl,javax/security/auth,javax/security/auth/callback,javax/security/auth/login,javax/security/auth/spi,javax/security/auth/x500,javax/security/cert,jdk/internal/event[jdk.jfr],jdk/internal/javac[java.compiler\u005C;u002C;jdk.compiler],jdk/internal/vm/vector[jdk.incubator.vector] extraModulePackages jdk/internal/access/foreign,jdk/internal/classfile/impl,jdk/internal/constant,jdk/internal/foreign/abi,jdk/internal/foreign/abi/aarch64/linux,jdk/internal/foreign/abi/aarch64/macos,jdk/internal/foreign/abi/aarch64/windows,jdk/internal/foreign/abi/fallback,jdk/internal/foreign/abi/ppc64/aix,jdk/internal/foreign/abi/ppc64/linux,jdk/internal/foreign/abi/riscv64/linux,jdk/internal/foreign/abi/s390/linux,jdk/internal/foreign/abi/x64/sysv,jdk/internal/foreign/abi/x64/windows,jdk/internal/foreign/layout,jdk/internal/lang,sun/nio,sun/security/internal,sun/nio/ch,sun/net,jdk/internal/foreign,jdk/internal/foreign,sun/net,sun/nio/ch uses java/lang/System$LoggerFinder,java/net/ContentHandlerFactory,java/net/spi/InetAddressResolverProvider,java/net/spi/URLStreamHandlerProvider,java/nio/channels/spi/AsynchronousChannelProvider,java/nio/channels/spi/SelectorProvider,java/nio/charset/spi/CharsetProvider,java/nio/file/spi/FileSystemProvider,java/nio/file/spi/FileTypeDetector,java/security/Provider,java/text/spi/BreakIteratorProvider,java/text/spi/CollatorProvider,java/text/spi/DateFormatProvider,java/text/spi/DateFormatSymbolsProvider,java/text/spi/DecimalFormatSymbolsProvider,java/text/spi/NumberFormatProvider,java/time/chrono/AbstractChronology,java/time/chrono/Chronology,java/time/format/DateTimeFormatterPatternProvider,java/time/zone/ZoneRulesProvider,java/util/spi/CalendarDataProvider,java/util/spi/CalendarNameProvider,java/util/spi/CurrencyNameProvider,java/util/spi/LocaleNameProvider,java/util/spi/ResourceBundleControlProvider,java/util/spi/ResourceBundleProvider,java/util/spi/TimeZoneNameProvider,java/util/spi/ToolProvider,javax/security/auth/spi/LoginModule,jdk/internal/io/JdkConsoleProvider,jdk/internal/logger/DefaultLoggerFinder,sun/util/locale/provider/LocaleDataMetaInfo,sun/util/resources/LocaleData$LocaleDataResourceBundleProvider,sun/util/spi/CalendarProvider provides interface\u0020;java/nio/file/spi/FileSystemProvider\u0020;impls\u0020;jdk/internal/jrtfs/JrtFileSystemProvider target macos-aarch64 flags 8000 class name java/io/ProxyingConsole header extends java/io/Console flags 30 runtimeAnnotations @Ljdk/internal/ValueBased; @@ -124,7 +124,7 @@ class name java/security/AsymmetricKey header extends java/lang/Object implements java/security/Key,java/security/BinaryEncodable flags 601 class name java/security/BinaryEncodable -header extends java/lang/Object sealed true permittedSubclasses java/security/AsymmetricKey,java/security/KeyPair,java/security/spec/PKCS8EncodedKeySpec,java/security/spec/X509EncodedKeySpec,javax/crypto/EncryptedPrivateKeyInfo,java/security/cert/X509Certificate,java/security/cert/X509CRL,java/security/PEM flags 601 classAnnotations @Ljdk/internal/javac/PreviewFeature;(feature=eLjdk/internal/javac/PreviewFeature$Feature;PEM_API;) +header extends java/lang/Object sealed true permittedSubclasses java/security/AsymmetricKey,java/security/KeyPair,java/security/spec/PKCS8EncodedKeySpec,java/security/spec/X509EncodedKeySpec,javax/crypto/EncryptedPrivateKeyInfo,java/security/cert/X509Certificate,java/security/cert/X509CRL,java/security/PEM,sun/security/internal/InternalBinaryEncodable flags 601 classAnnotations @Ljdk/internal/javac/PreviewFeature;(feature=eLjdk/internal/javac/PreviewFeature$Feature;PEM_API;) -class name java/security/DEREncodable @@ -483,3 +483,6 @@ method name convert descriptor (ILjava/lang/Class;IILjava/lang/Class;IILjdk/inte method name compressExpandOp descriptor (ILjava/lang/Class;Ljava/lang/Class;IILjdk/internal/vm/vector/VectorSupport$Vector;Ljdk/internal/vm/vector/VectorSupport$VectorMask;Ljdk/internal/vm/vector/VectorSupport$CompressExpandOperation;)Ljdk/internal/vm/vector/VectorSupport$VectorPayload; flags 9 signature ;M:Ljdk/internal/vm/vector/VectorSupport$VectorMask;E:Ljava/lang/Object;>(ILjava/lang/Class<+TV;>;Ljava/lang/Class<+TM;>;IITV;TM;Ljdk/internal/vm/vector/VectorSupport$CompressExpandOperation;)Ljdk/internal/vm/vector/VectorSupport$VectorPayload; runtimeAnnotations @Ljdk/internal/vm/annotation/IntrinsicCandidate; method name maskReductionCoerced descriptor (ILjava/lang/Class;IILjdk/internal/vm/vector/VectorSupport$VectorMask;Ljdk/internal/vm/vector/VectorSupport$VectorMaskOp;)J flags 9 signature ;E:Ljava/lang/Object;>(ILjava/lang/Class<+TM;>;IITM;Ljdk/internal/vm/vector/VectorSupport$VectorMaskOp;)J runtimeAnnotations @Ljdk/internal/vm/annotation/IntrinsicCandidate; +class name sun/security/internal/InternalBinaryEncodable +header extends java/lang/Object implements java/security/BinaryEncodable flags 31 + From 3f03e104edbcfdc8465415e20882950d2b7d3dee Mon Sep 17 00:00:00 2001 From: Shawn Emery Date: Thu, 25 Jun 2026 08:20:04 +0000 Subject: [PATCH 067/707] 8385304: X25519 should utilize aarch64 intrinsics Reviewed-by: adinn, shade --- .../cpu/aarch64/stubGenerator_aarch64.cpp | 126 ++++++++++++++++++ .../cpu/aarch64/vm_version_aarch64.cpp | 4 + src/hotspot/share/code/aotCodeCache.hpp | 1 + 3 files changed, 131 insertions(+) diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index f89b6e2d579..cae69ac4621 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -8654,6 +8654,123 @@ class StubGenerator: public StubCodeGenerator { return start; } + /** + * Arithmetic polynomial multiplication in Curve25519. The algorithm mimics + * the version in the IntegerPolynomial25519 class, including the use of all + * columns (no folding method). + * + * Arguments: + * + * Inputs: + * c_rarg0 - long[] aLimbs + * c_rarg1 - long[] bLimbs + * + * Output: + * c_rarg2 - long[] rLimbs result + */ + address generate_intpoly_mult_25519() { + StubId stub_id = StubId::stubgen_intpoly_mult_25519_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); + StubCodeMark mark(this, stub_id); + start = __ pc(); + __ enter(); + + // Register Map + const Register aLimbs = c_rarg0; // r0 + const Register bLimbs = c_rarg1; // r1 + const Register rLimbs = c_rarg2; // r2 + + Register c[] = {r3, r4, r5, r6, r7, r8, r9, r10, r11, r12}; + Register a = r13; + Register b = r14; + Register term = r15; + Register low = r16; + Register high = r17; + + const int32_t limbs = 5; + const int32_t bpl = 51; + const int32_t rem = 64 - bpl; + const int32_t TERM = 19; + const int32_t columns = limbs * 2; + const uint64_t mask = (uint64_t) -1 >> rem; + const uint64_t CARRY_ADD = (uint64_t) 1 << (bpl - 1); + + __ mov(term, TERM); + for (int i = 0; i < columns; i++) { + __ mov(c[i], zr); + } + + // Perform high/low multiplication with signed 5x51 bit limbs + for (int i = 0; i < limbs; i++) { + __ ldr(b, Address(bLimbs, i * 8)); + for (int j = 0; j < limbs; j++) { + __ ldr(a, Address(aLimbs, j * 8)); + __ smulh(high, a, b); + __ mul(low, a, b); + __ extr(high, high, low, bpl); + __ andr(low, low, mask); + __ add(c[i + j], c[i + j], low); + __ add(c[i + j + 1], c[i + j + 1], high); + } + } + + for (int i = 0; i < limbs; i++) { + __ mul(c[i + 5], c[i + 5], term); + __ add(c[i], c[i], c[i + 5]); + } + + // Carry-add with reduction from high limb + Register tmp = low; + Register carry_add = high; + __ mov(carry_add, CARRY_ADD); + + // Limb 3 + __ add(tmp, c[3], carry_add); + __ asr(tmp, tmp, bpl); + __ add(c[4], c[4], tmp); + __ lsl(tmp, tmp, bpl); + __ sub(c[3], c[3], tmp); + + // Limb 4 + __ add(tmp, c[4], carry_add); + __ asr(tmp, tmp, bpl); + + // Reduce high order limb and fold back into low order limb + __ mul(term, tmp, term); + __ add(c[0], c[0], term); + + __ lsl(tmp, tmp, bpl); + __ sub(c[4], c[4], tmp); + + // Limbs 0 - 3 + for (int i = 0; i < (limbs - 1); i++) { + __ add(tmp, c[i], carry_add); + __ asr(tmp, tmp, bpl); + __ add(c[i + 1], c[i + 1], tmp); + __ lsl(tmp, tmp, bpl); + __ sub(c[i], c[i], tmp); + } + + for (int i = 0; i < limbs; i++) { + __ str(c[i], Address(rLimbs, i * 8)); + } + + __ mov(r0, 0); + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(lr); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + + return start; + } + void bcax5(Register a0, Register a1, Register a2, Register a3, Register a4, Register tmp0, Register tmp1, Register tmp2) { __ bic(tmp0, a2, a1); // for a0 @@ -13791,6 +13908,15 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_poly1305_processBlocks = generate_poly1305_processBlocks(); } + // The difference between AArch64 vs. x86_64 intrinsics implementation + // include the lack of square() intrinsics; usage caused a 3.3% performance + // degradation due to the efficiencies of the symmetric squaring shape in + // Java vs. the inefficiencies of the leaf calls and the additional cycles + // required for 64 bit multiplication in AArch64. + if (UseIntPoly25519Intrinsics) { + StubRoutines::_intpoly_mult_25519 = generate_intpoly_mult_25519(); + } + // generate Adler32 intrinsics code if (UseAdler32Intrinsics) { StubRoutines::_updateBytesAdler32 = generate_updateBytesAdler32(); diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index e746447e013..5462ccf2a76 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -661,6 +661,10 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UsePoly1305Intrinsics, true); } + if (FLAG_IS_DEFAULT(UseIntPoly25519Intrinsics)) { + FLAG_SET_DEFAULT(UseIntPoly25519Intrinsics, true); + } + if (FLAG_IS_DEFAULT(UseVectorizedHashCodeIntrinsic)) { FLAG_SET_DEFAULT(UseVectorizedHashCodeIntrinsic, true); } diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp index 448bab6fbc2..c65b9cb23d1 100644 --- a/src/hotspot/share/code/aotCodeCache.hpp +++ b/src/hotspot/share/code/aotCodeCache.hpp @@ -291,6 +291,7 @@ class AOTStubData : public StackObj { do_var(bool, UseCRC32Intrinsics) \ do_var(bool, UseDilithiumIntrinsics) \ do_var(bool, UseGHASHIntrinsics) \ + do_var(bool, UseIntPoly25519Intrinsics) \ do_var(bool, UseKyberIntrinsics) \ do_var(bool, UseMD5Intrinsics) \ do_var(bool, UsePoly1305Intrinsics) \ From 3b30a57e3012330f85b24dbc86fc8a5023bb3e1a Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Thu, 25 Jun 2026 08:30:07 +0000 Subject: [PATCH 068/707] 6356745: (coll) Add PriorityQueue(Collection, Comparator) Co-authored-by: Valeh Hajiyev Reviewed-by: vklang, smarks, liach --- .../classes/java/util/PriorityQueue.java | 28 +++++- .../concurrent/tck/PriorityQueueTest.java | 96 +++++++++++++++++++ 2 files changed, 123 insertions(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/util/PriorityQueue.java b/src/java.base/share/classes/java/util/PriorityQueue.java index bacce5ef97e..b9ef312d66d 100644 --- a/src/java.base/share/classes/java/util/PriorityQueue.java +++ b/src/java.base/share/classes/java/util/PriorityQueue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -209,6 +209,32 @@ else if (c instanceof PriorityQueue) { } } + /** + * Creates a {@code PriorityQueue} containing the elements in the + * specified collection. The elements of the new {@code PriorityQueue} + * will be ordered according to the specified comparator. + * + * @param c the collection whose elements are to be placed + * into this priority queue + * @param comparator the comparator that will be used to order this + * priority queue. If {@code null}, the {@linkplain Comparable + * natural ordering} of the elements will be used. + * @throws NullPointerException if the specified collection or any + * of its elements are null + * @since 28 + */ + public PriorityQueue(Collection c, + Comparator comparator) { + this.comparator = comparator; + if (c instanceof SortedSet ss && comparator == ss.comparator()) { + initElementsFromCollection(ss); + } else if (c instanceof PriorityQueue pq && comparator == pq.comparator()) { + initFromPriorityQueue(pq); + } else { + initFromCollection(c); + } + } + /** * Creates a {@code PriorityQueue} containing the elements in the * specified priority queue. This priority queue will be diff --git a/test/jdk/java/util/concurrent/tck/PriorityQueueTest.java b/test/jdk/java/util/concurrent/tck/PriorityQueueTest.java index 690c056e23b..706816f4880 100644 --- a/test/jdk/java/util/concurrent/tck/PriorityQueueTest.java +++ b/test/jdk/java/util/concurrent/tck/PriorityQueueTest.java @@ -40,6 +40,7 @@ import java.util.NoSuchElementException; import java.util.PriorityQueue; import java.util.Queue; +import java.util.TreeSet; import junit.framework.Test; @@ -168,6 +169,101 @@ public void testConstructor7() { mustEqual(items[i], q.poll()); } + /** + * Queue contains all elements of collection used to initialize and + * uses the custom comparator provided to order its elements + */ + public void testConstructor8() { + Item[] items = seqItems(SIZE); + MyReverseComparator cmp = new MyReverseComparator(); + @SuppressWarnings("unchecked") + PriorityQueue q = new PriorityQueue<>(Arrays.asList(items), cmp); + assertEquals(cmp, q.comparator()); + for (int i = SIZE - 1; i >= 0; --i) + mustEqual(items[i], q.poll()); + } + + /** + * Initializing from Collection with a comparator has the order + * of its elements the same as the queue initialized with a comparator + * and populated with Collection after initialization + */ + public void testConstructor9() { + Item[] items = seqItems(SIZE); + MyReverseComparator cmp = new MyReverseComparator(); + @SuppressWarnings("unchecked") + PriorityQueue q1 = new PriorityQueue<>(Arrays.asList(items), cmp); + @SuppressWarnings("unchecked") + PriorityQueue q2 = new PriorityQueue<>(SIZE, cmp); + q2.addAll(Arrays.asList(items)); + for (int i = 0; i < SIZE; ++i) + mustEqual(q1.poll(), q2.poll()); + } + + /** + * Initializing from null Collection throws NPE + */ + public void testConstructor10() { + try { + new PriorityQueue((Collection)null, new MyReverseComparator()); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection of null elements throws NPE + */ + public void testConstructor11() { + try { + new PriorityQueue(Arrays.asList(new Item[SIZE]), new MyReverseComparator()); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from PriorityQueue and its comparator + */ + public void testConstructor12() { + Item[] items = seqItems(SIZE); + MyReverseComparator cmp = new MyReverseComparator(); + @SuppressWarnings("unchecked") + PriorityQueue q1 = new PriorityQueue<>(cmp); + q1.addAll(Arrays.asList(items)); + @SuppressWarnings("unchecked") + PriorityQueue q2 = new PriorityQueue<>(q1, q1.comparator()); + for (int i = 0; i < SIZE; ++i) + mustEqual(q1.poll(), q2.poll()); + } + + /** + * Initializing from SortedSet and its comparator + */ + public void testConstructor13() { + Item[] items = seqItems(SIZE); + MyReverseComparator cmp = new MyReverseComparator(); + @SuppressWarnings("unchecked") + TreeSet s = new TreeSet<>(cmp); + s.addAll(Arrays.asList(items)); + @SuppressWarnings("unchecked") + PriorityQueue q = new PriorityQueue<>(s, s.comparator()); + for (int i = 0; i < SIZE; ++i) + mustEqual(q.poll(), s.removeFirst()); + } + + /** + * Initializing with null comparator + */ + public void testConstructor14() { + Item[] items = seqItems(SIZE); + @SuppressWarnings("unchecked") + PriorityQueue q1 = new PriorityQueue<>((Comparator) null); + q1.addAll(Arrays.asList(items)); + @SuppressWarnings("unchecked") + PriorityQueue q2 = new PriorityQueue<>(Arrays.asList(items), null); + for (int i = 0; i < SIZE; ++i) + mustEqual(q1.poll(), q2.poll()); + } + /** * isEmpty is true before add, false after */ From bd8072d22ced9a75da0d6578df66be65f886c746 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Thu, 25 Jun 2026 09:20:20 +0000 Subject: [PATCH 069/707] 8379816: C2: Possible integer overflow in BCEscapeAnalyzer::iterate_blocks Reviewed-by: dlong, kvn --- src/hotspot/share/ci/bcEscapeAnalyzer.cpp | 36 ++-- src/hotspot/share/ci/bcEscapeAnalyzer.hpp | 6 + src/hotspot/share/ci/ciMethod.hpp | 4 +- src/hotspot/share/ci/ciMethodBlocks.hpp | 2 +- .../TestBCEscapeAnalyzerOverflow.java | 160 ++++++++++++++++++ 5 files changed, 195 insertions(+), 13 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/escapeAnalysis/TestBCEscapeAnalyzerOverflow.java diff --git a/src/hotspot/share/ci/bcEscapeAnalyzer.cpp b/src/hotspot/share/ci/bcEscapeAnalyzer.cpp index 712f7af4139..fd4ac33eb69 100644 --- a/src/hotspot/share/ci/bcEscapeAnalyzer.cpp +++ b/src/hotspot/share/ci/bcEscapeAnalyzer.cpp @@ -34,6 +34,7 @@ #include "utilities/align.hpp" #include "utilities/bitMap.inline.hpp" #include "utilities/copy.hpp" +#include "utilities/integerCast.hpp" #ifndef PRODUCT #define TRACE_BCEA(level, code) \ @@ -1077,17 +1078,32 @@ void BCEscapeAnalyzer::merge_block_states(StateInfo *blockstates, ciBlock *dest, } } +bool BCEscapeAnalyzer::datasize_overflow(uint numblocks, uint stkSize, uint numLocals, size_t& datasize) { + uint64_t datacount64 = (uint64_t)(numblocks + 1) * (stkSize + numLocals); + if (datacount64 > SIZE_MAX / sizeof(ArgumentMap)) { + return true; + } + datasize = integer_cast_permit_tautology(datacount64 * sizeof(ArgumentMap)); + return false; +} + void BCEscapeAnalyzer::iterate_blocks(Arena *arena) { - int numblocks = _methodBlocks->num_blocks(); - int stkSize = _method->max_stack(); - int numLocals = _method->max_locals(); + uint numblocks = _methodBlocks->num_blocks(); + uint stkSize = _method->max_stack(); + uint numLocals = _method->max_locals(); StateInfo state; - int datacount = (numblocks + 1) * (stkSize + numLocals); - int datasize = datacount * sizeof(ArgumentMap); + size_t datasize; + if (datasize_overflow(numblocks, stkSize, numLocals, datasize)) { + _conservative = true; + return; + } + size_t datacount = datasize / sizeof(ArgumentMap); StateInfo *blockstates = (StateInfo *) arena->Amalloc(numblocks * sizeof(StateInfo)); ArgumentMap *statedata = (ArgumentMap *) arena->Amalloc(datasize); - for (int i = 0; i < datacount; i++) ::new ((void*)&statedata[i]) ArgumentMap(); + for (size_t i = 0; i < datacount; i++) { + ::new ((void*)&statedata[i]) ArgumentMap(); + } ArgumentMap *dp = statedata; state._vars = dp; dp += numLocals; @@ -1095,7 +1111,7 @@ void BCEscapeAnalyzer::iterate_blocks(Arena *arena) { dp += stkSize; state._initialized = false; state._max_stack = stkSize; - for (int i = 0; i < numblocks; i++) { + for (uint i = 0; i < numblocks; i++) { blockstates[i]._vars = dp; dp += numLocals; blockstates[i]._stack = dp; @@ -1142,7 +1158,7 @@ void BCEscapeAnalyzer::iterate_blocks(Arena *arena) { if (blk->is_handler() || blk->is_ret_target()) { // for an exception handler or a target of a ret instruction, we assume the worst case, // that any variable could contain any argument - for (int i = 0; i < numLocals; i++) { + for (uint i = 0; i < numLocals; i++) { state._vars[i] = allVars; } if (blk->is_handler()) { @@ -1155,7 +1171,7 @@ void BCEscapeAnalyzer::iterate_blocks(Arena *arena) { state._stack[i] = allVars; } } else { - for (int i = 0; i < numLocals; i++) { + for (uint i = 0; i < numLocals; i++) { state._vars[i] = blkState->_vars[i]; } for (int i = 0; i < blkState->_stack_height; i++) { @@ -1170,7 +1186,7 @@ void BCEscapeAnalyzer::iterate_blocks(Arena *arena) { DEBUG_ONLY(int handler_count = 0;) int blk_start = blk->start_bci(); int blk_end = blk->limit_bci(); - for (int i = 0; i < numblocks; i++) { + for (uint i = 0; i < numblocks; i++) { ciBlock *b = _methodBlocks->block(i); if (b->is_handler()) { int ex_start = b->ex_start_bci(); diff --git a/src/hotspot/share/ci/bcEscapeAnalyzer.hpp b/src/hotspot/share/ci/bcEscapeAnalyzer.hpp index b75cb6a56f4..7bdd4a58146 100644 --- a/src/hotspot/share/ci/bcEscapeAnalyzer.hpp +++ b/src/hotspot/share/ci/bcEscapeAnalyzer.hpp @@ -152,6 +152,12 @@ class BCEscapeAnalyzer : public ArenaObj { // Copy dependencies from this analysis into "deps" void copy_dependencies(Dependencies *deps); + // Returns true if the datasize computation for iterate_blocks would + // overflow, i.e. the allocation size exceeds what can be represented. + // On success, sets datasize to the computed allocation size in bytes. + // Extracted as a public static method for testability (JDK-8216486). + static bool datasize_overflow(uint numblocks, uint stkSize, uint numLocals, size_t& datasize); + #ifndef PRODUCT // dump escape information void dump(); diff --git a/src/hotspot/share/ci/ciMethod.hpp b/src/hotspot/share/ci/ciMethod.hpp index eecd9427585..c3805b4a054 100644 --- a/src/hotspot/share/ci/ciMethod.hpp +++ b/src/hotspot/share/ci/ciMethod.hpp @@ -76,8 +76,8 @@ class ciMethod : public ciMetadata { // Code attributes. int _code_size; - int _max_stack; - int _max_locals; + uint _max_stack; + u2 _max_locals; vmIntrinsicID _intrinsic_id; int _handler_count; int _interpreter_invocation_count; diff --git a/src/hotspot/share/ci/ciMethodBlocks.hpp b/src/hotspot/share/ci/ciMethodBlocks.hpp index f1b446c2a87..567ea1e39b4 100644 --- a/src/hotspot/share/ci/ciMethodBlocks.hpp +++ b/src/hotspot/share/ci/ciMethodBlocks.hpp @@ -38,7 +38,7 @@ class ciMethodBlocks : public ArenaObj { Arena *_arena; GrowableArray *_blocks; ciBlock **_bci_to_block; - int _num_blocks; + u2 _num_blocks; int _code_size; void do_analysis(); diff --git a/test/hotspot/jtreg/compiler/escapeAnalysis/TestBCEscapeAnalyzerOverflow.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestBCEscapeAnalyzerOverflow.java new file mode 100644 index 00000000000..f33a16785d1 --- /dev/null +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestBCEscapeAnalyzerOverflow.java @@ -0,0 +1,160 @@ +/* + * 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 8216486 + * @summary Verify BCEscapeAnalyzer handles methods where + * (numblocks+1)*(max_stack+max_locals) overflows a 32-bit int. + * On a UBSAN build the signed overflow would be caught as UB; + * on a normal build the test verifies no crash from the bogus + * allocation size that resulted from the overflow. + * + * @requires vm.compiler2.enabled + * + * @run main/othervm -Xcomp -XX:-TieredCompilation + * compiler.escapeAnalysis.TestBCEscapeAnalyzerOverflow + */ + +package compiler.escapeAnalysis; + +import java.lang.classfile.ClassFile; +import java.lang.classfile.Label; +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; +import java.lang.constant.MethodTypeDesc; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +public class TestBCEscapeAnalyzerOverflow { + + // Number of goto instructions in the generated method. + // Creates NUM_GOTOS + 1 basic blocks. With max_stack = 0xFFFF and + // max_locals = 0xFFFF the product (numblocks+1)*(max_stack+max_locals) + // is 16386 * 131070 = 2,147,713,020 which exceeds Integer.MAX_VALUE. + static final int NUM_GOTOS = 16384; + static final int TARGET_MAX_STACK = 0xFFFF; + static final int TARGET_MAX_LOCALS = 0xFFFF; + + static final ClassDesc CD_HELPER = + ClassDesc.of("compiler.escapeAnalysis.BCEscapeOverflowHelper"); + + public static void main(String[] args) throws Throwable { + byte[] classBytes = buildClass(); + MethodHandles.Lookup lookup = MethodHandles.lookup(); + Class cls = lookup.defineClass(classBytes); + + // caller() allocates an Object and passes it to bigMethod() via + // invokestatic. Under -Xcomp -XX:-TieredCompilation, C2 compiles + // caller() and invokes BCEscapeAnalyzer on bigMethod to determine + // whether the argument escapes. Without the fix the 32-bit + // overflow in iterate_blocks leads to undefined behavior. + var mh = lookup.findStatic(cls, "caller", + MethodType.methodType(void.class)); + mh.invoke(); + } + + /** + * Builds a minimal class (version 50, no StackMapTable needed) with: + * public static void bigMethod(Object o) -- pathological method + * public static void caller() -- calls bigMethod + * + * The ClassFile API generates the bytecode; max_stack and max_locals + * of bigMethod are then patched to the target overflow-triggering values. + */ + static byte[] buildClass() { + var mtd_Obj_void = MethodTypeDesc.of(ConstantDescs.CD_void, + ConstantDescs.CD_Object); + var mtd_void = MethodTypeDesc.of(ConstantDescs.CD_void); + + byte[] bytes = ClassFile.of(ClassFile.StackMapsOption.DROP_STACK_MAPS) + .build(CD_HELPER, cb -> { + cb.withVersion(50, 0); + cb.withFlags(ClassFile.ACC_PUBLIC | ClassFile.ACC_SUPER); + + // bigMethod(Object o): aload_0, pop, , return + cb.withMethod("bigMethod", mtd_Obj_void, + ClassFile.ACC_PUBLIC | ClassFile.ACC_STATIC, + mb -> mb.withCode(code -> { + code.aload(0); + code.pop(); + for (int i = 0; i < NUM_GOTOS; i++) { + Label next = code.newLabel(); + code.goto_(next); + code.labelBinding(next); + } + code.return_(); + })); + + // caller(): new Object → dup → invokespecial → + // invokestatic bigMethod → return + cb.withMethod("caller", mtd_void, + ClassFile.ACC_PUBLIC | ClassFile.ACC_STATIC, + mb -> mb.withCode(code -> { + code.new_(ConstantDescs.CD_Object); + code.dup(); + code.invokespecial(ConstantDescs.CD_Object, + "", mtd_void); + code.invokestatic(CD_HELPER, + "bigMethod", mtd_Obj_void); + code.return_(); + })); + }); + + patchBigMethodMaxes(bytes); + return bytes; + } + + /** + * Locates bigMethod's Code attribute and patches max_stack/max_locals + * to TARGET_MAX_STACK/TARGET_MAX_LOCALS. The ClassFile API computes + * small values (max_stack=1, max_locals=1); we inflate them to create + * the pathological overflow case. + * + * The Code attribute layout is: + * attribute_name_index(u2), attribute_length(u4), + * max_stack(u2), max_locals(u2), code_length(u4), code[...]... + * + * We search for bigMethod's unique code_length and patch the two u2 + * fields immediately before it. + */ + static void patchBigMethodMaxes(byte[] b) { + int expectedCodeLen = NUM_GOTOS * 3 + 3; + for (int i = 4; i <= b.length - 4; i++) { + int codeLen = ((b[i] & 0xFF) << 24) | ((b[i + 1] & 0xFF) << 16) + | ((b[i + 2] & 0xFF) << 8) | (b[i + 3] & 0xFF); + if (codeLen == expectedCodeLen) { + int ms = ((b[i - 4] & 0xFF) << 8) | (b[i - 3] & 0xFF); + int ml = ((b[i - 2] & 0xFF) << 8) | (b[i - 1] & 0xFF); + if (ms <= 2 && ml <= 2) { + b[i - 4] = (byte)(TARGET_MAX_STACK >>> 8); + b[i - 3] = (byte)(TARGET_MAX_STACK); + b[i - 2] = (byte)(TARGET_MAX_LOCALS >>> 8); + b[i - 1] = (byte)(TARGET_MAX_LOCALS); + return; + } + } + } + throw new RuntimeException("Could not find bigMethod Code attribute"); + } +} From 2b20a1391b12132089aa271802fecf8020226a0f Mon Sep 17 00:00:00 2001 From: Ivan Walulya Date: Thu, 25 Jun 2026 10:52:04 +0000 Subject: [PATCH 070/707] 8385562: G1: Remove obsolete young_list prefix in identifiers used before JDK-8150721 Reviewed-by: stefank, tschatzl --- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 22 +- src/hotspot/share/gc/g1/g1CollectedHeap.hpp | 9 +- .../share/gc/g1/g1CollectedHeap.inline.hpp | 4 +- src/hotspot/share/gc/g1/g1CollectionSet.cpp | 10 +- .../share/gc/g1/g1ConcurrentMarkThread.cpp | 2 +- .../share/gc/g1/g1ConcurrentRefine.cpp | 2 +- .../share/gc/g1/g1HeapSizingPolicy.cpp | 2 +- src/hotspot/share/gc/g1/g1HeapTransition.cpp | 73 ++-- src/hotspot/share/gc/g1/g1HeapTransition.hpp | 14 +- .../share/gc/g1/g1MonitoringSupport.cpp | 12 +- src/hotspot/share/gc/g1/g1Policy.cpp | 402 +++++++++--------- src/hotspot/share/gc/g1/g1Policy.hpp | 73 ++-- ...sk.cpp => g1ReviseNumYoungRegionsTask.cpp} | 22 +- ...sk.hpp => g1ReviseNumYoungRegionsTask.hpp} | 24 +- src/hotspot/share/gc/g1/g1YoungGenSizer.cpp | 48 +-- src/hotspot/share/gc/g1/g1YoungGenSizer.hpp | 26 +- .../share/gc/g1/jvmFlagConstraintsG1.cpp | 4 +- src/hotspot/share/runtime/mutexLocker.cpp | 4 +- src/hotspot/share/runtime/mutexLocker.hpp | 2 +- .../jtreg/gc/arguments/TestNewRatioFlag.java | 12 +- .../gc/arguments/TestSurvivorRatioFlag.java | 8 +- .../TestTargetSurvivorRatioFlag.java | 4 +- 22 files changed, 392 insertions(+), 387 deletions(-) rename src/hotspot/share/gc/g1/{g1ReviseYoungLengthTask.cpp => g1ReviseNumYoungRegionsTask.cpp} (79%) rename src/hotspot/share/gc/g1/{g1ReviseYoungLengthTask.hpp => g1ReviseNumYoungRegionsTask.hpp} (73%) diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index ebf7a1086fa..b4758897dd6 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -61,7 +61,7 @@ #include "gc/g1/g1RegionPinCache.inline.hpp" #include "gc/g1/g1RegionToSpaceMapper.hpp" #include "gc/g1/g1RemSet.hpp" -#include "gc/g1/g1ReviseYoungLengthTask.hpp" +#include "gc/g1/g1ReviseNumYoungRegionsTask.hpp" #include "gc/g1/g1RootClosures.hpp" #include "gc/g1/g1SATBMarkQueueSet.hpp" #include "gc/g1/g1ServiceThread.hpp" @@ -916,7 +916,7 @@ void G1CollectedHeap::verify_after_full_collection() { // At this point there should be no regions in the // entire heap tagged as young. - assert(check_young_list_empty(), "young list should be empty at this point"); + assert(check_no_young_regions(), "We should not have young regions at this point"); // Note: since we've just done a full GC, concurrent // marking is no longer active. Therefore we need not @@ -1295,7 +1295,7 @@ G1CollectedHeap::G1CollectedHeap() : _service_thread(nullptr), _periodic_gc_task(nullptr), _free_arena_memory_task(nullptr), - _revise_young_length_task(nullptr), + _revise_num_young_regions_task(nullptr), _workers(nullptr), _refinement_epoch(0), _last_synchronized_start(0), @@ -1604,9 +1604,9 @@ jint G1CollectedHeap::initialize() { _free_arena_memory_task = new G1MonotonicArenaFreeMemoryTask("Card Set Free Memory Task"); _service_thread->register_task(_free_arena_memory_task); - if (policy()->use_adaptive_young_list_length()) { - _revise_young_length_task = new G1ReviseYoungLengthTask("Revise Young Length List Task"); - _service_thread->register_task(_revise_young_length_task); + if (policy()->use_adaptive_num_young_regions()) { + _revise_num_young_regions_task = new G1ReviseNumYoungRegionsTask("Revise Num Young Regions Task"); + _service_thread->register_task(_revise_num_young_regions_task); } // Here we allocate the dummy G1HeapRegion that is required by the @@ -2282,7 +2282,7 @@ bool G1CollectedHeap::block_is_obj(const HeapWord* addr) const { } size_t G1CollectedHeap::tlab_capacity() const { - return eden_target_length() * G1HeapRegion::GrainBytes; + return target_num_eden_regions() * G1HeapRegion::GrainBytes; } size_t G1CollectedHeap::tlab_used() const { @@ -2451,7 +2451,7 @@ G1HeapSummary G1CollectedHeap::create_g1_heap_summary() { size_t heap_used = Heap_lock->owned_by_self() ? used() : used_unlocked(); size_t eden_capacity_bytes = - (policy()->young_list_target_length() * G1HeapRegion::GrainBytes) - survivor_used_bytes; + (policy()->target_num_young_regions() * G1HeapRegion::GrainBytes) - survivor_used_bytes; VirtualSpaceSummary heap_summary = create_heap_space_summary(); return G1HeapSummary(heap_summary, heap_used, eden_used_bytes, eden_capacity_bytes, @@ -2989,7 +2989,7 @@ class NoYoungRegionsClosure: public G1HeapRegionClosure { bool success() { return _success; } }; -bool G1CollectedHeap::check_young_list_empty() { +bool G1CollectedHeap::check_no_young_regions() { bool ret = (young_regions_count() == 0); NoYoungRegionsClosure closure; @@ -3008,8 +3008,8 @@ void G1CollectedHeap::prepare_region_for_full_compaction(G1HeapRegion* hr) { } else if (hr->is_old()) { _old_set.remove(hr); } else if (hr->is_young()) { - // Note that emptying the eden and survivor lists is postponed and instead - // done as the first step when rebuilding the regions sets again. The reason + // Note that clearing eden and survivor region tracking is postponed and + // done as the first step when rebuilding the region sets again. The reason // for this is that during a full GC string deduplication needs to know if // a collected region was young or old when the full GC was initiated. hr->uninstall_surv_rate_group(); diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index fc31878097b..718c230851f 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -75,7 +75,7 @@ class G1GCPhaseTimes; class G1HeapSizingPolicy; class G1NewTracer; class G1RemSet; -class G1ReviseYoungLengthTask; +class G1ReviseNumYoungRegionsTask; class G1ServiceTask; class G1ServiceThread; class GCMemoryManager; @@ -176,7 +176,7 @@ class G1CollectedHeap : public CollectedHeap { G1ServiceThread* _service_thread; G1ServiceTask* _periodic_gc_task; G1MonotonicArenaFreeMemoryTask* _free_arena_memory_task; - G1ReviseYoungLengthTask* _revise_young_length_task; + G1ReviseNumYoungRegionsTask* _revise_num_young_regions_task; WorkerThreads* _workers; @@ -394,7 +394,6 @@ class G1CollectedHeap : public CollectedHeap { #define assert_used_and_recalculate_used_equal(g1h) do {} while(0) #endif - // The young region list. G1EdenRegions _eden; G1SurvivorRegions _survivor; @@ -1237,7 +1236,7 @@ class G1CollectedHeap : public CollectedHeap { G1SurvivorRegions* survivor() { return &_survivor; } - inline uint eden_target_length() const; + inline uint target_num_eden_regions() const; uint eden_regions_count() const { return _eden.length(); } uint eden_regions_count(uint node_index) const { return _eden.regions_on_node(node_index); } uint survivor_regions_count() const { return _survivor.length(); } @@ -1249,7 +1248,7 @@ class G1CollectedHeap : public CollectedHeap { uint humongous_regions_count() const { return _humongous_set.length(); } #ifdef ASSERT - bool check_young_list_empty(); + bool check_no_young_regions(); #endif bool is_marked(oop obj) const; diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp index bad9ac18eec..5d23a7d463e 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp @@ -295,8 +295,8 @@ inline bool G1CollectedHeap::is_collection_set_candidate(const G1HeapRegion* r) return candidates->contains(r); } -inline uint G1CollectedHeap::eden_target_length() const { - return _policy->young_list_target_length() - survivor_regions_count(); +inline uint G1CollectedHeap::target_num_eden_regions() const { + return _policy->target_num_young_regions() - survivor_regions_count(); } #endif // SHARE_GC_G1_G1COLLECTEDHEAP_INLINE_HPP diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.cpp b/src/hotspot/share/gc/g1/g1CollectionSet.cpp index b0eb493120b..9f1bbf1b48e 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.cpp @@ -332,9 +332,9 @@ double G1CollectionSet::finalize_young_part(double target_pause_time_ms, G1Survi log_trace(gc, ergo, cset)("Start choosing CSet. Pending cards: %zu target pause time: %1.2fms", pending_cards, target_pause_time_ms); - // The young list is laid with the survivor regions from the previous - // pause are appended to the RHS of the young list, i.e. - // [Newly Young Regions ++ Survivors from last pause]. + // Young region indexes are assigned with eden regions first, followed by + // survivor regions from the previous pause: + // [Eden regions ++ Survivors from last pause]. uint num_eden_regions = _g1h->eden_regions_count(); uint num_survivor_regions = survivors->length(); @@ -355,7 +355,7 @@ double G1CollectionSet::finalize_young_part(double target_pause_time_ms, G1Survi num_eden_regions, num_survivor_regions, predicted_eden_time, predicted_base_time_ms, target_pause_time_ms, remaining_time_ms); - // Clear the fields that point to the survivor list - they are all young now. + // Set survivor regions as eden and clear survivor tracking for this pause. survivors->convert_to_eden(); phase_times()->record_young_cset_choice_time_ms((Ticks::now() - start_time).seconds() * 1000.0); @@ -426,7 +426,7 @@ double G1CollectionSet::select_candidates_from_marking(double time_remaining_ms) uint min_old_cset_length = _policy->calc_min_old_cset_length(candidates()->last_marking_candidates_length()); uint max_old_cset_length = MAX2(min_old_cset_length, _policy->calc_max_old_cset_length()); - bool check_time_remaining = _policy->use_adaptive_young_list_length(); + bool check_time_remaining = _policy->use_adaptive_num_young_regions(); G1CSetCandidateGroupList* from_marking_groups = &candidates()->from_marking_groups(); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp index a41d5cf54b9..948897a538c 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp @@ -77,7 +77,7 @@ double G1ConcurrentMarkThread::mmu_delay_end(G1Policy* policy, bool remark) { void G1ConcurrentMarkThread::delay_to_keep_mmu(bool remark) { G1Policy* policy = G1CollectedHeap::heap()->policy(); - if (policy->use_adaptive_young_list_length()) { + if (policy->use_adaptive_num_young_regions()) { double delay_end_sec = mmu_delay_end(policy, remark); // Wait for timeout or thread termination request. MonitorLocker ml(G1CGC_lock, Monitor::_no_safepoint_check_flag); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp index d58d980b651..4d4730de0b2 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp @@ -381,7 +381,7 @@ void G1ConcurrentRefineSweepState::complete_refinement(jlong total_yield_during_ policy->record_refinement_stats(stats()); { - MutexLocker x(G1ReviseYoungLength_lock, Mutex::_no_safepoint_check_flag); + MutexLocker x(G1ReviseNumYoungRegions_lock, Mutex::_no_safepoint_check_flag); policy->record_dirtying_stats(TimeHelper::counter_to_millis(g1h->last_refinement_epoch_start()), TimeHelper::counter_to_millis(next_epoch_start), _stats.cards_pending(), diff --git a/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp b/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp index 1b9704e8ad3..6158bc47c47 100644 --- a/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp +++ b/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp @@ -185,7 +185,7 @@ size_t G1HeapSizingPolicy::young_collection_shrink_amount(double cpu_usage_delta // going to use during this mutator phase. uint target_regions_to_shrink = _g1h->num_free_regions(); - uint needed_for_allocation = _g1h->eden_target_length(); + uint needed_for_allocation = _g1h->target_num_eden_regions(); if (_g1h->is_humongous(allocation_word_size)) { needed_for_allocation += (uint) _g1h->humongous_obj_size_in_regions(allocation_word_size); } diff --git a/src/hotspot/share/gc/g1/g1HeapTransition.cpp b/src/hotspot/share/gc/g1/g1HeapTransition.cpp index 690bda4e7e6..e91afc79267 100644 --- a/src/hotspot/share/gc/g1/g1HeapTransition.cpp +++ b/src/hotspot/share/gc/g1/g1HeapTransition.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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,13 +29,13 @@ #include "memory/metaspaceUtils.hpp" G1HeapTransition::Data::Data(G1CollectedHeap* g1_heap) : - _eden_length(g1_heap->eden_regions_count()), - _survivor_length(g1_heap->survivor_regions_count()), - _old_length(g1_heap->old_regions_count()), - _humongous_length(g1_heap->humongous_regions_count()), + _num_eden_regions(g1_heap->eden_regions_count()), + _num_survivor_regions(g1_heap->survivor_regions_count()), + _num_old_regions(g1_heap->old_regions_count()), + _num_humongous_regions(g1_heap->humongous_regions_count()), _meta_sizes(MetaspaceUtils::get_combined_statistics()), - _eden_length_per_node(nullptr), - _survivor_length_per_node(nullptr) { + _num_eden_regions_per_node(nullptr), + _num_survivor_regions_per_node(nullptr) { uint node_count = G1NUMA::numa()->num_active_nodes(); @@ -43,20 +43,20 @@ G1HeapTransition::Data::Data(G1CollectedHeap* g1_heap) : LogTarget(Debug, gc, heap, numa) lt; if (lt.is_enabled()) { - _eden_length_per_node = NEW_C_HEAP_ARRAY(uint, node_count, mtGC); - _survivor_length_per_node = NEW_C_HEAP_ARRAY(uint, node_count, mtGC); + _num_eden_regions_per_node = NEW_C_HEAP_ARRAY(uint, node_count, mtGC); + _num_survivor_regions_per_node = NEW_C_HEAP_ARRAY(uint, node_count, mtGC); for (uint i = 0; i < node_count; i++) { - _eden_length_per_node[i] = g1_heap->eden_regions_count(i); - _survivor_length_per_node[i] = g1_heap->survivor_regions_count(i); + _num_eden_regions_per_node[i] = g1_heap->eden_regions_count(i); + _num_survivor_regions_per_node[i] = g1_heap->survivor_regions_count(i); } } } } G1HeapTransition::Data::~Data() { - FREE_C_HEAP_ARRAY(_eden_length_per_node); - FREE_C_HEAP_ARRAY(_survivor_length_per_node); + FREE_C_HEAP_ARRAY(_num_eden_regions_per_node); + FREE_C_HEAP_ARRAY(_num_survivor_regions_per_node); } G1HeapTransition::G1HeapTransition(G1CollectedHeap* g1_heap) : _g1_heap(g1_heap), _before(g1_heap) { } @@ -101,23 +101,23 @@ class G1HeapTransition::DetailedUsageClosure: public G1HeapRegionClosure { } }; -static void log_regions(const char* msg, size_t before_length, size_t after_length, size_t capacity, - uint* before_per_node_length, uint* after_per_node_length) { +static void log_regions(const char* msg, size_t num_before, size_t num_after, size_t capacity, + uint* num_per_node_before, uint* num_per_node_after) { LogTarget(Info, gc, heap) lt; if (lt.is_enabled()) { LogStream ls(lt); ls.print("%s regions: %zu->%zu(%zu)", - msg, before_length, after_length, capacity); + msg, num_before, num_after, capacity); // Not null only if gc+heap+numa at Debug level is enabled. - if (before_per_node_length != nullptr && after_per_node_length != nullptr) { + if (num_per_node_before != nullptr && num_per_node_after != nullptr) { G1NUMA* numa = G1NUMA::numa(); uint num_nodes = numa->num_active_nodes(); const uint* node_ids = numa->node_ids(); ls.print(" ("); for (uint i = 0; i < num_nodes; i++) { - ls.print("%u: %u->%u", node_ids[i], before_per_node_length[i], after_per_node_length[i]); + ls.print("%u: %u->%u", node_ids[i], num_per_node_before[i], num_per_node_after[i]); // Skip adding below if it is the last one. if (i != num_nodes - 1) { ls.print(", "); @@ -132,8 +132,8 @@ static void log_regions(const char* msg, size_t before_length, size_t after_leng void G1HeapTransition::print() { Data after(_g1_heap); - size_t eden_capacity_length_after_gc = _g1_heap->policy()->young_list_target_length() - after._survivor_length; - size_t survivor_capacity_length_before_gc = _g1_heap->policy()->max_survivor_regions(); + size_t num_eden_after_gc = _g1_heap->policy()->target_num_young_regions() - after._num_survivor_regions; + size_t num_survivor_before_gc = _g1_heap->policy()->max_survivor_regions(); DetailedUsage usage; if (log_is_enabled(Trace, gc, heap)) { @@ -141,32 +141,35 @@ void G1HeapTransition::print() { _g1_heap->heap_region_iterate(&blk); usage = blk._usage; assert(usage._eden_region_count == 0, "Expected no eden regions, but got %zu", usage._eden_region_count); - assert(usage._survivor_region_count == after._survivor_length, "Expected survivors to be %zu but was %zu", - after._survivor_length, usage._survivor_region_count); - assert(usage._old_region_count == after._old_length, "Expected old to be %zu but was %zu", - after._old_length, usage._old_region_count); - assert(usage._humongous_region_count == after._humongous_length, "Expected humongous to be %zu but was %zu", - after._humongous_length, usage._humongous_region_count); + assert(usage._survivor_region_count == after._num_survivor_regions, "Expected survivors to be %zu but was %zu", + after._num_survivor_regions, usage._survivor_region_count); + assert(usage._old_region_count == after._num_old_regions, "Expected old to be %zu but was %zu", + after._num_old_regions, usage._old_region_count); + assert(usage._humongous_region_count == after._num_humongous_regions, "Expected humongous to be %zu but was %zu", + after._num_humongous_regions, usage._humongous_region_count); } - log_regions("Eden", _before._eden_length, after._eden_length, eden_capacity_length_after_gc, - _before._eden_length_per_node, after._eden_length_per_node); + log_regions("Eden", _before._num_eden_regions, after._num_eden_regions, num_eden_after_gc, + _before._num_eden_regions_per_node, after._num_eden_regions_per_node); log_trace(gc, heap)(" Used: 0K, Waste: 0K"); - log_regions("Survivor", _before._survivor_length, after._survivor_length, survivor_capacity_length_before_gc, - _before._survivor_length_per_node, after._survivor_length_per_node); + log_regions("Survivor", _before._num_survivor_regions, after._num_survivor_regions, num_survivor_before_gc, + _before._num_survivor_regions_per_node, after._num_survivor_regions_per_node); log_trace(gc, heap)(" Used: %zuK, Waste: %zuK", - usage._survivor_used / K, ((after._survivor_length * G1HeapRegion::GrainBytes) - usage._survivor_used) / K); + usage._survivor_used / K, + ((after._num_survivor_regions * G1HeapRegion::GrainBytes) - usage._survivor_used) / K); log_info(gc, heap)("Old regions: %zu->%zu", - _before._old_length, after._old_length); + _before._num_old_regions, after._num_old_regions); log_trace(gc, heap)(" Used: %zuK, Waste: %zuK", - usage._old_used / K, ((after._old_length * G1HeapRegion::GrainBytes) - usage._old_used) / K); + usage._old_used / K, + ((after._num_old_regions * G1HeapRegion::GrainBytes) - usage._old_used) / K); log_info(gc, heap)("Humongous regions: %zu->%zu", - _before._humongous_length, after._humongous_length); + _before._num_humongous_regions, after._num_humongous_regions); log_trace(gc, heap)(" Used: %zuK, Waste: %zuK", - usage._humongous_used / K, ((after._humongous_length * G1HeapRegion::GrainBytes) - usage._humongous_used) / K); + usage._humongous_used / K, + ((after._num_humongous_regions * G1HeapRegion::GrainBytes) - usage._humongous_used) / K); MetaspaceUtils::print_metaspace_change(_before._meta_sizes); } diff --git a/src/hotspot/share/gc/g1/g1HeapTransition.hpp b/src/hotspot/share/gc/g1/g1HeapTransition.hpp index 18bcd153505..4b69d26c5a6 100644 --- a/src/hotspot/share/gc/g1/g1HeapTransition.hpp +++ b/src/hotspot/share/gc/g1/g1HeapTransition.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -35,16 +35,16 @@ class G1HeapTransition { class DetailedUsageClosure; struct Data { - size_t _eden_length; - size_t _survivor_length; - size_t _old_length; - size_t _humongous_length; + size_t _num_eden_regions; + size_t _num_survivor_regions; + size_t _num_old_regions; + size_t _num_humongous_regions; const MetaspaceCombinedStats _meta_sizes; // Only includes current eden regions. - uint* _eden_length_per_node; + uint* _num_eden_regions_per_node; // Only includes current survivor regions. - uint* _survivor_length_per_node; + uint* _num_survivor_regions_per_node; Data(G1CollectedHeap* g1_heap); ~Data(); diff --git a/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp b/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp index dba57d487ea..0af07282fc7 100644 --- a/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp +++ b/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp @@ -245,14 +245,14 @@ void G1MonitoringSupport::recalculate_sizes() { // use smaller value to subtract. _old_gen_used = _overall_used - MIN2(_overall_used, _eden_space_used + _survivor_space_used); - uint survivor_list_length = _g1h->survivor_regions_count(); + uint num_survivor_regions = _g1h->survivor_regions_count(); - uint young_list_target_length = _g1h->policy()->young_list_target_length(); - assert(young_list_target_length >= survivor_list_length, "invariant"); - uint eden_list_max_length = young_list_target_length - survivor_list_length; + uint target_num_young_regions = _g1h->policy()->target_num_young_regions(); + assert(target_num_young_regions >= num_survivor_regions, "invariant"); + uint max_num_eden_regions = target_num_young_regions - num_survivor_regions; // First calculate the committed sizes that can be calculated independently. - _survivor_space_committed = survivor_list_length * G1HeapRegion::GrainBytes; + _survivor_space_committed = num_survivor_regions * G1HeapRegion::GrainBytes; _old_gen_committed = G1HeapRegion::align_up_to_region_byte_size(_old_gen_used); // Next, start with the overall committed size. @@ -265,7 +265,7 @@ void G1MonitoringSupport::recalculate_sizes() { committed -= _survivor_space_committed + _old_gen_committed; // Next, calculate and remove the committed size for the eden. - _eden_space_committed = (size_t) eden_list_max_length * G1HeapRegion::GrainBytes; + _eden_space_committed = (size_t) max_num_eden_regions * G1HeapRegion::GrainBytes; // Somewhat defensive: be robust in case there are inaccuracies in // the calculations _eden_space_committed = MIN2(_eden_space_committed, committed); diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 6aeabb9ac9b..e2c01f9a13e 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -60,8 +60,8 @@ G1Policy::G1Policy(STWGCTimer* gc_timer) : _ihop_control(create_ihop_control(&_predictor)), _policy_counters(new GCPolicyCounters("GarbageFirst", 1, 2)), _cur_pause_start_sec(0.0), - _young_list_desired_length(0), - _young_list_target_length(0), + _desired_num_young_regions(0), + _target_num_young_regions(0), _eden_surv_rate_group(new G1SurvRateGroup()), _survivor_surv_rate_group(new G1SurvRateGroup()), _reserve_factor((double) G1ReservePercent / 100.0), @@ -95,45 +95,45 @@ void G1Policy::init(G1CollectedHeap* g1h, G1CollectionSet* collection_set) { _free_regions_at_end_of_collection = _g1h->num_free_regions(); - update_young_length_bounds(); + update_young_regions_bounds(); } void G1Policy::record_young_gc_pause_start() { phase_times()->record_gc_pause_start(); } -class G1YoungLengthPredictor { +class G1NumYoungRegionsPredictor { const double _base_time_ms; const double _base_free_regions; const double _target_pause_time_ms; const G1Policy* const _policy; public: - G1YoungLengthPredictor(double base_time_ms, - double base_free_regions, - double target_pause_time_ms, - const G1Policy* policy) : + G1NumYoungRegionsPredictor(double base_time_ms, + double base_free_regions, + double target_pause_time_ms, + const G1Policy* policy) : _base_time_ms(base_time_ms), _base_free_regions(base_free_regions), _target_pause_time_ms(target_pause_time_ms), _policy(policy) {} - bool will_fit(uint young_length) const { - if (young_length >= _base_free_regions) { + bool will_fit(uint num_young_regions) const { + if (num_young_regions >= _base_free_regions) { // end condition 1: not enough space for the young regions return false; } size_t bytes_to_copy = 0; - const double copy_time_ms = _policy->predict_eden_copy_time_ms(young_length, &bytes_to_copy); - const double young_other_time_ms = _policy->analytics()->predict_young_other_time_ms(young_length); + const double copy_time_ms = _policy->predict_eden_copy_time_ms(num_young_regions, &bytes_to_copy); + const double young_other_time_ms = _policy->analytics()->predict_young_other_time_ms(num_young_regions); const double pause_time_ms = _base_time_ms + copy_time_ms + young_other_time_ms; if (pause_time_ms > _target_pause_time_ms) { // end condition 2: prediction is over the target pause time return false; } - const size_t free_bytes = (_base_free_regions - young_length) * G1HeapRegion::GrainBytes; + const size_t free_bytes = (_base_free_regions - num_young_regions) * G1HeapRegion::GrainBytes; // When copying, we will likely need more bytes free than is live in the region. // Add some safety margin to factor in the confidence of our guess, and the @@ -167,61 +167,62 @@ void G1Policy::record_new_heap_size(uint new_number_of_regions) { _ihop_control->update_target_occupancy(new_number_of_regions * G1HeapRegion::GrainBytes); } -uint G1Policy::calculate_desired_eden_length_by_mmu() const { - assert(use_adaptive_young_list_length(), "precondition"); +uint G1Policy::calculate_desired_num_eden_regions_by_mmu() const { + assert(use_adaptive_num_young_regions(), "precondition"); double now_sec = os::elapsedTime(); double when_ms = _mmu_tracker->when_max_gc_sec(now_sec) * 1000.0; double alloc_rate_ms = _analytics->predict_alloc_rate_ms(); return (uint) ceil(alloc_rate_ms * when_ms); } -void G1Policy::update_young_length_bounds() { +void G1Policy::update_young_regions_bounds() { assert(!Universe::is_fully_initialized() || SafepointSynchronize::is_at_safepoint(), "must be"); bool for_young_only_phase = collector_state()->is_in_young_only_phase(); - update_young_length_bounds(_analytics->predict_pending_cards(for_young_only_phase), + update_young_regions_bounds(_analytics->predict_pending_cards(for_young_only_phase), _analytics->predict_card_rs_length(for_young_only_phase), _analytics->predict_code_root_rs_length(for_young_only_phase)); } -void G1Policy::update_young_length_bounds(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length) { - uint old_young_list_target_length = young_list_target_length(); +void G1Policy::update_young_regions_bounds(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length) { + uint old_target_num_young_regions = target_num_young_regions(); - uint min_young_length_by_sizer = _young_gen_sizer.min_desired_young_length(); - uint max_young_length_by_sizer = _young_gen_sizer.max_desired_young_length(); + uint min_num_young_regions_by_sizer = _young_gen_sizer.min_desired_num_regions(); + uint max_num_young_regions_by_sizer = _young_gen_sizer.max_desired_num_regions(); - if (max_young_length_by_sizer < min_young_length_by_sizer) { - // This can happen due to races with heap_size_changed() at mutator time. Do not update the young gen - // lengths. Will be updated on the next regular call anyway. + if (max_num_young_regions_by_sizer < min_num_young_regions_by_sizer) { + // This can happen due to races with heap_size_changed() at mutator time. Do not update the + // young regions. Will be updated on the next regular call anyway. assert(!SafepointSynchronize::is_at_safepoint(), "must be"); return; } - uint new_young_list_desired_length = calculate_young_desired_length(pending_cards, - card_rs_length, - code_root_rs_length, - min_young_length_by_sizer, - max_young_length_by_sizer); - uint new_young_list_target_length = calculate_young_target_length(new_young_list_desired_length, min_young_length_by_sizer); + uint new_desired_num_young_regions = calculate_desired_num_young_regions(pending_cards, + card_rs_length, + code_root_rs_length, + min_num_young_regions_by_sizer, + max_num_young_regions_by_sizer); + uint new_target_num_young_regions = calculate_target_num_young_regions(new_desired_num_young_regions, + min_num_young_regions_by_sizer); - log_trace(gc, ergo, heap)("Young list length update: pending cards %zu card_rs_length %zu old target %u desired: %u target: %u", + log_trace(gc, ergo, heap)("Young num regions update: pending cards %zu card_rs_length %zu old target %u desired: %u target: %u", pending_cards, card_rs_length, - old_young_list_target_length, - new_young_list_desired_length, - new_young_list_target_length); + old_target_num_young_regions, + new_desired_num_young_regions, + new_target_num_young_regions); // Write back. This is not an attempt to control visibility order to other threads - // here; all the revising of the young gen length are best effort to keep pause time. + // here; all the revising of the number of young regions are best effort to keep pause time. // E.g. we could be "too late" revising young gen upwards to avoid GC because // there is some time left, or some threads could get different values for stopping // allocation. // That is "fine" - at most this will schedule a GC (hopefully only a little) too // early or too late. - _young_list_desired_length.store_relaxed(new_young_list_desired_length); - _young_list_target_length.store_relaxed(new_young_list_target_length); + _desired_num_young_regions.store_relaxed(new_desired_num_young_regions); + _target_num_young_regions.store_relaxed(new_target_num_young_regions); } -// Calculates desired young gen length. It is calculated from: +// Calculates desired number of young regions. It is calculated from: // // - sizer min/max bounds on young gen // - pause time goal for whole young gen evacuation @@ -236,40 +237,40 @@ void G1Policy::update_young_length_bounds(size_t pending_cards, size_t card_rs_l // value smaller than what is already allocated or what can actually be allocated. // This return value is only an expectation. // -uint G1Policy::calculate_young_desired_length(size_t pending_cards, - size_t card_rs_length, - size_t code_root_rs_length, - uint min_young_length_by_sizer, - uint max_young_length_by_sizer) const { +uint G1Policy::calculate_desired_num_young_regions(size_t pending_cards, + size_t card_rs_length, + size_t code_root_rs_length, + uint min_num_young_regions_by_sizer, + uint max_num_young_regions_by_sizer) const { - assert(min_young_length_by_sizer >= 1, "invariant"); - assert(max_young_length_by_sizer >= min_young_length_by_sizer, "invariant"); + assert(min_num_young_regions_by_sizer >= 1, "invariant"); + assert(max_num_young_regions_by_sizer >= min_num_young_regions_by_sizer, "invariant"); // Calculate the absolute and desired min bounds first. // This is how many survivor regions we already have. - const uint survivor_length = _g1h->survivor_regions_count(); + const uint num_survivor_regions = _g1h->survivor_regions_count(); // Size of the already allocated young gen. - const uint allocated_young_length = _g1h->young_regions_count(); - // This is the absolute minimum young length that we can return. Ensure that we + const uint allocated_num_young_regions = _g1h->young_regions_count(); + // This is the absolute minimum number of young regions that we can return. Ensure that we // don't go below any user-defined minimum bound. Also, we must have at least // one eden region, to ensure progress. But when revising during the ensuing // mutator phase we might have already allocated more than either of those, in // which case use that. - uint absolute_min_young_length = MAX3(min_young_length_by_sizer, - survivor_length + 1, - allocated_young_length); + uint absolute_min_num_young_regions = MAX3(min_num_young_regions_by_sizer, + num_survivor_regions + 1, + allocated_num_young_regions); // Calculate the absolute max bounds. After evac failure or when revising the - // young length we might have exceeded absolute min length or absolute_max_length, + // number of young regions we might have exceeded absolute min or max_num_young_regions, // so adjust the result accordingly. - uint absolute_max_young_length = MAX2(max_young_length_by_sizer, absolute_min_young_length); + uint absolute_max_num_young_regions = MAX2(max_num_young_regions_by_sizer, absolute_min_num_young_regions); - uint desired_eden_length_by_mmu = 0; - uint desired_eden_length_by_pause = 0; + uint desired_num_eden_regions_by_mmu = 0; + uint desired_num_eden_regions_by_pause = 0; - uint desired_young_length = 0; - if (use_adaptive_young_list_length()) { - desired_eden_length_by_mmu = calculate_desired_eden_length_by_mmu(); + uint desired_num_young_regions = 0; + if (use_adaptive_num_young_regions()) { + desired_num_eden_regions_by_mmu = calculate_desired_num_eden_regions_by_mmu(); double base_time_ms = predict_base_time_ms(pending_cards, card_rs_length, code_root_rs_length); double retained_time_ms = predict_retained_regions_evac_time(); @@ -278,55 +279,56 @@ uint G1Policy::calculate_young_desired_length(size_t pending_cards, log_trace(gc, ergo, heap)("Predicted total base time: total %f base_time %f retained_time %f", total_time_ms, base_time_ms, retained_time_ms); - desired_eden_length_by_pause = - calculate_desired_eden_length_by_pause(total_time_ms, - absolute_min_young_length - survivor_length, - absolute_max_young_length - survivor_length); + desired_num_eden_regions_by_pause = + calculate_desired_num_eden_regions_by_pause(total_time_ms, + absolute_min_num_young_regions - num_survivor_regions, + absolute_max_num_young_regions - num_survivor_regions); // Incorporate MMU concerns; assume that it overrides the pause time // goal, as the default value has been chosen to effectively disable it. - uint desired_eden_length = MAX2(desired_eden_length_by_pause, - desired_eden_length_by_mmu); + uint desired_num_eden_regions = MAX2(desired_num_eden_regions_by_pause, + desired_num_eden_regions_by_mmu); - desired_young_length = desired_eden_length + survivor_length; + desired_num_young_regions = desired_num_eden_regions + num_survivor_regions; } else { // The user asked for a fixed young gen so we'll fix the young gen // whether the next GC is young or mixed. - desired_young_length = min_young_length_by_sizer; + desired_num_young_regions = min_num_young_regions_by_sizer; } - // Clamp to absolute min/max after we determined desired lengths. - desired_young_length = clamp(desired_young_length, absolute_min_young_length, absolute_max_young_length); - - log_trace(gc, ergo, heap)("Young desired length %u " - "survivor length %u " - "allocated young length %u " - "absolute min young length %u " - "absolute max young length %u " - "desired eden length by mmu %u " - "desired eden length by pause %u ", - desired_young_length, survivor_length, - allocated_young_length, absolute_min_young_length, - absolute_max_young_length, desired_eden_length_by_mmu, - desired_eden_length_by_pause); - - assert(desired_young_length >= allocated_young_length, "must be"); - return desired_young_length; -} - -// Limit the desired (wished) young length by current free regions. If the request + // Clamp to absolute min/max after we determined desired number of regions. + desired_num_young_regions = clamp(desired_num_young_regions, absolute_min_num_young_regions, absolute_max_num_young_regions); + + log_trace(gc, ergo, heap)("Desired young regions %u " + "survivor regions %u " + "allocated young regions %u " + "absolute min young regions %u " + "absolute max young regions %u " + "desired eden regions by mmu %u " + "desired eden regions by pause %u ", + desired_num_young_regions, num_survivor_regions, + allocated_num_young_regions, absolute_min_num_young_regions, + absolute_max_num_young_regions, desired_num_eden_regions_by_mmu, + desired_num_eden_regions_by_pause); + + assert(desired_num_young_regions >= allocated_num_young_regions, "must be"); + return desired_num_young_regions; +} + +// Limit the desired (wished) number of young regions by current free regions. If the request // can be satisfied without using up reserve regions, do so, otherwise eat into // the reserve, giving away at most what the heap sizer allows. -uint G1Policy::calculate_young_target_length(uint desired_young_length, uint min_young_length_by_sizer) const { - uint allocated_young_length = _g1h->young_regions_count(); +uint G1Policy::calculate_target_num_young_regions(uint desired_num_young_regions, + uint min_num_young_regions_by_sizer) const { + uint num_young_regions = _g1h->young_regions_count(); uint receiving_additional_eden; - if (allocated_young_length >= desired_young_length) { + if (num_young_regions >= desired_num_young_regions) { // Already used up all we actually want (may happen as G1 revises the - // young list length concurrently). Do not allow more, potentially resulting in GC. + // number of young regions concurrently). Do not allow more, potentially resulting in GC. receiving_additional_eden = 0; - log_trace(gc, ergo, heap)("Young target length: Already used up desired young %u allocated %u", - desired_young_length, - allocated_young_length); + log_trace(gc, ergo, heap)("Target young regions: Already used up desired young regions %u allocated young regions %u", + desired_num_young_regions, + num_young_regions); } else { // Now look at how many free regions are there currently, and the heap reserve. // We will try our best not to "eat" into the reserve as long as we can. If we @@ -336,57 +338,57 @@ uint G1Policy::calculate_young_target_length(uint desired_young_length, uint min // The heap reserve needs to be snapshotted for consistent use in the following. // It can be concurrently modified by the mutator as it expands the heap. It can // only increase at that time, so this is a conservative snapshot. So at worst this - // method will return a too small young gen length in that case. + // method will return a too small number of young regions in that case. uint reserve_regions = _reserve_regions.load_relaxed(); - uint max_to_eat_into_reserve = MIN2(min_young_length_by_sizer, + uint max_to_eat_into_reserve = MIN2(min_num_young_regions_by_sizer, (reserve_regions + 1) / 2); - log_trace(gc, ergo, heap)("Young target length: Common " + log_trace(gc, ergo, heap)("Target young regions: Common " "free regions at end of collection %u " - "desired young length %u " + "desired number of young regions %u " "reserve region %u " "max to eat into reserve %u", _free_regions_at_end_of_collection, - desired_young_length, + desired_num_young_regions, reserve_regions, max_to_eat_into_reserve); uint survivor_regions_count = _g1h->survivor_regions_count(); - uint desired_eden_length = desired_young_length - survivor_regions_count; - uint allocated_eden_length = allocated_young_length - survivor_regions_count; + uint desired_num_eden_regions = desired_num_young_regions - survivor_regions_count; + uint num_eden_regions = num_young_regions - survivor_regions_count; if (_free_regions_at_end_of_collection <= reserve_regions) { - // Fully eat (or already eating) into the reserve, hand back at most absolute_min_length regions. + // Fully eat (or already eating) into the reserve. uint receiving_eden = MIN3(_free_regions_at_end_of_collection, - desired_eden_length, - max_to_eat_into_reserve); + desired_num_eden_regions, + max_to_eat_into_reserve); // Ensure that we provision for at least one Eden region. receiving_eden = MAX2(receiving_eden, 1u); // We could already have allocated more regions than what we could get // above. - receiving_additional_eden = allocated_eden_length < receiving_eden ? - receiving_eden - allocated_eden_length : 0; + receiving_additional_eden = num_eden_regions < receiving_eden ? + receiving_eden - num_eden_regions : 0; - log_trace(gc, ergo, heap)("Young target length: Fully eat into reserve " + log_trace(gc, ergo, heap)("Target young regions: Fully eat into reserve " "receiving eden %u receiving additional eden %u", receiving_eden, receiving_additional_eden); - } else if (_free_regions_at_end_of_collection < (desired_eden_length + reserve_regions)) { + } else if (_free_regions_at_end_of_collection < (desired_num_eden_regions + reserve_regions)) { // Partially eat into the reserve, at most max_to_eat_into_reserve regions. uint free_outside_reserve = _free_regions_at_end_of_collection - reserve_regions; - assert(free_outside_reserve < desired_eden_length, + assert(free_outside_reserve < desired_num_eden_regions, "must be %u %u", - free_outside_reserve, desired_eden_length); + free_outside_reserve, desired_num_eden_regions); - uint receiving_within_reserve = MIN2(desired_eden_length - free_outside_reserve, + uint receiving_within_reserve = MIN2(desired_num_eden_regions - free_outside_reserve, max_to_eat_into_reserve); uint receiving_eden = free_outside_reserve + receiving_within_reserve; // Again, we could have already allocated more than we could get. - receiving_additional_eden = allocated_eden_length < receiving_eden ? - receiving_eden - allocated_eden_length : 0; + receiving_additional_eden = num_eden_regions < receiving_eden ? + receiving_eden - num_eden_regions : 0; - log_trace(gc, ergo, heap)("Young target length: Partially eat into reserve " + log_trace(gc, ergo, heap)("Target young regions: Partially eat into reserve " "free outside reserve %u " "receiving within reserve %u " "receiving eden %u " @@ -395,116 +397,116 @@ uint G1Policy::calculate_young_target_length(uint desired_young_length, uint min receiving_eden, receiving_additional_eden); } else { // No need to use the reserve. - receiving_additional_eden = desired_young_length - allocated_young_length; - log_trace(gc, ergo, heap)("Young target length: No need to use reserve " + receiving_additional_eden = desired_num_young_regions - num_young_regions; + log_trace(gc, ergo, heap)("Target young regions: No need to use reserve " "receiving additional eden %u", receiving_additional_eden); } } - uint target_young_length = allocated_young_length + receiving_additional_eden; + uint target_num_young_regions = num_young_regions + receiving_additional_eden; - assert(target_young_length >= allocated_young_length, "must be"); + assert(target_num_young_regions >= num_young_regions, "must be"); - log_trace(gc, ergo, heap)("Young target length: " - "young target length %u " - "allocated young length %u " + log_trace(gc, ergo, heap)("Target num young regions: " + "target num young regions %u " + "allocated number of young regions %u " "received additional eden %u", - target_young_length, allocated_young_length, + target_num_young_regions, num_young_regions, receiving_additional_eden); - return target_young_length; + return target_num_young_regions; } -uint G1Policy::calculate_desired_eden_length_by_pause(double base_time_ms, - uint min_eden_length, - uint max_eden_length) const { +uint G1Policy::calculate_desired_num_eden_regions_by_pause(double base_time_ms, + uint min_num_eden_regions, + uint max_num_eden_regions) const { if (!next_gc_should_be_mixed()) { - return calculate_desired_eden_length_before_young_only(base_time_ms, - min_eden_length, - max_eden_length); + return calculate_desired_num_eden_regions_before_young_only(base_time_ms, + min_num_eden_regions, + max_num_eden_regions); } else { - return calculate_desired_eden_length_before_mixed(base_time_ms, - min_eden_length, - max_eden_length); + return calculate_desired_num_eden_regions_before_mixed(base_time_ms, + min_num_eden_regions, + max_num_eden_regions); } } -uint G1Policy::calculate_desired_eden_length_before_young_only(double base_time_ms, - uint min_eden_length, - uint max_eden_length) const { - assert(use_adaptive_young_list_length(), "pre-condition"); +uint G1Policy::calculate_desired_num_eden_regions_before_young_only(double base_time_ms, + uint min_num_eden_regions, + uint max_num_eden_regions) const { + assert(use_adaptive_num_young_regions(), "pre-condition"); - assert(min_eden_length <= max_eden_length, "must be %u %u", min_eden_length, max_eden_length); + assert(min_num_eden_regions <= max_num_eden_regions, "must be %u %u", min_num_eden_regions, max_num_eden_regions); - // Here, we will make sure that the shortest young length that + // Here, we will make sure that the smallest number of eden regions that // makes sense fits within the target pause time. - G1YoungLengthPredictor p(base_time_ms, - _free_regions_at_end_of_collection, - _mmu_tracker->max_gc_time() * 1000.0, - this); - if (p.will_fit(min_eden_length)) { - // The shortest young length will fit into the target pause time; - // we'll now check whether the absolute maximum number of young - // regions will fit in the target pause time. If not, we'll do - // a binary search between min_young_length and max_young_length. - if (p.will_fit(max_eden_length)) { - // The maximum young length will fit into the target pause time. - // We are done so set min young length to the maximum length (as - // the result is assumed to be returned in min_young_length). - min_eden_length = max_eden_length; + G1NumYoungRegionsPredictor p(base_time_ms, + _free_regions_at_end_of_collection, + _mmu_tracker->max_gc_time() * 1000.0, + this); + if (p.will_fit(min_num_eden_regions)) { + // The smallest number of eden regions will fit into the target pause time; + // we'll now check whether the absolute maximum number of young regions will fit + // in the target pause time. If not, we'll do a binary search between + // min_num_eden_regions and max_num_eden_regions. + if (p.will_fit(max_num_eden_regions)) { + // The maximum number of eden regions will fit into the target pause time. + // We are done, so set min_num_eden_regions to max_num_eden_regions (as the result is + // assumed to be returned in min_num_eden_regions). + min_num_eden_regions = max_num_eden_regions; } else { - // The maximum possible number of young regions will not fit within + // The maximum possible number of eden regions will not fit within // the target pause time so we'll search for the optimal - // length. The loop invariants are: + // number of eden regions. The loop invariants are: // - // min_young_length < max_young_length - // min_young_length is known to fit into the target pause time - // max_young_length is known not to fit into the target pause time + // min_num_eden_regions < max_num_eden_regions + // min_num_eden_regions is known to fit into the target pause time + // max_num_eden_regions is known not to fit into the target pause time // // Going into the loop we know the above hold as we've just // checked them. Every time around the loop we check whether - // the middle value between min_young_length and - // max_young_length fits into the target pause time. If it + // the middle value between min_num_eden_regions and + // max_num_eden_regions fits into the target pause time. If it // does, it becomes the new min. If it doesn't, it becomes // the new max. This way we maintain the loop invariants. - assert(min_eden_length < max_eden_length, "invariant"); - uint diff = (max_eden_length - min_eden_length) / 2; + precond(min_num_eden_regions < max_num_eden_regions); + uint diff = (max_num_eden_regions - min_num_eden_regions) / 2; while (diff > 0) { - uint eden_length = min_eden_length + diff; - if (p.will_fit(eden_length)) { - min_eden_length = eden_length; + uint num_eden_regions = min_num_eden_regions + diff; + if (p.will_fit(num_eden_regions)) { + min_num_eden_regions = num_eden_regions; } else { - max_eden_length = eden_length; + max_num_eden_regions = num_eden_regions; } - assert(min_eden_length < max_eden_length, "invariant"); - diff = (max_eden_length - min_eden_length) / 2; + postcond(min_num_eden_regions < max_num_eden_regions); + diff = (max_num_eden_regions - min_num_eden_regions) / 2; } - // The results is min_young_length which, according to the + // The result is min_num_eden_regions which, according to the // loop invariants, should fit within the target pause time. // These are the post-conditions of the binary search above: - assert(min_eden_length < max_eden_length, - "otherwise we should have discovered that max_eden_length " + assert(min_num_eden_regions < max_num_eden_regions, + "otherwise we should have discovered that max_num_eden_regions " "fits into the pause target and not done the binary search"); - assert(p.will_fit(min_eden_length), - "min_eden_length, the result of the binary search, should " + assert(p.will_fit(min_num_eden_regions), + "min_num_eden_regions, the result of the binary search, should " "fit into the pause target"); - assert(!p.will_fit(min_eden_length + 1), - "min_eden_length, the result of the binary search, should be " - "optimal, so no larger length should fit into the pause target"); + assert(!p.will_fit(min_num_eden_regions + 1), + "min_num_eden_regions, the result of the binary search, should be " + "optimal, so no larger number of eden regions should fit into the pause target"); } } else { - // Even the minimum length doesn't fit into the pause time - // target, return it as the result nevertheless. + // Even the minimum number of eden regions does not fit into the target pause time, + // return it as the result nevertheless. } - return min_eden_length; + return min_num_eden_regions; } -uint G1Policy::calculate_desired_eden_length_before_mixed(double base_time_ms, - uint min_eden_length, - uint max_eden_length) const { +uint G1Policy::calculate_desired_num_eden_regions_before_mixed(double base_time_ms, + uint min_num_eden_regions, + uint max_num_eden_regions) const { uint min_marking_candidates = MIN2(calc_min_old_cset_length(candidates()->last_marking_candidates_length()), candidates()->from_marking_groups().num_regions()); double predicted_region_evac_time_ms = base_time_ms; @@ -517,9 +519,9 @@ uint G1Policy::calculate_desired_eden_length_before_mixed(double base_time_ms, selected_candidates += gr->length(); } - return calculate_desired_eden_length_before_young_only(predicted_region_evac_time_ms, - min_eden_length, - max_eden_length); + return calculate_desired_num_eden_regions_before_young_only(predicted_region_evac_time_ms, + min_num_eden_regions, + max_num_eden_regions); } double G1Policy::predict_survivor_regions_evac_time() const { @@ -572,10 +574,10 @@ G1GCPhaseTimes* G1Policy::phase_times() const { return _phase_times; } -void G1Policy::revise_young_list_target_length(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length) { - guarantee(use_adaptive_young_list_length(), "should not call this otherwise" ); +void G1Policy::revise_target_num_young_regions(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length) { + guarantee(use_adaptive_num_young_regions(), "should not call this otherwise" ); - update_young_length_bounds(pending_cards, card_rs_length, code_root_rs_length); + update_young_regions_bounds(pending_cards, card_rs_length, code_root_rs_length); } void G1Policy::record_full_collection_start() { @@ -601,7 +603,7 @@ void G1Policy::record_full_collection_end(size_t allocation_word_size) { _free_regions_at_end_of_collection = _g1h->num_free_regions(); _survivor_surv_rate_group->reset(); - update_young_length_bounds(); + update_young_regions_bounds(); record_pause(Pause::Full, start_time_sec, end_sec); } @@ -641,9 +643,9 @@ void G1Policy::record_dirtying_stats(double last_mutator_start_dirty_ms, double yield_duration_ms, size_t next_pending_cards_from_gc, size_t next_to_collection_set_cards) { - assert(SafepointSynchronize::is_at_safepoint() || G1ReviseYoungLength_lock->is_locked(), + assert(SafepointSynchronize::is_at_safepoint() || G1ReviseNumYoungRegions_lock->is_locked(), "must be (at safepoint %s locked %s)", - BOOL_TO_STR(SafepointSynchronize::is_at_safepoint()), BOOL_TO_STR(G1ReviseYoungLength_lock->is_locked())); + BOOL_TO_STR(SafepointSynchronize::is_at_safepoint()), BOOL_TO_STR(G1ReviseNumYoungRegions_lock->is_locked())); // Record mutator's card logging rate. // Unlike above for conc-refine rate, here we should not require a @@ -693,7 +695,7 @@ void G1Policy::record_young_collection_start() { record_pause_start_time(); // We only need to do this here as the policy will only be applied // to the GC we're about to start. so, no point is calculating this - // every time we calculate / recalculate the target young length. + // every time we calculate / recalculate the target number of young regions. update_survivors_policy(); assert(max_survivor_regions() + _g1h->num_used_regions() <= _g1h->max_num_regions(), @@ -992,7 +994,7 @@ G1CollectorState G1Policy::record_young_collection_end(bool concurrent_operation // Do not update dynamic IHOP due to G1 periodic collection as it is highly likely // that in this case we are not running in a "normal" operating mode. if (_g1h->gc_cause() != GCCause::_g1_periodic_collection) { - update_young_length_bounds(); + update_young_regions_bounds(); // Take snapshots of these values here as update_ihop_prediction // may complete the concurrent cycle and reset the values. @@ -1068,11 +1070,11 @@ bool G1Policy::update_ihop_prediction(double mutator_time_s, // The second clause prevents skewing the IHOP prediction with (typically) degenerate // back-to-back young-gen-size samples. if (this_gc_was_young_only && mutator_time_s > min_valid_time) { - // IHOP control wants to know the expected young gen length if it were not - // restrained by the heap reserve. Using the actual length would make the + // IHOP control wants to know the expected number of young regions if it were not + // restrained by the heap reserve. Using the current number of regions would make the // prediction too small and the limit the young gen every time we get to the // predicted target occupancy. - size_t young_gen_size = young_list_desired_length() * G1HeapRegion::GrainBytes; + size_t young_gen_size = desired_num_young_regions() * G1HeapRegion::GrainBytes; _ihop_control->record_expected_young_gen_size(young_gen_size); report = true; @@ -1188,7 +1190,7 @@ double G1Policy::predict_region_code_root_scan_time(G1HeapRegion* hr, bool for_y } bool G1Policy::should_allocate_mutator_region() const { - if (_g1h->young_regions_count() < young_list_target_length()) { + if (_g1h->young_regions_count() < target_num_young_regions()) { return true; } @@ -1205,8 +1207,8 @@ bool G1Policy::should_expand_on_mutator_allocation() const { return !is_init_completed(); } -bool G1Policy::use_adaptive_young_list_length() const { - return _young_gen_sizer.use_adaptive_young_list_length(); +bool G1Policy::use_adaptive_num_young_regions() const { + return _young_gen_sizer.use_adaptive_num_young_regions(); } size_t G1Policy::estimate_used_young_bytes_locked() const { @@ -1231,7 +1233,7 @@ void G1Policy::print_age_table() { // Calculates survivor space parameters. void G1Policy::update_survivors_policy() { double max_survivor_regions_d = - (double)young_list_target_length() / (double) SurvivorRatio; + (double)target_num_young_regions() / (double) SurvivorRatio; // Calculate desired survivor size based on desired max survivor regions (unconstrained // by remaining heap). Otherwise we may cause undesired promotions as we are @@ -1440,7 +1442,7 @@ bool G1Policy::try_get_available_bytes_estimate(size_t& available_bytes) const { size_t used_bytes = estimate_used_young_bytes_locked(); Heap_lock->unlock(); - size_t young_bytes = young_list_target_length() * G1HeapRegion::GrainBytes; + size_t young_bytes = target_num_young_regions() * G1HeapRegion::GrainBytes; available_bytes = young_bytes - MIN2(young_bytes, used_bytes); return true; } else { @@ -1532,7 +1534,7 @@ void G1Policy::transfer_survivors_to_cset(const G1SurvivorRegions* survivors) { } stop_adding_survivor_regions(); - // Don't clear the survivor list handles until the start of + // Don't clear the survivor region tracking until the start of // the next evacuation pause - we need it in order to re-tag // the survivor regions from this evacuation pause as 'young' // at the start of the next. diff --git a/src/hotspot/share/gc/g1/g1Policy.hpp b/src/hotspot/share/gc/g1/g1Policy.hpp index b661daa9a3e..1fa81fe60b6 100644 --- a/src/hotspot/share/gc/g1/g1Policy.hpp +++ b/src/hotspot/share/gc/g1/g1Policy.hpp @@ -79,11 +79,11 @@ class G1Policy: public CHeapObj { double _cur_pause_start_sec; - // Desired young gen length without taking actually available free regions into + // Desired number of young regions without taking actually available free regions into // account. - Atomic _young_list_desired_length; - // Actual target length given available free memory. - Atomic _young_list_target_length; + Atomic _desired_num_young_regions; + // Actual target number of young regions given available free memory. + Atomic _target_num_young_regions; // The survivor rate groups below must be initialized after the predictor because they // indirectly use it through the "this" object passed to their constructor. @@ -193,44 +193,45 @@ class G1Policy: public CHeapObj { // Lazily initialized mutable G1GCPhaseTimes* _phase_times; - // Updates the internal young gen maximum and target and desired lengths. + // Updates the internal young gen maximum and target and desired number of young regions. // If no parameters are passed, predict pending cards, card set remset length and // code root remset length using the prediction model. - void update_young_length_bounds(); - void update_young_length_bounds(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length); + void update_young_regions_bounds(); + void update_young_regions_bounds(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length); - // Calculate and return the minimum desired eden length based on the MMU target. - uint calculate_desired_eden_length_by_mmu() const; + // Calculate and return the minimum desired number of eden regions based on the MMU target. + uint calculate_desired_num_eden_regions_by_mmu() const; - // Calculate the desired eden length meeting the pause time goal. - // Min_eden_length and max_eden_length are the bounds + // Calculate the desired number of eden regions meeting the pause time goal. + // min_num_eden_regions and max_num_eden_regions are the bounds // (inclusive) within which eden can grow. - uint calculate_desired_eden_length_by_pause(double base_time_ms, - uint min_eden_length, - uint max_eden_length) const; + uint calculate_desired_num_eden_regions_by_pause(double base_time_ms, + uint min_num_eden_regions, + uint max_num_eden_regions) const; - // Calculate the desired eden length that can fit into the pause time + // Calculate the desired number of eden regions that can fit into the pause time // goal before young only gcs. - uint calculate_desired_eden_length_before_young_only(double base_time_ms, - uint min_eden_length, - uint max_eden_length) const; + uint calculate_desired_num_eden_regions_before_young_only(double base_time_ms, + uint min_num_eden_regions, + uint max_num_eden_regions) const; - // Calculates the desired eden length before mixed gc so that after adding the + // Calculates the desired number of eden regions before mixed gc so that after adding the // minimum amount of old gen regions from the collection set, the eden fits into // the pause time goal. - uint calculate_desired_eden_length_before_mixed(double base_time_ms, - uint min_eden_length, - uint max_eden_length) const; + uint calculate_desired_num_eden_regions_before_mixed(double base_time_ms, + uint min_num_eden_regions, + uint max_num_eden_regions) const; - // Calculate desired young length based on current situation without taking actually + // Calculate desired number of young regions based on current situation without taking actually // available free regions into account. - uint calculate_young_desired_length(size_t pending_cards, - size_t card_rs_length, - size_t code_root_rs_length, - uint min_young_length_by_sizer, - uint max_young_length_by_sizer) const; - // Limit the given desired young length to available free regions. - uint calculate_young_target_length(uint desired_young_length, uint min_young_length_by_sizer) const; + uint calculate_desired_num_young_regions(size_t pending_cards, + size_t card_rs_length, + size_t code_root_rs_length, + uint min_num_young_regions_by_sizer, + uint max_num_young_regions_by_sizer) const; + // Limit the given desired number of young regions to available free regions. + uint calculate_target_num_young_regions(uint desired_num_young_regions, + uint min_num_young_regions_by_sizer) const; double predict_survivor_regions_evac_time() const; double predict_retained_regions_evac_time() const; @@ -283,10 +284,10 @@ class G1Policy: public CHeapObj { G1GCPhaseTimes* phase_times() const; - // Check the current value of the young list RSet length and + // Check the current value of the young generation RSet length and // compare it against the last prediction. If the current value is - // higher, recalculate the young list target length prediction. - void revise_young_list_target_length(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length); + // higher, recalculate the target number of young regions prediction. + void revise_target_num_young_regions(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length); // This should be called after the heap is resized. void record_new_heap_size(uint new_number_of_regions); @@ -347,13 +348,13 @@ class G1Policy: public CHeapObj { // This must be called at the very beginning of an evacuation pause. void decide_on_concurrent_start_pause(); - uint young_list_desired_length() const { return _young_list_desired_length.load_relaxed(); } - uint young_list_target_length() const { return _young_list_target_length.load_relaxed(); } + uint desired_num_young_regions() const { return _desired_num_young_regions.load_relaxed(); } + uint target_num_young_regions() const { return _target_num_young_regions.load_relaxed(); } bool should_allocate_mutator_region() const; bool should_expand_on_mutator_allocation() const; - bool use_adaptive_young_list_length() const; + bool use_adaptive_num_young_regions() const; // Try to get an estimate of the currently available bytes in the young gen. This // operation considers itself low-priority: if other threads need the resources diff --git a/src/hotspot/share/gc/g1/g1ReviseYoungLengthTask.cpp b/src/hotspot/share/gc/g1/g1ReviseNumYoungRegionsTask.cpp similarity index 79% rename from src/hotspot/share/gc/g1/g1ReviseYoungLengthTask.cpp rename to src/hotspot/share/gc/g1/g1ReviseNumYoungRegionsTask.cpp index 2f7acd9b710..71c8d7bf772 100644 --- a/src/hotspot/share/gc/g1/g1ReviseYoungLengthTask.cpp +++ b/src/hotspot/share/gc/g1/g1ReviseNumYoungRegionsTask.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 @@ -24,12 +24,12 @@ #include "gc/g1/g1CollectedHeap.hpp" #include "gc/g1/g1Policy.hpp" -#include "gc/g1/g1ReviseYoungLengthTask.hpp" +#include "gc/g1/g1ReviseNumYoungRegionsTask.hpp" #include "gc/g1/g1ServiceThread.hpp" #include "gc/shared/suspendibleThreadSet.hpp" -jlong G1ReviseYoungLengthTask::reschedule_delay_ms() const { +jlong G1ReviseNumYoungRegionsTask::reschedule_delay_ms() const { G1Policy* policy = G1CollectedHeap::heap()->policy(); size_t available_bytes; if (policy->try_get_available_bytes_estimate(available_bytes)) { @@ -47,7 +47,7 @@ jlong G1ReviseYoungLengthTask::reschedule_delay_ms() const { } } -class G1ReviseYoungLengthTask::RemSetSamplingClosure : public G1HeapRegionClosure { +class G1ReviseNumYoungRegionsTask::RemSetSamplingClosure : public G1HeapRegionClosure { size_t _sampled_code_root_rs_length; public: @@ -62,16 +62,16 @@ class G1ReviseYoungLengthTask::RemSetSamplingClosure : public G1HeapRegionClosur size_t sampled_code_root_rs_length() const { return _sampled_code_root_rs_length; } }; -void G1ReviseYoungLengthTask::adjust_young_list_target_length() { +void G1ReviseNumYoungRegionsTask::adjust_target_num_young_regions() { G1CollectedHeap* g1h = G1CollectedHeap::heap(); G1Policy* policy = g1h->policy(); - assert(policy->use_adaptive_young_list_length(), "should not call otherwise"); + assert(policy->use_adaptive_num_young_regions(), "should not call otherwise"); size_t pending_cards; size_t current_to_collection_set_cards; { - MutexLocker x(G1ReviseYoungLength_lock, Mutex::_no_safepoint_check_flag); + MutexLocker x(G1ReviseNumYoungRegions_lock, Mutex::_no_safepoint_check_flag); pending_cards = policy->current_pending_cards(); current_to_collection_set_cards = policy->current_to_collection_set_cards(); } @@ -79,18 +79,18 @@ void G1ReviseYoungLengthTask::adjust_young_list_target_length() { RemSetSamplingClosure cl; g1h->collection_set()->iterate(&cl); - policy->revise_young_list_target_length(pending_cards, + policy->revise_target_num_young_regions(pending_cards, current_to_collection_set_cards, cl.sampled_code_root_rs_length()); } -G1ReviseYoungLengthTask::G1ReviseYoungLengthTask(const char* name) : +G1ReviseNumYoungRegionsTask::G1ReviseNumYoungRegionsTask(const char* name) : G1ServiceTask(name) { } -void G1ReviseYoungLengthTask::execute() { +void G1ReviseNumYoungRegionsTask::execute() { SuspendibleThreadSetJoiner sts; - adjust_young_list_target_length(); + adjust_target_num_young_regions(); schedule(reschedule_delay_ms()); } diff --git a/src/hotspot/share/gc/g1/g1ReviseYoungLengthTask.hpp b/src/hotspot/share/gc/g1/g1ReviseNumYoungRegionsTask.hpp similarity index 73% rename from src/hotspot/share/gc/g1/g1ReviseYoungLengthTask.hpp rename to src/hotspot/share/gc/g1/g1ReviseNumYoungRegionsTask.hpp index baa8af75fb7..8bc9e256366 100644 --- a/src/hotspot/share/gc/g1/g1ReviseYoungLengthTask.hpp +++ b/src/hotspot/share/gc/g1/g1ReviseNumYoungRegionsTask.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_GC_G1_G1REVISEYOUNGLENGTHTASK_HPP -#define SHARE_GC_G1_G1REVISEYOUNGLENGTHTASK_HPP +#ifndef SHARE_GC_G1_G1REVISENUMYOUNGREGIONSTASK_HPP +#define SHARE_GC_G1_G1REVISENUMYOUNGREGIONSTASK_HPP #include "gc/g1/g1CardSetMemory.hpp" #include "gc/g1/g1HeapRegionRemSet.hpp" @@ -32,18 +32,18 @@ #include "utilities/growableArray.hpp" #include "utilities/ticks.hpp" -// ServiceTask to revise the young generation target length. -class G1ReviseYoungLengthTask : public G1ServiceTask { +// ServiceTask to revise the target number of young regions. +class G1ReviseNumYoungRegionsTask : public G1ServiceTask { // The delay used to reschedule this task. jlong reschedule_delay_ms() const; class RemSetSamplingClosure; // Helper class for calculating remembered set summary. - // Adjust the target length (in regions) of the young gen, based on the - // current length of the remembered sets. + // Adjust the target number of young regions, based on the + // current occupancy of the remembered sets. // - // At the end of the GC G1 determines the length of the young gen based on + // At the end of the GC G1 determines the number of young regions based on // how much time the next GC can take, and when the next GC may occur // according to the MMU. // @@ -51,13 +51,13 @@ class G1ReviseYoungLengthTask : public G1ServiceTask { // the remembered sets (and many other components), so this thread constantly // reevaluates the prediction for the remembered set scanning costs, and potentially // resizes the young gen. This may do a premature GC or even increase the young - // gen size to keep pause time length goal. - void adjust_young_list_target_length(); + // gen size to keep pause time goal. + void adjust_target_num_young_regions(); public: - explicit G1ReviseYoungLengthTask(const char* name); + explicit G1ReviseNumYoungRegionsTask(const char* name); void execute() override; }; -#endif // SHARE_GC_G1_G1REVISEYOUNGLENGTHTASK_HPP \ No newline at end of file +#endif // SHARE_GC_G1_G1REVISENUMYOUNGREGIONSTASK_HPP diff --git a/src/hotspot/share/gc/g1/g1YoungGenSizer.cpp b/src/hotspot/share/gc/g1/g1YoungGenSizer.cpp index 60c79ec28df..e817ebcab56 100644 --- a/src/hotspot/share/gc/g1/g1YoungGenSizer.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGenSizer.cpp @@ -30,7 +30,7 @@ #include "runtime/globals_extension.hpp" G1YoungGenSizer::G1YoungGenSizer() : _sizer_kind(SizerDefaults), - _use_adaptive_sizing(true), _min_desired_young_length(), _max_desired_young_length(0) { + _use_adaptive_sizing(true), _min_desired_num_regions(), _max_desired_num_regions(0) { precond(!FLAG_IS_ERGO(NewRatio)); precond(!FLAG_IS_ERGO(NewSize)); @@ -100,16 +100,16 @@ G1YoungGenSizer::G1YoungGenSizer() : _sizer_kind(SizerDefaults), } if (user_specified_NewSize) { - _min_desired_young_length.store_relaxed(MAX2((uint)(NewSize / G1HeapRegion::GrainBytes), 1U)); + _min_desired_num_regions.store_relaxed(MAX2((uint)(NewSize / G1HeapRegion::GrainBytes), 1U)); } if (user_specified_MaxNewSize) { - _max_desired_young_length.store_relaxed(MAX2((uint)(MaxNewSize / G1HeapRegion::GrainBytes), 1U)); + _max_desired_num_regions.store_relaxed(MAX2((uint)(MaxNewSize / G1HeapRegion::GrainBytes), 1U)); } if (user_specified_NewSize && user_specified_MaxNewSize) { _sizer_kind = SizerMaxAndNewSize; - _use_adaptive_sizing = min_desired_young_length() != max_desired_young_length(); + _use_adaptive_sizing = min_desired_num_regions() != max_desired_num_regions(); } else if (user_specified_NewSize) { _sizer_kind = SizerNewSizeOnly; } else { @@ -118,52 +118,52 @@ G1YoungGenSizer::G1YoungGenSizer() : _sizer_kind(SizerDefaults), } } -uint G1YoungGenSizer::calculate_default_min_length(uint new_number_of_heap_regions) { +uint G1YoungGenSizer::calculate_default_min_num_regions(uint new_number_of_heap_regions) { uint default_value = (new_number_of_heap_regions * G1NewSizePercent) / 100; return MAX2(1U, default_value); } -uint G1YoungGenSizer::calculate_default_max_length(uint new_number_of_heap_regions) { +uint G1YoungGenSizer::calculate_default_max_num_regions(uint new_number_of_heap_regions) { uint default_value = (new_number_of_heap_regions * G1MaxNewSizePercent) / 100; return MAX2(1U, default_value); } -void G1YoungGenSizer::recalculate_min_max_young_length(uint number_of_heap_regions, uint* min_young_length, uint* max_young_length) { +void G1YoungGenSizer::recalculate_min_max_num_regions(uint number_of_heap_regions, uint* min_num_young_regions, uint* max_num_young_regions) { assert(number_of_heap_regions > 0, "Heap must be initialized"); switch (_sizer_kind) { case SizerDefaults: - *min_young_length = calculate_default_min_length(number_of_heap_regions); - *max_young_length = calculate_default_max_length(number_of_heap_regions); + *min_num_young_regions = calculate_default_min_num_regions(number_of_heap_regions); + *max_num_young_regions = calculate_default_max_num_regions(number_of_heap_regions); break; case SizerNewSizeOnly: - *max_young_length = calculate_default_max_length(number_of_heap_regions); - *max_young_length = MAX2(*min_young_length, *max_young_length); + *max_num_young_regions = calculate_default_max_num_regions(number_of_heap_regions); + *max_num_young_regions = MAX2(*min_num_young_regions, *max_num_young_regions); break; case SizerMaxNewSizeOnly: - *min_young_length = calculate_default_min_length(number_of_heap_regions); - *min_young_length = MIN2(*min_young_length, *max_young_length); + *min_num_young_regions = calculate_default_min_num_regions(number_of_heap_regions); + *min_num_young_regions = MIN2(*min_num_young_regions, *max_num_young_regions); break; case SizerMaxAndNewSize: // Do nothing. Values set on the command line, don't update them at runtime. break; case SizerNewRatio: - *min_young_length = MAX2((uint)(number_of_heap_regions / (NewRatio + 1)), 1u); - *max_young_length = *min_young_length; + *min_num_young_regions = MAX2((uint)(number_of_heap_regions / (NewRatio + 1)), 1u); + *max_num_young_regions = *min_num_young_regions; break; default: ShouldNotReachHere(); } - assert(*min_young_length <= *max_young_length, "Invalid min/max young gen size values"); + assert(*min_num_young_regions <= *max_num_young_regions, "Invalid min/max young gen size values"); } void G1YoungGenSizer::adjust_max_new_size(uint number_of_heap_regions) { // We need to pass the desired values because recalculation may not update these // values in some cases. - uint unused_new_min = min_desired_young_length(); - uint new_max = max_desired_young_length(); - recalculate_min_max_young_length(number_of_heap_regions, &unused_new_min, &new_max); + uint unused_new_min = min_desired_num_regions(); + uint new_max = max_desired_num_regions(); + recalculate_min_max_num_regions(number_of_heap_regions, &unused_new_min, &new_max); size_t max_young_size = new_max * G1HeapRegion::GrainBytes; if (max_young_size != MaxNewSize) { @@ -172,9 +172,9 @@ void G1YoungGenSizer::adjust_max_new_size(uint number_of_heap_regions) { } void G1YoungGenSizer::heap_size_changed(uint new_number_of_heap_regions) { - uint min = min_desired_young_length(); - uint max = max_desired_young_length(); - recalculate_min_max_young_length(new_number_of_heap_regions, &min, &max); - _min_desired_young_length.store_relaxed(min); - _max_desired_young_length.store_relaxed(max); + uint min = min_desired_num_regions(); + uint max = max_desired_num_regions(); + recalculate_min_max_num_regions(new_number_of_heap_regions, &min, &max); + _min_desired_num_regions.store_relaxed(min); + _max_desired_num_regions.store_relaxed(max); } diff --git a/src/hotspot/share/gc/g1/g1YoungGenSizer.hpp b/src/hotspot/share/gc/g1/g1YoungGenSizer.hpp index c60c3c373a9..ea269f72559 100644 --- a/src/hotspot/share/gc/g1/g1YoungGenSizer.hpp +++ b/src/hotspot/share/gc/g1/g1YoungGenSizer.hpp @@ -79,31 +79,31 @@ class G1YoungGenSizer { // true otherwise. bool _use_adaptive_sizing; - Atomic _min_desired_young_length; - Atomic _max_desired_young_length; + Atomic _min_desired_num_regions; + Atomic _max_desired_num_regions; - uint calculate_default_min_length(uint new_number_of_heap_regions); - uint calculate_default_max_length(uint new_number_of_heap_regions); + uint calculate_default_min_num_regions(uint new_number_of_heap_regions); + uint calculate_default_max_num_regions(uint new_number_of_heap_regions); - // Update the given values for minimum and maximum young gen length in regions - // given the number of heap regions depending on the kind of sizing algorithm. - void recalculate_min_max_young_length(uint number_of_heap_regions, uint* min_young_length, uint* max_young_length); + // Recalculate the minimum and maximum number of young regions for the + // given number of heap regions according to the current sizing algorithm. + void recalculate_min_max_num_regions(uint number_of_heap_regions, uint* min_num_young_regions, uint* max_num_young_regions); public: G1YoungGenSizer(); - // Calculate the maximum length of the young gen given the number of regions + // Calculate the maximum size of the young gen given the number of regions // depending on the sizing algorithm. virtual void adjust_max_new_size(uint number_of_heap_regions); virtual void heap_size_changed(uint new_number_of_heap_regions); - uint min_desired_young_length() const { - return _min_desired_young_length.load_relaxed(); + uint min_desired_num_regions() const { + return _min_desired_num_regions.load_relaxed(); } - uint max_desired_young_length() const { - return _max_desired_young_length.load_relaxed(); + uint max_desired_num_regions() const { + return _max_desired_num_regions.load_relaxed(); } - bool use_adaptive_young_list_length() const { + bool use_adaptive_num_young_regions() const { return _use_adaptive_sizing; } }; diff --git a/src/hotspot/share/gc/g1/jvmFlagConstraintsG1.cpp b/src/hotspot/share/gc/g1/jvmFlagConstraintsG1.cpp index df6adeb8041..2ad3500de4a 100644 --- a/src/hotspot/share/gc/g1/jvmFlagConstraintsG1.cpp +++ b/src/hotspot/share/gc/g1/jvmFlagConstraintsG1.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 @@ -165,7 +165,7 @@ JVMFlag::Error GCPauseIntervalMillisConstraintFuncG1(uintx value, bool verbose) JVMFlag::Error NewSizeConstraintFuncG1(size_t value, bool verbose) { #ifdef _LP64 - // Overflow would happen for uint type variable of YoungGenSizer::_min_desired_young_length + // Overflow would happen for uint type variable of YoungGenSizer::_min_desired_num_regions // when the value to be assigned exceeds uint range. // i.e. result of '(uint)(NewSize / region size(1~32MB))' // So maximum of NewSize should be 'max_juint * 1M' diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp index e2473bdfb04..c9fa936f203 100644 --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -104,8 +104,8 @@ Mutex* G1MarkStackChunkList_lock = nullptr; Mutex* G1MarkStackFreeList_lock = nullptr; Monitor* G1OldGCCount_lock = nullptr; Mutex* G1OldSets_lock = nullptr; -Mutex* G1ReviseYoungLength_lock = nullptr; Mutex* G1RareEvent_lock = nullptr; +Mutex* G1ReviseNumYoungRegions_lock = nullptr; Mutex* G1Uncommit_lock = nullptr; #endif @@ -335,7 +335,7 @@ void mutex_init() { if (UseG1GC) { MUTEX_DEFL(G1OldGCCount_lock , PaddedMonitor, Threads_lock, true); MUTEX_DEFL(G1RareEvent_lock , PaddedMutex , Threads_lock, true); - MUTEX_DEFL(G1ReviseYoungLength_lock , PaddedMutex , Threads_lock, true); + MUTEX_DEFL(G1ReviseNumYoungRegions_lock , PaddedMutex , Threads_lock, true); } #endif diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp index aeee000b377..ae9c5e8a1f1 100644 --- a/src/hotspot/share/runtime/mutexLocker.hpp +++ b/src/hotspot/share/runtime/mutexLocker.hpp @@ -100,7 +100,7 @@ extern Mutex* G1MarkStackFreeList_lock; // Protects access to the G1 gl extern Monitor* G1OldGCCount_lock; // in support of "concurrent" full gc extern Mutex* G1OldSets_lock; // protects the G1 old region sets extern Mutex* G1RareEvent_lock; // Synchronizes (rare) parallel GC operations. -extern Mutex* G1ReviseYoungLength_lock; // Protects access to young gen length revising operations. +extern Mutex* G1ReviseNumYoungRegions_lock; // Protects access to number of young regions revising operations. extern Mutex* G1Uncommit_lock; // protects the G1 uncommit list when not at safepoints #endif diff --git a/test/hotspot/jtreg/gc/arguments/TestNewRatioFlag.java b/test/hotspot/jtreg/gc/arguments/TestNewRatioFlag.java index 014181b2fce..6be05def108 100644 --- a/test/hotspot/jtreg/gc/arguments/TestNewRatioFlag.java +++ b/test/hotspot/jtreg/gc/arguments/TestNewRatioFlag.java @@ -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 @@ -167,13 +167,13 @@ public static void verifyG1NewRatio(int expectedRatio) { long maxOld = HeapRegionUsageTool.getOldUsage().getMax(); int regionSize = wb.g1RegionSize(); - int youngListLength = (int) ((initEden + initSurv) / regionSize); + int numYoungRegions = (int) ((initEden + initSurv) / regionSize); int maxRegions = (int) (maxOld / regionSize); - int expectedYoungListLength = (int) (maxRegions / (double) (expectedRatio + 1)); + int expectedNumYoungRegions = (int) (maxRegions / (double) (expectedRatio + 1)); - if (youngListLength != expectedYoungListLength) { - throw new RuntimeException("Expected G1 young list length is: " + expectedYoungListLength - + ", but observed young list length is: " + youngListLength); + if (numYoungRegions != expectedNumYoungRegions) { + throw new RuntimeException("Expected G1 number of young regions is: " + expectedNumYoungRegions + + ", but observed number of young regions is: " + numYoungRegions); } } } diff --git a/test/hotspot/jtreg/gc/arguments/TestSurvivorRatioFlag.java b/test/hotspot/jtreg/gc/arguments/TestSurvivorRatioFlag.java index bdf8186bfaa..36b85e8c09d 100644 --- a/test/hotspot/jtreg/gc/arguments/TestSurvivorRatioFlag.java +++ b/test/hotspot/jtreg/gc/arguments/TestSurvivorRatioFlag.java @@ -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 @@ -116,7 +116,7 @@ public static void main(String args[]) throws Exception { * Depending on selected young GC we verify that: * - for DefNew and ParNew: eden_size / survivor_size is close to expectedRatio; * - for PSNew: survivor_size equal to young_gen_size / expectedRatio; - * - for G1: survivor_regions <= young_list_length / expectedRatio. + * - for G1: survivor_regions <= num_young_regions / expectedRatio. */ public static Void verifySurvivorRatio(int expectedRatio) { GCTypes.YoungGCType type = GCTypes.YoungGCType.getYoungGCType(); @@ -166,8 +166,8 @@ private static void verifyG1SurvivorRatio(int expectedRatio) { MemoryUsage survivorUsage = HeapRegionUsageTool.getSurvivorUsage(); int regionSize = wb.g1RegionSize(); - int youngListLength = (int) Math.max(NEW_SIZE / regionSize, 1); - int expectedSurvivorRegions = (int) Math.ceil(youngListLength / (double) expectedRatio); + int numYoungRegions = (int) Math.max(NEW_SIZE / regionSize, 1); + int expectedSurvivorRegions = (int) Math.ceil(numYoungRegions / (double) expectedRatio); int observedSurvivorRegions = (int) (survivorUsage.getCommitted() / regionSize); if (expectedSurvivorRegions < observedSurvivorRegions) { diff --git a/test/hotspot/jtreg/gc/arguments/TestTargetSurvivorRatioFlag.java b/test/hotspot/jtreg/gc/arguments/TestTargetSurvivorRatioFlag.java index 34a221b1025..ca34707bf12 100644 --- a/test/hotspot/jtreg/gc/arguments/TestTargetSurvivorRatioFlag.java +++ b/test/hotspot/jtreg/gc/arguments/TestTargetSurvivorRatioFlag.java @@ -310,8 +310,8 @@ public static void allocateMemory(double ratio, long maxSize) throws Exception { */ public static long getMaxSurvivorSize() { if (GCTypes.YoungGCType.getYoungGCType() == GCTypes.YoungGCType.G1) { - int youngLength = (int) Math.max(MAX_NEW_SIZE / wb.g1RegionSize(), 1); - return (long) Math.ceil(youngLength / (double) SURVIVOR_RATIO) * wb.g1RegionSize(); + int numYoungRegions = (int) Math.max(MAX_NEW_SIZE / wb.g1RegionSize(), 1); + return (long) Math.ceil(numYoungRegions / (double) SURVIVOR_RATIO) * wb.g1RegionSize(); } else { return HeapRegionUsageTool.getSurvivorUsage().getMax(); } From 5a19e49dc48b9fb2cf480dab890922a2de59d2de Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Thu, 25 Jun 2026 14:06:19 +0000 Subject: [PATCH 071/707] 8378796: java.lang.runtime bootstrap methods missing lookup validation Reviewed-by: jvernee --- .../java/lang/runtime/ObjectMethods.java | 12 +-- .../java/lang/runtime/SwitchBootstraps.java | 16 ++-- .../java/lang/runtime/package-info.java | 14 +++- .../java/lang/runtime/ObjectMethodsTest.java | 4 + .../lang/runtime/SwitchBootstrapsTest.java | 75 ++++++++++--------- 5 files changed, 67 insertions(+), 54 deletions(-) diff --git a/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java b/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java index e4b2886404f..922ac651f7e 100644 --- a/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java +++ b/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java @@ -479,12 +479,7 @@ private static List> split(MethodHandle[] getters) { * {@link java.lang.Record#toString()}. * * - * @param lookup Every bootstrap method is expected to have a {@code lookup} - * which usually represents a lookup context with the - * accessibility privileges of the caller. This is because - * {@code invokedynamic} call sites always provide a {@code lookup} - * to the corresponding bootstrap method, but this method just - * ignores the {@code lookup} parameter + * @param lookup the full-privilege lookup context of the caller * @param methodName the name of the method to generate, which must be one of * {@code "equals"}, {@code "hashCode"}, or {@code "toString"} * @param type a {@link MethodType} corresponding the descriptor type @@ -503,8 +498,6 @@ private static List> split(MethodHandle[] getters) { * if invoked by a condy * @throws IllegalArgumentException if the bootstrap arguments are invalid * or inconsistent - * @throws NullPointerException if any argument is {@code null} or if any element - * in the {@code getters} array is {@code null} * @throws Throwable if any exception is thrown during call site construction */ public static Object bootstrap(MethodHandles.Lookup lookup, String methodName, TypeDescriptor type, @@ -518,6 +511,9 @@ public static Object bootstrap(MethodHandles.Lookup lookup, String methodName, T requireNonNull(names); List getterList = List.of(getters); // deep null check + if (!lookup.hasFullPrivilegeAccess()) + throw new IllegalArgumentException("Unprivileged lookup ".concat(lookup.toString())); + MethodType methodType; if (type instanceof MethodType mt) methodType = mt; diff --git a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java index d15e701b94d..c52fc9ec75c 100644 --- a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java +++ b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java @@ -169,16 +169,13 @@ private static class StaticHolders { * the length of the {@code labels} array (inclusive), * both or an {@link IndexOutOfBoundsException} is thrown. * - * @param lookup Represents a lookup context with the accessibility - * privileges of the caller. When used with {@code invokedynamic}, - * this is stacked automatically by the VM. + * @param lookup the full-privilege lookup context of the caller * @param invocationName unused, {@code null} is permitted * @param invocationType The invocation type of the {@code CallSite} with two parameters, * a target type, an {@code int}, and {@code int} as a return type. * @param labels case labels as described above * @return a {@code CallSite} returning the first matching element as described above * - * @throws NullPointerException if any argument is {@code null}, unless noted otherwise * @throws IllegalArgumentException if any element in the labels array is null * @throws IllegalArgumentException if the invocation type is not a method type of first parameter of a target type, * second parameter of type {@code int} and with {@code int} as its return type @@ -198,6 +195,9 @@ public static CallSite typeSwitch(MethodHandles.Lookup lookup, requireNonNull(invocationType); requireNonNull(labels); + if (!lookup.hasFullPrivilegeAccess()) + throw new IllegalArgumentException("Unprivileged lookup ".concat(lookup.toString())); + Class selectorType = invocationType.parameterType(0); if (invocationType.parameterCount() != 2 || (!invocationType.returnType().equals(int.class)) @@ -275,9 +275,7 @@ private static void verifyLabel(Object label, Class selectorType) { * @apiNote It is permissible for the {@code labels} array to contain {@code String} * values that do not represent any enum constants at runtime. * - * @param lookup Represents a lookup context with the accessibility - * privileges of the caller. When used with {@code invokedynamic}, - * this is stacked automatically by the VM. + * @param lookup the full-privilege lookup context of the caller * @param invocationName unused, {@code null} is permitted * @param invocationType The invocation type of the {@code CallSite} with two parameters, * an enum type, an {@code int}, and {@code int} as a return type. @@ -285,7 +283,6 @@ private static void verifyLabel(Object label, Class selectorType) { * in any combination * @return a {@code CallSite} returning the first matching element as described above * - * @throws NullPointerException if any argument is {@code null}, unless noted otherwise * @throws IllegalArgumentException if any element in the labels array is null * @throws IllegalArgumentException if any element in the labels array is an empty {@code String} * @throws IllegalArgumentException if the invocation type is not a method type @@ -305,6 +302,9 @@ public static CallSite enumSwitch(MethodHandles.Lookup lookup, requireNonNull(invocationType); requireNonNull(labels); + if (!lookup.hasFullPrivilegeAccess()) + throw new IllegalArgumentException("Unprivileged lookup ".concat(lookup.toString())); + if (invocationType.parameterCount() != 2 || (!invocationType.returnType().equals(int.class)) || invocationType.parameterType(0).isPrimitive() diff --git a/src/java.base/share/classes/java/lang/runtime/package-info.java b/src/java.base/share/classes/java/lang/runtime/package-info.java index 9e19ef9bd7e..e2597e45c34 100644 --- a/src/java.base/share/classes/java/lang/runtime/package-info.java +++ b/src/java.base/share/classes/java/lang/runtime/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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,8 +26,20 @@ /** * The {@code java.lang.runtime} package provides low-level runtime support * for the Java language. + *

+ * Unless otherwise specified:

    + *
  • Methods and constructors in this package throw a {@link + * NullPointerException} when they are called with {@code null} or an array + * that contains {@code null} as an argument. + *
  • {@linkplain java.lang.invoke##bsm Bootstrap methods} in this package + * throw an {@link IllegalArgumentException} when they are called with a + * {@link Lookup Lookup} that does not have {@linkplain + * Lookup#hasFullPrivilegeAccess() full privilege access}. + *
* * @since 14 */ package java.lang.runtime; + +import java.lang.invoke.MethodHandles.Lookup; diff --git a/test/jdk/java/lang/runtime/ObjectMethodsTest.java b/test/jdk/java/lang/runtime/ObjectMethodsTest.java index d7ca5912273..56be7008f41 100644 --- a/test/jdk/java/lang/runtime/ObjectMethodsTest.java +++ b/test/jdk/java/lang/runtime/ObjectMethodsTest.java @@ -79,6 +79,7 @@ static class Empty { } static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); + static final MethodHandles.Lookup UNPRIVILEGED_LOOKUP = LOOKUP.dropLookupMode(MethodHandles.Lookup.PRIVATE); @Test public void testEqualsC() throws Throwable { @@ -184,6 +185,9 @@ void commonExceptions(NamePlusType npt) { assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, null, type, C.class, "x;y", C.ACCESSORS)); assertThrows(NPE, () -> ObjectMethods.bootstrap(null, name, type, C.class, "x;y", C.ACCESSORS)); + // Unprivileged lookup + assertThrows(IAE, () -> ObjectMethods.bootstrap(UNPRIVILEGED_LOOKUP, name, type, C.class, "x;y", C.ACCESSORS)); + // Bad indy call receiver type - change C to this test class assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, name, type.changeParameterType(0, this.getClass()), C.class, "x;y", C.ACCESSORS)); diff --git a/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java b/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java index 061ce2ae241..76bfe7616f6 100644 --- a/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java +++ b/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java @@ -42,40 +42,25 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -/** +/* * @test * @bug 8318144 * @enablePreview * @compile SwitchBootstrapsTest.java - * @run junit/othervm SwitchBootstrapsTest + * @run junit SwitchBootstrapsTest */ public class SwitchBootstrapsTest { - public static final MethodHandle BSM_TYPE_SWITCH; - public static final MethodHandle BSM_ENUM_SWITCH; - - static { - try { - BSM_TYPE_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "typeSwitch", - MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, Object[].class)); - BSM_ENUM_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "enumSwitch", - MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, Object[].class)); - } - catch (ReflectiveOperationException e) { - throw new AssertionError("Should not happen", e); - } - } - private void testType(Object target, int start, int result, Object... labels) throws Throwable { MethodType switchType = MethodType.methodType(int.class, Object.class, int.class); - MethodHandle indy = ((CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels)).dynamicInvoker(); + MethodHandle indy = SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType, labels).dynamicInvoker(); assertEquals(result, (int) indy.invoke(target, start)); assertEquals(-1, (int) indy.invoke(null, start)); } private void testPrimitiveType(Object target, Class targetType, int start, int result, Object... labels) throws Throwable { MethodType switchType = MethodType.methodType(int.class, targetType, int.class); - MethodHandle indy = ((CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels)).dynamicInvoker(); + MethodHandle indy = SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType, labels).dynamicInvoker(); assertEquals(result, (int) indy.invoke(target, start)); } @@ -85,7 +70,7 @@ private void testEnum(Enum target, int start, int result, Object... labels) t private void testEnum(Class targetClass, Enum target, int start, int result, Object... labels) throws Throwable { MethodType switchType = MethodType.methodType(int.class, targetClass, int.class); - MethodHandle indy = ((CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels)).dynamicInvoker(); + MethodHandle indy = SwitchBootstraps.enumSwitch(MethodHandles.lookup(), "", switchType, labels).dynamicInvoker(); assertEquals(result, (int) indy.invoke(target, start)); assertEquals(-1, (int) indy.invoke(null, start)); } @@ -188,7 +173,7 @@ public void testEnums() throws Throwable { //null invocation name: MethodType switchType = MethodType.methodType(int.class, E1.class, int.class); - MethodHandle indy = ((CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), null, switchType)).dynamicInvoker(); + MethodHandle indy = SwitchBootstraps.enumSwitch(MethodHandles.lookup(), null, switchType).dynamicInvoker(); assertEquals(0, (int) indy.invoke(E1.A, 0)); } @@ -229,7 +214,7 @@ public void testWrongSwitchTypes() throws Throwable { }; for (MethodType switchType : switchTypes) { assertThrows(IllegalArgumentException.class, () -> - BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType) + SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType) ); } MethodType[] enumSwitchTypes = new MethodType[] { @@ -240,7 +225,7 @@ public void testWrongSwitchTypes() throws Throwable { }; for (MethodType enumSwitchType : enumSwitchTypes) { assertThrows(IllegalArgumentException.class, () -> - BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType) + SwitchBootstraps.enumSwitch(MethodHandles.lookup(), "", enumSwitchType) ); } } @@ -270,23 +255,23 @@ enum E {A, B, C} public void testNullLabels() throws Throwable { MethodType switchType = MethodType.methodType(int.class, Object.class, int.class); assertThrows(NullPointerException.class, () -> - BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, (Object[]) null) + SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType, (Object[]) null) ); assertThrows(IllegalArgumentException.class, () -> - BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, + SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType, new Object[] {1, null, String.class}) ); MethodType enumSwitchType = MethodType.methodType(int.class, E1.class, int.class); assertThrows(NullPointerException.class, () -> - BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType, (Object[]) null) + SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", enumSwitchType, (Object[]) null) ); assertThrows(IllegalArgumentException.class, () -> - BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType, + SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", enumSwitchType, new Object[] {1, null, String.class}) ); //null invocationName is OK: - BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), null, switchType, - new Object[] {Object.class}); + SwitchBootstraps.typeSwitch(MethodHandles.lookup(), null, switchType, + Object.class); } private static AtomicBoolean enumInitialized = new AtomicBoolean(); @@ -304,7 +289,7 @@ enum E { MethodType enumSwitchType = MethodType.methodType(int.class, E.class, int.class); - CallSite invocation = (CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType, new Object[] {"A"}); + CallSite invocation = SwitchBootstraps.enumSwitch(MethodHandles.lookup(), "", enumSwitchType, "A"); assertFalse(enumInitialized.get()); assertEquals(-1, invocation.dynamicInvoker().invoke(null, 0)); assertFalse(enumInitialized.get()); @@ -330,7 +315,7 @@ enum E { EnumDesc.of(ClassDesc.of(E.class.getName()), "A"), "test" }; - CallSite invocation = (CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels); + CallSite invocation = (CallSite) SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType, labels); assertFalse(enumInitialized.get()); assertEquals(-1, invocation.dynamicInvoker().invoke(null, 0)); assertFalse(enumInitialized.get()); @@ -402,21 +387,37 @@ private static byte[] createClass() { } @Test - public void testNullLookup() throws Throwable { + public void testNullLookup() { assertThrows(NullPointerException.class, () -> { MethodType switchType = MethodType.methodType(int.class, Object.class, int.class); - BSM_TYPE_SWITCH.invoke(null, "", switchType, Object.class); + SwitchBootstraps.typeSwitch(null, "", switchType, Object.class); }); enum E {} assertThrows(NullPointerException.class, () -> { MethodType switchType = MethodType.methodType(int.class, E.class, int.class); - BSM_ENUM_SWITCH.invoke(null, "", switchType, - new Object[] {}); + SwitchBootstraps.enumSwitch(null, "", switchType); }); assertThrows(NullPointerException.class, () -> { MethodType switchType = MethodType.methodType(int.class, E.class, int.class); - BSM_ENUM_SWITCH.invoke(null, "", switchType, - new Object[] {"A"}); + SwitchBootstraps.enumSwitch(null, "", switchType, "A"); + }); + } + + @Test + public void testUnprivilegedLookup() { + var lookup = MethodHandles.lookup().dropLookupMode(MethodHandles.Lookup.PRIVATE); + assertThrows(IllegalArgumentException.class, () -> { + MethodType switchType = MethodType.methodType(int.class, Object.class, int.class); + SwitchBootstraps.typeSwitch(lookup, "", switchType, Object.class); + }); + enum E {} + assertThrows(IllegalArgumentException.class, () -> { + MethodType switchType = MethodType.methodType(int.class, E.class, int.class); + SwitchBootstraps.enumSwitch(lookup, "", switchType); + }); + assertThrows(IllegalArgumentException.class, () -> { + MethodType switchType = MethodType.methodType(int.class, E.class, int.class); + SwitchBootstraps.enumSwitch(lookup, "", switchType, "A"); }); } } From b5fba9428e24b34dc26c3642c9f28593ab7bdd39 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 25 Jun 2026 14:49:17 +0000 Subject: [PATCH 072/707] 8387148: Linux perf map should record individual vtable trampolines Reviewed-by: adinn, ysuenaga --- src/hotspot/share/code/codeCache.cpp | 12 +++++++++-- src/hotspot/share/code/vtableStubs.cpp | 11 +--------- src/hotspot/share/code/vtableStubs.hpp | 21 +++++++++++++++---- .../dcmd/compiler/PerfMapTest.java | 6 ++++++ 4 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index 6f3a1b09c48..f1ca317d36f 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -30,6 +30,7 @@ #include "code/dependencyContext.hpp" #include "code/nmethod.hpp" #include "code/pcDesc.hpp" +#include "code/vtableStubs.hpp" #include "compiler/compilationPolicy.hpp" #include "compiler/compileBroker.hpp" #include "compiler/compilerDefinitions.inline.hpp" @@ -1971,8 +1972,8 @@ void CodeCache::write_perf_map(const char* filename, outputStream* st) { AllCodeBlobsIterator iter(AllCodeBlobsIterator::not_unloading); while (iter.next()) { CodeBlob *cb = iter.method(); - if (is_stub_code_blob(cb)) { - // Individual stub routines are dumped after the main loop. + if (is_stub_code_blob(cb) || cb->is_vtable_blob()) { + // Individual stub routines and vtable stubs are dumped after the main loop. continue; } ResourceMark rm; @@ -1991,6 +1992,13 @@ void CodeCache::write_perf_map(const char* filename, outputStream* st) { (intptr_t)d->begin(), (intptr_t)d->size_in_bytes(), d->group(), d->name()); } + VtableStubs::vtable_stub_do([&](VtableStub* s) { + fs.print_cr(INTPTR_FORMAT " " INTPTR_FORMAT " %s [%d]", + (intptr_t)s->code_begin(), + (intptr_t)s->code_size(), + s->is_vtable_stub() ? "vtable stub" : "itable stub", + s->index()); + }); } #endif // LINUX diff --git a/src/hotspot/share/code/vtableStubs.cpp b/src/hotspot/share/code/vtableStubs.cpp index 35b226a8798..df3e80bea41 100644 --- a/src/hotspot/share/code/vtableStubs.cpp +++ b/src/hotspot/share/code/vtableStubs.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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -319,15 +319,6 @@ void vtableStubs_init() { VtableStubs::initialize(); } -void VtableStubs::vtable_stub_do(void f(VtableStub*)) { - for (int i = 0; i < N; i++) { - for (VtableStub* s = AtomicAccess::load_acquire(&_table[i]); s != nullptr; s = s->next()) { - f(s); - } - } -} - - //----------------------------------------------------------------------------------------------------- // Non-product code #ifndef PRODUCT diff --git a/src/hotspot/share/code/vtableStubs.hpp b/src/hotspot/share/code/vtableStubs.hpp index 06acd8f25b9..fd06bf7c647 100644 --- a/src/hotspot/share/code/vtableStubs.hpp +++ b/src/hotspot/share/code/vtableStubs.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -28,6 +28,7 @@ #include "asm/macroAssembler.hpp" #include "code/vmreg.hpp" #include "memory/allStatic.hpp" +#include "runtime/atomicAccess.hpp" #include "utilities/checkedCast.hpp" // A VtableStub holds an individual code stub for a pair (vtable index, #args) for either itables or vtables @@ -111,7 +112,9 @@ class VtableStubs : AllStatic { static bool contains(address pc); // is pc within any stub? static VtableStub* stub_containing(address pc); // stub containing pc or nullptr static void initialize(); - static void vtable_stub_do(void f(VtableStub*)); // iterates over all vtable stubs + + template + static void vtable_stub_do(F f); }; @@ -142,13 +145,14 @@ class VtableStub { : _next(nullptr), _index(index), _ame_offset(-1), _npe_offset(-1), _type(is_vtable_stub ? Type::vtable_stub : Type::itable_stub) {} VtableStub* next() const { return _next; } - int index() const { return _index; } static VMReg receiver_location() { return _receiver_location; } void set_next(VtableStub* n) { _next = n; } public: + int index() const { return _index; } + int code_size() const { return VtableStubs::code_size_limit(is_vtable_stub()); } address code_begin() const { return (address)(this + 1); } - address code_end() const { return code_begin() + VtableStubs::code_size_limit(is_vtable_stub()); } + address code_end() const { return code_begin() + code_size(); } address entry_point() const { return code_begin(); } static int entry_offset() { return sizeof(class VtableStub); } @@ -189,4 +193,13 @@ class VtableStub { }; +template +void VtableStubs::vtable_stub_do(F f) { + for (int i = 0; i < N; i++) { + for (VtableStub* s = AtomicAccess::load_acquire(&_table[i]); s != nullptr; s = s->next()) { + f(s); + } + } +} + #endif // SHARE_CODE_VTABLESTUBS_HPP diff --git a/test/hotspot/jtreg/serviceability/dcmd/compiler/PerfMapTest.java b/test/hotspot/jtreg/serviceability/dcmd/compiler/PerfMapTest.java index 11e43b8d630..f93bfb8b3a3 100644 --- a/test/hotspot/jtreg/serviceability/dcmd/compiler/PerfMapTest.java +++ b/test/hotspot/jtreg/serviceability/dcmd/compiler/PerfMapTest.java @@ -75,6 +75,7 @@ public void run(CommandExecutor executor, String cmd, Path path) { // Sanity check the file contents boolean sawCallStub = false; + boolean sawVtableStub = false; try { for (String entry : Files.readAllLines(path)) { Matcher m = LINE_PATTERN.matcher(entry); @@ -82,12 +83,17 @@ public void run(CommandExecutor executor, String cmd, Path path) { if (m.group(3).contains("StubRoutines call_stub")) { sawCallStub = true; } + if (m.group(3).contains("vtable stub [")) { + sawVtableStub = true; + } } } catch (IOException e) { Assert.fail(e.toString()); } Assert.assertTrue(sawCallStub, "Expected StubRoutines call_stub entry in " + path); + Assert.assertTrue(sawVtableStub, + "Expected vtable stub entry in " + path); } @Test From a9fa4999459ee44bc9318db903f99a71bc6c3943 Mon Sep 17 00:00:00 2001 From: Ashay Rane Date: Thu, 25 Jun 2026 15:47:12 +0000 Subject: [PATCH 073/707] 8387044: test/jdk/javax/script/CommonSetup.sh incorrectly sets isCygwin=true for MSys/MinGW Reviewed-by: jpai, syan --- .../lang/instrument/appendToClassLoaderSearch/CommonSetup.sh | 4 ++-- test/jdk/javax/script/CommonSetup.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/jdk/java/lang/instrument/appendToClassLoaderSearch/CommonSetup.sh b/test/jdk/java/lang/instrument/appendToClassLoaderSearch/CommonSetup.sh index f2412a92a43..af2f3f8b3f1 100644 --- a/test/jdk/java/lang/instrument/appendToClassLoaderSearch/CommonSetup.sh +++ b/test/jdk/java/lang/instrument/appendToClassLoaderSearch/CommonSetup.sh @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright (c) 2005, 2020, 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 @@ -52,7 +52,7 @@ case "$OS" in OS="Windows" FS="\\" ;; - CYGWIN* | MSYS* | MINGW*) + CYGWIN*) PS=";" OS="Windows" FS="\\" diff --git a/test/jdk/javax/script/CommonSetup.sh b/test/jdk/javax/script/CommonSetup.sh index 4b59f9cbbc7..363d679df54 100644 --- a/test/jdk/javax/script/CommonSetup.sh +++ b/test/jdk/javax/script/CommonSetup.sh @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright (c) 2005, 2020, 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 @@ -45,7 +45,7 @@ case "$OS" in OS="Windows" FS="\\" ;; - CYGWIN* | MSYS* | MINGW* ) + CYGWIN* ) PS=";" OS="Windows" FS="\\" From da7bde5f14b75259d20c80021f83e6f95cf2049d Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Thu, 25 Jun 2026 17:43:51 +0000 Subject: [PATCH 074/707] 8385643: Shenandoah: Rework mark loop inlining Reviewed-by: rkennke, xpeng, wkemper --- make/hotspot/lib/JvmOverrideFiles.gmk | 4 -- .../gc/shenandoah/shenandoahClosures.hpp | 10 +++-- .../shenandoah/shenandoahClosures.inline.hpp | 3 +- .../share/gc/shenandoah/shenandoahMark.hpp | 39 ++++++++++++------- .../gc/shenandoah/shenandoahMark.inline.hpp | 22 ++++++----- .../gc/shenandoah/shenandoahMarkBitMap.hpp | 6 ++- .../shenandoah/shenandoahMarkingContext.hpp | 7 +++- .../gc/shenandoah/shenandoahTaskqueue.hpp | 6 ++- .../shenandoah/shenandoahTaskqueue.inline.hpp | 2 +- 9 files changed, 59 insertions(+), 40 deletions(-) diff --git a/make/hotspot/lib/JvmOverrideFiles.gmk b/make/hotspot/lib/JvmOverrideFiles.gmk index 80f3582043c..b417cc19b03 100644 --- a/make/hotspot/lib/JvmOverrideFiles.gmk +++ b/make/hotspot/lib/JvmOverrideFiles.gmk @@ -33,10 +33,6 @@ ifeq ($(INCLUDE), true) ifeq ($(TOOLCHAIN_TYPE), gcc) BUILD_LIBJVM_vmStructs.cpp_CXXFLAGS := -fno-var-tracking-assignments - ifeq ($(DEBUG_LEVEL), release) - # Need extra inlining to collapse shared marking code into the hot marking loop - BUILD_LIBJVM_shenandoahMark.cpp_CXXFLAGS := --param inline-unit-growth=1000 - endif # disable lto in g1ParScanThreadState because of special inlining/flattening used there ifeq ($(call check-jvm-feature, link-time-opt), true) BUILD_LIBJVM_g1ParScanThreadState.cpp_CXXFLAGS := -fno-lto diff --git a/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp b/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp index 976a505c713..eb40dfbd31d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp @@ -95,14 +95,18 @@ template class ShenandoahMarkRefsClosure : public ShenandoahMarkRefsSuperClosure { private: template - inline void do_oop_work(T* p) { work(p); } + ALWAYSINLINE + void do_oop_work(T* p) { work(p); } public: ShenandoahMarkRefsClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp, ShenandoahObjToScanQueue* old_q) : ShenandoahMarkRefsSuperClosure(q, rp, old_q) {}; - virtual void do_oop(narrowOop* p) { do_oop_work(p); } - virtual void do_oop(oop* p) { do_oop_work(p); } + ALWAYSINLINE + void do_oop(narrowOop* p) override { do_oop_work(p); } + + ALWAYSINLINE + void do_oop(oop* p) override { do_oop_work(p); } }; class ShenandoahForwardedIsAliveClosure : public BoolObjectClosure { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp index 83aede5b7d9..0f2a5b48d84 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp @@ -77,7 +77,8 @@ ShenandoahMarkRefsSuperClosure::ShenandoahMarkRefsSuperClosure(ShenandoahObjToSc _weak(false) {} template -inline void ShenandoahMarkRefsSuperClosure::work(T* p) { +ALWAYSINLINE +void ShenandoahMarkRefsSuperClosure::work(T* p) { ShenandoahMark::mark_through_ref(p, _queue, _old_queue, _mark_context, _weak); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp index ee29c76dcaf..1ba2cd067b6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp @@ -51,7 +51,8 @@ class ShenandoahMark: public StackObj { public: template - static inline void mark_through_ref(T* p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak); + ALWAYSINLINE + static void mark_through_ref(T* p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak); // Loom support void start_mark(); @@ -72,34 +73,42 @@ class ShenandoahMark: public StackObj { // ---------- Marking loop and tasks template - inline void do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveData* live_data, StringDedup::Requests* const req, ShenandoahMarkTask* task, uint worker_id); + ALWAYSINLINE + static void do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveData* live_data, StringDedup::Requests* const req, ShenandoahMarkTask* task, uint worker_id); template - inline void do_chunked_array_start(ShenandoahObjToScanQueue* q, T* cl, oop array, Klass* klass, bool weak); + ALWAYSINLINE + static void do_chunked_array_start(ShenandoahObjToScanQueue* q, T* cl, oop array, Klass* klass, bool weak); template - inline void do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, oop array, int chunk, int pow, bool weak); + ALWAYSINLINE + static void do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, oop array, int chunk, int pow, bool weak); template - inline void count_liveness(ShenandoahLiveData* live_data, oop obj, Klass* klass, uint worker_id); - - template - void mark_loop_work(T* cl, ShenandoahLiveData* live_data, uint worker_id, TaskTerminator *t, StringDedup::Requests* const req); - - template - void mark_loop_prework(uint worker_id, TaskTerminator *terminator, StringDedup::Requests* const req, bool update_refs); + ALWAYSINLINE + static void count_liveness(ShenandoahLiveData* live_data, oop obj, Klass* klass, uint worker_id); template + ALWAYSINLINE static bool in_generation(ShenandoahHeap* const heap, oop obj); template + ALWAYSINLINE static void mark_non_generational_ref(T *p, ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, bool weak); - static void mark_ref(ShenandoahObjToScanQueue* q, - ShenandoahMarkingContext* const mark_context, - bool weak, oop obj); + ALWAYSINLINE + static void mark_ref(ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, bool weak, oop obj); + + ALWAYSINLINE + static void dedup_string(oop obj, StringDedup::Requests* const req); + + template + void mark_loop_prework(uint worker_id, TaskTerminator *terminator, StringDedup::Requests* const req, bool update_refs); + + template + NOINLINE // Main hot loop, start inlining from here + void mark_loop_work(T* cl, ShenandoahLiveData* live_data, uint worker_id, TaskTerminator *t, StringDedup::Requests* const req); - static inline void dedup_string(oop obj, StringDedup::Requests* const req); protected: template void mark_loop(uint worker_id, TaskTerminator* terminator, ShenandoahGenerationType generation_type, diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp index 71ef99b17ac..72129ff9e14 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp @@ -94,7 +94,7 @@ void ShenandoahMark::do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveD } } -void ShenandoahMark::dedup_string(oop obj, StringDedup::Requests* const req) { +inline void ShenandoahMark::dedup_string(oop obj, StringDedup::Requests* const req) { assert(req != nullptr, "Should be available if dedup is enabled"); // Skip if already requested or dedup is forbidden. @@ -111,7 +111,7 @@ void ShenandoahMark::dedup_string(oop obj, StringDedup::Requests* const req) { } template -inline void ShenandoahMark::count_liveness(ShenandoahLiveData* live_data, oop obj, Klass* klass, uint worker_id) { +void ShenandoahMark::count_liveness(ShenandoahLiveData* live_data, oop obj, Klass* klass, uint worker_id) { const ShenandoahHeap* const heap = ShenandoahHeap::heap(); const size_t region_idx = heap->heap_region_index_containing(obj); ShenandoahHeapRegion* const region = heap->get_region(region_idx); @@ -155,7 +155,7 @@ inline void ShenandoahMark::count_liveness(ShenandoahLiveData* live_data, oop ob } template -inline void ShenandoahMark::do_chunked_array_start(ShenandoahObjToScanQueue* q, T* cl, oop obj, Klass* klass, bool weak) { +void ShenandoahMark::do_chunked_array_start(ShenandoahObjToScanQueue* q, T* cl, oop obj, Klass* klass, bool weak) { assert(obj->is_objArray(), "expect object array"); objArrayOop array = objArrayOop(obj); int len = array->length(); @@ -222,7 +222,7 @@ inline void ShenandoahMark::do_chunked_array_start(ShenandoahObjToScanQueue* q, } template -inline void ShenandoahMark::do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, oop obj, int chunk, int pow, bool weak) { +void ShenandoahMark::do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, oop obj, int chunk, int pow, bool weak) { assert(obj->is_objArray(), "expect object array"); objArrayOop array = objArrayOop(obj); @@ -291,7 +291,7 @@ bool ShenandoahMark::in_generation(ShenandoahHeap* const heap, oop obj) { } template -inline void ShenandoahMark::mark_through_ref(T *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { +void ShenandoahMark::mark_through_ref(T *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { // Note: This is a very hot code path, so the code should be conditional on GENERATION template // parameter where possible, in order to generate the most efficient code. @@ -327,17 +327,19 @@ inline void ShenandoahMark::mark_through_ref(T *p, ShenandoahObjToScanQueue* q, } template<> -inline void ShenandoahMark::mark_through_ref(oop *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { +ALWAYSINLINE +void ShenandoahMark::mark_through_ref(oop *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { mark_non_generational_ref(p, q, mark_context, weak); } template<> -inline void ShenandoahMark::mark_through_ref(narrowOop *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { +ALWAYSINLINE +void ShenandoahMark::mark_through_ref(narrowOop *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { mark_non_generational_ref(p, q, mark_context, weak); } template -inline void ShenandoahMark::mark_non_generational_ref(T* p, ShenandoahObjToScanQueue* q, +void ShenandoahMark::mark_non_generational_ref(T* p, ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, bool weak) { oop o = RawAccess<>::oop_load(p); if (!CompressedOops::is_null(o)) { @@ -353,8 +355,8 @@ inline void ShenandoahMark::mark_non_generational_ref(T* p, ShenandoahObjToScanQ } inline void ShenandoahMark::mark_ref(ShenandoahObjToScanQueue* q, - ShenandoahMarkingContext* const mark_context, - bool weak, oop obj) { + ShenandoahMarkingContext* const mark_context, + bool weak, oop obj) { bool skip_live = false; bool marked; if (weak) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp index 73bf3ecbeea..177d953d4f3 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp @@ -160,13 +160,15 @@ class ShenandoahMarkBitMap { // strong. // Words that have been marked final before or by a concurrent thread will be // upgraded to strong. In this case, this method also returns true. - inline bool mark_strong(HeapWord* w, bool& was_upgraded); + ALWAYSINLINE + bool mark_strong(HeapWord* w, bool& was_upgraded); // Mark word as 'weak' if it hasn't been marked weak or strong yet. // Return true if the word has been marked weak, false if it has already been // marked strong or weak or if another thread has beat us by marking it // strong or weak. - inline bool mark_weak(HeapWord* heap_addr); + ALWAYSINLINE + bool mark_weak(HeapWord* heap_addr); inline bool is_marked(HeapWord* addr) const; inline bool is_marked_strong(HeapWord* w) const; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.hpp index d8e0c74ea4e..870a81e4588 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.hpp @@ -55,8 +55,11 @@ class ShenandoahMarkingContext : public CHeapObj { * been marked by this thread. Returns false if the object has already been marked, * or if a competing thread succeeded in marking this object. */ - inline bool mark_strong(oop obj, bool& was_upgraded); - inline bool mark_weak(oop obj); + ALWAYSINLINE + bool mark_strong(oop obj, bool& was_upgraded); + + ALWAYSINLINE + bool mark_weak(oop obj); // Simple versions of marking accessors, to be used outside of marking (e.g. no possible concurrent updates) inline bool is_marked(oop obj) const; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp index ad4e29a5cc2..7bdf2d27349 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp @@ -49,10 +49,12 @@ class BufferedOverflowTaskQueue: public OverflowTaskQueue TASKQUEUE_STATS_ONLY(using taskqueue_t::stats;) // Push task t into the queue. Returns true on success. - inline bool push(E t); + ALWAYSINLINE + bool push(E t); // Attempt to pop from the queue. Returns true on success. - inline bool pop(E &t); + ALWAYSINLINE + bool pop(E &t); inline void clear(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.inline.hpp index 9fa4fabc1c7..0f01425f3e9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.inline.hpp @@ -47,7 +47,7 @@ bool BufferedOverflowTaskQueue::pop(E &t) { } template -inline bool BufferedOverflowTaskQueue::push(E t) { +bool BufferedOverflowTaskQueue::push(E t) { if (_buf_empty) { _elem = t; _buf_empty = false; From 892c881258280e08273cafc5c111ac50388200fc Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Thu, 25 Jun 2026 18:08:00 +0000 Subject: [PATCH 075/707] 8387253: Locale incorrectly accepts extlangs after non 2*3ALPHA lang Reviewed-by: naoto --- .../classes/sun/util/locale/LanguageTag.java | 9 ++++++--- test/jdk/java/util/Locale/LocaleEnhanceTest.java | 16 +++++++++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/java.base/share/classes/sun/util/locale/LanguageTag.java b/src/java.base/share/classes/sun/util/locale/LanguageTag.java index bdbbc5eec2d..5ce62a275cc 100644 --- a/src/java.base/share/classes/sun/util/locale/LanguageTag.java +++ b/src/java.base/share/classes/sun/util/locale/LanguageTag.java @@ -120,7 +120,7 @@ public static LanguageTag parse(String languageTag, ParsePosition pp, List extensions; // langtag must start with either language or privateuse if (!language.isEmpty()) { - extlangs = parseExtlangs(itr, pp); + extlangs = parseExtlangs(itr, pp, language); script = parseScript(itr, pp); region = parseRegion(itr, pp); variants = parseVariants(itr, pp); @@ -170,8 +170,11 @@ private static String parseLanguage(StringTokenIterator itr, ParsePosition pp) { return EMPTY_SUBTAG; } - private static List parseExtlangs(StringTokenIterator itr, ParsePosition pp) { - if (itr.isDone() || pp.getErrorIndex() != -1) { + private static List parseExtlangs(StringTokenIterator itr, ParsePosition pp, String lang) { + var langLen = lang.length(); + if (itr.isDone() || pp.getErrorIndex() != -1 + // Extlangs only accepted after 2*3ALPHA lang + || (langLen != 2 && langLen != 3)) { return EMPTY_SUBTAGS; } List extlangs = null; diff --git a/test/jdk/java/util/Locale/LocaleEnhanceTest.java b/test/jdk/java/util/Locale/LocaleEnhanceTest.java index 1e38f0b887a..3fe3745034d 100644 --- a/test/jdk/java/util/Locale/LocaleEnhanceTest.java +++ b/test/jdk/java/util/Locale/LocaleEnhanceTest.java @@ -44,6 +44,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EmptySource; import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -57,7 +58,7 @@ * @test * @bug 6875847 6992272 7002320 7015500 7023613 7032820 7033504 7004603 * 7044019 8008577 8176853 8255086 8263202 8287868 8174269 8369452 - * 8369590 8387185 + * 8369590 8387185 8387253 * @summary test API changes to Locale * @modules jdk.localedata * @run junit/othervm -esa LocaleEnhanceTest @@ -1396,6 +1397,19 @@ public void numericSingletonRoundTripTest() { assertEquals(tag, locale.toLanguageTag()); } + // Ensure that extlang is only accepted after a 2*3ALPHA language subtag + // That is, the 4 ALPHA and 5*8 ALPHA language subtags should not accept extlangs + @ParameterizedTest + @ValueSource(strings = {"quux", "foobar"}) + public void testExtlangAfterReservedLanguage(String lang) { + String tag = lang + "-baz"; + // Locale.forLanguageTag is lenient and truncates the extlang + assertEquals(lang, Locale.forLanguageTag(tag).toLanguageTag()); + // Locale.Builder is strict and should throw + assertThrows(IllformedLocaleException.class, + () -> new Builder().setLanguageTag(tag)); + } + private void checkCalendar(Locale loc, String expected) { Calendar cal = Calendar.getInstance(loc); assertEquals(expected, cal.getClass().getName(), "Wrong calendar"); From a34314dd79db0dd8ec1e7a5267762760bf5b0711 Mon Sep 17 00:00:00 2001 From: Xiaolong Peng Date: Thu, 25 Jun 2026 21:52:19 +0000 Subject: [PATCH 076/707] 8387260: Shenandoah: ShenandoahOldGeneration::_promoted_reserve should be atomic Reviewed-by: shade, kdnilsen, ruili, wkemper --- .../share/gc/shenandoah/shenandoahOldGeneration.cpp | 10 +++++++--- .../share/gc/shenandoah/shenandoahOldGeneration.hpp | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp index 0a0beaaffee..c92f74364fb 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp @@ -132,16 +132,18 @@ ShenandoahOldGeneration::ShenandoahOldGeneration(uint max_queues) void ShenandoahOldGeneration::set_promoted_reserve(size_t new_val) { shenandoah_assert_heaplocked_or_safepoint(); - _promoted_reserve = new_val; + _promoted_reserve.store_relaxed(new_val); } size_t ShenandoahOldGeneration::get_promoted_reserve() const { - return _promoted_reserve; + return _promoted_reserve.load_relaxed(); } void ShenandoahOldGeneration::augment_promoted_reserve(size_t increment) { shenandoah_assert_heaplocked_or_safepoint(); - _promoted_reserve += increment; + // Writers are serialized by the heap lock, so relaxed ordering is sufficient; the atomic RMW + // only guards against tearing the concurrent lock-free reader (get_promoted_reserve). + _promoted_reserve.fetch_then_add(increment, memory_order_relaxed); } void ShenandoahOldGeneration::reset_promoted_expended() { @@ -194,6 +196,8 @@ void ShenandoahOldGeneration::maybe_log_promotion_failure_stats(bool concurrent) } bool ShenandoahOldGeneration::try_expend_promoted(size_t increment) { + // The promote reserve rarely changes during evacuation(only when there is PIP region), so snapshot it once; + // only _promoted_expended is contended and re-read on CAS failure. const size_t reserve = get_promoted_reserve(); size_t cur = _promoted_expended.load_relaxed(); while (cur + increment <= reserve) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp index 43151af4c87..61a3114f906 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp @@ -58,7 +58,7 @@ class ShenandoahOldGeneration : public ShenandoahGeneration { // and in addition to the evacuation reserve for intra-generation evacuations (ShenandoahGeneration::_evacuation_reserve). // If there is more data ready to be promoted than can fit within this reserve, the promotion of some objects will be // deferred until a subsequent evacuation pass. - size_t _promoted_reserve; + Atomic _promoted_reserve; // Bytes of old-gen memory expended on promotions. This may be modified concurrently // by mutators and gc workers when promotion LABs are retired during evacuation. It From 8740fbb4eeaf742e88999d4f243e29d53d17be2b Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Fri, 26 Jun 2026 04:02:20 +0000 Subject: [PATCH 077/707] 8386255: Float16Vector NaN canonicalization for hashCode computation Reviewed-by: psandoz, sherman --- .../jdk/incubator/vector/Float16Vector.java | 16 ++++++++++++++-- .../incubator/vector/X-Vector.java.template | 19 +++++++++++++++++++ .../vector/Float16Vector128Tests.java | 16 +++++++++++++--- .../vector/Float16Vector256Tests.java | 16 +++++++++++++--- .../vector/Float16Vector512Tests.java | 16 +++++++++++++--- .../vector/Float16Vector64Tests.java | 16 +++++++++++++--- .../vector/Float16VectorMaxTests.java | 16 +++++++++++++--- .../templates/Unit-Miscellaneous.template | 14 ++++++++++++++ .../vector/templates/Unit-header.template | 6 ++++-- 9 files changed, 116 insertions(+), 19 deletions(-) diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector.java index cf7eae5dd6a..ce3a67357f9 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector.java @@ -2861,6 +2861,17 @@ public final short[] toArray() { return a; } + // Returns the lane values boxed as Float16 elements. + @ForceInline + final Float16[] toFloat16Array() { + short[] bits = vec(); + Float16[] a = new Float16[bits.length]; + for (int i = 0; i < bits.length; i++) { + a[i] = Float16.shortBitsToFloat16(bits[i]); + } + return a; + } + /** {@inheritDoc} */ @ForceInline @@ -3734,8 +3745,9 @@ boolean equals(Object obj) { @ForceInline public final int hashCode() { - // now that toArray is strongly typed, we can define this - return Objects.hash(species(), Arrays.hashCode(toArray())); + // Hash the lanes as Float16 values; Float16.hashCode canonicalizes NaN + // so that all NaN representations contribute the same hash code. + return Objects.hash(species(), Arrays.hashCode(toFloat16Array())); } // ================================================ diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template index 7c6fb3bcfb2..f11c6283685 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template @@ -3705,6 +3705,19 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp return a; } +#if[FP16] + // Returns the lane values boxed as Float16 elements. + @ForceInline + final Float16[] toFloat16Array() { + short[] bits = vec(); + Float16[] a = new Float16[bits.length]; + for (int i = 0; i < bits.length; i++) { + a[i] = Float16.shortBitsToFloat16(bits[i]); + } + return a; + } + +#end[FP16] #if[int] /** * {@inheritDoc} @@ -5749,8 +5762,14 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp @ForceInline public final int hashCode() { +#if[FP16] + // Hash the lanes as Float16 values; Float16.hashCode canonicalizes NaN + // so that all NaN representations contribute the same hash code. + return Objects.hash(species(), Arrays.hashCode(toFloat16Array())); +#else[FP16] // now that toArray is strongly typed, we can define this return Objects.hash(species(), Arrays.hashCode(toArray())); +#end[FP16] } // ================================================ diff --git a/test/jdk/jdk/incubator/vector/Float16Vector128Tests.java b/test/jdk/jdk/incubator/vector/Float16Vector128Tests.java index ad971e9b9bf..a33e83d14ea 100644 --- a/test/jdk/jdk/incubator/vector/Float16Vector128Tests.java +++ b/test/jdk/jdk/incubator/vector/Float16Vector128Tests.java @@ -1561,14 +1561,16 @@ static short[] fill(short[] a, ToFloat16F f) { } static short cornerCaseValue(int i) { - return switch(i % 8) { + return switch(i % 10) { case 0 -> float16ToRawShortBits(Float16.MAX_VALUE); case 1 -> float16ToRawShortBits(Float16.MIN_VALUE); case 2 -> float16ToRawShortBits(Float16.NEGATIVE_INFINITY); case 3 -> float16ToRawShortBits(Float16.POSITIVE_INFINITY); case 4 -> float16ToRawShortBits(Float16.NaN); case 5 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7FFA)); - case 6 -> float16ToShortBits(Float16.valueOf(0.0f)); + case 6 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7c01)); // signaling NaN + case 7 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7e00)); // quiet NaN + case 8 -> float16ToShortBits(Float16.valueOf(0.0f)); default -> float16ToShortBits(Float16.valueOf(-0.0f)); }; } @@ -5423,11 +5425,19 @@ static void hashCodeFloat16Vector128TestsSmokeTest(IntFunction fa) { int hash = av.hashCode(); short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); - int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(subarr)); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(toFloat16Array(subarr))); Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); } } + static Float16[] toFloat16Array(short[] bits) { + Float16[] a = new Float16[bits.length]; + for (int j = 0; j < bits.length; j++) { + a[j] = shortBitsToFloat16(bits[j]); + } + return a; + } + static long ADDReduceLong(short[] a, int idx) { short res = 0; diff --git a/test/jdk/jdk/incubator/vector/Float16Vector256Tests.java b/test/jdk/jdk/incubator/vector/Float16Vector256Tests.java index a946e0d8585..99b167d4024 100644 --- a/test/jdk/jdk/incubator/vector/Float16Vector256Tests.java +++ b/test/jdk/jdk/incubator/vector/Float16Vector256Tests.java @@ -1561,14 +1561,16 @@ static short[] fill(short[] a, ToFloat16F f) { } static short cornerCaseValue(int i) { - return switch(i % 8) { + return switch(i % 10) { case 0 -> float16ToRawShortBits(Float16.MAX_VALUE); case 1 -> float16ToRawShortBits(Float16.MIN_VALUE); case 2 -> float16ToRawShortBits(Float16.NEGATIVE_INFINITY); case 3 -> float16ToRawShortBits(Float16.POSITIVE_INFINITY); case 4 -> float16ToRawShortBits(Float16.NaN); case 5 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7FFA)); - case 6 -> float16ToShortBits(Float16.valueOf(0.0f)); + case 6 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7c01)); // signaling NaN + case 7 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7e00)); // quiet NaN + case 8 -> float16ToShortBits(Float16.valueOf(0.0f)); default -> float16ToShortBits(Float16.valueOf(-0.0f)); }; } @@ -5423,11 +5425,19 @@ static void hashCodeFloat16Vector256TestsSmokeTest(IntFunction fa) { int hash = av.hashCode(); short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); - int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(subarr)); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(toFloat16Array(subarr))); Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); } } + static Float16[] toFloat16Array(short[] bits) { + Float16[] a = new Float16[bits.length]; + for (int j = 0; j < bits.length; j++) { + a[j] = shortBitsToFloat16(bits[j]); + } + return a; + } + static long ADDReduceLong(short[] a, int idx) { short res = 0; diff --git a/test/jdk/jdk/incubator/vector/Float16Vector512Tests.java b/test/jdk/jdk/incubator/vector/Float16Vector512Tests.java index 0e70b4c85ec..1c391497015 100644 --- a/test/jdk/jdk/incubator/vector/Float16Vector512Tests.java +++ b/test/jdk/jdk/incubator/vector/Float16Vector512Tests.java @@ -1561,14 +1561,16 @@ static short[] fill(short[] a, ToFloat16F f) { } static short cornerCaseValue(int i) { - return switch(i % 8) { + return switch(i % 10) { case 0 -> float16ToRawShortBits(Float16.MAX_VALUE); case 1 -> float16ToRawShortBits(Float16.MIN_VALUE); case 2 -> float16ToRawShortBits(Float16.NEGATIVE_INFINITY); case 3 -> float16ToRawShortBits(Float16.POSITIVE_INFINITY); case 4 -> float16ToRawShortBits(Float16.NaN); case 5 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7FFA)); - case 6 -> float16ToShortBits(Float16.valueOf(0.0f)); + case 6 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7c01)); // signaling NaN + case 7 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7e00)); // quiet NaN + case 8 -> float16ToShortBits(Float16.valueOf(0.0f)); default -> float16ToShortBits(Float16.valueOf(-0.0f)); }; } @@ -5423,11 +5425,19 @@ static void hashCodeFloat16Vector512TestsSmokeTest(IntFunction fa) { int hash = av.hashCode(); short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); - int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(subarr)); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(toFloat16Array(subarr))); Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); } } + static Float16[] toFloat16Array(short[] bits) { + Float16[] a = new Float16[bits.length]; + for (int j = 0; j < bits.length; j++) { + a[j] = shortBitsToFloat16(bits[j]); + } + return a; + } + static long ADDReduceLong(short[] a, int idx) { short res = 0; diff --git a/test/jdk/jdk/incubator/vector/Float16Vector64Tests.java b/test/jdk/jdk/incubator/vector/Float16Vector64Tests.java index 94017042b7b..6ef651860ad 100644 --- a/test/jdk/jdk/incubator/vector/Float16Vector64Tests.java +++ b/test/jdk/jdk/incubator/vector/Float16Vector64Tests.java @@ -1561,14 +1561,16 @@ static short[] fill(short[] a, ToFloat16F f) { } static short cornerCaseValue(int i) { - return switch(i % 8) { + return switch(i % 10) { case 0 -> float16ToRawShortBits(Float16.MAX_VALUE); case 1 -> float16ToRawShortBits(Float16.MIN_VALUE); case 2 -> float16ToRawShortBits(Float16.NEGATIVE_INFINITY); case 3 -> float16ToRawShortBits(Float16.POSITIVE_INFINITY); case 4 -> float16ToRawShortBits(Float16.NaN); case 5 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7FFA)); - case 6 -> float16ToShortBits(Float16.valueOf(0.0f)); + case 6 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7c01)); // signaling NaN + case 7 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7e00)); // quiet NaN + case 8 -> float16ToShortBits(Float16.valueOf(0.0f)); default -> float16ToShortBits(Float16.valueOf(-0.0f)); }; } @@ -5423,11 +5425,19 @@ static void hashCodeFloat16Vector64TestsSmokeTest(IntFunction fa) { int hash = av.hashCode(); short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); - int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(subarr)); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(toFloat16Array(subarr))); Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); } } + static Float16[] toFloat16Array(short[] bits) { + Float16[] a = new Float16[bits.length]; + for (int j = 0; j < bits.length; j++) { + a[j] = shortBitsToFloat16(bits[j]); + } + return a; + } + static long ADDReduceLong(short[] a, int idx) { short res = 0; diff --git a/test/jdk/jdk/incubator/vector/Float16VectorMaxTests.java b/test/jdk/jdk/incubator/vector/Float16VectorMaxTests.java index d8649c838ee..61efa3de9a0 100644 --- a/test/jdk/jdk/incubator/vector/Float16VectorMaxTests.java +++ b/test/jdk/jdk/incubator/vector/Float16VectorMaxTests.java @@ -1567,14 +1567,16 @@ static short[] fill(short[] a, ToFloat16F f) { } static short cornerCaseValue(int i) { - return switch(i % 8) { + return switch(i % 10) { case 0 -> float16ToRawShortBits(Float16.MAX_VALUE); case 1 -> float16ToRawShortBits(Float16.MIN_VALUE); case 2 -> float16ToRawShortBits(Float16.NEGATIVE_INFINITY); case 3 -> float16ToRawShortBits(Float16.POSITIVE_INFINITY); case 4 -> float16ToRawShortBits(Float16.NaN); case 5 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7FFA)); - case 6 -> float16ToShortBits(Float16.valueOf(0.0f)); + case 6 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7c01)); // signaling NaN + case 7 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7e00)); // quiet NaN + case 8 -> float16ToShortBits(Float16.valueOf(0.0f)); default -> float16ToShortBits(Float16.valueOf(-0.0f)); }; } @@ -5429,11 +5431,19 @@ static void hashCodeFloat16VectorMaxTestsSmokeTest(IntFunction fa) { int hash = av.hashCode(); short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); - int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(subarr)); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(toFloat16Array(subarr))); Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); } } + static Float16[] toFloat16Array(short[] bits) { + Float16[] a = new Float16[bits.length]; + for (int j = 0; j < bits.length; j++) { + a[j] = shortBitsToFloat16(bits[j]); + } + return a; + } + static long ADDReduceLong(short[] a, int idx) { short res = 0; diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-Miscellaneous.template b/test/jdk/jdk/incubator/vector/templates/Unit-Miscellaneous.template index 8606b9ba598..0ae9342539f 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-Miscellaneous.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-Miscellaneous.template @@ -100,11 +100,25 @@ int hash = av.hashCode(); $type$ subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); +#if[FP16] + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(toFloat16Array(subarr))); +#else[FP16] int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(subarr)); +#end[FP16] Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); } } +#if[FP16] + static Float16[] toFloat16Array(short[] bits) { + Float16[] a = new Float16[bits.length]; + for (int j = 0; j < bits.length; j++) { + a[j] = shortBitsToFloat16(bits[j]); + } + return a; + } + +#end[FP16] #if[byte] @Test(dataProvider = "$type$UnaryOpProvider") static void reinterpretAsBytes$vectorteststype$SmokeTest(IntFunction<$type$[]> fa) { diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-header.template b/test/jdk/jdk/incubator/vector/templates/Unit-header.template index eac7edbbb3f..7047e27b797 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-header.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-header.template @@ -2013,14 +2013,16 @@ relativeError)); static $type$ cornerCaseValue(int i) { #if[FP] #if[FP16] - return switch(i % 8) { + return switch(i % 10) { case 0 -> float16ToRawShortBits($Wideboxtype$.MAX_VALUE); case 1 -> float16ToRawShortBits($Wideboxtype$.MIN_VALUE); case 2 -> float16ToRawShortBits($Wideboxtype$.NEGATIVE_INFINITY); case 3 -> float16ToRawShortBits($Wideboxtype$.POSITIVE_INFINITY); case 4 -> float16ToRawShortBits($Wideboxtype$.NaN); case 5 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7FFA)); - case 6 -> float16ToShortBits(Float16.valueOf(0.0f)); + case 6 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7c01)); // signaling NaN + case 7 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7e00)); // quiet NaN + case 8 -> float16ToShortBits(Float16.valueOf(0.0f)); default -> float16ToShortBits(Float16.valueOf(-0.0f)); }; #else[FP16] From 60e4b91f61da1a168135013874617406ded36871 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Fri, 26 Jun 2026 06:17:54 +0000 Subject: [PATCH 078/707] 8386292: Shenandoah: Simplify and strengthen C1 barriers Co-authored-by: Martin Doerr Reviewed-by: rkennke, fyang, kdnilsen --- .../shenandoahBarrierSetAssembler_aarch64.cpp | 169 +++-------- .../shenandoahBarrierSetAssembler_aarch64.hpp | 11 +- .../shenandoahBarrierSetAssembler_ppc.cpp | 274 +++++------------- .../shenandoahBarrierSetAssembler_ppc.hpp | 26 +- .../shenandoahBarrierSetAssembler_riscv.cpp | 168 +++-------- .../shenandoahBarrierSetAssembler_riscv.hpp | 11 +- .../shenandoahBarrierSetAssembler_x86.cpp | 205 +++---------- .../shenandoahBarrierSetAssembler_x86.hpp | 11 +- .../shenandoah/c1/shenandoahBarrierSetC1.cpp | 210 ++++++-------- .../shenandoah/c1/shenandoahBarrierSetC1.hpp | 117 ++------ 10 files changed, 337 insertions(+), 865 deletions(-) diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp index c590b6699c0..bc8af2354c8 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp @@ -456,79 +456,38 @@ void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssemb #define __ ce->masm()-> -void ShenandoahBarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub) { - ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); - // At this point we know that marking is in progress. - // If do_load() is true then we have to emit the - // load of the previous value; otherwise it has already - // been loaded into _pre_val. - +void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub) { __ bind(*stub->entry()); - assert(stub->pre_val()->is_register(), "Precondition."); + ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); - Register pre_val_reg = stub->pre_val()->as_register(); + Register obj = stub->obj()->as_register(); if (stub->do_load()) { - ce->mem2reg(stub->addr(), stub->pre_val(), T_OBJECT, stub->patch_code(), stub->info(), false /*wide*/); + ce->mem2reg(stub->addr(), stub->obj(), T_OBJECT, lir_patch_none, nullptr, /* wide = */ false); } - __ cbz(pre_val_reg, *stub->continuation()); - ce->store_parameter(stub->pre_val()->as_register(), 0); - __ far_call(RuntimeAddress(bs->pre_barrier_c1_runtime_code_blob()->code_begin())); + __ cbz(obj, *stub->continuation()); + ce->store_parameter(obj, 0); + __ far_call(RuntimeAddress(bs->keepalive_barrier_stub())); __ b(*stub->continuation()); } -void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub) { - ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); +void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub) { __ bind(*stub->entry()); - DecoratorSet decorators = stub->decorators(); - bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); - bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); - bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); - bool is_native = ShenandoahBarrierSet::is_native_access(decorators); + ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); Register obj = stub->obj()->as_register(); - Register res = stub->result()->as_register(); Register addr = stub->addr()->as_pointer_register(); - Register tmp1 = stub->tmp1()->as_register(); - Register tmp2 = stub->tmp2()->as_register(); - - assert(res == r0, "result must arrive in r0"); - - if (res != obj) { - __ mov(res, obj); - } - - if (is_strong) { - // Check for object in cset. - if (AOTCodeCache::is_on_for_dump()) { - __ lea(tmp2, ExternalAddress(AOTRuntimeConstants::cset_base_address())); - __ ldr(tmp2, Address(tmp2)); - __ lea(tmp1, ExternalAddress(AOTRuntimeConstants::grain_shift_address())); - __ ldrw(tmp1, Address(tmp1)); - __ lsrv(tmp1, res, tmp1); - } else { - __ mov(tmp2, ShenandoahHeap::in_cset_fast_test_addr()); - __ lsr(tmp1, res, ShenandoahHeapRegion::region_size_bytes_shift_jint()); - } - __ ldrb(tmp2, Address(tmp2, tmp1)); - __ cbz(tmp2, *stub->continuation()); - } + Register slow_result = stub->slow_result()->as_register(); + assert_different_registers(obj, addr, slow_result); + assert(slow_result == r0, "C1 must know about our slow call result register"); - ce->store_parameter(res, 0); + ce->store_parameter(obj, 0); ce->store_parameter(addr, 1); - if (is_strong) { - if (is_native) { - __ far_call(RuntimeAddress(bs->load_reference_barrier_strong_native_rt_code_blob()->code_begin())); - } else { - __ far_call(RuntimeAddress(bs->load_reference_barrier_strong_rt_code_blob()->code_begin())); - } - } else if (is_weak) { - __ far_call(RuntimeAddress(bs->load_reference_barrier_weak_rt_code_blob()->code_begin())); - } else { - assert(is_phantom, "only remaining strength"); - __ far_call(RuntimeAddress(bs->load_reference_barrier_phantom_rt_code_blob()->code_begin())); + __ far_call(RuntimeAddress(bs->load_reference_barrier_stub(stub->decorators()))); + if (obj != slow_result) { + __ mov(obj, slow_result); } __ b(*stub->continuation()); @@ -538,89 +497,27 @@ void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assemble #define __ sasm-> -void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm) { - __ prologue("shenandoah_pre_barrier", false); - - // arg0 : previous value of memory - - BarrierSet* bs = BarrierSet::barrier_set(); - - const Register pre_val = r0; - const Register thread = rthread; - const Register tmp = rscratch1; - - Address queue_index(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset())); - Address buffer(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset())); - - Label done; - Label runtime; - - // Is marking still active? - Address gc_state(thread, in_bytes(ShenandoahThreadLocalData::gc_state_offset())); - __ ldrb(tmp, gc_state); - __ tbz(tmp, ShenandoahHeap::MARKING_BITPOS, done); - - // Can we store original value in the thread's buffer? - __ ldr(tmp, queue_index); - __ cbz(tmp, runtime); - - __ sub(tmp, tmp, wordSize); - __ str(tmp, queue_index); - __ ldr(rscratch2, buffer); - __ add(tmp, tmp, rscratch2); - __ load_parameter(0, rscratch2); - __ str(rscratch2, Address(tmp, 0)); - __ b(done); - - __ bind(runtime); - __ push_call_clobbered_registers(); - __ load_parameter(0, pre_val); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), pre_val); - __ pop_call_clobbered_registers(); - __ bind(done); - +void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_runtime_stub(StubAssembler* sasm) { + __ prologue("shenandoah_keepalive_barrier", false); + const Register tmp_obj = r0; + const Register tmp1 = r1; + const Register tmp2 = r2; + __ push(RegSet::of(tmp1, tmp2, tmp_obj), sp); + __ load_parameter(0, tmp_obj); + satb_barrier(sasm, noreg, tmp_obj, rthread, tmp1, tmp2); + __ pop(RegSet::of(tmp1, tmp2, tmp_obj), sp); __ epilogue(); } -void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, DecoratorSet decorators) { +void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators) { __ prologue("shenandoah_load_reference_barrier", false); - // arg0 : object to be resolved - - __ push_call_clobbered_registers(); - __ load_parameter(0, r0); - __ load_parameter(1, r1); - - bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); - bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); - bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); - bool is_native = ShenandoahBarrierSet::is_native_access(decorators); - if (is_strong) { - if (is_native) { - __ lea(lr, RuntimeAddress(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong))); - } else { - if (UseCompressedOops) { - __ lea(lr, RuntimeAddress(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow))); - } else { - __ lea(lr, RuntimeAddress(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong))); - } - } - } else if (is_weak) { - assert(!is_native, "weak must not be called off-heap"); - if (UseCompressedOops) { - __ lea(lr, RuntimeAddress(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow))); - } else { - __ lea(lr, RuntimeAddress(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak))); - } - } else { - assert(is_phantom, "only remaining strength"); - assert(is_native, "phantom must only be called off-heap"); - __ lea(lr, RuntimeAddress(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom))); - } - __ blr(lr); - __ mov(rscratch1, r0); - __ pop_call_clobbered_registers(); - __ mov(r0, rscratch1); - + const Register tmp_obj = r0; + const Register tmp_addr = r1; + __ push(RegSet::of(tmp_addr), sp); + __ load_parameter(0, tmp_obj); + __ load_parameter(1, tmp_addr); + load_reference_barrier(sasm, tmp_obj, Address(tmp_addr, 0), decorators); + __ pop(RegSet::of(tmp_addr), sp); __ epilogue(); } diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp index bab4fb3b37a..d25dd8871f9 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp @@ -32,7 +32,7 @@ #include "gc/shenandoah/shenandoahBarrierSet.hpp" #ifdef COMPILER1 class LIR_Assembler; -class ShenandoahPreBarrierStub; +class ShenandoahKeepaliveBarrierStub; class ShenandoahLoadReferenceBarrierStub; class StubAssembler; #endif @@ -76,10 +76,11 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { Register tmp, Label& slow_path); #ifdef COMPILER1 - void gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub); - void gen_load_reference_barrier_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); - void generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm); - void generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); + void keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub); + void keepalive_barrier_c1_runtime_stub(StubAssembler* sasm); + + void load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); + void load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); #endif #ifdef COMPILER2 diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp index 582327282fd..b17f0f924ae 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp @@ -56,10 +56,11 @@ void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler *masm, Register base, RegisterOrConstant ind_or_offs, Register tmp1, Register tmp2, Register tmp3, - MacroAssembler::PreservationLevel preservation_level) { + MacroAssembler::PreservationLevel preservation_level, + int extra_stack_space) { if (ShenandoahSATBBarrier) { __ block_comment("satb_barrier (shenandoahgc) {"); - satb_barrier_impl(masm, 0, base, ind_or_offs, tmp1, tmp2, tmp3, preservation_level); + satb_barrier_impl(masm, 0, base, ind_or_offs, tmp1, tmp2, tmp3, preservation_level, extra_stack_space); __ block_comment("} satb_barrier (shenandoahgc)"); } } @@ -68,10 +69,11 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler *masm, Register base, RegisterOrConstant ind_or_offs, Register dst, Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level) { + MacroAssembler::PreservationLevel preservation_level, + int extra_stack_space) { if (ShenandoahLoadRefBarrier) { __ block_comment("load_reference_barrier (shenandoahgc) {"); - load_reference_barrier_impl(masm, decorators, base, ind_or_offs, dst, tmp1, tmp2, preservation_level); + load_reference_barrier_impl(masm, decorators, base, ind_or_offs, dst, tmp1, tmp2, preservation_level, extra_stack_space); __ block_comment("} load_reference_barrier (shenandoahgc)"); } } @@ -205,7 +207,8 @@ void ShenandoahBarrierSetAssembler::satb_barrier_impl(MacroAssembler *masm, Deco Register base, RegisterOrConstant ind_or_offs, Register pre_val, Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level) { + MacroAssembler::PreservationLevel preservation_level, + int extra_stack_space) { assert(ShenandoahSATBBarrier, "Should be checked by caller"); assert_different_registers(tmp1, tmp2, pre_val, noreg); @@ -299,7 +302,7 @@ void ShenandoahBarrierSetAssembler::satb_barrier_impl(MacroAssembler *masm, Deco if (preserve_gp_registers) { nbytes_save = (preserve_fp_registers ? MacroAssembler::num_volatile_gp_regs + MacroAssembler::num_volatile_fp_regs - : MacroAssembler::num_volatile_gp_regs) * BytesPerWord; + : MacroAssembler::num_volatile_gp_regs) * BytesPerWord + extra_stack_space; __ save_volatile_gprs(R1_SP, -nbytes_save, preserve_fp_registers); } @@ -343,7 +346,8 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier_impl( Register base, RegisterOrConstant ind_or_offs, Register dst, Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level) { + MacroAssembler::PreservationLevel preservation_level, + int extra_stack_space) { if (ind_or_offs.is_register()) { assert_different_registers(tmp1, tmp2, base, ind_or_offs.as_register(), dst, noreg); } else { @@ -430,7 +434,7 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier_impl( if (preserve_gp_registers) { nbytes_save = (preserve_fp_registers ? MacroAssembler::num_volatile_gp_regs + MacroAssembler::num_volatile_fp_regs - : MacroAssembler::num_volatile_gp_regs) * BytesPerWord; + : MacroAssembler::num_volatile_gp_regs) * BytesPerWord + extra_stack_space; __ save_volatile_gprs(R1_SP, -nbytes_save, preserve_fp_registers); } @@ -693,243 +697,119 @@ void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssemb #define __ ce->masm()-> -void ShenandoahBarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler *ce, ShenandoahPreBarrierStub *stub) { - __ block_comment("gen_pre_barrier_stub (shenandoahgc) {"); - - ShenandoahBarrierSetC1 *bs = (ShenandoahBarrierSetC1*) BarrierSet::barrier_set()->barrier_set_c1(); +void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub) { + __ block_comment("keepalive_barrier_stub (shenandoahgc) {"); __ bind(*stub->entry()); - // GC status has already been verified by 'ShenandoahBarrierSetC1::pre_barrier'. - // This stub is the slowpath of that function. + ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*) BarrierSet::barrier_set()->barrier_set_c1(); - assert(stub->pre_val()->is_register(), "pre_val must be a register"); - Register pre_val = stub->pre_val()->as_register(); + Register obj = stub->obj()->as_register(); - // If 'do_load()' returns false, the to-be-stored value is already available in 'stub->pre_val()' - // ("preloaded mode" of the store barrier). + // If 'do_load()' returns false, the to-be-stored value is already available in 'obj' if (stub->do_load()) { - ce->mem2reg(stub->addr(), stub->pre_val(), T_OBJECT, stub->patch_code(), stub->info(), false); + ce->mem2reg(stub->addr(), stub->obj(), T_OBJECT, lir_patch_none, nullptr, false); } - // Fast path: Reference is null. - __ cmpdi(CR0, pre_val, 0); + // Fast path: reference is null. + __ cmpdi(CR0, obj, 0); __ bc_far_optimized(Assembler::bcondCRbiIs1_bhintNoHint, __ bi0(CR0, Assembler::equal), *stub->continuation()); // Argument passing via the stack. - __ std(pre_val, -8, R1_SP); + __ std(obj, -8, R1_SP); - __ load_const_optimized(R0, bs->pre_barrier_c1_runtime_code_blob()->code_begin()); + address blob_addr = bs->keepalive_barrier_stub(); + __ load_const_optimized(R0, blob_addr); __ call_stub(R0); __ b(*stub->continuation()); - __ block_comment("} gen_pre_barrier_stub (shenandoahgc)"); + __ block_comment("} keepalive_barrier_stub (shenandoahgc)"); } -void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assembler *ce, - ShenandoahLoadReferenceBarrierStub *stub) { - __ block_comment("gen_load_reference_barrier_stub (shenandoahgc) {"); +void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub) { + __ block_comment("load_reference_barrier_stub (shenandoahgc) {"); - ShenandoahBarrierSetC1 *bs = (ShenandoahBarrierSetC1*) BarrierSet::barrier_set()->barrier_set_c1(); __ bind(*stub->entry()); + ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*) BarrierSet::barrier_set()->barrier_set_c1(); + Register obj = stub->obj()->as_register(); - Register res = stub->result()->as_register(); Register addr = stub->addr()->as_pointer_register(); - Register tmp1 = stub->tmp1()->as_register(); - Register tmp2 = stub->tmp2()->as_register(); - assert_different_registers(addr, res, tmp1, tmp2); - - assert(R3_RET == res, "res must be r3"); - - if (res != obj) { - __ mr(res, obj); - } - - DecoratorSet decorators = stub->decorators(); - - /* ==== Check whether region is in collection set ==== */ - // GC status (unstable) has already been verified by 'ShenandoahBarrierSetC1::load_reference_barrier_impl'. - // This stub is the slowpath of that function. - - bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); - bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); - bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); - bool is_native = ShenandoahBarrierSet::is_native_access(decorators); - - if (is_strong) { - // Check whether object is in collection set. - __ load_const_optimized(tmp2, ShenandoahHeap::in_cset_fast_test_addr(), tmp1); - __ srdi(tmp1, obj, ShenandoahHeapRegion::region_size_bytes_shift_jint()); - __ lbzx(tmp2, tmp1, tmp2); + Register slow_result = stub->slow_result()->as_register(); + assert_different_registers(obj, addr, slow_result); + assert(slow_result == R3_RET, "C1 must know about our slow call result register"); - __ andi_(tmp2, tmp2, 1); - __ bc_far_optimized(Assembler::bcondCRbiIs1_bhintNoHint, __ bi0(CR0, Assembler::equal), *stub->continuation()); - } - - address blob_addr = nullptr; + // Argument passing via the stack. + __ std(obj, -8, R1_SP); + __ std(addr, -16, R1_SP); - if (is_strong) { - if (is_native) { - blob_addr = bs->load_reference_barrier_strong_native_rt_code_blob()->code_begin(); - } else { - blob_addr = bs->load_reference_barrier_strong_rt_code_blob()->code_begin(); - } - } else if (is_weak) { - blob_addr = bs->load_reference_barrier_weak_rt_code_blob()->code_begin(); - } else { - assert(is_phantom, "only remaining strength"); - blob_addr = bs->load_reference_barrier_phantom_rt_code_blob()->code_begin(); + address blob_addr = bs->load_reference_barrier_stub(stub->decorators()); + __ load_const_optimized(R0, blob_addr); + __ call_stub(R0); + if (obj != slow_result) { + __ mr(obj, slow_result); } - assert(blob_addr != nullptr, "code blob cannot be found"); - - // Argument passing via the stack. 'obj' is passed implicitly (as asserted above). - __ std(addr, -8, R1_SP); - - __ load_const_optimized(tmp1, blob_addr, tmp2); - __ call_stub(tmp1); - - // 'res' is 'R3_RET'. The result is thus already in the correct register. - __ b(*stub->continuation()); - __ block_comment("} gen_load_reference_barrier_stub (shenandoahgc)"); + __ block_comment("} load_reference_barrier_stub (shenandoahgc)"); } #undef __ #define __ sasm-> -void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAssembler *sasm) { - __ block_comment("generate_c1_pre_barrier_runtime_stub (shenandoahgc) {"); +void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_runtime_stub(StubAssembler* sasm) { + __ block_comment("keepalive_barrier_runtime_stub (shenandoahgc) {"); - Label runtime, skip_barrier; - BarrierSet *bs = BarrierSet::barrier_set(); - - // Argument passing via the stack. - const int caller_stack_slots = 3; - - Register R0_pre_val = R0; - __ ld(R0, -8, R1_SP); - Register R11_tmp1 = R11_scratch1; - __ std(R11_tmp1, -16, R1_SP); - Register R12_tmp2 = R12_scratch2; - __ std(R12_tmp2, -24, R1_SP); - - /* ==== Check whether marking is active ==== */ - // Even though gc status was checked in 'ShenandoahBarrierSetAssembler::gen_pre_barrier_stub', - // another check is required as a safepoint might have been reached in the meantime (JDK-8140588). - __ lbz(R12_tmp2, in_bytes(ShenandoahThreadLocalData::gc_state_offset()), R16_thread); - - __ andi_(R12_tmp2, R12_tmp2, ShenandoahHeap::MARKING); - __ beq(CR0, skip_barrier); + Register obj = R3_ARG1; + Register tmp1 = R11_scratch1; + Register tmp2 = R12_scratch2; - /* ==== Add previous value directly to thread-local SATB mark queue ==== */ - // Check queue's capacity. Jump to runtime if no free slot is available. - __ ld(R12_tmp2, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset()), R16_thread); - __ cmpdi(CR0, R12_tmp2, 0); - __ beq(CR0, runtime); + // Save registers we are about to clobber + __ std(obj, -16, R1_SP); + __ std(tmp1, -24, R1_SP); + __ std(tmp2, -32, R1_SP); - // Capacity suffices. Decrement the queue's size by one slot (size of one oop). - __ addi(R12_tmp2, R12_tmp2, -wordSize); - __ std(R12_tmp2, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset()), R16_thread); + // Pull the arguments from stack + __ ld(obj, -8, R1_SP); - // Enqueue the previous value and skip the runtime invocation. - __ ld(R11_tmp1, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset()), R16_thread); - __ stdx(R0_pre_val, R11_tmp1, R12_tmp2); - __ b(skip_barrier); + satb_barrier(sasm, noreg, noreg, obj, tmp1, tmp2, MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS, 4 * BytesPerWord); - __ bind(runtime); - - /* ==== Invoke runtime to commit SATB mark queue to gc and allocate a new buffer ==== */ - // Save to-be-preserved registers. - const int nbytes_save = (MacroAssembler::num_volatile_regs + caller_stack_slots) * BytesPerWord; - __ save_volatile_gprs(R1_SP, -nbytes_save); - __ save_LR(R11_tmp1); - __ push_frame_reg_args(nbytes_save, R11_tmp1); - - // Invoke runtime. - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), R0_pre_val); - - // Restore to-be-preserved registers. - __ pop_frame(); - __ restore_LR(R11_tmp1); - __ restore_volatile_gprs(R1_SP, -nbytes_save); - - __ bind(skip_barrier); - - // Restore spilled registers. - __ ld(R11_tmp1, -16, R1_SP); - __ ld(R12_tmp2, -24, R1_SP); + // Restore registers + __ ld(tmp2, -32, R1_SP); + __ ld(tmp1, -24, R1_SP); + __ ld(obj, -16, R1_SP); __ blr(); - __ block_comment("} generate_c1_pre_barrier_runtime_stub (shenandoahgc)"); + __ block_comment("} keepalive_barrier_runtime_stub (shenandoahgc)"); } -void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_stub(StubAssembler *sasm, - DecoratorSet decorators) { - __ block_comment("generate_c1_load_reference_barrier_runtime_stub (shenandoahgc) {"); - - // Argument passing via the stack. - const int caller_stack_slots = 1; - - // Save to-be-preserved registers. - const int nbytes_save = (MacroAssembler::num_volatile_regs - 1 // 'R3_ARG1' is skipped - + caller_stack_slots) * BytesPerWord; - __ save_volatile_gprs(R1_SP, -nbytes_save, true, false); - - // Load arguments from stack. - // No load required, as caller has already loaded obj into R3. - Register R3_obj = R3_ARG1; - Register R4_load_addr = R4_ARG2; - __ ld(R4_load_addr, -8, R1_SP); - - Register R11_tmp = R11_scratch1; - - /* ==== Invoke runtime ==== */ - bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); - bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); - bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); - bool is_native = ShenandoahBarrierSet::is_native_access(decorators); +void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators) { + __ block_comment("load_reference_barrier_runtime_stub (shenandoahgc) {"); - address jrt_address = nullptr; + Register obj = R3_ARG1; + Register addr = R4_ARG2; + Register tmp1 = R11_scratch1; + Register tmp2 = R12_scratch2; - if (is_strong) { - if (is_native) { - jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong); - } else { - if (UseCompressedOops) { - jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow); - } else { - jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong); - } - } - } else if (is_weak) { - assert(!is_native, "weak load reference barrier must not be called off-heap"); - if (UseCompressedOops) { - jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow); - } else { - jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak); - } - } else { - assert(is_phantom, "reference type must be phantom"); - assert(is_native, "phantom load reference barrier must be called off-heap"); - jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom); - } - assert(jrt_address != nullptr, "load reference barrier runtime routine cannot be found"); + // Save registers we are about to clobber + __ std(addr, -24, R1_SP); + __ std(tmp1, -32, R1_SP); + __ std(tmp2, -40, R1_SP); - __ save_LR(R11_tmp); - __ push_frame_reg_args(nbytes_save, R11_tmp); + // Pull the arguments from the stack + __ ld(obj, -8, R1_SP); + __ ld(addr, -16, R1_SP); - // Invoke runtime. Arguments are already stored in the corresponding registers. - __ call_VM_leaf(jrt_address, R3_obj, R4_load_addr); + load_reference_barrier(sasm, decorators, addr, noreg, obj, tmp1, tmp2, + MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS, 5 * BytesPerWord); - // Restore to-be-preserved registers. - __ pop_frame(); - __ restore_LR(R11_tmp); - __ restore_volatile_gprs(R1_SP, -nbytes_save, true, false); // Skip 'R3_RET' register. + // Restore registers + __ ld(tmp2, -40, R1_SP); + __ ld(tmp1, -32, R1_SP); + __ ld(addr, -24, R1_SP); __ blr(); - __ block_comment("} generate_c1_load_reference_barrier_runtime_stub (shenandoahgc)"); + __ block_comment("} load_reference_barrier_runtime_stub (shenandoahgc)"); } #undef __ diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp index bd1043c2d76..8d741e6104b 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp @@ -34,7 +34,7 @@ #ifdef COMPILER1 class LIR_Assembler; -class ShenandoahPreBarrierStub; +class ShenandoahKeepaliveBarrierStub; class ShenandoahLoadReferenceBarrierStub; class StubAssembler; @@ -56,7 +56,8 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { Register base, RegisterOrConstant ind_or_offs, Register pre_val, Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level); + MacroAssembler::PreservationLevel preservation_level, + int extra_stack_space = 0); void card_barrier(MacroAssembler* masm, Register base, RegisterOrConstant ind_or_offs, @@ -66,7 +67,8 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { Register base, RegisterOrConstant ind_or_offs, Register dst, Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level); + MacroAssembler::PreservationLevel preservation_level, + int extra_stack_space = 0); /* ==== Helper methods for barrier implementations ==== */ void gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, @@ -78,28 +80,26 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { /* ==== C1 stubs ==== */ #ifdef COMPILER1 + void keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub); + void keepalive_barrier_c1_runtime_stub(StubAssembler* sasm); - void gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub); - - void gen_load_reference_barrier_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); - - void generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm); - - void generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); - + void load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); + void load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); #endif /* ==== Available barriers (facades of the actual implementations) ==== */ void satb_barrier(MacroAssembler* masm, Register base, RegisterOrConstant ind_or_offs, Register tmp1, Register tmp2, Register tmp3, - MacroAssembler::PreservationLevel preservation_level); + MacroAssembler::PreservationLevel preservation_level, + int extra_stack_space = 0); void load_reference_barrier(MacroAssembler* masm, DecoratorSet decorators, Register base, RegisterOrConstant ind_or_offs, Register dst, Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level); + MacroAssembler::PreservationLevel preservation_level, + int extra_stack_space = 0); /* ==== Access api ==== */ virtual void arraycopy_prologue(MacroAssembler* masm, DecoratorSet decorators, BasicType type, diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp index eec5f9a5165..574c70c8ea4 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp @@ -471,74 +471,39 @@ void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssemb #define __ ce->masm()-> -void ShenandoahBarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub) { - ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); - // At this point we know that marking is in progress. - // If do_load() is true then we have to emit the - // load of the previous value; otherwise it has already - // been loaded into _pre_val. +void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub) { __ bind(*stub->entry()); - assert(stub->pre_val()->is_register(), "Precondition."); + ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); - Register pre_val_reg = stub->pre_val()->as_register(); + Register obj = stub->obj()->as_register(); if (stub->do_load()) { - ce->mem2reg(stub->addr(), stub->pre_val(), T_OBJECT, stub->patch_code(), stub->info(), false /* wide */); + ce->mem2reg(stub->addr(), stub->obj(), T_OBJECT, lir_patch_none, nullptr, false /* wide */); } - __ beqz(pre_val_reg, *stub->continuation(), /* is_far */ true); - ce->store_parameter(stub->pre_val()->as_register(), 0); - __ far_call(RuntimeAddress(bs->pre_barrier_c1_runtime_code_blob()->code_begin())); + __ beqz(obj, *stub->continuation(), /* is_far */ true); + + ce->store_parameter(obj, 0); + __ far_call(RuntimeAddress(bs->keepalive_barrier_stub())); __ j(*stub->continuation()); } -void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assembler* ce, - ShenandoahLoadReferenceBarrierStub* stub) { - ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); +void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub) { __ bind(*stub->entry()); - DecoratorSet decorators = stub->decorators(); - bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); - bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); - bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); - bool is_native = ShenandoahBarrierSet::is_native_access(decorators); + ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*) BarrierSet::barrier_set()->barrier_set_c1(); Register obj = stub->obj()->as_register(); - Register res = stub->result()->as_register(); Register addr = stub->addr()->as_pointer_register(); - Register tmp1 = stub->tmp1()->as_register(); - Register tmp2 = stub->tmp2()->as_register(); + Register slow_result = stub->slow_result()->as_register(); + assert_different_registers(obj, addr, slow_result); + assert(slow_result == x10, "C1 must know about our slow call result register"); - assert(res == x10, "result must arrive in x10"); - assert_different_registers(tmp1, tmp2, t0); - - if (res != obj) { - __ mv(res, obj); - } - - if (is_strong) { - // Check for object in cset. - __ mv(tmp2, ShenandoahHeap::in_cset_fast_test_addr()); - __ srli(tmp1, res, ShenandoahHeapRegion::region_size_bytes_shift_jint()); - __ add(tmp2, tmp2, tmp1); - __ lbu(tmp2, Address(tmp2)); - __ beqz(tmp2, *stub->continuation(), true /* is_far */); - } - - ce->store_parameter(res, 0); + ce->store_parameter(obj, 0); ce->store_parameter(addr, 1); - - if (is_strong) { - if (is_native) { - __ far_call(RuntimeAddress(bs->load_reference_barrier_strong_native_rt_code_blob()->code_begin())); - } else { - __ far_call(RuntimeAddress(bs->load_reference_barrier_strong_rt_code_blob()->code_begin())); - } - } else if (is_weak) { - __ far_call(RuntimeAddress(bs->load_reference_barrier_weak_rt_code_blob()->code_begin())); - } else { - assert(is_phantom, "only remaining strength"); - __ far_call(RuntimeAddress(bs->load_reference_barrier_phantom_rt_code_blob()->code_begin())); + __ far_call(RuntimeAddress(bs->load_reference_barrier_stub(stub->decorators()))); + if (obj != slow_result) { + __ mv(obj, slow_result); } __ j(*stub->continuation()); @@ -548,92 +513,27 @@ void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assemble #define __ sasm-> -void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm) { - __ prologue("shenandoah_pre_barrier", false); - - // arg0 : previous value of memory - - BarrierSet* bs = BarrierSet::barrier_set(); - - const Register pre_val = x10; - const Register thread = xthread; - const Register tmp = t0; - - Address queue_index(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset())); - Address buffer(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset())); - - Label done; - Label runtime; - - // Is marking still active? - Address gc_state(thread, in_bytes(ShenandoahThreadLocalData::gc_state_offset())); - __ lb(tmp, gc_state); - __ test_bit(tmp, tmp, ShenandoahHeap::MARKING_BITPOS); - __ beqz(tmp, done); - - // Can we store original value in the thread's buffer? - __ ld(tmp, queue_index); - __ beqz(tmp, runtime); - - __ subi(tmp, tmp, wordSize); - __ sd(tmp, queue_index); - __ ld(t1, buffer); - __ add(tmp, tmp, t1); - __ load_parameter(0, t1); - __ sd(t1, Address(tmp, 0)); - __ j(done); - - __ bind(runtime); - __ push_call_clobbered_registers(); - __ load_parameter(0, pre_val); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), pre_val); - __ pop_call_clobbered_registers(); - __ bind(done); - +void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_runtime_stub(StubAssembler* sasm) { + __ prologue("shenandoah_keepalive_barrier", false); + const Register tmp_obj = x10; + const Register tmp1 = x11; + const Register tmp2 = x12; + __ push_reg(RegSet::of(tmp1, tmp2, tmp_obj), sp); + __ load_parameter(0, tmp_obj); + satb_barrier(sasm, noreg, tmp_obj, xthread, tmp1, tmp2); + __ pop_reg(RegSet::of(tmp1, tmp2, tmp_obj), sp); __ epilogue(); } -void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, - DecoratorSet decorators) { +void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators) { __ prologue("shenandoah_load_reference_barrier", false); - // arg0 : object to be resolved - - __ push_call_clobbered_registers(); - __ load_parameter(0, x10); - __ load_parameter(1, x11); - - bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); - bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); - bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); - bool is_native = ShenandoahBarrierSet::is_native_access(decorators); - address target = nullptr; - if (is_strong) { - if (is_native) { - target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong); - } else { - if (UseCompressedOops) { - target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow); - } else { - target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong); - } - } - } else if (is_weak) { - assert(!is_native, "weak must not be called off-heap"); - if (UseCompressedOops) { - target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow); - } else { - target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak); - } - } else { - assert(is_phantom, "only remaining strength"); - assert(is_native, "phantom must only be called off-heap"); - target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom); - } - __ rt_call(target); - __ mv(t0, x10); - __ pop_call_clobbered_registers(); - __ mv(x10, t0); - + const Register tmp_obj = x10; + const Register tmp_addr = x11; + __ push_reg(RegSet::of(tmp_addr), sp); + __ load_parameter(0, tmp_obj); + __ load_parameter(1, tmp_addr); + load_reference_barrier(sasm, tmp_obj, Address(tmp_addr, 0), decorators); + __ pop_reg(RegSet::of(tmp_addr), sp); __ epilogue(); } diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp index d41809f1ef7..ecb63e68a01 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp @@ -33,7 +33,7 @@ #ifdef COMPILER1 class LIR_Assembler; -class ShenandoahPreBarrierStub; +class ShenandoahKeepaliveBarrierStub; class ShenandoahLoadReferenceBarrierStub; class StubAssembler; #endif @@ -81,10 +81,11 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { Register tmp, Label& slow_path); #ifdef COMPILER1 - void gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub); - void gen_load_reference_barrier_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); - void generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm); - void generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); + void keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub); + void keepalive_barrier_c1_runtime_stub(StubAssembler* sasm); + + void load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); + void load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); #endif #ifdef COMPILER2 diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp index fdf10e5b5e6..bdb98d4b4c0 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp @@ -154,11 +154,7 @@ void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm, Label runtime; assert(pre_val != noreg, "check this code"); - - if (obj != noreg) { - assert_different_registers(obj, pre_val, tmp); - assert(pre_val != rax, "check this code"); - } + assert_different_registers(obj, pre_val, tmp); Address index(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset())); Address buffer(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset())); @@ -560,99 +556,42 @@ void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssemb #define __ ce->masm()-> -void ShenandoahBarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub) { - ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); - // At this point we know that marking is in progress. - // If do_load() is true then we have to emit the - // load of the previous value; otherwise it has already - // been loaded into _pre_val. - +void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub) { __ bind(*stub->entry()); - assert(stub->pre_val()->is_register(), "Precondition."); - Register pre_val_reg = stub->pre_val()->as_register(); + ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); + + Register obj = stub->obj()->as_register(); if (stub->do_load()) { - ce->mem2reg(stub->addr(), stub->pre_val(), T_OBJECT, stub->patch_code(), stub->info(), false /*wide*/); + ce->mem2reg(stub->addr(), stub->obj(), T_OBJECT, lir_patch_none, nullptr, /* wide = */ false); } - - __ cmpptr(pre_val_reg, NULL_WORD); + __ cmpptr(obj, NULL_WORD); __ jcc(Assembler::equal, *stub->continuation()); - ce->store_parameter(stub->pre_val()->as_register(), 0); - __ call(RuntimeAddress(bs->pre_barrier_c1_runtime_code_blob()->code_begin())); - __ jmp(*stub->continuation()); + ce->store_parameter(obj, 0); + __ call(RuntimeAddress(bs->keepalive_barrier_stub())); + __ jmp(*stub->continuation()); } -void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub) { - ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); +void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub) { __ bind(*stub->entry()); - DecoratorSet decorators = stub->decorators(); - bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); - bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); - bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); - bool is_native = ShenandoahBarrierSet::is_native_access(decorators); + ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); Register obj = stub->obj()->as_register(); - Register res = stub->result()->as_register(); Register addr = stub->addr()->as_pointer_register(); - Register tmp1 = stub->tmp1()->as_register(); - Register tmp2 = stub->tmp2()->as_register(); - assert_different_registers(obj, res, addr, tmp1, tmp2); - - Label slow_path; + Register slow_result = stub->slow_result()->as_register(); + assert_different_registers(obj, addr, slow_result); + assert(slow_result == rax, "C1 must know about our slow call result register"); - assert(res == rax, "result must arrive in rax"); - - if (res != obj) { - __ mov(res, obj); - } - - if (is_strong) { - // Check for object being in the collection set. - __ mov(tmp1, res); - if (AOTCodeCache::is_on_for_dump()) { - __ push(rcx); - __ lea(rcx, ExternalAddress(AOTRuntimeConstants::grain_shift_address())); - __ movl(rcx, Address(rcx)); - if (tmp1 != rcx) { - __ mov(tmp1, res); - __ shrptr(tmp1); - __ pop(rcx); - } else { - assert_different_registers(tmp2, rcx); - __ mov(tmp2, res); - __ shrptr(tmp2); - __ pop(rcx); - __ movptr(tmp1, tmp2); - } - __ lea(tmp2, ExternalAddress(AOTRuntimeConstants::cset_base_address())); - __ movptr(tmp2, Address(tmp2)); - } else { - __ shrptr(tmp1, ShenandoahHeapRegion::region_size_bytes_shift_jint()); - __ movptr(tmp2, (intptr_t) ShenandoahHeap::in_cset_fast_test_addr()); - } - __ movbool(tmp2, Address(tmp2, tmp1, Address::times_1)); - __ testbool(tmp2); - __ jcc(Assembler::zero, *stub->continuation()); - } - - __ bind(slow_path); - ce->store_parameter(res, 0); + ce->store_parameter(obj, 0); ce->store_parameter(addr, 1); - if (is_strong) { - if (is_native) { - __ call(RuntimeAddress(bs->load_reference_barrier_strong_native_rt_code_blob()->code_begin())); - } else { - __ call(RuntimeAddress(bs->load_reference_barrier_strong_rt_code_blob()->code_begin())); - } - } else if (is_weak) { - __ call(RuntimeAddress(bs->load_reference_barrier_weak_rt_code_blob()->code_begin())); - } else { - assert(is_phantom, "only remaining strength"); - __ call(RuntimeAddress(bs->load_reference_barrier_phantom_rt_code_blob()->code_begin())); + __ call(RuntimeAddress(bs->load_reference_barrier_stub(stub->decorators()))); + if (obj != slow_result) { + __ mov(obj, slow_result); } + __ jmp(*stub->continuation()); } @@ -660,98 +599,28 @@ void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assemble #define __ sasm-> -void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm) { - __ prologue("shenandoah_pre_barrier", false); - // arg0 : previous value of memory - - __ push(rax); - __ push(rdx); - - const Register pre_val = rax; - const Register thread = r15_thread; +void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_runtime_stub(StubAssembler* sasm) { + __ prologue("shenandoah_keepalive_barrier", false); + const Register tmp_obj = rax; const Register tmp = rdx; - - Address queue_index(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset())); - Address buffer(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset())); - - Label done; - Label runtime; - - // Is SATB still active? - Address gc_state(thread, in_bytes(ShenandoahThreadLocalData::gc_state_offset())); - __ testb(gc_state, ShenandoahHeap::MARKING); - __ jcc(Assembler::zero, done); - - // Can we store original value in the thread's buffer? - - __ movptr(tmp, queue_index); - __ testptr(tmp, tmp); - __ jcc(Assembler::zero, runtime); - __ subptr(tmp, wordSize); - __ movptr(queue_index, tmp); - __ addptr(tmp, buffer); - - // prev_val (rax) - __ load_parameter(0, pre_val); - __ movptr(Address(tmp, 0), pre_val); - __ jmp(done); - - __ bind(runtime); - - __ save_live_registers_no_oop_map(true); - - // load the pre-value - __ load_parameter(0, rcx); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), rcx); - - __ restore_live_registers(true); - - __ bind(done); - - __ pop(rdx); - __ pop(rax); - + __ push(tmp); + __ push(tmp_obj); + __ load_parameter(0, tmp_obj); + satb_barrier(sasm, noreg, tmp_obj, tmp); + __ pop(tmp_obj); + __ pop(tmp); __ epilogue(); } -void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, DecoratorSet decorators) { +void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators) { __ prologue("shenandoah_load_reference_barrier", false); - // arg0 : object to be resolved - - __ save_live_registers_no_oop_map(true); - - bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); - bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); - bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); - bool is_native = ShenandoahBarrierSet::is_native_access(decorators); - - __ load_parameter(0, c_rarg0); - __ load_parameter(1, c_rarg1); - if (is_strong) { - if (is_native) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong), c_rarg0, c_rarg1); - } else { - if (UseCompressedOops) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow), c_rarg0, c_rarg1); - } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong), c_rarg0, c_rarg1); - } - } - } else if (is_weak) { - assert(!is_native, "weak must not be called off-heap"); - if (UseCompressedOops) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow), c_rarg0, c_rarg1); - } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak), c_rarg0, c_rarg1); - } - } else { - assert(is_phantom, "only remaining strength"); - assert(is_native, "phantom must only be called off-heap"); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom), c_rarg0, c_rarg1); - } - - __ restore_live_registers_except_rax(true); - + const Register tmp_obj = rax; + const Register tmp_addr = rdx; + __ push(tmp_addr); + __ load_parameter(0, tmp_obj); + __ load_parameter(1, tmp_addr); + load_reference_barrier(sasm, tmp_obj, Address(tmp_addr, 0), decorators); + __ pop(tmp_addr); __ epilogue(); } diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp index f608760ce42..7f417d3c262 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp @@ -33,7 +33,7 @@ #ifdef COMPILER1 class LIR_Assembler; -class ShenandoahPreBarrierStub; +class ShenandoahKeepaliveBarrierStub; class ShenandoahLoadReferenceBarrierStub; class StubAssembler; #endif @@ -73,10 +73,11 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { virtual void try_peek_weak_handle_in_nmethod(MacroAssembler* masm, Register weak_handle, Register obj, Label& slowpath); #ifdef COMPILER1 - void gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub); - void gen_load_reference_barrier_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); - void generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm); - void generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); + void keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub); + void keepalive_barrier_c1_runtime_stub(StubAssembler* sasm); + + void load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); + void load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); #endif #ifdef COMPILER2 diff --git a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp index 637ed6e6407..de0b838fe45 100644 --- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp +++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp @@ -31,8 +31,6 @@ #include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" -#include "gc/shenandoah/shenandoahHeapRegion.hpp" -#include "gc/shenandoah/shenandoahRuntime.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" #ifdef ASSERT @@ -41,43 +39,61 @@ #define __ gen->lir()-> #endif -void ShenandoahPreBarrierStub::emit_code(LIR_Assembler* ce) { +void ShenandoahKeepaliveBarrierStub::emit_code(LIR_Assembler* ce) { ShenandoahBarrierSetAssembler* bs = (ShenandoahBarrierSetAssembler*)BarrierSet::barrier_set()->barrier_set_assembler(); - bs->gen_pre_barrier_stub(ce, this); + bs->keepalive_barrier_c1_stub(ce, this); } void ShenandoahLoadReferenceBarrierStub::emit_code(LIR_Assembler* ce) { ShenandoahBarrierSetAssembler* bs = (ShenandoahBarrierSetAssembler*)BarrierSet::barrier_set()->barrier_set_assembler(); - bs->gen_load_reference_barrier_stub(ce, this); + bs->load_reference_barrier_c1_stub(ce, this); } ShenandoahBarrierSetC1::ShenandoahBarrierSetC1() : - _pre_barrier_c1_runtime_code_blob(nullptr), + _keepalive_barrier_c1_runtime_code_blob(nullptr), _load_reference_barrier_strong_rt_code_blob(nullptr), _load_reference_barrier_strong_native_rt_code_blob(nullptr), _load_reference_barrier_weak_rt_code_blob(nullptr), _load_reference_barrier_phantom_rt_code_blob(nullptr) {} -void ShenandoahBarrierSetC1::pre_barrier(LIRGenerator* gen, CodeEmitInfo* info, DecoratorSet decorators, LIR_Opr addr_opr, LIR_Opr pre_val) { - // First we test whether marking is in progress. +address ShenandoahBarrierSetC1::keepalive_barrier_stub() { + assert(_keepalive_barrier_c1_runtime_code_blob != nullptr, "Must be available"); + return _keepalive_barrier_c1_runtime_code_blob->code_begin(); +} - bool patch = (decorators & C1_NEEDS_PATCHING) != 0; - bool do_load = pre_val == LIR_OprFact::illegalOpr; +address ShenandoahBarrierSetC1::load_reference_barrier_stub(DecoratorSet decorators) { + bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); + bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); + bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); + bool is_native = ShenandoahBarrierSet::is_native_access(decorators); + if (is_strong) { + if (is_native) { + assert(_load_reference_barrier_strong_native_rt_code_blob != nullptr, "Must be available"); + return _load_reference_barrier_strong_native_rt_code_blob->code_begin(); + } else { + assert(_load_reference_barrier_strong_rt_code_blob != nullptr, "Must be available"); + return _load_reference_barrier_strong_rt_code_blob->code_begin(); + } + } else if (is_weak) { + assert(_load_reference_barrier_weak_rt_code_blob != nullptr, "Must be available"); + return _load_reference_barrier_weak_rt_code_blob->code_begin(); + } else if (is_phantom) { + assert(_load_reference_barrier_phantom_rt_code_blob != nullptr, "Must be available"); + return _load_reference_barrier_phantom_rt_code_blob->code_begin(); + } + ShouldNotReachHere(); + return nullptr; +} + +void ShenandoahBarrierSetC1::enter_if_gc_state(LIRGenerator* gen, int flags, CodeStub* slow_stub) { LIR_Opr thrd = gen->getThreadPointer(); - LIR_Address* gc_state_addr = - new LIR_Address(thrd, - in_bytes(ShenandoahThreadLocalData::gc_state_offset()), - T_BYTE); - // Read the gc_state flag. LIR_Opr flag_val = gen->new_register(T_INT); - __ load(gc_state_addr, flag_val); - - // Create a mask to test if the marking bit is set. - LIR_Opr mask = LIR_OprFact::intConst(ShenandoahHeap::MARKING); LIR_Opr mask_reg = gen->new_register(T_INT); - __ move(mask, mask_reg); + LIR_Address* gc_state_addr = new LIR_Address(thrd, in_bytes(ShenandoahThreadLocalData::gc_state_offset()), T_BYTE); + __ load(gc_state_addr, flag_val); + __ move(LIR_OprFact::intConst(flags), mask_reg); if (two_operand_lir_form) { __ logical_and(flag_val, mask_reg, flag_val); } else { @@ -86,91 +102,54 @@ void ShenandoahBarrierSetC1::pre_barrier(LIRGenerator* gen, CodeEmitInfo* info, flag_val = masked_flag; } __ cmp(lir_cond_notEqual, flag_val, LIR_OprFact::intConst(0)); + __ branch(lir_cond_notEqual, slow_stub); + __ branch_destination(slow_stub->continuation()); +} - LIR_PatchCode pre_val_patch_code = lir_patch_none; - - CodeStub* slow; - - if (do_load) { - assert(pre_val == LIR_OprFact::illegalOpr, "sanity"); - assert(addr_opr != LIR_OprFact::illegalOpr, "sanity"); - - if (patch) - pre_val_patch_code = lir_patch_normal; - - pre_val = gen->new_register(T_OBJECT); +void ShenandoahBarrierSetC1::keepalive_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators) { + CodeStub* slow_stub; + if (obj == LIR_OprFact::illegalOpr) { + // Caller wants us to do the load. + obj = gen->new_register(T_OBJECT); - if (!addr_opr->is_address()) { - assert(addr_opr->is_register(), "must be"); - addr_opr = LIR_OprFact::address(new LIR_Address(addr_opr, T_OBJECT)); + assert(addr != LIR_OprFact::illegalOpr, "sanity"); + if (!addr->is_address()) { + assert(addr->is_register(), "must be"); + addr = LIR_OprFact::address(new LIR_Address(addr, T_OBJECT)); } - slow = new ShenandoahPreBarrierStub(addr_opr, pre_val, pre_val_patch_code, info ? new CodeEmitInfo(info) : nullptr); + + slow_stub = new ShenandoahKeepaliveBarrierStub(obj, addr); } else { - assert(addr_opr == LIR_OprFact::illegalOpr, "sanity"); - assert(pre_val->is_register(), "must be"); - assert(pre_val->type() == T_OBJECT, "must be an object"); + // Caller gave us the obj to work with. + assert(addr == LIR_OprFact::illegalOpr, "sanity"); + assert(obj->is_register(), "must be"); + assert(obj->type() == T_OBJECT, "must be an object"); - slow = new ShenandoahPreBarrierStub(pre_val); + slow_stub = new ShenandoahKeepaliveBarrierStub(obj); } - __ branch(lir_cond_notEqual, slow); - __ branch_destination(slow->continuation()); -} - -LIR_Opr ShenandoahBarrierSetC1::load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators) { - if (ShenandoahLoadRefBarrier) { - return load_reference_barrier_impl(gen, obj, addr, decorators); - } else { - return obj; - } + enter_if_gc_state(gen, ShenandoahHeap::MARKING, slow_stub); } -LIR_Opr ShenandoahBarrierSetC1::load_reference_barrier_impl(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators) { +void ShenandoahBarrierSetC1::load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators) { assert(ShenandoahLoadRefBarrier, "Should be enabled"); obj = ensure_in_register(gen, obj, T_OBJECT); - assert(obj->is_register(), "must be a register at this point"); addr = ensure_in_register(gen, addr, T_ADDRESS); + assert(obj->is_register(), "must be a register at this point"); assert(addr->is_register(), "must be a register at this point"); - LIR_Opr result = gen->result_register_for(obj->value_type()); - LIR_Opr tmp1 = gen->new_register(T_ADDRESS); - LIR_Opr tmp2 = gen->new_register(T_ADDRESS); - LIR_Opr thrd = gen->getThreadPointer(); - LIR_Address* active_flag_addr = - new LIR_Address(thrd, - in_bytes(ShenandoahThreadLocalData::gc_state_offset()), - T_BYTE); - // Read and check the gc-state-flag. - LIR_Opr flag_val = gen->new_register(T_INT); - __ load(active_flag_addr, flag_val); + // Barrier slowpaths return value in this register. Declare it in the stub + // as clobbered. The obj would remain as result for both fast- and slow-paths. + LIR_Opr slow_result = gen->result_register_for(obj->value_type()); + + CodeStub* slow_stub = new ShenandoahLoadReferenceBarrierStub(obj, addr, slow_result, decorators); + int flags = ShenandoahHeap::HAS_FORWARDED; if (!ShenandoahBarrierSet::is_strong_access(decorators)) { flags |= ShenandoahHeap::WEAK_ROOTS; } - LIR_Opr mask = LIR_OprFact::intConst(flags); - LIR_Opr mask_reg = gen->new_register(T_INT); - __ move(mask, mask_reg); - - if (two_operand_lir_form) { - __ logical_and(flag_val, mask_reg, flag_val); - } else { - LIR_Opr masked_flag = gen->new_register(T_INT); - __ logical_and(flag_val, mask_reg, masked_flag); - flag_val = masked_flag; - } - __ cmp(lir_cond_notEqual, flag_val, LIR_OprFact::intConst(0)); - - CodeStub* slow = new ShenandoahLoadReferenceBarrierStub(obj, addr, result, tmp1, tmp2, decorators); - __ branch(lir_cond_notEqual, slow); - - // No barrier is needed, move obj to result now. - __ move(obj, result); - - // Slow-path re-enters here with result set. - __ branch_destination(slow->continuation()); - - return result; + enter_if_gc_state(gen, flags, slow_stub); } LIR_Opr ShenandoahBarrierSetC1::ensure_in_register(LIRGenerator* gen, LIR_Opr obj, BasicType type) { @@ -189,21 +168,21 @@ LIR_Opr ShenandoahBarrierSetC1::ensure_in_register(LIRGenerator* gen, LIR_Opr ob } void ShenandoahBarrierSetC1::store_at_resolved(LIRAccess& access, LIR_Opr value) { - if (access.is_oop()) { - if (ShenandoahSATBBarrier) { - pre_barrier(access.gen(), access.access_emit_info(), access.decorators(), access.resolved_addr(), LIR_OprFact::illegalOpr /* pre_val */); - } + DecoratorSet decorators = access.decorators(); + LIRGenerator* gen = access.gen(); + + if (ShenandoahSATBBarrier && access.is_oop()) { + keepalive_barrier(gen, /* obj = */ LIR_OprFact::illegalOpr, /* addr = */ access.resolved_addr(), decorators); } BarrierSetC1::store_at_resolved(access, value); if (ShenandoahCardBarrier && access.is_oop()) { - DecoratorSet decorators = access.decorators(); bool is_array = (decorators & IS_ARRAY) != 0; bool on_anonymous = (decorators & ON_UNKNOWN_OOP_REF) != 0; bool precise = is_array || on_anonymous; LIR_Opr post_addr = precise ? access.resolved_addr() : access.base().opr(); - post_barrier(access, post_addr); + card_barrier(gen, post_addr, decorators); } } @@ -230,7 +209,7 @@ void ShenandoahBarrierSetC1::load_at_resolved(LIRAccess& access, LIR_Opr result) if (ShenandoahBarrierSet::need_load_reference_barrier(decorators, type)) { LIR_Opr tmp = gen->new_register(T_OBJECT); BarrierSetC1::load_at_resolved(access, tmp); - tmp = load_reference_barrier(gen, tmp, access.resolved_addr(), decorators); + load_reference_barrier(gen, tmp, access.resolved_addr(), decorators); __ move(tmp, result); } else { BarrierSetC1::load_at_resolved(access, result); @@ -246,18 +225,17 @@ void ShenandoahBarrierSetC1::load_at_resolved(LIRAccess& access, LIR_Opr result) Lcont_anonymous = new LabelObj(); generate_referent_check(access, Lcont_anonymous); } - pre_barrier(gen, access.access_emit_info(), decorators, LIR_OprFact::illegalOpr /* addr_opr */, - result /* pre_val */); + keepalive_barrier(gen, /* obj = */ result, /* addr = */ LIR_OprFact::illegalOpr, decorators); if (is_anonymous) { __ branch_destination(Lcont_anonymous->label()); } } } -class C1ShenandoahPreBarrierCodeGenClosure : public StubAssemblerCodeGenClosure { +class C1ShenandoahKeepaliveBarrierCodeGenClosure : public StubAssemblerCodeGenClosure { virtual OopMapSet* generate_code(StubAssembler* sasm) { ShenandoahBarrierSetAssembler* bs = (ShenandoahBarrierSetAssembler*)BarrierSet::barrier_set()->barrier_set_assembler(); - bs->generate_c1_pre_barrier_runtime_stub(sasm); + bs->keepalive_barrier_c1_runtime_stub(sasm); return nullptr; } }; @@ -271,18 +249,20 @@ class C1ShenandoahLoadReferenceBarrierCodeGenClosure : public StubAssemblerCodeG virtual OopMapSet* generate_code(StubAssembler* sasm) { ShenandoahBarrierSetAssembler* bs = (ShenandoahBarrierSetAssembler*)BarrierSet::barrier_set()->barrier_set_assembler(); - bs->generate_c1_load_reference_barrier_runtime_stub(sasm, _decorators); + bs->load_reference_barrier_c1_runtime_stub(sasm, _decorators); return nullptr; } }; bool ShenandoahBarrierSetC1::generate_c1_runtime_stubs(BufferBlob* buffer_blob) { - C1ShenandoahPreBarrierCodeGenClosure pre_code_gen_cl; - _pre_barrier_c1_runtime_code_blob = Runtime1::generate_blob(buffer_blob, StubId::NO_STUBID, - "shenandoah_pre_barrier_slow", - false, &pre_code_gen_cl); - if (_pre_barrier_c1_runtime_code_blob == nullptr) { - return false; + if (ShenandoahSATBBarrier) { + C1ShenandoahKeepaliveBarrierCodeGenClosure keepalive_code_gen_cl; + _keepalive_barrier_c1_runtime_code_blob = Runtime1::generate_blob(buffer_blob, StubId::NO_STUBID, + "shenandoah_keepalive_barrier_slow", + false, &keepalive_code_gen_cl); + if (_keepalive_barrier_c1_runtime_code_blob == nullptr) { + return false; + } } if (ShenandoahLoadRefBarrier) { C1ShenandoahLoadReferenceBarrierCodeGenClosure lrb_strong_code_gen_cl(ON_STRONG_OOP_REF); @@ -318,11 +298,9 @@ bool ShenandoahBarrierSetC1::generate_c1_runtime_stubs(BufferBlob* buffer_blob) return true; } -void ShenandoahBarrierSetC1::post_barrier(LIRAccess& access, LIR_Opr addr) { +void ShenandoahBarrierSetC1::card_barrier(LIRGenerator* gen, LIR_Opr addr, DecoratorSet decorators) { assert(ShenandoahCardBarrier, "Should have been checked by caller"); - DecoratorSet decorators = access.decorators(); - LIRGenerator* gen = access.gen(); bool in_heap = (decorators & IN_HEAP) != 0; if (!in_heap) { return; @@ -378,6 +356,7 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_cmpxchg_at_resolved(LIRAccess& access, LI return BarrierSetC1::atomic_cmpxchg_at_resolved(access, cmp_value, new_value); } + DecoratorSet decorators = access.decorators(); LIRGenerator* gen = access.gen(); LIR_Opr tmp = gen->new_register(T_OBJECT); @@ -386,22 +365,20 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_cmpxchg_at_resolved(LIRAccess& access, LI // Handle the previous value through SATB, as we are about to perform the store. __ load(addr->as_address_ptr(), tmp); if (ShenandoahSATBBarrier) { - pre_barrier(gen, access.access_emit_info(), access.decorators(), - /* addr_opr (unused) = */ LIR_OprFact::illegalOpr, - /* pre_val = */ tmp); + keepalive_barrier(gen, /* obj = */ tmp, /* addr = */ LIR_OprFact::illegalOpr, decorators); } // Perform LRB on location to fix it up for this and all following accesses. // This guarantees there are no false negatives due to concurrent evacuation, // and the value loaded later by CAS is sanitized by some LRB, or is null. if (ShenandoahLoadRefBarrier) { - load_reference_barrier(gen, /* obj = */ tmp, /* addr = */ addr, access.decorators()); + load_reference_barrier(gen, /* obj = */ tmp, /* addr = */ addr, decorators); } LIR_Opr result = BarrierSetC1::atomic_cmpxchg_at_resolved(access, cmp_value, new_value); if (ShenandoahCardBarrier) { - post_barrier(access, /* addr = */ addr); + card_barrier(gen, /* addr = */ addr, decorators); } return result; @@ -412,6 +389,7 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_xchg_at_resolved(LIRAccess& access, LIRIt return BarrierSetC1::atomic_xchg_at_resolved(access, value); } + DecoratorSet decorators = access.decorators(); LIRGenerator* gen = access.gen(); LIR_Opr tmp = gen->new_register(T_OBJECT); @@ -420,22 +398,20 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_xchg_at_resolved(LIRAccess& access, LIRIt // Handle the previous value through SATB, as we are about to perform the store. __ load(addr->as_address_ptr(), tmp); if (ShenandoahSATBBarrier) { - pre_barrier(gen, access.access_emit_info(), access.decorators(), - /* addr_opr (unused) = */ LIR_OprFact::illegalOpr, - /* pre_val = */ tmp); + keepalive_barrier(gen, /* obj = */ tmp, /* addr = */ LIR_OprFact::illegalOpr, decorators); } // Perform LRB on location to fix it up for this and all following accesses. // This is purely opportunistic: we would not have any false negatives here. // This guarantees the value loaded later by XCHG is sanitized by some LRB, or is null. if (ShenandoahLoadRefBarrier) { - load_reference_barrier(gen, /* obj = */ tmp, /* addr = */ addr, access.decorators()); + load_reference_barrier(gen, /* obj = */ tmp, /* addr = */ addr, decorators); } LIR_Opr result = BarrierSetC1::atomic_xchg_at_resolved(access, value); if (ShenandoahCardBarrier) { - post_barrier(access, /* addr = */ addr); + card_barrier(gen, /* addr = */ addr, decorators); } return result; diff --git a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp index 413777a61ee..3f064c3569b 100644 --- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp +++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp @@ -29,63 +29,48 @@ #include "c1/c1_CodeStubs.hpp" #include "gc/shared/c1/barrierSetC1.hpp" -class ShenandoahPreBarrierStub: public CodeStub { +class ShenandoahKeepaliveBarrierStub: public CodeStub { friend class ShenandoahBarrierSetC1; private: - bool _do_load; + LIR_Opr _obj; LIR_Opr _addr; - LIR_Opr _pre_val; - LIR_PatchCode _patch_code; - CodeEmitInfo* _info; + bool _do_load; public: - // Version that _does_ generate a load of the previous value from addr. - // addr (the address of the field to be read) must be a LIR_Address - // pre_val (a temporary register) must be a register; - ShenandoahPreBarrierStub(LIR_Opr addr, LIR_Opr pre_val, LIR_PatchCode patch_code, CodeEmitInfo* info) : - _do_load(true), _addr(addr), _pre_val(pre_val), - _patch_code(patch_code), _info(info) + ShenandoahKeepaliveBarrierStub(LIR_Opr obj, LIR_Opr addr) : + _obj(obj), _addr(addr), _do_load(true) { - assert(_pre_val->is_register(), "should be temporary register"); + assert(_obj->is_register(), "should be temporary register"); assert(_addr->is_address(), "should be the address of the field"); FrameMap* f = Compilation::current()->frame_map(); - f->update_reserved_argument_area_size(2 * BytesPerWord); + f->update_reserved_argument_area_size(1 * BytesPerWord); } - // Version that _does not_ generate load of the previous value; the - // previous value is assumed to have already been loaded into pre_val. - ShenandoahPreBarrierStub(LIR_Opr pre_val) : - _do_load(false), _addr(LIR_OprFact::illegalOpr), _pre_val(pre_val), - _patch_code(lir_patch_none), _info(nullptr) + ShenandoahKeepaliveBarrierStub(LIR_Opr obj) : + _obj(obj), _addr(LIR_OprFact::illegalOpr), _do_load(false) { - assert(_pre_val->is_register(), "should be a register"); + assert(_obj->is_register(), "should be a register"); + FrameMap* f = Compilation::current()->frame_map(); + f->update_reserved_argument_area_size(1 * BytesPerWord); } LIR_Opr addr() const { return _addr; } - LIR_Opr pre_val() const { return _pre_val; } - LIR_PatchCode patch_code() const { return _patch_code; } - CodeEmitInfo* info() const { return _info; } + LIR_Opr obj() const { return _obj; } bool do_load() const { return _do_load; } virtual void emit_code(LIR_Assembler* e); virtual void visit(LIR_OpVisitState* visitor) { + visitor->do_slow_case(); if (_do_load) { - // don't pass in the code emit info since it's processed in the fast - // path - if (_info != nullptr) - visitor->do_slow_case(_info); - else - visitor->do_slow_case(); - visitor->do_input(_addr); - visitor->do_temp(_pre_val); + visitor->do_temp(_addr); + visitor->do_temp(_obj); } else { - visitor->do_slow_case(); - visitor->do_input(_pre_val); + visitor->do_input(_obj); } } #ifndef PRODUCT - virtual void print_name(outputStream* out) const { out->print("ShenandoahPreBarrierStub"); } + virtual void print_name(outputStream* out) const { out->print("ShenandoahKeepaliveBarrierStub"); } #endif // PRODUCT }; @@ -94,29 +79,21 @@ class ShenandoahLoadReferenceBarrierStub: public CodeStub { private: LIR_Opr _obj; LIR_Opr _addr; - LIR_Opr _result; - LIR_Opr _tmp1; - LIR_Opr _tmp2; + LIR_Opr _slow_result; DecoratorSet _decorators; public: - ShenandoahLoadReferenceBarrierStub(LIR_Opr obj, LIR_Opr addr, LIR_Opr result, LIR_Opr tmp1, LIR_Opr tmp2, DecoratorSet decorators) : - _obj(obj), _addr(addr), _result(result), _tmp1(tmp1), _tmp2(tmp2), _decorators(decorators) + ShenandoahLoadReferenceBarrierStub(LIR_Opr obj, LIR_Opr addr, LIR_Opr slow_result, DecoratorSet decorators) : + _obj(obj), _addr(addr), _slow_result(slow_result), _decorators(decorators) { assert(_obj->is_register(), "should be register"); assert(_addr->is_register(), "should be register"); - assert(_result->is_register(), "should be register"); - assert(_tmp1->is_register(), "should be register"); - assert(_tmp2->is_register(), "should be register"); - FrameMap* f = Compilation::current()->frame_map(); f->update_reserved_argument_area_size(2 * BytesPerWord); } LIR_Opr obj() const { return _obj; } LIR_Opr addr() const { return _addr; } - LIR_Opr result() const { return _result; } - LIR_Opr tmp1() const { return _tmp1; } - LIR_Opr tmp2() const { return _tmp2; } + LIR_Opr slow_result() const { return _slow_result; } DecoratorSet decorators() const { return _decorators; } virtual void emit_code(LIR_Assembler* e); @@ -124,12 +101,10 @@ class ShenandoahLoadReferenceBarrierStub: public CodeStub { visitor->do_slow_case(); visitor->do_input(_obj); visitor->do_temp(_obj); + visitor->do_output(_obj); visitor->do_input(_addr); visitor->do_temp(_addr); - visitor->do_temp(_result); - visitor->do_output(_result); - visitor->do_temp(_tmp1); - visitor->do_temp(_tmp2); + visitor->do_temp(_slow_result); } #ifndef PRODUCT virtual void print_name(outputStream* out) const { out->print("ShenandoahLoadReferenceBarrierStub"); } @@ -138,63 +113,35 @@ class ShenandoahLoadReferenceBarrierStub: public CodeStub { class ShenandoahBarrierSetC1 : public BarrierSetC1 { private: - CodeBlob* _pre_barrier_c1_runtime_code_blob; + CodeBlob* _keepalive_barrier_c1_runtime_code_blob; CodeBlob* _load_reference_barrier_strong_rt_code_blob; CodeBlob* _load_reference_barrier_strong_native_rt_code_blob; CodeBlob* _load_reference_barrier_weak_rt_code_blob; CodeBlob* _load_reference_barrier_phantom_rt_code_blob; - void pre_barrier(LIRGenerator* gen, CodeEmitInfo* info, DecoratorSet decorators, LIR_Opr addr_opr, LIR_Opr pre_val); - - LIR_Opr load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators); + void enter_if_gc_state(LIRGenerator* gen, int flags, CodeStub* slow_stub); - LIR_Opr load_reference_barrier_impl(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators); + void keepalive_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators); + void load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators); + void card_barrier(LIRGenerator* gen, LIR_Opr addr, DecoratorSet decorators); LIR_Opr ensure_in_register(LIRGenerator* gen, LIR_Opr obj, BasicType type); public: ShenandoahBarrierSetC1(); - CodeBlob* pre_barrier_c1_runtime_code_blob() { - assert(_pre_barrier_c1_runtime_code_blob != nullptr, ""); - return _pre_barrier_c1_runtime_code_blob; - } - - CodeBlob* load_reference_barrier_strong_rt_code_blob() { - 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() { - 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() { - assert(_load_reference_barrier_weak_rt_code_blob != nullptr, ""); - return _load_reference_barrier_weak_rt_code_blob; - } + address keepalive_barrier_stub(); + address load_reference_barrier_stub(DecoratorSet decorators); - CodeBlob* load_reference_barrier_phantom_rt_code_blob() { - assert(_load_reference_barrier_phantom_rt_code_blob != nullptr, ""); - return _load_reference_barrier_phantom_rt_code_blob; - } + virtual bool generate_c1_runtime_stubs(BufferBlob* buffer_blob); protected: - virtual void store_at_resolved(LIRAccess& access, LIR_Opr value); virtual LIR_Opr resolve_address(LIRAccess& access, bool resolve_in_register); virtual void load_at_resolved(LIRAccess& access, LIR_Opr result); virtual LIR_Opr atomic_cmpxchg_at_resolved(LIRAccess& access, LIRItem& cmp_value, LIRItem& new_value); - virtual LIR_Opr atomic_xchg_at_resolved(LIRAccess& access, LIRItem& value); - - void post_barrier(LIRAccess& access, LIR_Opr addr); - -public: - - virtual bool generate_c1_runtime_stubs(BufferBlob* buffer_blob); }; #endif // SHARE_GC_SHENANDOAH_C1_SHENANDOAHBARRIERSETC1_HPP From 38ee41bee390e3d4aaa57cc090f7956cf8b9fe8b Mon Sep 17 00:00:00 2001 From: April Ivy Date: Fri, 26 Jun 2026 06:32:10 +0000 Subject: [PATCH 079/707] 8365887: Outdated comments in String::decode Reviewed-by: liach, sherman --- src/java.base/share/classes/java/lang/String.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java index 760f3ebc255..9f56ceb445a 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -671,14 +671,6 @@ private static String ascii(byte[] bytes, int offset, int length) { } private static String decode(Charset charset, byte[] bytes, int offset, int length) { - // (1)We never cache the "external" cs, the only benefit of creating - // an additional StringDe/Encoder object to wrap it is to share the - // de/encode() method. These SD/E objects are short-lived, the young-gen - // gc should be able to take care of them well. But the best approach - // is still not to generate them if not really necessary. - // (2)The defensive copy of the input byte/char[] has a big performance - // impact, as well as the outgoing result byte/char[]. Need to do the - // optimization check of (sm==null && classLoader0==null) for both. CharsetDecoder cd = charset.newDecoder(); // ArrayDecoder fastpaths if (cd instanceof ArrayDecoder ad) { From fea0c229130770230f97a7665dadefe42ef1f9fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Du=EF=BF=BDan=20B=EF=BF=BDlek?= Date: Fri, 26 Jun 2026 07:04:54 +0000 Subject: [PATCH 080/707] 8387215: On-demand attribution of a record constructor body causes javac to emit an invalid diagnostic Reviewed-by: jlahoda --- .../JavacProcessingEnvironment.java | 3 +- .../OnDemandAttributionRecordConstructor.java | 215 ++++++++++++++++++ 2 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 test/langtools/tools/javac/processing/model/trees/OnDemandAttributionRecordConstructor.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java index 11fa3a5aebf..ede75a73824 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java @@ -1547,7 +1547,8 @@ public void visitClassDef(JCClassDecl node) { } public void visitMethodDef(JCMethodDecl node) { // remove super constructor call that may have been added during attribution: - if (TreeInfo.isConstructor(node) && node.sym != null && node.sym.owner.isEnum() && + if (TreeInfo.isConstructor(node) && node.sym != null && + (node.sym.owner.isEnum() || TreeInfo.isCanonicalConstructor(node)) && node.body != null && node.body.stats.nonEmpty() && TreeInfo.isSuperCall(node.body.stats.head) && node.body.stats.head.pos == node.body.pos) { node.body.stats = node.body.stats.tail; diff --git a/test/langtools/tools/javac/processing/model/trees/OnDemandAttributionRecordConstructor.java b/test/langtools/tools/javac/processing/model/trees/OnDemandAttributionRecordConstructor.java new file mode 100644 index 00000000000..37d1541f18a --- /dev/null +++ b/test/langtools/tools/javac/processing/model/trees/OnDemandAttributionRecordConstructor.java @@ -0,0 +1,215 @@ +/* + * 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 8387215 + * @summary Check that javac does not report invalid errors when compiling a valid + * compact record constructor when an on-demand attribution is triggered + * by an annotation processor calling Trees.getElement(...) for identifiers + * inside the constructor. + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @run junit ${test.main.class} + */ + +import com.sun.source.tree.IdentifierTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; +import com.sun.source.util.TreePathScanner; +import com.sun.source.util.Trees; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Set; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedOptions; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.tools.Diagnostic; +import toolbox.JavacTask; +import toolbox.ToolBox; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import toolbox.Task; + +public class OnDemandAttributionRecordConstructor { + + Path base; + ToolBox tb = new ToolBox(); + + @Test + void testCompactRecordConstructorWithGetElementCall() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + new JavacTask(tb) + .options("-d", classes.toString()) + .sources(""" + record Repro(String name) { + Repro { + name = name.trim(); + } + } + """) + .processors(new ProcessorImpl()) + .run() + .writeAll(); + } + + @Test + void testCompactRecordConstructorWithoutGetElementCall() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + new JavacTask(tb) + .options("-d", classes.toString(), "-AskipGetElement=true") + .sources(""" + record Repro(String name) { + Repro { + name = name.trim(); + } + } + """) + .processors(new ProcessorImpl()) + .run() + .writeAll(); + } + + @Test + void testCanonicalRecordConstructorWithGetElementCall() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + new JavacTask(tb) + .options("-d", classes.toString()) + .sources(""" + record Repro(String name) { + Repro(String name) { + this.name = name.trim(); + } + } + """) + .processors(new ProcessorImpl()) + .run() + .writeAll(); + } + + @Test + void testBrokenRecordConstructorWithGetElementCall() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + List out = new JavacTask(tb) + .options("-d", classes.toString(), "-XDrawDiagnostics", "-nowarn") + .sources(""" + record Repro(String name) { + Repro(String name) { + super(); //illegal + this.name = name.trim(); + } + } + """) + .processors(new ProcessorImpl()) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out, List.of( + "Repro.java:2:5: compiler.err.invalid.canonical.constructor.in.record: (compiler.misc.canonical), Repro, (compiler.misc.canonical.must.not.contain.explicit.constructor.invocation)", + "1 error")); + } + + @SupportedAnnotationTypes("*") + @SupportedOptions(ProcessorImpl.SKIP_GET_ELEMENT) + private static class ProcessorImpl extends AbstractProcessor { + + private static final String SKIP_GET_ELEMENT = "skipGetElement"; + private Trees trees; + + @Override + public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + trees = Trees.instance(processingEnv); + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (roundEnv.processingOver()) { + return false; + } + for (Element rootElement : roundEnv.getRootElements()) { + TreePath rootPath = trees.getPath(rootElement); + if (rootPath == null) { + continue; + } + new TreePathScanner() { + @Override + public Void visitIdentifier(IdentifierTree node, Void unused) { + TreePath currentPath = getCurrentPath(); + if (!skipGetElement() && insideRecordConstructor(currentPath)) { + processingEnv.getMessager() + .printMessage(Diagnostic.Kind.NOTE, + "Calling Trees.getElement for identifier '" + node.getName() + + "' inside a record constructor"); + trees.getElement(currentPath); + } + return super.visitIdentifier(node, unused); + } + }.scan(rootPath, null); + } + return false; + } + + private boolean skipGetElement() { + return Boolean.parseBoolean(processingEnv.getOptions().get(SKIP_GET_ELEMENT)); + } + + private static boolean insideRecordConstructor(TreePath path) { + TreePath current = path; + while (current != null) { + if (current.getLeaf() instanceof MethodTree method + && method.getReturnType() == null + && current.getParentPath() != null + && current.getParentPath().getLeaf().getKind() == Tree.Kind.RECORD) { + return true; + } + current = current.getParentPath(); + } + return false; + } + } + + @BeforeEach + public void setUp(TestInfo info) { + base = Paths.get(".") + .resolve(info.getTestMethod() + .orElseThrow() + .getName()); + } +} From b6e7b2b29213134a1a35fe34501b2fb94c04d70a Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Fri, 26 Jun 2026 08:04:23 +0000 Subject: [PATCH 081/707] 8385420: C2: SIGSEGV in compiled code due to missing ctrl Reviewed-by: vlivanov, epeter, dlong --- src/hotspot/share/opto/compile.cpp | 35 ++++++++---- src/hotspot/share/opto/node.cpp | 21 ++++++++ src/hotspot/share/opto/node.hpp | 1 + .../TestRemoveCastPPWithCMoveUse.java | 53 +++++++++++++++++++ 4 files changed, 100 insertions(+), 10 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/controldependency/TestRemoveCastPPWithCMoveUse.java diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index e7dc57524eb..e283d9b97ad 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -3481,22 +3481,37 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f ResourceMark rm; Unique_Node_List wq; wq.push(n); + + + // When we remove a CastPP, we need to pin all of its transitive users under the control of + // the removed node. The simplest approach is to pin all of the uses of the removed CastPP, + // but it is overly conservative, as an AddP does not really need pinning. As a result, we + // look through those nodes that do not need pinning and only pin memory access nodes under + // n->in(0). for (uint next = 0; next < wq.size(); ++next) { Node *m = wq.at(next); for (DUIterator_Fast imax, i = m->fast_outs(imax); i < imax; i++) { Node* use = m->fast_out(i); - if (use->is_Mem() || use->is_EncodeNarrowPtr()) { + int use_op = use->Opcode(); + if (use->is_CFG() || use->pinned() || // already pinned at the exact control + use->is_Cmp() || use->Opcode() == Op_CastP2X) { // pure computations + continue; + } else if (use->is_EncodeNarrowPtr() || // EncodeP remembers whether its input is nullable, so it must be pinned + use_op == Op_PartialSubtypeCheck || // This accesses its pointer inputs, so it must depend on them being not-null + use->is_Mem() || use->is_memory_access_intrinsic()) { use->ensure_control_or_add_prec(n->in(0)); + } else if (use_op == Op_AddP || + use_op == Op_CastPP || use_op == Op_CheckCastPP || + use_op == Op_CMoveP || use_op == Op_CMoveN || + use_op == Op_DecodeN || use_op == Op_DecodeNKlass) { + // Look through use to find memory accesses if use does not need pinning + wq.push(use); } else { - switch(use->Opcode()) { - case Op_AddP: - case Op_DecodeN: - case Op_DecodeNKlass: - case Op_CheckCastPP: - case Op_CastPP: - wq.push(use); - break; - } + // Should have handled all kinds of nodes, verify that we do not unexpectedly arrive + // here + assert(false, "unexpected node %s", use->Name()); + // Be conservative in product and pin the unexpected use + use->ensure_control_or_add_prec(n->in(0)); } } } diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index 997ce92fe1c..1210693f957 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -3001,6 +3001,27 @@ bool Node::is_data_proj_of_pure_function(const Node* maybe_pure_function) const return Opcode() == Op_Proj && as_Proj()->_con == TypeFunc::Parms && maybe_pure_function->is_CallLeafPure(); } +// Whether this is an intrinsic node that accesses memory and has a memory input, such as array +// equal intrinsic. Some nodes do access memory but do not have a memory input, such as +// PartialSubTypeCheck, they are not included here. +bool Node::is_memory_access_intrinsic() const { + switch (Opcode()) { + case Op_StrComp: + case Op_StrEquals: + case Op_StrIndexOf: + case Op_StrIndexOfChar: + case Op_StrCompressedCopy: + case Op_StrInflatedCopy: + case Op_AryEq: + case Op_CountPositives: + case Op_VectorizedHashCode: + case Op_EncodeISOArray: + return true; + default: + return false; + } +} + //--------------------------has_non_debug_uses------------------------------ // Checks whether the node has any non-debug uses or not. bool Node::has_non_debug_uses() const { diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index 1ef4b5a51b6..92bd03c0d63 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -1069,6 +1069,7 @@ class Node { uint is_Copy() const { return (_flags & Flag_is_Copy); } virtual bool is_CFG() const { return false; } + bool is_memory_access_intrinsic() const; // If this node is control-dependent on a test, can it be rerouted to a dominating equivalent // test? This means that the node can be executed safely as long as it happens after the test diff --git a/test/hotspot/jtreg/compiler/controldependency/TestRemoveCastPPWithCMoveUse.java b/test/hotspot/jtreg/compiler/controldependency/TestRemoveCastPPWithCMoveUse.java new file mode 100644 index 00000000000..3d752cc74f5 --- /dev/null +++ b/test/hotspot/jtreg/compiler/controldependency/TestRemoveCastPPWithCMoveUse.java @@ -0,0 +1,53 @@ +/* + * 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.controldependency; + +/* + * @test + * @bug 8385420 + * @summary C2 correctly handles the case when the removed CastPPNode has a CMove use. + * @run main ${test.main.class} + * @run main/othervm -Xbatch -XX:CompileOnly=${test.main.class}::test + * -XX:+UnlockDiagnosticVMOptions -XX:+StressGCM ${test.main.class} + * + */ +public class TestRemoveCastPPWithCMoveUse { + public static void main(String[] args) { + for (int i = 0; i < 10_000; i++) { + test(null, false); + test(null, true); + test("", false); + test("", true); + } + } + + static int test(String a, boolean flag) { + StringBuilder sb = new StringBuilder(); + if (a == null) { + sb.append(""); + } else { + sb.append(flag ? a : ""); + } + return sb.length(); + } +} From c289cf502c1f946aae863e71692a2fefeb891dee Mon Sep 17 00:00:00 2001 From: Sean Mullan Date: Fri, 26 Jun 2026 15:12:03 +0000 Subject: [PATCH 082/707] 8377102: cacerts jlink plugin Reviewed-by: alanb --- .../jlink/internal/plugins/CACertsPlugin.java | 114 +++++++++++++++++ .../tools/jlink/resources/plugins.properties | 16 ++- src/jdk.jlink/share/classes/module-info.java | 5 +- src/jdk.jlink/share/man/jlink.md | 10 ++ .../jlink/plugins/CACertsPluginTest.java | 120 ++++++++++++++++++ 5 files changed, 262 insertions(+), 3 deletions(-) create mode 100644 src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/CACertsPlugin.java create mode 100644 test/jdk/tools/jlink/plugins/CACertsPluginTest.java diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/CACertsPlugin.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/CACertsPlugin.java new file mode 100644 index 00000000000..3f663983828 --- /dev/null +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/CACertsPlugin.java @@ -0,0 +1,114 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.tools.jlink.internal.plugins; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.util.HashMap; +import java.util.Map; + +import jdk.tools.jlink.internal.ResourcePoolEntryFactory; +import jdk.tools.jlink.plugin.PluginException; +import jdk.tools.jlink.plugin.ResourcePool; +import jdk.tools.jlink.plugin.ResourcePoolBuilder; +import jdk.tools.jlink.plugin.ResourcePoolEntry; + +/** + * Creates the cacerts keystore in the output image with the certificates of + * the specified aliases only. + */ +public class CACertsPlugin extends AbstractPlugin { + + private static final String RES = "/java.base/lib/security/cacerts"; + + // cacerts keystore aliases + private String[] aliases; + + public CACertsPlugin() { + super("cacerts"); + } + + @Override + public boolean hasArguments() { + return true; + } + + @Override + public void configure(Map config) { + String option = config.get(getName()); + if (option == null) { + throw new AssertionError(); + } + // If alias has a comma in it, this won't work, but no cacerts + // aliases have commas. + aliases = option.split(","); + } + + @Override + public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) { + in.transformAndCopy(res -> { + if (res.type() == ResourcePoolEntry.Type.NATIVE_LIB && + res.path().equals(RES)) { + byte[] cacerts = transformCACerts(res.content()); + return ResourcePoolEntryFactory.create(res, cacerts); + } + return res; + }, out); + return out.build(); + } + + /** + * Creates a keystore containing only the certificates of the specified + * aliases. + */ + private byte[] transformCACerts(InputStream content) { + try { + var ks = KeyStore.getInstance("PKCS12"); + ks.load(content, null); + Map certs = new HashMap<>(aliases.length); + for (var alias : aliases) { + var cert = ks.getCertificate(alias); + if (cert == null) { + throw new PluginException( + "alias " + alias + " does not exist"); + } + certs.put(alias, cert); + } + ks.load(null, null); + for (var entry : certs.entrySet()) { + ks.setCertificateEntry(entry.getKey(), entry.getValue()); + } + var baos = new ByteArrayOutputStream(); + ks.store(baos, null); + return baos.toByteArray(); + } catch (PluginException pe) { + throw pe; + } catch (Exception ex) { + throw new PluginException(ex); + } + } +} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties index 7e3c26fa7b8..892ba73249e 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties @@ -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,20 @@ release-info.usage=\ \ Any number of = pairs can be passed.\n\ \ del: is to delete the list of keys in release file. +cacerts.argument=[,]* + +cacerts.description=\ +Create the cacerts keystore in the output image with only the certificates\n\ +of the specified aliases. is the name of an alias in the cacerts\n\ +keystore in the java.base module. + +cacerts.usage=\ +\ --cacerts [,]*\n\ +\ Create the cacerts keystore in the output image\n\ +\ with only the certificates of the specified\n\ +\ aliases. is the name of an alias in the\n\ +\ cacerts keystore in the java.base module. + class-for-name.argument= class-for-name.description=\ diff --git a/src/jdk.jlink/share/classes/module-info.java b/src/jdk.jlink/share/classes/module-info.java index ba66da53604..0adc1ce6d37 100644 --- a/src/jdk.jlink/share/classes/module-info.java +++ b/src/jdk.jlink/share/classes/module-info.java @@ -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 @@ -81,5 +81,6 @@ jdk.tools.jlink.internal.plugins.VendorVMBugURLPlugin, jdk.tools.jlink.internal.plugins.VendorVersionPlugin, jdk.tools.jlink.internal.plugins.CDSPlugin, - jdk.tools.jlink.internal.plugins.SaveJlinkArgfilesPlugin; + jdk.tools.jlink.internal.plugins.SaveJlinkArgfilesPlugin, + jdk.tools.jlink.internal.plugins.CACertsPlugin; } diff --git a/src/jdk.jlink/share/man/jlink.md b/src/jdk.jlink/share/man/jlink.md index b95424fdde9..1ee4d08646d 100644 --- a/src/jdk.jlink/share/man/jlink.md +++ b/src/jdk.jlink/share/man/jlink.md @@ -235,6 +235,16 @@ Options Description : Generate CDS archive if the runtime image supports the CDS feature. +### Plugin `cacerts` + +Options +: `--cacerts=`*alias*\[`,`*alias*\]\* + +Description +: Create the `cacerts` keystore in the output image with only the + certificates of the specified aliases. *alias* is the name of an alias + in the `cacerts` keystore in the java.base module. + ## jlink Examples The following command creates a runtime image in the directory `greetingsapp`. diff --git a/test/jdk/tools/jlink/plugins/CACertsPluginTest.java b/test/jdk/tools/jlink/plugins/CACertsPluginTest.java new file mode 100644 index 00000000000..8ca720be639 --- /dev/null +++ b/test/jdk/tools/jlink/plugins/CACertsPluginTest.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. + */ + +import java.nio.file.Path; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.util.Enumeration; + +import jtreg.SkippedException; +import jdk.test.lib.Asserts; +import jdk.test.lib.security.SecurityUtils; +import jdk.tools.jlink.internal.LinkableRuntimeImage; +import tests.Helper; + +/* @test + * @bug 8377102 + * @summary Test the --cacerts plugin + * @library ../../lib /test/lib + * @modules java.base/jdk.internal.jimage + * jdk.jlink/jdk.tools.jimage + * jdk.jlink/jdk.tools.jlink.internal + * @build tests.* + * @run main/othervm CACertsPluginTest + */ + +public class CACertsPluginTest { + + private static Helper helper; + + private static final String CACERTS_PATH = "lib/security/cacerts"; + private static final boolean LINKABLE_RUNTIME = + LinkableRuntimeImage.isLinkableRuntime(); + + public static void main(String[] args) throws Throwable { + + helper = Helper.newHelper(LINKABLE_RUNTIME); + if (helper == null) { + throw new SkippedException("Test not run"); + } + + KeyStore jdkCacerts = SecurityUtils.getCacertsKeyStore(); + Enumeration aliases = jdkCacerts.aliases(); + String alias1 = aliases.nextElement(); + String alias2 = aliases.nextElement(); + + // test one alias + test("testOne", jdkCacerts, alias1); + + // test two aliases + test("testTwo", jdkCacerts, alias1, alias2); + + // test illegal/bad options + testBadOptions(); + } + + private static void test(String module, KeyStore jdkCacerts, + String... aliases) throws Exception { + + helper.generateDefaultJModule(module); + + String option = toOption(aliases); + Path image = helper.generateDefaultImage( + new String[] { "--cacerts", option }, module).assertSuccess(); + helper.checkImage(image, module, null, null, + new String[] { CACERTS_PATH }); + + KeyStore imageCacerts = KeyStore.getInstance( + image.resolve(CACERTS_PATH).toFile(), (char[]) null); + + Asserts.assertEquals(imageCacerts.size(), aliases.length); + for (String alias : aliases) { + Asserts.assertTrue(imageCacerts.isCertificateEntry(alias)); + Asserts.assertEquals( + jdkCacerts.getCertificate(alias), + imageCacerts.getCertificate(alias)); + } + } + + private static void testBadOptions() throws Exception { + + String module = "testBad"; + helper.generateDefaultJModule(module); + helper.generateDefaultImage(new String[] + { "--cacerts", "bogus-alias" }, module) + .assertFailure("alias bogus-alias does not exist"); + } + + private static String toOption(String... aliases) { + int max = aliases.length - 1; + + StringBuilder sb = new StringBuilder(); + for (int i = 0; ; i++) { + sb.append(aliases[i]); + if (i == max) { + return sb.toString(); + } + sb.append(","); + } + } +} From 548a95379f159a0dc369f6bb80d8167ec835c7cd Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Sat, 27 Jun 2026 01:46:53 +0000 Subject: [PATCH 083/707] 8386163: C2 Vector API: assert(collect_unique_inputs(n, inputs) == 1) failed: not unary Reviewed-by: vlivanov, epeter --- src/hotspot/share/opto/compile.cpp | 12 ++- .../vectorapi/TestMaskedNotAllOnes.java | 84 +++++++++++++++++++ 2 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/vectorapi/TestMaskedNotAllOnes.java diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index e283d9b97ad..a2e5899a1e9 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -2721,13 +2721,11 @@ static uint collect_unique_inputs(Node* n, Unique_Node_List& inputs) { if (is_vector_bitwise_op(n)) { uint inp_cnt = n->is_predicated_vector() ? n->req()-1 : n->req(); if (VectorNode::is_vector_bitwise_not_pattern(n)) { - for (uint i = 1; i < inp_cnt; i++) { - Node* in = n->in(i); - bool skip = VectorNode::is_all_ones_vector(in); - if (!skip && !inputs.member(in)) { - inputs.push(in); - cnt++; - } + assert(n->req() == (n->is_predicated_vector() ? 4 : 3), "must have 2 data inputs"); + Node* opnd = VectorNode::is_all_ones_vector(n->in(1)) ? n->in(2) : n->in(1); + if (!inputs.member(opnd)) { + inputs.push(opnd); + cnt++; } assert(cnt <= 1, "not unary"); } else { diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestMaskedNotAllOnes.java b/test/hotspot/jtreg/compiler/vectorapi/TestMaskedNotAllOnes.java new file mode 100644 index 00000000000..6abf88ed06f --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestMaskedNotAllOnes.java @@ -0,0 +1,84 @@ +/* + * 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 8386163 + * @summary Checks there is no assertion failure with macro logic optimization when both inputs of not patterns are all one vectors + * @modules jdk.incubator.vector + * @library /test/lib / + * @run driver ${test.main.class} + */ + +package compiler.vectorapi; + +import compiler.lib.ir_framework.*; +import compiler.lib.verify.Verify; +import jdk.incubator.vector.IntVector; +import jdk.incubator.vector.VectorMask; +import jdk.incubator.vector.VectorOperators; +import jdk.incubator.vector.VectorSpecies; + +public class TestMaskedNotAllOnes { + + private static final VectorSpecies ISP = IntVector.SPECIES_PREFERRED; + private static final int VALUE = 1234567; + + public static void main(String[] args) { + TestFramework.runWithFlags("--add-modules=jdk.incubator.vector"); + } + + @Test + @Warmup(10000) + static int[] testMaskedDivNegOne() { + IntVector v = IntVector.broadcast(ISP, VALUE); + VectorMask mask = VectorMask.fromLong(ISP, -1L); + int[] out = new int[ISP.length()]; + v.div(-1, mask).intoArray(out, 0); + return out; + } + + static final int[] GOLD_DIV = testMaskedDivNegOne(); + + @Check(test = "testMaskedDivNegOne") + static void checkMaskedDivNegOne(int[] out) { + Verify.checkEQ(GOLD_DIV, out); + } + + @Test + @Warmup(10000) + static int[] testMaskedNotAllOnesVector() { + IntVector allOnes = IntVector.broadcast(ISP, -1); + VectorMask mask = VectorMask.fromLong(ISP, -1L); + int[] out = new int[ISP.length()]; + allOnes.lanewise(VectorOperators.NOT, mask).intoArray(out, 0); + return out; + } + + static final int[] GOLD_NOT = testMaskedNotAllOnesVector(); + + @Check(test = "testMaskedNotAllOnesVector") + static void checkMaskedNotAllOnesVector(int[] out) { + Verify.checkEQ(GOLD_NOT, out); + } +} From dc4b150bcf816c65baba831bd4bc8f4d1dda468e Mon Sep 17 00:00:00 2001 From: Saint Wesonga Date: Mon, 29 Jun 2026 03:32:15 +0000 Subject: [PATCH 084/707] 8378892: TestTrampoline fails on Windows AArch64 Reviewed-by: dlong, macarte --- .../jtreg/compiler/c2/aarch64/TestTrampoline.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/aarch64/TestTrampoline.java b/test/hotspot/jtreg/compiler/c2/aarch64/TestTrampoline.java index 114f7f9bfab..084f63279bf 100644 --- a/test/hotspot/jtreg/compiler/c2/aarch64/TestTrampoline.java +++ b/test/hotspot/jtreg/compiler/c2/aarch64/TestTrampoline.java @@ -89,15 +89,19 @@ private static void checkOutput(OutputAnalyzer output) { } static class Test { - private static void test(String s, int i) { + // Use a StringBuilder to avoid issues with String.charAt() not being + // inlined on Windows because its UTF-16 path was executed at startup + // but not enough for C2 to inline it. + private static void test(StringBuilder s, int i) { if (s.charAt(i) > 128) throw new RuntimeException(); } public static void main(String[] args) { - String s = "Returns the char value at the specified index."; + var sb = new StringBuilder(); + sb.append("Returns the char value at the specified index."); for (int i = 0; i < ITERATIONS_TO_HEAT_LOOP; ++i) { - test(s, i % s.length()); + test(sb, i % sb.length()); } } } From db1482615e4c8489a8d16bc0985d6e50a88c9409 Mon Sep 17 00:00:00 2001 From: David Holmes Date: Mon, 29 Jun 2026 05:00:28 +0000 Subject: [PATCH 085/707] 8387378: [BACKOUT] C2: SIGSEGV in compiled code due to missing ctrl Reviewed-by: jpai --- src/hotspot/share/opto/compile.cpp | 35 ++++-------- src/hotspot/share/opto/node.cpp | 21 -------- src/hotspot/share/opto/node.hpp | 1 - .../TestRemoveCastPPWithCMoveUse.java | 53 ------------------- 4 files changed, 10 insertions(+), 100 deletions(-) delete mode 100644 test/hotspot/jtreg/compiler/controldependency/TestRemoveCastPPWithCMoveUse.java diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index a2e5899a1e9..a273bb6053e 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -3479,37 +3479,22 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f ResourceMark rm; Unique_Node_List wq; wq.push(n); - - - // When we remove a CastPP, we need to pin all of its transitive users under the control of - // the removed node. The simplest approach is to pin all of the uses of the removed CastPP, - // but it is overly conservative, as an AddP does not really need pinning. As a result, we - // look through those nodes that do not need pinning and only pin memory access nodes under - // n->in(0). for (uint next = 0; next < wq.size(); ++next) { Node *m = wq.at(next); for (DUIterator_Fast imax, i = m->fast_outs(imax); i < imax; i++) { Node* use = m->fast_out(i); - int use_op = use->Opcode(); - if (use->is_CFG() || use->pinned() || // already pinned at the exact control - use->is_Cmp() || use->Opcode() == Op_CastP2X) { // pure computations - continue; - } else if (use->is_EncodeNarrowPtr() || // EncodeP remembers whether its input is nullable, so it must be pinned - use_op == Op_PartialSubtypeCheck || // This accesses its pointer inputs, so it must depend on them being not-null - use->is_Mem() || use->is_memory_access_intrinsic()) { + if (use->is_Mem() || use->is_EncodeNarrowPtr()) { use->ensure_control_or_add_prec(n->in(0)); - } else if (use_op == Op_AddP || - use_op == Op_CastPP || use_op == Op_CheckCastPP || - use_op == Op_CMoveP || use_op == Op_CMoveN || - use_op == Op_DecodeN || use_op == Op_DecodeNKlass) { - // Look through use to find memory accesses if use does not need pinning - wq.push(use); } else { - // Should have handled all kinds of nodes, verify that we do not unexpectedly arrive - // here - assert(false, "unexpected node %s", use->Name()); - // Be conservative in product and pin the unexpected use - use->ensure_control_or_add_prec(n->in(0)); + switch(use->Opcode()) { + case Op_AddP: + case Op_DecodeN: + case Op_DecodeNKlass: + case Op_CheckCastPP: + case Op_CastPP: + wq.push(use); + break; + } } } } diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index 1210693f957..997ce92fe1c 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -3001,27 +3001,6 @@ bool Node::is_data_proj_of_pure_function(const Node* maybe_pure_function) const return Opcode() == Op_Proj && as_Proj()->_con == TypeFunc::Parms && maybe_pure_function->is_CallLeafPure(); } -// Whether this is an intrinsic node that accesses memory and has a memory input, such as array -// equal intrinsic. Some nodes do access memory but do not have a memory input, such as -// PartialSubTypeCheck, they are not included here. -bool Node::is_memory_access_intrinsic() const { - switch (Opcode()) { - case Op_StrComp: - case Op_StrEquals: - case Op_StrIndexOf: - case Op_StrIndexOfChar: - case Op_StrCompressedCopy: - case Op_StrInflatedCopy: - case Op_AryEq: - case Op_CountPositives: - case Op_VectorizedHashCode: - case Op_EncodeISOArray: - return true; - default: - return false; - } -} - //--------------------------has_non_debug_uses------------------------------ // Checks whether the node has any non-debug uses or not. bool Node::has_non_debug_uses() const { diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index 92bd03c0d63..1ef4b5a51b6 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -1069,7 +1069,6 @@ class Node { uint is_Copy() const { return (_flags & Flag_is_Copy); } virtual bool is_CFG() const { return false; } - bool is_memory_access_intrinsic() const; // If this node is control-dependent on a test, can it be rerouted to a dominating equivalent // test? This means that the node can be executed safely as long as it happens after the test diff --git a/test/hotspot/jtreg/compiler/controldependency/TestRemoveCastPPWithCMoveUse.java b/test/hotspot/jtreg/compiler/controldependency/TestRemoveCastPPWithCMoveUse.java deleted file mode 100644 index 3d752cc74f5..00000000000 --- a/test/hotspot/jtreg/compiler/controldependency/TestRemoveCastPPWithCMoveUse.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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.controldependency; - -/* - * @test - * @bug 8385420 - * @summary C2 correctly handles the case when the removed CastPPNode has a CMove use. - * @run main ${test.main.class} - * @run main/othervm -Xbatch -XX:CompileOnly=${test.main.class}::test - * -XX:+UnlockDiagnosticVMOptions -XX:+StressGCM ${test.main.class} - * - */ -public class TestRemoveCastPPWithCMoveUse { - public static void main(String[] args) { - for (int i = 0; i < 10_000; i++) { - test(null, false); - test(null, true); - test("", false); - test("", true); - } - } - - static int test(String a, boolean flag) { - StringBuilder sb = new StringBuilder(); - if (a == null) { - sb.append(""); - } else { - sb.append(flag ? a : ""); - } - return sb.length(); - } -} From 56b4a547d81d968b1441186e8cb28360a8ce6cfd Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Mon, 29 Jun 2026 05:35:47 +0000 Subject: [PATCH 086/707] 8386480: Parallel: Avoid Triggering GC Before VM Initialization Completes Reviewed-by: gli, tschatzl, aboldtch --- .../gc/parallel/parallelScavengeHeap.cpp | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index b77294a2ac1..7aa88110fc8 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -308,11 +308,26 @@ HeapWord* ParallelScavengeHeap::mem_allocate_work(size_t size, bool is_tlab) { for (uint loop_count = 0; /* empty */; ++loop_count) { HeapWord* result; { + // This lock is needed to sync with the VM-init expansion below. ConditionalMutexLocker locker(Heap_lock, !is_init_completed()); result = mem_allocate_cas_noexpand(size, is_tlab); if (result != nullptr) { return result; } + + if (!is_init_completed()) { + // Double checked locking, this ensure that is_init_completed() does not + // transition while expanding the heap. + MonitorLocker ml(InitCompleted_lock, Monitor::_no_safepoint_check_flag); + if (!is_init_completed()) { + result = expand_heap_and_allocate(size, is_tlab); + // Return the result if it's tlab-allocation. If the result is null, callers will retry + // non-tlab allocation. + if (result != nullptr || is_tlab) { + return result; + } + } + } } // Read total_collections() under the lock so that multiple @@ -328,19 +343,6 @@ HeapWord* ParallelScavengeHeap::mem_allocate_work(size_t size, bool is_tlab) { return result; } - if (!is_init_completed()) { - // Double checked locking, this ensure that is_init_completed() does not - // transition while expanding the heap. - MonitorLocker ml(InitCompleted_lock, Monitor::_no_safepoint_check_flag); - if (!is_init_completed()) { - // Can't do GC; try heap expansion to satisfy the request. - result = expand_heap_and_allocate(size, is_tlab); - if (result != nullptr) { - return result; - } - } - } - gc_count = total_collections(); } From b735de6d7190afde0fb056d7c439938439744576 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Mon, 29 Jun 2026 06:48:27 +0000 Subject: [PATCH 087/707] 8386844: Update to use jtreg 8.3 Reviewed-by: erikj, lancea, iris, vromero --- make/autoconf/lib-tests.m4 | 2 +- make/conf/github-actions.conf | 2 +- make/conf/jib-profiles.js | 4 ++-- test/docs/TEST.ROOT | 2 +- test/hotspot/jtreg/TEST.ROOT | 2 +- test/jaxp/TEST.ROOT | 2 +- test/jdk/TEST.ROOT | 2 +- test/langtools/TEST.ROOT | 2 +- test/lib-test/TEST.ROOT | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/make/autoconf/lib-tests.m4 b/make/autoconf/lib-tests.m4 index faaf229eacd..89f9bf425e1 100644 --- a/make/autoconf/lib-tests.m4 +++ b/make/autoconf/lib-tests.m4 @@ -28,7 +28,7 @@ ################################################################################ # Minimum supported versions -JTREG_MINIMUM_VERSION=8.2.1 +JTREG_MINIMUM_VERSION=8.3 GTEST_MINIMUM_VERSION=1.14.0 ################################################################################ diff --git a/make/conf/github-actions.conf b/make/conf/github-actions.conf index 9aee8e87e3c..6c5805f0764 100644 --- a/make/conf/github-actions.conf +++ b/make/conf/github-actions.conf @@ -26,7 +26,7 @@ # Versions and download locations for dependencies used by GitHub Actions (GHA) GTEST_VERSION=1.14.0 -JTREG_VERSION=8.2.1+1 +JTREG_VERSION=8.3+1 LINUX_X64_BOOT_JDK_EXT=tar.gz LINUX_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk26/c3cc523845074aa0af4f5e1e1ed4151d/35/GPL/openjdk-26_linux-x64_bin.tar.gz diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index 20315cda97d..b425c66a34c 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -1174,9 +1174,9 @@ var getJibProfilesDependencies = function (input, common) { jtreg: { server: "jpg", product: "jtreg", - version: "8.2.1", + version: "8.3", build_number: "1", - file: "bundles/jtreg-8.2.1+1.zip", + file: "bundles/jtreg-8.3+1.zip", environment_name: "JT_HOME", environment_path: input.get("jtreg", "home_path") + "/bin", configure_args: "--with-jtreg=" + input.get("jtreg", "home_path"), diff --git a/test/docs/TEST.ROOT b/test/docs/TEST.ROOT index 11cba9c1c88..a42f6c99aa1 100644 --- a/test/docs/TEST.ROOT +++ b/test/docs/TEST.ROOT @@ -38,7 +38,7 @@ groups=TEST.groups # Minimum jtreg version -requiredVersion=8.2.1+1 +requiredVersion=8.3+1 # Path to libraries in the topmost test directory. This is needed so @library diff --git a/test/hotspot/jtreg/TEST.ROOT b/test/hotspot/jtreg/TEST.ROOT index 964c33bc57c..77f48171522 100644 --- a/test/hotspot/jtreg/TEST.ROOT +++ b/test/hotspot/jtreg/TEST.ROOT @@ -102,7 +102,7 @@ requires.properties= \ jdk.static # Minimum jtreg version -requiredVersion=8.2.1+1 +requiredVersion=8.3+1 # Path to libraries in the topmost test directory. This is needed so @library # does not need ../../../ notation to reach them diff --git a/test/jaxp/TEST.ROOT b/test/jaxp/TEST.ROOT index ddf29839e20..695645315d8 100644 --- a/test/jaxp/TEST.ROOT +++ b/test/jaxp/TEST.ROOT @@ -23,7 +23,7 @@ modules=java.xml groups=TEST.groups # Minimum jtreg version -requiredVersion=8.2.1+1 +requiredVersion=8.3+1 # Path to libraries in the topmost test directory. This is needed so @library # does not need ../../ notation to reach them diff --git a/test/jdk/TEST.ROOT b/test/jdk/TEST.ROOT index 7048aafc638..08bc31ffdb8 100644 --- a/test/jdk/TEST.ROOT +++ b/test/jdk/TEST.ROOT @@ -120,7 +120,7 @@ requires.properties= \ jdk.static # Minimum jtreg version -requiredVersion=8.2.1+1 +requiredVersion=8.3+1 # Path to libraries in the topmost test directory. This is needed so @library # does not need ../../ notation to reach them diff --git a/test/langtools/TEST.ROOT b/test/langtools/TEST.ROOT index c76f99d1396..8319e724e89 100644 --- a/test/langtools/TEST.ROOT +++ b/test/langtools/TEST.ROOT @@ -15,7 +15,7 @@ keys=intermittent randomness needs-src needs-src-jdk_javadoc groups=TEST.groups # Minimum jtreg version -requiredVersion=8.2.1+1 +requiredVersion=8.3+1 # Path to libraries in the topmost test directory. This is needed so @library diff --git a/test/lib-test/TEST.ROOT b/test/lib-test/TEST.ROOT index 33c9a9c2a43..9c9db2998a5 100644 --- a/test/lib-test/TEST.ROOT +++ b/test/lib-test/TEST.ROOT @@ -29,7 +29,7 @@ keys=randomness # Minimum jtreg version -requiredVersion=8.2.1+1 +requiredVersion=8.3+1 # Prevent TestNG-based tests under this root, use @run junit actions instead disallowedActions=testng From 58b646545519e727525ef06a5dfbd01decbf148d Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Mon, 29 Jun 2026 06:50:38 +0000 Subject: [PATCH 088/707] 8387142: BUILD_LIBMANAGEMENT_EXT remove special warning settings Reviewed-by: lucy, kevinw --- make/modules/jdk.management/Lib.gmk | 4 +--- .../share/native/libmanagement_ext/DiagnosticCommandImpl.c | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/make/modules/jdk.management/Lib.gmk b/make/modules/jdk.management/Lib.gmk index 8991414b44e..f65348e9381 100644 --- a/make/modules/jdk.management/Lib.gmk +++ b/make/modules/jdk.management/Lib.gmk @@ -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 @@ -41,8 +41,6 @@ endif $(eval $(call SetupJdkLibrary, BUILD_LIBMANAGEMENT_EXT, \ NAME := management_ext, \ OPTIMIZATION := HIGH, \ - DISABLED_WARNINGS_gcc_DiagnosticCommandImpl.c := unused-variable, \ - DISABLED_WARNINGS_clang_DiagnosticCommandImpl.c := unused-variable, \ DISABLED_WARNINGS_clang_UnixOperatingSystem.c := format-nonliteral, \ CFLAGS := $(LIBMANAGEMENT_EXT_CFLAGS), \ JDK_LIBS := java.base:libjava java.base:libjvm, \ diff --git a/src/jdk.management/share/native/libmanagement_ext/DiagnosticCommandImpl.c b/src/jdk.management/share/native/libmanagement_ext/DiagnosticCommandImpl.c index 6c0554a5c32..5a01e3ad738 100644 --- a/src/jdk.management/share/native/libmanagement_ext/DiagnosticCommandImpl.c +++ b/src/jdk.management/share/native/libmanagement_ext/DiagnosticCommandImpl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -151,7 +151,7 @@ Java_com_sun_management_internal_DiagnosticCommandImpl_getDiagnosticCommandInfo jobjectArray args; jobject obj; jmmOptionalSupport mos; - jint ret = jmm_interface_management_ext->GetOptionalSupport(env, &mos); + jmm_interface_management_ext->GetOptionalSupport(env, &mos); jsize num_commands; dcmdInfo* dcmd_info_array; jstring jname, jdesc, jimpact, cmd; From 007c7e38be8d419cd60169e8af9065e0ac2c4fcb Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 29 Jun 2026 07:28:54 +0000 Subject: [PATCH 089/707] 8387303: G1: Convert G1ConcurrentRefine::_num_threads_wanted to use the Atomic API Reviewed-by: iwalulya, stefank --- src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp | 8 ++++---- src/hotspot/share/gc/g1/g1ConcurrentRefine.hpp | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp index 4d4730de0b2..c1c820cb554 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp @@ -575,7 +575,7 @@ bool G1ConcurrentRefine::adjust_num_threads_periodically() { if (!_needs_adjust) { Tickspan since_adjust = Ticks::now() - _last_adjust; if (since_adjust.milliseconds() < adjust_threads_period_ms()) { - _num_threads_wanted = 0; + _num_threads_wanted.store_relaxed(0); return false; } } @@ -592,7 +592,7 @@ bool G1ConcurrentRefine::adjust_num_threads_periodically() { _needs_adjust = true; } - return (_num_threads_wanted > 0) && !heap_was_locked(); + return (num_threads_wanted() > 0) && !heap_was_locked(); } void G1ConcurrentRefine::adjust_threads_wanted(size_t available_bytes) { @@ -603,7 +603,7 @@ void G1ConcurrentRefine::adjust_threads_wanted(size_t available_bytes) { size_t num_cards = policy->current_pending_cards(); - _threads_needed.update(_num_threads_wanted, + _threads_needed.update(num_threads_wanted(), available_bytes, num_cards, _pending_cards_target); @@ -613,7 +613,7 @@ void G1ConcurrentRefine::adjust_threads_wanted(size_t available_bytes) { new_wanted = _thread_control.max_num_threads(); } - _num_threads_wanted = new_wanted; + _num_threads_wanted.store_relaxed(new_wanted); log_debug(gc, refine)("Concurrent refinement: wanted %u, pending cards: %zu (pending-from-gc %zu), " "predicted: %zu, goal %zu, time-until-next-gc: %1.2fms pred-refine-rate %1.2fc/ms log-rate %1.2fc/ms", diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefine.hpp b/src/hotspot/share/gc/g1/g1ConcurrentRefine.hpp index 50fb412f3af..62e56c14c68 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefine.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefine.hpp @@ -28,6 +28,7 @@ #include "gc/g1/g1ConcurrentRefineStats.hpp" #include "gc/g1/g1ConcurrentRefineThreadsNeeded.hpp" #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/growableArray.hpp" @@ -212,7 +213,7 @@ class G1ConcurrentRefineSweepState { // class G1ConcurrentRefine : public CHeapObj { G1Policy* _policy; - volatile uint _num_threads_wanted; + Atomic _num_threads_wanted; size_t _pending_cards_target; Ticks _last_adjust; Ticks _last_deactivate; @@ -306,7 +307,7 @@ class G1ConcurrentRefine : public CHeapObj { // obtaining the heap lock. bool heap_was_locked() const { return _heap_was_locked; } - uint num_threads_wanted() const { return _num_threads_wanted; } + uint num_threads_wanted() const { return _num_threads_wanted.load_relaxed(); } uint max_num_threads() const { return _thread_control.max_num_threads(); } // Iterate over all concurrent refinement threads applying the given closure. From 5f1355b0851d95a53a760fa045e16d9bde3d1a04 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 29 Jun 2026 08:45:26 +0000 Subject: [PATCH 090/707] 8387322: G1: G1CSetCandidateGroupList::_num_regions should be Atomic Reviewed-by: stefank --- src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp | 10 +++++----- src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp | 5 +++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp index 3637d477229..ac1b29a6bd7 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp @@ -134,7 +134,7 @@ void G1CSetCandidateGroupList::append(G1CSetCandidateGroup* group) { assert(group->length() > 0, "Do not add empty groups"); assert(!_groups.contains(group), "Already added to list"); _groups.append(group); - _num_regions += group->length(); + _num_regions.store_relaxed(num_regions() + group->length()); } G1CSetCandidateGroup* G1CSetCandidateGroupList::at(uint index) { @@ -147,7 +147,7 @@ void G1CSetCandidateGroupList::clear(bool uninstall_group_cardset) { delete gr; } _groups.clear(); - _num_regions = 0; + _num_regions.store_relaxed(0); } void G1CSetCandidateGroupList::prepare_for_scan() { @@ -156,9 +156,9 @@ void G1CSetCandidateGroupList::prepare_for_scan() { } } -void G1CSetCandidateGroupList::remove_selected(uint count, uint num_regions) { +void G1CSetCandidateGroupList::remove_selected(uint count, uint num_regions_to_remove) { _groups.remove_till(count); - _num_regions -= num_regions; + _num_regions.store_relaxed(num_regions() - num_regions_to_remove); } void G1CSetCandidateGroupList::remove(G1CSetCandidateGroupList* other) { @@ -172,7 +172,7 @@ void G1CSetCandidateGroupList::remove(G1CSetCandidateGroupList* other) { // Create a list from scratch, copying over the elements from the candidate // list not in the other list. Finally deallocate and overwrite the old list. int new_length = _groups.length() - other->length(); - _num_regions = num_regions() - other->num_regions(); + _num_regions.store_relaxed(num_regions() - other->num_regions()); GrowableArray new_list(new_length, mtGC); uint other_idx = 0; diff --git a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp index 8a2235cf89c..a70f9e395b6 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp @@ -29,6 +29,7 @@ #include "gc/g1/g1CollectionSetCandidates.hpp" #include "gc/shared/gc_globals.hpp" #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" #include "runtime/globals.hpp" #include "utilities/growableArray.hpp" @@ -147,7 +148,7 @@ using G1CSetCandidateGroupListIterator = GrowableArrayIterator _groups; - volatile uint _num_regions; + Atomic _num_regions; public: G1CSetCandidateGroupList(); @@ -163,7 +164,7 @@ class G1CSetCandidateGroupList { uint length() const { return (uint)_groups.length(); } - uint num_regions() const { return _num_regions; } + uint num_regions() const { return _num_regions.load_relaxed(); } void remove_selected(uint count, uint num_regions); From 78112cbcaafcc7de0dfbf67b3f83677abaeaee87 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 29 Jun 2026 08:52:38 +0000 Subject: [PATCH 091/707] 8385903: G1: G1CollectionSet::_num_regions needs to be Atomic Reviewed-by: stefank --- src/hotspot/share/gc/g1/g1CollectionSet.cpp | 16 ++++++++-------- src/hotspot/share/gc/g1/g1CollectionSet.hpp | 9 +++++---- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.cpp b/src/hotspot/share/gc/g1/g1CollectionSet.cpp index 9f1bbf1b48e..3a086d8b09b 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.cpp @@ -33,7 +33,6 @@ #include "gc/g1/g1ParScanThreadState.hpp" #include "gc/g1/g1Policy.hpp" #include "logging/logStream.hpp" -#include "runtime/orderAccess.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" @@ -128,8 +127,11 @@ void G1CollectionSet::add_old_region(G1HeapRegion* hr) { _g1h->register_old_collection_set_region_with_region_attr(hr); - assert(num_regions() < _max_num_regions, "Collection set now larger than maximum size."); - _regions[_num_regions++] = hr->hrm_index(); + uint local_num_regions = num_regions(); + assert(local_num_regions < _max_num_regions, "Collection set now larger than maximum size."); + _regions[local_num_regions] = hr->hrm_index(); + _num_regions.store_relaxed(local_num_regions + 1); + _num_initial_old_regions++; _g1h->old_set_remove(hr); @@ -162,14 +164,13 @@ void G1CollectionSet::stop_incremental_building() { void G1CollectionSet::clear() { assert_at_safepoint_on_vm_thread(); - _num_regions = 0; + _num_regions.store_relaxed(0); _groups.clear(); assert(_optional_groups.length() == 0, "must be"); } void G1CollectionSet::iterate(G1HeapRegionClosure* cl) const { - uint len = _num_regions; - OrderAccess::loadload(); + uint len = _num_regions.load_acquire(); for (uint i = 0; i < len; i++) { G1HeapRegion* r = _g1h->region_at(_regions[i]); @@ -233,8 +234,7 @@ void G1CollectionSet::add_young_region_common(G1HeapRegion* hr) { _regions[index] = hr->hrm_index(); // Concurrent readers must observe the store of the value in the array before an // update to the _num_regions field. - OrderAccess::storestore(); - _num_regions++; + _num_regions.fetch_then_add(1u, memory_order_release); } void G1CollectionSet::add_survivor_regions(G1HeapRegion* hr) { diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.hpp b/src/hotspot/share/gc/g1/g1CollectionSet.hpp index 5fa9868f2b2..eee985f259d 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.hpp @@ -26,6 +26,7 @@ #define SHARE_GC_G1_G1COLLECTIONSET_HPP #include "gc/g1/g1CollectionSetCandidates.hpp" +#include "runtime/atomic.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" @@ -142,14 +143,14 @@ class G1CollectionSet { // All regions in _regions below _num_regions are assumed to be part of the // collection set. // We assume that at any time there is at most only one writer and (one or more) - // concurrent readers. This means synchronization using storestore and loadload - // barriers on the writer and reader respectively only are sufficient. + // concurrent readers. This means synchronization using release and acquire + // on the writer and reader respectively only are sufficient. // // This corresponds to the regions referenced by the candidate groups further below. uint* _regions; uint _max_num_regions; - volatile uint _num_regions; + Atomic _num_regions; // Old gen groups selected for evacuation. G1CSetCandidateGroupList _groups; @@ -285,7 +286,7 @@ class G1CollectionSet { // Returns the number of regions in the current collection set increment. uint num_regions_in_increment() const { return num_regions() - _regions_inc_part_start; } // Returns the total number of regions in the current collection set. - uint num_regions() const { return _num_regions; } + uint num_regions() const { return _num_regions.load_relaxed(); } // Iterate over the entire collection set (all increments calculated so far), applying // the given G1HeapRegionClosure on all of the regions. From 22313f85bac81e68e318976ff69439658d0a602f Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 29 Jun 2026 08:56:19 +0000 Subject: [PATCH 092/707] 8387206: G1: Code root verification crashes because of stale table scanner Reviewed-by: iwalulya --- src/hotspot/share/gc/g1/g1HeapRegion.cpp | 2 ++ src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp | 6 +++++- src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/gc/g1/g1HeapRegion.cpp b/src/hotspot/share/gc/g1/g1HeapRegion.cpp index 810bd4df2ee..2c85e2fcc0d 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegion.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegion.cpp @@ -413,6 +413,8 @@ bool G1HeapRegion::verify_code_roots(VerifyOption vo) const { return has_code_roots; } + rem_set()->reset_code_root_table_scanner(); + VerifyCodeRootNMethodClosure nm_cl(this); code_roots_do(&nm_cl); diff --git a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp index 13c7a6a8d3e..ef42538d4d6 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp @@ -87,8 +87,12 @@ void G1HeapRegionRemSet::clear(bool only_cardset, bool keep_tracked) { } } -void G1HeapRegionRemSet::reset_table_scanner() { +void G1HeapRegionRemSet::reset_code_root_table_scanner() { _code_roots.reset_table_scanner(); +} + +void G1HeapRegionRemSet::reset_table_scanner() { + reset_code_root_table_scanner(); if (has_cset_group()) { card_set()->reset_table_scanner(); } diff --git a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp index 950098c706e..2e97d6a7597 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp @@ -154,6 +154,7 @@ class G1HeapRegionRemSet : public CHeapObj { // entries for this region in other remsets. void clear(bool only_cardset = false, bool keep_tracked = false); + void reset_code_root_table_scanner(); void reset_table_scanner(); G1MonotonicArenaMemoryStats card_set_memory_stats() const; From 9ee63d6359382ea65677d547068dcabb9151dc5c Mon Sep 17 00:00:00 2001 From: Guanqiang Han Date: Mon, 29 Jun 2026 09:29:49 +0000 Subject: [PATCH 093/707] 8387197: C2: Improve klass_ptr_type in GraphKit::gen_instanceof() similarly to GraphKit::gen_checkcast() Reviewed-by: qamai, vlivanov --- src/hotspot/share/opto/graphKit.cpp | 16 ++-- .../TestInstanceOfImprovedKlassPtrType.java | 84 +++++++++++++++++++ 2 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/parsing/TestInstanceOfImprovedKlassPtrType.java diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 3112bb6b169..4f5251f39e1 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -3240,9 +3240,10 @@ Node* GraphKit::maybe_cast_profiled_obj(Node* obj, Node* GraphKit::gen_instanceof(Node* obj, Node* superklass, bool safe_for_replace) { kill_dead_locals(); // Benefit all the uncommon traps assert( !stopped(), "dead parse path should be checked in callers" ); - assert(!TypePtr::NULL_PTR->higher_equal(_gvn.type(superklass)->is_klassptr()), + const TypeKlassPtr* klass_ptr_type = _gvn.type(superklass)->isa_klassptr(); + assert(klass_ptr_type != nullptr && !TypePtr::NULL_PTR->higher_equal(klass_ptr_type), "must check for not-null not-dead klass in callers"); - + const TypeKlassPtr* improved_klass_ptr_type = klass_ptr_type->try_improve(); // Make the merge point enum { _obj_path = 1, _fail_path, _null_path, PATH_LIMIT }; RegionNode* region = new RegionNode(PATH_LIMIT); @@ -3278,11 +3279,10 @@ Node* GraphKit::gen_instanceof(Node* obj, Node* superklass, bool safe_for_replac // Do we know the type check always succeed? bool known_statically = false; - if (_gvn.type(superklass)->singleton()) { - const TypeKlassPtr* superk = _gvn.type(superklass)->is_klassptr(); + if (improved_klass_ptr_type->singleton()) { const TypeKlassPtr* subk = _gvn.type(obj)->is_oopptr()->as_klass_type(); if (subk->is_loaded()) { - int static_res = C->static_subtype_check(superk, subk); + int static_res = C->static_subtype_check(improved_klass_ptr_type, subk); known_statically = (static_res == Compile::SSC_always_true || static_res == Compile::SSC_always_false); } } @@ -3305,7 +3305,11 @@ Node* GraphKit::gen_instanceof(Node* obj, Node* superklass, bool safe_for_replac } // Generate the subtype check - Node* not_subtype_ctrl = gen_subtype_check(not_null_obj, superklass); + Node* improved_superklass = superklass; + if (improved_klass_ptr_type != klass_ptr_type && improved_klass_ptr_type->singleton()) { + improved_superklass = makecon(improved_klass_ptr_type); + } + Node* not_subtype_ctrl = gen_subtype_check(not_null_obj, improved_superklass); // Plug in the success path to the general merge in slot 1. region->init_req(_obj_path, control()); diff --git a/test/hotspot/jtreg/compiler/parsing/TestInstanceOfImprovedKlassPtrType.java b/test/hotspot/jtreg/compiler/parsing/TestInstanceOfImprovedKlassPtrType.java new file mode 100644 index 00000000000..19214738dd6 --- /dev/null +++ b/test/hotspot/jtreg/compiler/parsing/TestInstanceOfImprovedKlassPtrType.java @@ -0,0 +1,84 @@ +/* + * 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 8387197 + * @summary Verify that improving klass_ptr_type in GraphKit::gen_instanceof() allows + * eliminating SubTypeCheckNode when the receiver implements an interface + * unrelated to the checked class. + * @library /test/lib / + * @run driver ${test.main.class} + */ + +package compiler.parsing; + +import compiler.lib.ir_framework.*; +import jdk.test.lib.Asserts; + +public class TestInstanceOfImprovedKlassPtrType { + static abstract class B {} + static final class C extends B {} + + interface I {} + static class D implements I {} + static class E implements I {} + + public static void main(String[] args) { + TestFramework.run(); + } + + @DontInline + int testHelper2(Object o) { + return 1; + } + + @Test + @IR(counts = {IRNode.SUBTYPE_CHECK, "1"}, + phase = CompilePhase.AFTER_PARSING) + int test1(Object o) { + Object o1 = (I) o; + if (o1 instanceof B) { + return testHelper2(o1); + } else { + return 2; + } + } + + @Run(test = "test1") + @Warmup(0) + void runTest() { + int sum = 0; + Object[] arr = new Object[] {new C(), new D(), new E()}; + for (int i = 0; i < 3; i++){ + Object o = arr[i]; + if (o instanceof I) { + sum += test1(o); + } else { + sum += 3; + } + } + Asserts.assertEquals(sum, 7); + return; + } +} From 027eb8b416d2f3238f4c4c65d5d485911ad79ca5 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 29 Jun 2026 10:49:58 +0000 Subject: [PATCH 094/707] 8371720: G1: Move concurrent mark initialization to first concurrent start pause Reviewed-by: iwalulya --- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 5 +++-- src/hotspot/share/gc/g1/g1ConcurrentMark.hpp | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index b4758897dd6..7396c1ee9ce 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -2723,13 +2723,14 @@ void G1CollectedHeap::do_collection_pause_at_safepoint(size_t allocation_word_si _bytes_used_during_gc = 0; - _cm->fully_initialize(); - policy()->decide_on_concurrent_start_pause(); // Record whether this pause may need to trigger a concurrent operation. Later, // when we signal the G1ConcurrentMarkThread, the collector state has already // been reset for the next pause. bool should_start_concurrent_mark_operation = collector_state()->is_in_concurrent_start_gc(); + if (should_start_concurrent_mark_operation) { + _cm->fully_initialize(); + } // Perform the collection. G1YoungCollector collector(gc_cause(), allocation_word_size); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index f0071286e04..21518423957 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -569,6 +569,11 @@ class G1ConcurrentMark : public CHeapObj { uint worker_id_offset() const { return _worker_id_offset; } + // Fully allocates and initializes data structures for the concurrent cycle. + // Methods that use concurrent cycle state such as the concurrent mark threads, + // tasks, marking stack, statistics, TAMS or TARS require this initialization. + // Callers that run before the first concurrent start pause, which calls this, + // should guard calls with is_fully_initialized(). void fully_initialize(); bool is_fully_initialized() const { return _cm_thread != nullptr; } From 1d514a55555248c74bb7587ecf8fac44748df001 Mon Sep 17 00:00:00 2001 From: David Briemann Date: Mon, 29 Jun 2026 11:44:47 +0000 Subject: [PATCH 095/707] 8387016: PPC64: Remove postalloc_expand from float/double compare nodes Reviewed-by: mdoerr, rrich --- src/hotspot/cpu/ppc/ppc.ad | 153 +++++-------------------------------- 1 file changed, 20 insertions(+), 133 deletions(-) diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 9bec99e90cc..3cdc820b5f9 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -3556,9 +3556,6 @@ ins_attrib ins_alignment(1); ins_attrib ins_cannot_rematerialize(false); ins_attrib ins_should_rematerialize(false); -// Instruction has variable size depending on alignment. -ins_attrib ins_variable_size_depending_on_alignment(false); - // Instruction is a nop. ins_attrib ins_is_nop(false); @@ -7015,8 +7012,6 @@ instruct cmovF_reg(cmpOp cmp, flagsRegSrc crx, regF dst, regF src) %{ match(Set dst (CMoveF (Binary cmp crx) (Binary dst src))); ins_cost(DEFAULT_COST+BRANCH_COST); - ins_variable_size_depending_on_alignment(true); - format %{ "CMOVEF $cmp, $crx, $dst, $src\n\t" %} size(8); ins_encode %{ @@ -7034,8 +7029,6 @@ instruct cmovD_reg(cmpOp cmp, flagsRegSrc crx, regD dst, regD src) %{ match(Set dst (CMoveD (Binary cmp crx) (Binary dst src))); ins_cost(DEFAULT_COST+BRANCH_COST); - ins_variable_size_depending_on_alignment(true); - format %{ "CMOVEF $cmp, $crx, $dst, $src\n\t" %} size(8); ins_encode %{ @@ -8276,8 +8269,6 @@ instruct cmovI_bne_negI_reg(iRegIdst dst, flagsRegSrc crx, iRegIsrc src1) %{ effect(USE_DEF dst, USE src1, USE crx); predicate(false); - ins_variable_size_depending_on_alignment(true); - format %{ "CMOVE $dst, neg($src1), $crx" %} size(8); ins_encode %{ @@ -8334,8 +8325,6 @@ instruct cmovL_bne_negL_reg(iRegLdst dst, flagsRegSrc crx, iRegLsrc src1) %{ effect(USE_DEF dst, USE src1, USE crx); predicate(false); - ins_variable_size_depending_on_alignment(true); - format %{ "CMOVE $dst, neg($src1), $crx" %} size(8); ins_encode %{ @@ -10044,8 +10033,6 @@ instruct cmovI_bso_stackSlotL(iRegIdst dst, flagsRegSrc crx, stackSlotL src) %{ effect(DEF dst, USE crx, USE src); predicate(false); - ins_variable_size_depending_on_alignment(true); - format %{ "CMOVI $crx, $dst, $src" %} size(8); ins_encode( enc_cmove_bso_stackSlotL(dst, crx, src) ); @@ -10057,8 +10044,6 @@ instruct cmovI_bso_reg(iRegIdst dst, flagsRegSrc crx, regD src) %{ effect(DEF dst, USE crx, USE src); predicate(false); - ins_variable_size_depending_on_alignment(true); - format %{ "CMOVI $crx, $dst, $src" %} size(8); ins_encode( enc_cmove_bso_reg(dst, crx, src) ); @@ -10219,8 +10204,6 @@ instruct cmovL_bso_stackSlotL(iRegLdst dst, flagsRegSrc crx, stackSlotL src) %{ effect(DEF dst, USE crx, USE src); predicate(false); - ins_variable_size_depending_on_alignment(true); - format %{ "CMOVL $crx, $dst, $src" %} size(8); ins_encode( enc_cmove_bso_stackSlotL(dst, crx, src) ); @@ -10232,8 +10215,6 @@ instruct cmovL_bso_reg(iRegLdst dst, flagsRegSrc crx, regD src) %{ effect(DEF dst, USE crx, USE src); predicate(false); - ins_variable_size_depending_on_alignment(true); - format %{ "CMOVL $crx, $dst, $src" %} size(8); ins_encode( enc_cmove_bso_reg(dst, crx, src) ); @@ -10853,86 +10834,24 @@ instruct cmpFUnordered_reg_reg(flagsReg crx, regF src1, regF src2) %{ ins_pipe(pipe_class_default); %} -instruct cmov_bns_less(flagsReg crx) %{ - // no match-rule, false predicate - effect(DEF crx); - predicate(false); - - ins_variable_size_depending_on_alignment(true); +// Compare floating, generate condition code. +instruct cmpF_reg_reg(flagsReg crx, regF src1, regF src2) %{ + match(Set crx (CmpF src1 src2)); + ins_cost(DEFAULT_COST+BRANCH_COST); - format %{ "CMOV $crx" %} - size(12); + format %{ "CMPF $crx, $src1, $src2" %} + size(16); ins_encode %{ Label done; - __ bns($crx$$CondRegister, done); // not unordered -> keep crx + __ fcmpu($crx$$CondRegister, $src1$$FloatRegister, $src2$$FloatRegister); + __ bns($crx$$CondRegister, done); __ li(R0, 0); - __ cmpwi($crx$$CondRegister, R0, 1); // unordered -> set crx to 'less' + __ cmpwi($crx$$CondRegister, R0, 1); __ bind(done); %} ins_pipe(pipe_class_default); %} -// Compare floating, generate condition code. -instruct cmpF_reg_reg_Ex(flagsReg crx, regF src1, regF src2) %{ - // FIXME: should we match 'If cmp (CmpF src1 src2))' ?? - // - // The following code sequence occurs a lot in mpegaudio: - // - // block BXX: - // 0: instruct cmpFUnordered_reg_reg (cmpF_reg_reg-0): - // cmpFUrd CR6, F11, F9 - // 4: instruct cmov_bns_less (cmpF_reg_reg-1): - // cmov CR6 - // 8: instruct branchConSched: - // B_FARle CR6, B56 P=0.500000 C=-1.000000 - match(Set crx (CmpF src1 src2)); - ins_cost(DEFAULT_COST+BRANCH_COST); - - format %{ "CMPF $crx, $src1, $src2 \t// postalloc expanded" %} - postalloc_expand %{ - // - // replaces - // - // region src1 src2 - // \ | | - // crx=cmpF_reg_reg - // - // with - // - // region src1 src2 - // \ | | - // crx=cmpFUnordered_reg_reg - // | - // ^ region - // | \ - // crx=cmov_bns_less - // - - // Create new nodes. - MachNode *m1 = new cmpFUnordered_reg_regNode(); - MachNode *m2 = new cmov_bns_lessNode(); - - // inputs for new nodes - m1->add_req(n_region, n_src1, n_src2); - m2->add_req(n_region); - m2->add_prec(m1); - - // operands for new nodes - m1->_opnds[0] = op_crx; - m1->_opnds[1] = op_src1; - m1->_opnds[2] = op_src2; - m2->_opnds[0] = op_crx; - - // registers for new nodes - ra_->set_pair(m1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // crx - ra_->set_pair(m2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // crx - - // Insert new nodes. - nodes->push(m1); - nodes->push(m2); - %} -%} - // Compare float, generate -1,0,1 instruct cmpF3_reg_reg(iRegIdst dst, regF src1, regF src2, flagsRegCR0 cr0) %{ match(Set dst (CmpF3 src1 src2)); @@ -10968,53 +10887,21 @@ instruct cmpDUnordered_reg_reg(flagsReg crx, regD src1, regD src2) %{ ins_pipe(pipe_class_default); %} -instruct cmpD_reg_reg_Ex(flagsReg crx, regD src1, regD src2) %{ +instruct cmpD_reg_reg(flagsReg crx, regD src1, regD src2) %{ match(Set crx (CmpD src1 src2)); ins_cost(DEFAULT_COST+BRANCH_COST); - format %{ "CmpD $crx, $src1, $src2 \t// postalloc expanded" %} - postalloc_expand %{ - // - // replaces - // - // region src1 src2 - // \ | | - // crx=cmpD_reg_reg - // - // with - // - // region src1 src2 - // \ | | - // crx=cmpDUnordered_reg_reg - // | - // ^ region - // | \ - // crx=cmov_bns_less - // - - // create new nodes - MachNode *m1 = new cmpDUnordered_reg_regNode(); - MachNode *m2 = new cmov_bns_lessNode(); - - // inputs for new nodes - m1->add_req(n_region, n_src1, n_src2); - m2->add_req(n_region); - m2->add_prec(m1); - - // operands for new nodes - m1->_opnds[0] = op_crx; - m1->_opnds[1] = op_src1; - m1->_opnds[2] = op_src2; - m2->_opnds[0] = op_crx; - - // registers for new nodes - ra_->set_pair(m1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // crx - ra_->set_pair(m2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // crx - - // Insert new nodes. - nodes->push(m1); - nodes->push(m2); + format %{ "CMPD $crx, $src1, $src2" %} + size(16); + ins_encode %{ + Label done; + __ fcmpu($crx$$CondRegister, $src1$$FloatRegister, $src2$$FloatRegister); + __ bns($crx$$CondRegister, done); + __ li(R0, 0); + __ cmpwi($crx$$CondRegister, R0, 1); + __ bind(done); %} + ins_pipe(pipe_class_default); %} // Compare double, generate -1,0,1 From cc83fbd132cf4b121e9554255241eb175715865b Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Mon, 29 Jun 2026 12:37:38 +0000 Subject: [PATCH 096/707] 8387306: Replace InputStream#read(byte[]) with InputStream#readNBytes(int) in RtfConverter.isRtfFile() Reviewed-by: almatvee, aturbanov --- .../classes/jdk/jpackage/internal/RtfConverter.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/RtfConverter.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/RtfConverter.java index a0ff70066b9..404185ce832 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/RtfConverter.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/RtfConverter.java @@ -46,11 +46,11 @@ static boolean isRtfFile(Path path) throws IOException { } try (InputStream fin = Files.newInputStream(path)) { - byte[] firstBits = new byte[7]; + byte[] firstBits = fin.readNBytes(Details.RTF_HEADER.length()); - if (fin.read(firstBits) == firstBits.length) { + if (Details.RTF_HEADER.length() == firstBits.length) { String header = new String(firstBits); - return "{\\rtf1\\".equals(header); + return Details.RTF_HEADER.equals(header); } } @@ -136,5 +136,6 @@ private void convert(Stream textFile, Appendable sink) throws IOExceptio } } + private static final String RTF_HEADER = "{\\rtf1\\"; } } From f740f7c66bbf2dc16dbee965df0b7e5859594fc8 Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Mon, 29 Jun 2026 14:18:48 +0000 Subject: [PATCH 097/707] 8386985: PacketSpaceManagerTest failed with AssertionError; A race condition may cause packetSent to mistakenly skip rescheduling of the transmitter task Reviewed-by: djelinski --- .../net/http/quic/PacketSpaceManager.java | 23 ++--- .../quic/PacketSpaceManagerTest.java | 88 ++++++++++++++++--- 2 files changed, 82 insertions(+), 29 deletions(-) diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/PacketSpaceManager.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/PacketSpaceManager.java index 487a8a186f6..f2991c0738e 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/PacketSpaceManager.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/PacketSpaceManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 @@ -645,22 +645,15 @@ private synchronized boolean shouldLogWhenNewDeadline() { return false; } - boolean hasNoDeadline() { - return Deadline.MAX.equals(nextDeadline); - } - // reschedule this task void reschedule() { Deadline deadline = computeNextDeadline(); - Deadline nextDeadline = this.nextDeadline; if (Deadline.MAX.equals(deadline)) { - debug.log("no deadline, don't reschedule"); - } else if (deadline.equals(nextDeadline)) { - debug.log("deadline unchanged, don't reschedule"); - } else { - packetEmitter.reschedule(this, deadline); - debug.log("retransmission task: rescheduled"); + if (debug.on()) debug.log("no deadline, don't reschedule"); + return; } + if (debug.on()) debug.log("retransmission task: rescheduled"); + packetEmitter.reschedule(this, deadline); } @Override @@ -1304,7 +1297,7 @@ public void packetSent(QuicPacket packet, long previousPacketNumber, long packet } finally { transferLock.unlock(); } - if (found && packetTransmissionTask.hasNoDeadline()) { + if (found) { packetTransmissionTask.reschedule(); } if (!found) { @@ -1340,9 +1333,7 @@ public void packetSent(QuicPacket packet, long previousPacketNumber, long packet return; } addAcknowledgement(pending); - if (packetTransmissionTask.hasNoDeadline()) { - packetTransmissionTask.reschedule(); - } + packetTransmissionTask.reschedule(); } finally { transferLock.unlock(); } diff --git a/test/jdk/java/net/httpclient/quic/PacketSpaceManagerTest.java b/test/jdk/java/net/httpclient/quic/PacketSpaceManagerTest.java index 0a363e104ae..3a33bbcf95d 100644 --- a/test/jdk/java/net/httpclient/quic/PacketSpaceManagerTest.java +++ b/test/jdk/java/net/httpclient/quic/PacketSpaceManagerTest.java @@ -80,16 +80,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; /* * @test + * @bug 8349910 8386985 * @summary tests the logic to build an AckFrame * @library /test/lib * @library ../debug @@ -102,6 +105,7 @@ * @run junit/othervm -Dseed=-4159871071396382784 ${test.main.class} * @run junit/othervm -Dseed=2252276218459363615 ${test.main.class} * @run junit/othervm -Dseed=-5130588140709404919 ${test.main.class} + * @run junit/othervm -Dseed=4257295716830862528 ${test.main.class} */ // -Djdk.internal.httpclient.debug=true public class PacketSpaceManagerTest { @@ -766,6 +770,26 @@ boolean isDue(Deadline now) { } } + // Sends a trivial INITIAL packet, with a CRYPTO frame containing + // a payload of length 1 for the provided offset. If ackFrameToSend + // is not null it will be included in the packet. + public List sendPacket(long offset, AckFrame ackFrameToSend, Packet packet, long largestReceivedAckedPN) { + // add a crypto frame and build the packet + CryptoFrame crypto = new CryptoFrame(offset, 1, + ByteBuffer.wrap(new byte[] {nextByte(offset)})); + List frames = ackFrameToSend == null ? + List.of(crypto) : List.of(crypto, ackFrameToSend); + QuicPacket newPacket = codingContext.encoder + .newInitialPacket(localId, peerId, + null, + packet.packetNumber, + largestReceivedAckedPN, + frames, codingContext); + // pretend that we sent a packet + manager.packetSent(newPacket, -1, packet.packetNumber); + return frames; + } + /** * Drives the test by pretending to emit each packet in order, * then pretending to receive ack frames (as soon as possible @@ -830,19 +854,8 @@ public void run() throws Exception { debug.log("largestAckSent is: " + largestAckAcked); } - // add a crypto frame and build the packet - CryptoFrame crypto = new CryptoFrame(offset, 1, - ByteBuffer.wrap(new byte[] {nextByte(offset)})); - List frames = ackFrameToSend == null ? - List.of(crypto) : List.of(crypto, ackFrameToSend); - QuicPacket newPacket = codingContext.encoder - .newInitialPacket(localId, peerId, - null, - packet.packetNumber, - largestReceivedAckedPN, - frames, codingContext); - // pretend that we sent a packet - manager.packetSent(newPacket, -1, packet.packetNumber); + // send a packet + List frames = sendPacket(offset, ackFrameToSend, packet, largestReceivedAckedPN); // compute next deadline var nextDeadline = timerQueue.nextDeadline(); @@ -1125,4 +1138,53 @@ public void testPacketSpaceManager(TestCase testCase) throws Exception { driver.check(); } + @Test + public void testPacketSent() throws Exception { + // this test case is specifically for JDK-8386985 + System.out.printf("%n ------- testPacketSent ------- %n"); + + // create a minimal SynchronousTestDriver + TestCase testCase = new TestCase(List.of(new Acknowledged(1, 3), new Acknowledged(4,4)), + List.of(new Packet(3, 0), new Packet(4, 0))); + SynchronousTestDriver driver = new SynchronousTestDriver(testCase); + + // send a first ack-eliciting packet, and move the timeline past PTO + driver.sendPacket(0, null, new Packet(1, 0), -1); + Deadline pto = driver.manager.nextScheduledDeadline(); // should be PTO + driver.timeSource.advance(driver.timeSource.instant().until(pto, ChronoUnit.MILLIS) + 250, ChronoUnit.MILLIS); + + // start processing events, but delay the task that will run the transmitter + ArrayList tasks = new ArrayList<>(); + Executor executor = new Executor() { + @Override + public void execute(Runnable command) { + tasks.add(command); + } + }; + driver.timerQueue.processEventsAndReturnNextDeadline(driver.timeSource.instant(), executor); + + // acknowledge the first packet so that it's no longer pending retransmission + driver.manager.processAckFrame(new AckFrameBuilder().addAck(1).build()); + + // send a second packet, and examine the timerQueue next deadline + // if sending the second packet didn't cause the task to be rescheduled, we + // will observe Deadline.MAX, or a deadline before now: that's the bug. + driver.sendPacket(1, null, new Packet(2, 0), -1); + + Deadline next = driver.timerQueue.nextDeadline(); + assertNotEquals(next, Deadline.MAX); + assertTrue(next.isAfter(driver.timeSource.instant())); + + // now finish running the task and ack the second packet, + // so that we leave the packet space manager in a clean state + // for running the driver with the next two packets. + for (Runnable task : tasks) { + task.run(); + } + driver.manager.processAckFrame(new AckFrameBuilder() + .addAck(1).addAck(2).build()); + driver.run(); + driver.check(); + } + } From 17f2e11fe400ade68ee3b0ac4706209aae4e14dd Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Mon, 29 Jun 2026 14:26:50 +0000 Subject: [PATCH 098/707] 8386989: QuicEndpoint.ClosedConnection should not use QuicTimerQueue::offer Reviewed-by: djelinski --- .../internal/net/http/quic/QuicEndpoint.java | 11 ++--- .../net/http/quic/QuicTimerQueue.java | 45 +++++++------------ .../H3MultipleConnectionsToSameHost.java | 2 +- 3 files changed, 23 insertions(+), 35 deletions(-) diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java index 3dee814e1f1..18fd7717d6d 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java @@ -1788,9 +1788,10 @@ public final void processIncoming(SocketAddress source, ByteBuffer destConnId, H if (more > 16) { // the server doesn't seem to take into account our // connection close frame. Just stop responding - updatedDeadline = Deadline.MIN; + updated = updatedDeadline = Deadline.MIN; } else { - updatedDeadline = updated.plusMillis(maxIdleTimeMs); + updated = updatedDeadline = timeSource().instant() + .plusMillis(maxIdleTimeMs); } handleIncoming(source, destConnId, headersType, buffer); } else { @@ -1798,7 +1799,7 @@ public final void processIncoming(SocketAddress source, ByteBuffer destConnId, H dropIncoming(source, destConnId, headersType, buffer); } - timer().reschedule(this, updatedDeadline); + timer().reschedule(this, updated); } protected void handleIncoming(SocketAddress source, ByteBuffer idbytes, @@ -1821,8 +1822,8 @@ public final void onWriteError(Throwable t) { } public final void startTimer() { - deadline = updatedDeadline = timeSource().instant().plusMillis(maxIdleTimeMs); - timer().offer(this); + Deadline deadline = updatedDeadline = timeSource().instant().plusMillis(maxIdleTimeMs); + timer().reschedule(this, deadline); } @Override diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicTimerQueue.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicTimerQueue.java index 830415593cb..bbb88cf1c45 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicTimerQueue.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicTimerQueue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 @@ -74,6 +74,9 @@ public final class QuicTimerQueue { private volatile Deadline scheduledDeadline = Deadline.MAX; private volatile Deadline returnedDeadline = Deadline.MAX; + // Not volatile: never accessed without holding monitor + private Deadline notifiedDeadline = Deadline.MAX; + /** * Creates a new timer queue with the given notifier. * A notifier is used to notify the timer thread that @@ -113,33 +116,8 @@ private Deadline debugNow() { * @param event an event to be scheduled */ public void offer(QuicTimedEvent event) { - if (event instanceof Marker marker) - throw new IllegalArgumentException(marker.name()); - assert QuicTimedEvent.COMPARATOR.compare(event, FLOOR) > 0; - assert QuicTimedEvent.COMPARATOR.compare(event, CEILING) < 0; - Deadline deadline = event.deadline(); - scheduled.add(event); - scheduled(deadline); if (debug.on()) debug.log("QuicTimerQueue: event %s offered", event); - if (notify(deadline)) { - if (debug.on()) debug.log("QuicTimerQueue: event %s will be rescheduled", event); - if (Log.quicTimer()) { - var now = debugNow(); - Log.logQuic(String.format("%s: QuicTimerQueue: event %s will be scheduled" + - " at %s (returned deadline: %s, nextDeadline: %s)", - Thread.currentThread().getName(), event, d(now, deadline), - d(now, returnedDeadline), d(now, nextDeadline()))); - } - notifier.run(); - } else { - if (Log.quicTimer()) { - var now = debugNow(); - Log.logQuic(String.format("%s: QuicTimerQueue: event %s will not be scheduled" + - " at %s (returned deadline: %s, nextDeadline: %s)", - Thread.currentThread().getName(), event, d(now, deadline), - d(now, returnedDeadline), d(now, nextDeadline()))); - } - } + reschedule(event, event.deadline()); } /** @@ -181,7 +159,7 @@ public Deadline processEventsAndReturnNextDeadline(Deadline now, Executor execut int drained = 0; int dues; synchronized (this) { - scheduledDeadline = Deadline.MAX; + scheduledDeadline = returnedDeadline = notifiedDeadline = Deadline.MAX; } // moved scheduled / rescheduled tasks to due, until // nothing else is due. Then process dues. @@ -347,7 +325,16 @@ private boolean notify(Deadline deadline) { synchronized (this) { if (deadline.isBefore(nextDeadline()) || deadline.isBefore(returnedDeadline)) { - return true; + // notifiedDeadline will be reset to MAX first thing in + // processEventAndReturnNextDeadline; We do not want + // to call the notifier (wake the selector) again if it's + // been already called for a notifiedDeadline <= to deadline; + // On the other hand, if deadline < notifiedDeadline, we + // need to call the notifier to force an additional wakeup + if (deadline.isBefore(notifiedDeadline)) { + notifiedDeadline = deadline; + return true; + } } } return false; diff --git a/test/jdk/java/net/httpclient/http3/H3MultipleConnectionsToSameHost.java b/test/jdk/java/net/httpclient/http3/H3MultipleConnectionsToSameHost.java index c38671e65b8..ff8e3804996 100644 --- a/test/jdk/java/net/httpclient/http3/H3MultipleConnectionsToSameHost.java +++ b/test/jdk/java/net/httpclient/http3/H3MultipleConnectionsToSameHost.java @@ -77,7 +77,7 @@ */ /* * @test id=useNioSelector - * @bug 8087112 8372409 + * @bug 8087112 8372409 8386989 * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.http2.Http2TestServer From e28a58b48606c6bbb13c8fc9ad37225fca0e5442 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Mon, 29 Jun 2026 14:45:38 +0000 Subject: [PATCH 099/707] 8386656: C2 AVX512: -XX:-UseCountTrailingZerosInstruction causes assert(UseCountTrailingZerosInstruction) failed: tzcnt instruction not supported Reviewed-by: kvn, epeter, mhaessig, adinn --- src/hotspot/cpu/x86/x86.ad | 7 +++ .../TestUseCountTrailingZerosInstruction.java | 53 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/cpuflags/TestUseCountTrailingZerosInstruction.java diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index 370437edee2..df035f39f58 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -3179,6 +3179,13 @@ bool Matcher::match_rule_supported(int opcode) { break; case Op_VectorCmpMasked: + if (!UseCountTrailingZerosInstruction) { + return false; + } + if (UseAVX < 3 || !VM_Version::supports_bmi2()) { + return false; + } + break; case Op_VectorMaskGen: if (UseAVX < 3 || !VM_Version::supports_bmi2()) { return false; diff --git a/test/hotspot/jtreg/compiler/cpuflags/TestUseCountTrailingZerosInstruction.java b/test/hotspot/jtreg/compiler/cpuflags/TestUseCountTrailingZerosInstruction.java new file mode 100644 index 00000000000..0c7d04486f2 --- /dev/null +++ b/test/hotspot/jtreg/compiler/cpuflags/TestUseCountTrailingZerosInstruction.java @@ -0,0 +1,53 @@ +/* + * 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 8386656 + * @summary Verify no assertions when running with -XX:-UseCountTrailingZerosInstruction + * @requires os.simpleArch == "x64" + * @run main/othervm -Xbatch -XX:-UseCountTrailingZerosInstruction ${test.main.class} + */ + +/** + * @test + * @bug 8386656 + * @summary Verify no assertions when running with -XX:+UseCountTrailingZerosInstruction + * @requires os.simpleArch == "x64" + * @run main/othervm -Xbatch -XX:+UseCountTrailingZerosInstruction ${test.main.class} + */ + +package compiler.cpuflags; + +import java.util.Arrays; + +public class TestUseCountTrailingZerosInstruction { + public static void main(String[] args) { + byte[] a = new byte[32]; + byte[] b = new byte[32]; + for (int i = 0; i < 20_000; i++) { + Arrays.mismatch(a, b); + } + } +} + From 0a5b9d7fd4d2df6a3003f0586702f6d25d5c0559 Mon Sep 17 00:00:00 2001 From: Kieran Farrell Date: Mon, 29 Jun 2026 15:28:18 +0000 Subject: [PATCH 100/707] 8387273: Enhance httpserver logging to log when maxConnections is reached Reviewed-by: jpai, dfuchs, vyazici --- .../share/classes/sun/net/httpserver/ServerImpl.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java index 94fe78b9c64..3d77a61c0be 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java @@ -538,6 +538,8 @@ public void run() { if (MAX_CONNECTIONS > 0 && allConnections.size() >= MAX_CONNECTIONS) { // we've hit max limit of current open connections, so we go // ahead and close this connection without processing it + logger.log(Level.DEBUG, "connection limit reached, " + + "closing accepted connection " + chan); try { chan.close(); } catch (IOException ignore) { From db24b35a30cd28fda1f37d5e5e4a7241bdeeff3c Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Mon, 29 Jun 2026 16:27:54 +0000 Subject: [PATCH 101/707] 8387259: Clarify extlang in Locale composition description Reviewed-by: naoto, iris --- src/java.base/share/classes/java/util/Locale.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/util/Locale.java b/src/java.base/share/classes/java/util/Locale.java index f600afbb007..462cd5755c2 100644 --- a/src/java.base/share/classes/java/util/Locale.java +++ b/src/java.base/share/classes/java/util/Locale.java @@ -120,9 +120,13 @@ * {@code Locale} always canonicalizes to lower case. * *
Syntax: Well-formed {@code language} values have the form {@code [a-zA-Z]{2,8}}.
- *
BCP 47 deviation: this is not the full BCP 47 language production, since it excludes + *
BCP 47 deviation: {@code Locale} does not retain the * extlang - * (as modern three-letter language codes are preferred).
+ * subtag. This is because three-letter language codes are preferred over extlang + * subtags. When a {@code Locale} is created from a language tag containing an + * extlang subtag, the first extlang subtag is interpreted as the language + * field. The primary language subtag and any subsequent extlang subtags + * are ignored. * *
Example: "en" (English), "ja" (Japanese), "kok" (Konkani)
* From bc2fa43a6471cd04602f380e0f7a3974d829fa51 Mon Sep 17 00:00:00 2001 From: Mikhailo Seledtsov Date: Mon, 29 Jun 2026 16:45:57 +0000 Subject: [PATCH 102/707] 8387315: Add macosx-aarch64 bootcycle build profiles Reviewed-by: mikael, erikj --- make/conf/jib-profiles.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index b425c66a34c..32f07325c05 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -644,7 +644,7 @@ var getJibProfilesProfiles = function (input, common, data) { // Bootcycle profiles runs the build with itself as the boot jdk. This can // be done in two ways. Either using the builtin bootcycle target in the // build system. Or by supplying the main jdk build as bootjdk to configure. - [ "linux-x64", "macosx-x64", "windows-x64", "linux-aarch64" ] + [ "linux-x64", "macosx-aarch64", "macosx-x64", "windows-x64", "linux-aarch64" ] .forEach(function (name) { var bootcycleName = name + "-bootcycle"; var bootcyclePrebuiltName = name + "-bootcycle-prebuilt"; From 9d65845a6bc2183afc7f56fe9ebcdd3d2531fe6a Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Mon, 29 Jun 2026 17:00:20 +0000 Subject: [PATCH 103/707] 8387293: Shenandoah: Improve gc+stats logging for generational mode Reviewed-by: phh, xpeng --- .../gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp | 2 ++ src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp index 750022b274e..54ab4c27038 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp @@ -62,10 +62,12 @@ ShenandoahGenerationalEvacuationTask::ShenandoahGenerationalEvacuationTask(Shena void ShenandoahGenerationalEvacuationTask::work(uint worker_id) { if (_concurrent) { + ShenandoahWorkerTimingsTracker timer(ShenandoahPhaseTimings::conc_evac, ShenandoahPhaseTimings::Work, worker_id, true); ShenandoahConcurrentWorkerSession worker_session(worker_id); SuspendibleThreadSetJoiner stsj; do_work(); } else { + ShenandoahWorkerTimingsTracker timer(ShenandoahPhaseTimings::degen_gc_evac, ShenandoahPhaseTimings::Work, worker_id, true); ShenandoahParallelWorkerSession worker_session(worker_id); do_work(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp index e7638ed15c7..31129182380 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp @@ -722,10 +722,12 @@ class ShenandoahGenerationalUpdateHeapRefsTask : public WorkerTask { void work(uint worker_id) override { if (CONCURRENT) { + ShenandoahWorkerTimingsTracker timer(ShenandoahPhaseTimings::conc_update_refs, ShenandoahPhaseTimings::Work, worker_id, true); ShenandoahConcurrentWorkerSession worker_session(worker_id); SuspendibleThreadSetJoiner stsj; do_work(worker_id); } else { + ShenandoahWorkerTimingsTracker timer(ShenandoahPhaseTimings::degen_gc_update_refs, ShenandoahPhaseTimings::Work, worker_id, true); ShenandoahParallelWorkerSession worker_session(worker_id); do_work(worker_id); } From 58f118dd1c541a69b5c839609d026865a3365101 Mon Sep 17 00:00:00 2001 From: Volodymyr Paprotski Date: Mon, 29 Jun 2026 19:11:12 +0000 Subject: [PATCH 104/707] 8386911: Crypto benchmark regressions after JDK-8384353 Reviewed-by: weijun, semery --- .../com/sun/crypto/provider/ML_KEM.java | 2 +- .../classes/sun/security/provider/ML_DSA.java | 4 +-- .../sun/security/provider/SHA3Parallel.java | 27 ++++++++++++++++--- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java b/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java index 96a1eb686cc..5335357f8b9 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java @@ -858,7 +858,7 @@ private short[][][] generateA(byte[] rho, Boolean transposed) { allDone = false; while (!allDone) { allDone = true; - parXof.squeezeBlock(); + parXof.squeezeBlock(parInd); for (int k = 0; k < parInd; k++) { int parsedOfs = 0; int tmp; diff --git a/src/java.base/share/classes/sun/security/provider/ML_DSA.java b/src/java.base/share/classes/sun/security/provider/ML_DSA.java index 9c4e2c898b6..e1b41817435 100644 --- a/src/java.base/share/classes/sun/security/provider/ML_DSA.java +++ b/src/java.base/share/classes/sun/security/provider/ML_DSA.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -1184,7 +1184,7 @@ private int[][][] generateA(byte[] seed) { allDone = false; while (!allDone) { allDone = true; - parXof.squeezeBlock(); + parXof.squeezeBlock(parInd); for (int k = 0; k < parInd; k++) { int parsedOfs = 0; int tmp; diff --git a/src/java.base/share/classes/sun/security/provider/SHA3Parallel.java b/src/java.base/share/classes/sun/security/provider/SHA3Parallel.java index caf6a7a2899..0fcc91542fa 100644 --- a/src/java.base/share/classes/sun/security/provider/SHA3Parallel.java +++ b/src/java.base/share/classes/sun/security/provider/SHA3Parallel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 @@ -80,9 +80,28 @@ public void reset(byte[][] buffers) throws InvalidAlgorithmParameterException { } } - public int squeezeBlock() { - int retVal = quadKeccak(lanesArr[0], lanesArr[1], lanesArr[2], lanesArr[3]); - for (int i = 0; i < NRPAR; i++) { + public int squeezeBlock(int nr) throws InvalidAlgorithmParameterException { + int retVal = 0; + switch (nr) { + case 1: + // until we enable single keccak intrinsic, use the better + // doubleKeccak + case 2: + retVal = doubleKeccak(lanesArr[0], lanesArr[1]); + break; + case 3: + // until we enable single keccak intrinsic, use the better + // doubleKeccak/quadKeccak + case 4: + retVal = quadKeccak(lanesArr[0], lanesArr[1], lanesArr[2], + lanesArr[3]); + break; + default: + throw new InvalidAlgorithmParameterException( + "Bad parallel parameter."); + } + + for (int i = 0; i < nr; i++) { l2bLittle(lanesArr[i], 0, buffers[i], 0, blockSize); } return retVal; From c2348e645201b86eccae2646ec01d668a17c5271 Mon Sep 17 00:00:00 2001 From: Srinivas Vamsi Parasa Date: Mon, 29 Jun 2026 20:15:49 +0000 Subject: [PATCH 105/707] 8386448: Enable dumping of AVX registers (YMM/ZMM and K registers) in JVM fatal error logs Reviewed-by: kvn, drwhite, sviswanathan --- src/hotspot/cpu/x86/vm_version_x86.cpp | 19 +++ src/hotspot/cpu/x86/vm_version_x86.hpp | 11 ++ src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp | 132 +++++++++++++++- .../ErrorHandling/TestAVXRegisterDump.java | 142 ++++++++++++++++++ 4 files changed, 299 insertions(+), 5 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/ErrorHandling/TestAVXRegisterDump.java diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index 2ca1c172542..53696ee6ef3 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -489,6 +489,25 @@ class VM_Version_StubGenerator: public StubCodeGenerator { __ jmp(wrapup); __ bind(start_simd_check); + // Query CPUID 0xD sub-leaf 5, 6, and 7 offsets for AVX-512 XSAVE components + __ movl(rax, 0xD); + __ movl(rcx, 5); + __ cpuid(); + __ lea(rsi, Address(rbp, in_bytes(VM_Version::opmask_xstate_offset_offset()))); + __ movl(Address(rsi, 0), rbx); + + __ movl(rax, 0xD); + __ movl(rcx, 6); + __ cpuid(); + __ lea(rsi, Address(rbp, in_bytes(VM_Version::zmm0to15_hi256_xstate_offset_offset()))); + __ movl(Address(rsi, 0), rbx); + + __ movl(rax, 0xD); + __ movl(rcx, 7); + __ cpuid(); + __ lea(rsi, Address(rbp, in_bytes(VM_Version::zmm16to31_xstate_offset_offset()))); + __ movl(Address(rsi, 0), rbx); + // // Some OSs have a bug when upper 128/256bits of YMM/ZMM // registers are not restored after a signal processing. diff --git a/src/hotspot/cpu/x86/vm_version_x86.hpp b/src/hotspot/cpu/x86/vm_version_x86.hpp index 2fb1af71a10..d268665d091 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.hpp +++ b/src/hotspot/cpu/x86/vm_version_x86.hpp @@ -683,6 +683,11 @@ class VM_Version : public Abstract_VM_Version { uint32_t apx_xstate_size; // EAX: size of APX state (128) uint32_t apx_xstate_offset; // EBX: offset in standard XSAVE area + // cpuid function 0xD, subleaf 5, 6 and 7 (AVX-512 extended state) + uint32_t opmask_xstate_offset; // EBX: offset of Opmask component + uint32_t zmm0to15_hi256_xstate_offset; // EBX: offset of ZMM_Hi256 component + uint32_t zmm16to31_xstate_offset; // EBX: offset of Hi16_ZMM component + VM_Features feature_flags() const; // Asserts @@ -748,9 +753,15 @@ class VM_Version : public Abstract_VM_Version { static ByteSize apx_save_offset() { return byte_offset_of(CpuidInfo, apx_save); } static ByteSize apx_xstate_offset_offset() { return byte_offset_of(CpuidInfo, apx_xstate_offset); } static ByteSize apx_xstate_size_offset() { return byte_offset_of(CpuidInfo, apx_xstate_size); } + static ByteSize opmask_xstate_offset_offset() { return byte_offset_of(CpuidInfo, opmask_xstate_offset); } + static ByteSize zmm0to15_hi256_xstate_offset_offset() { return byte_offset_of(CpuidInfo, zmm0to15_hi256_xstate_offset); } + static ByteSize zmm16to31_xstate_offset_offset() { return byte_offset_of(CpuidInfo, zmm16to31_xstate_offset); } static uint32_t apx_xstate_offset() { return _cpuid_info.apx_xstate_offset; } static uint32_t apx_xstate_size() { return _cpuid_info.apx_xstate_size; } + static uint32_t opmask_xstate_offset() { return _cpuid_info.opmask_xstate_offset; } + static uint32_t zmm0to15_hi256_xstate_offset() { return _cpuid_info.zmm0to15_hi256_xstate_offset; } + static uint32_t zmm16to31_xstate_offset() { return _cpuid_info.zmm16to31_xstate_offset; } // The value used to check ymm register after signal handle static int ymm_test_value() { return 0xCAFEBABE; } diff --git a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp index 6750b71476b..25ee449d8b1 100644 --- a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp +++ b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp @@ -381,9 +381,22 @@ size_t os::Posix::default_stack_size(os::ThreadType thr_type) { ///////////////////////////////////////////////////////////////////////////// // helper functions for fatal error handler +// XSAVE Buffer Layout (Intel SDM Vol. 1, Section 13.4.1) +// Bytes 0-511: Legacy x87/FPU and SSE state (includes XMM0-15) +// Bytes 512-575: XSAVE Header (64 bytes) +// Bytes 576-831: YMMH state (upper 128 bits of YMM0-15) +// YMMH[i] at: buffer + 576 + (i * 16) +// Bytes 832+: Extended state components (e.g., AVX-512, APX, etc.). +// Component offsets and sizes are +// enumerated by CPUID.(EAX=0xD, ECX=n). // XSAVE constants - from Intel SDM Vol. 1, Chapter 13 #define XSAVE_HDR_OFFSET 512 +#define XSAVE_HDR_SIZE 64 #define XFEATURE_APX (1ULL << 19) +#define XFEATURE_YMM (1ULL << 2) +#define XFEATURE_OPMASK (1ULL << 5) +#define XFEATURE_ZMM_HI256 (1ULL << 6) +#define XFEATURE_HI16_ZMM (1ULL << 7) // XSAVE header structure // See: Intel SDM Vol. 1, Section 13.4.2 "XSAVE Header" @@ -417,6 +430,118 @@ static apx_state* get_apx_state(const ucontext_t* uc) { return (apx_state*)(xsave + offset); } +static void print_xmm_registers(outputStream* st, const ucontext_t* uc) { + for (int i = 0; i < 16; ++i) { + const uint64_t* xmm = (const uint64_t*)&uc->uc_mcontext.fpregs->_xmm[i]; + st->print_cr("XMM[%d]=" INTPTR_FORMAT " " INTPTR_FORMAT, i, xmm[1], xmm[0]); + } +} + +static void print_ymm_registers(outputStream* st, const ucontext_t* uc, bool has_ymm_hi128) { + const char* xsave = (const char*)uc->uc_mcontext.fpregs; + for (int i = 0; i < 16; ++i) { + const uint64_t* xmm = (const uint64_t*)&uc->uc_mcontext.fpregs->_xmm[i]; + uint64_t values[4] = {xmm[0], xmm[1], 0, 0}; + if (has_ymm_hi128) { + const uint64_t* ymmh = (const uint64_t*)(xsave + XSAVE_HDR_OFFSET + XSAVE_HDR_SIZE + (i * 16)); + values[2] = ymmh[0]; + values[3] = ymmh[1]; + } + st->print("YMM[%d]=", i); + for (int j = 3; j >= 0; --j) { + st->print("%s" INTPTR_FORMAT, (j == 3) ? "" : " ", values[j]); + } + st->cr(); + } +} + +static void print_zmm_registers(outputStream* st, const ucontext_t* uc, bool has_ymm_hi128, + bool has_zmm_hi256, bool has_hi16_zmm) { + const char* xsave = (const char*)uc->uc_mcontext.fpregs; + + for (int i = 0; i < 32; ++i) { + uint64_t values[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + if (i < 16) { + const uint64_t* xmm = (const uint64_t*)&uc->uc_mcontext.fpregs->_xmm[i]; + values[0] = xmm[0]; + values[1] = xmm[1]; + + if (has_ymm_hi128) { + const uint64_t* ymmh = (const uint64_t*)(xsave + XSAVE_HDR_OFFSET + XSAVE_HDR_SIZE + (i * 16)); + values[2] = ymmh[0]; + values[3] = ymmh[1]; + } + + if (has_zmm_hi256) { + const uint32_t zmm_hi256_offset = VM_Version::zmm0to15_hi256_xstate_offset(); + const uint64_t* zmm_hi256 = (const uint64_t*)(xsave + zmm_hi256_offset + (i * 32)); + values[4] = zmm_hi256[0]; + values[5] = zmm_hi256[1]; + values[6] = zmm_hi256[2]; + values[7] = zmm_hi256[3]; + } + } else if (has_hi16_zmm) { + const uint32_t hi16_zmm_offset = VM_Version::zmm16to31_xstate_offset(); + const uint64_t* zmm = (const uint64_t*)(xsave + hi16_zmm_offset + ((i - 16) * 64)); + values[0] = zmm[0]; + values[1] = zmm[1]; + values[2] = zmm[2]; + values[3] = zmm[3]; + values[4] = zmm[4]; + values[5] = zmm[5]; + values[6] = zmm[6]; + values[7] = zmm[7]; + } + + st->print("ZMM[%d]=", i); + for (int j = 7; j >= 0; --j) { + st->print("%s" INTPTR_FORMAT, (j == 7) ? "" : " ", values[j]); + } + st->cr(); + } +} + +static void print_kmask_registers(outputStream* st, const ucontext_t* uc, bool has_opmask) { + const uint32_t opmask_offset = VM_Version::opmask_xstate_offset(); + if (!has_opmask || opmask_offset == 0) { + return; + } + + const char* xsave = (const char*)uc->uc_mcontext.fpregs; + const uint64_t* kmask = (const uint64_t*)(xsave + opmask_offset); + + for (int i = 0; i < 8; ++i) { + st->print_cr("K[%d]=" INTPTR_FORMAT, i, kmask[i]); + } + st->cr(); +} + +static void print_vector_registers(outputStream* st, const ucontext_t* uc) { + if (uc->uc_mcontext.fpregs == nullptr) { + return; + } + + if (UseAVX < 2) { + return print_xmm_registers(st, uc); + } + + const char* xsave = (const char*)uc->uc_mcontext.fpregs; + const uint64_t* xstate_hdr_ptr = (const uint64_t*)(xsave + XSAVE_HDR_OFFSET); + const uint64_t xsave_state_bitmap = xstate_hdr_ptr[0]; + const bool has_ymm_hi128 = (xsave_state_bitmap & XFEATURE_YMM) != 0; + const bool has_opmask = (xsave_state_bitmap & XFEATURE_OPMASK) != 0; + const bool has_zmm_hi256 = (xsave_state_bitmap & XFEATURE_ZMM_HI256) != 0; + const bool has_hi16_zmm = (xsave_state_bitmap & XFEATURE_HI16_ZMM) != 0; + const bool should_print_zmm_registers = (UseAVX > 2) && (has_zmm_hi256 || has_hi16_zmm); + + if (!should_print_zmm_registers) { + return print_ymm_registers(st, uc, has_ymm_hi128); + } + + print_kmask_registers(st, uc, has_opmask); + print_zmm_registers(st, uc, has_ymm_hi128, has_zmm_hi256, has_hi16_zmm); +} void os::print_context(outputStream *st, const void *context) { if (context == nullptr) return; @@ -458,7 +583,7 @@ void os::print_context(outputStream *st, const void *context) { st->print(", ERR=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[REG_ERR]); st->cr(); st->print(" TRAPNO=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[REG_TRAPNO]); - // Add XMM registers + MXCSR. Note that C2 uses XMM to spill GPR values including pointers. + // Add vector registers + MXCSR. Note that C2 uses XMM to spill GPR values including pointers. st->cr(); st->cr(); // Sanity check: fpregs should point into the context. @@ -467,10 +592,7 @@ void os::print_context(outputStream *st, const void *context) { st->print_cr("bad uc->uc_mcontext.fpregs: " INTPTR_FORMAT " (uc: " INTPTR_FORMAT ")", p2i(uc->uc_mcontext.fpregs), p2i(uc)); } else { - for (int i = 0; i < 16; ++i) { - const int64_t* xmm_val_addr = (int64_t*)&(uc->uc_mcontext.fpregs->_xmm[i]); - st->print_cr("XMM[%d]=" INTPTR_FORMAT " " INTPTR_FORMAT, i, xmm_val_addr[1], xmm_val_addr[0]); - } + print_vector_registers(st, uc); st->print(" MXCSR=" UINT32_FORMAT_X_0, uc->uc_mcontext.fpregs->mxcsr); } st->cr(); diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/TestAVXRegisterDump.java b/test/hotspot/jtreg/runtime/ErrorHandling/TestAVXRegisterDump.java new file mode 100644 index 00000000000..1f2fec74fee --- /dev/null +++ b/test/hotspot/jtreg/runtime/ErrorHandling/TestAVXRegisterDump.java @@ -0,0 +1,142 @@ +/* + * 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 that YMM and ZMM registers are correctly dumped in hs_err for different UseAVX settings + * @library /test/lib + * @requires os.family == "linux" & os.arch == "amd64" + * @requires vm.debug == true + * @modules java.base/jdk.internal.misc + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run driver TestAVXRegisterDump + */ + +// Note: this test can only run on debug since it relies on VMError::controlled_crash() which +// only exists in debug builds. + +import java.io.File; +import java.util.regex.Pattern; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.whitebox.WhiteBox; + +public class TestAVXRegisterDump { + + public static void main(String[] args) throws Exception { + + if (args.length > 0 && args[0].equals("crash")) { + WhiteBox.getWhiteBox().controlledCrash(2); + throw new RuntimeException("Still alive?"); + } + + // Test UseAVX=1 (XMM only) + testWithUseAVX(1); + + // Test UseAVX=2 (YMM) + testWithUseAVX(2); + + // Test UseAVX=3 (ZMM + K masks if available) + testWithUseAVX(3); + } + + static void testWithUseAVX(int useAVX) throws Exception { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-Xbootclasspath/a:.", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-XX:UseAVX=" + useAVX, + "-XX:-CreateCoredumpOnCrash", + "-Xmx100M", + TestAVXRegisterDump.class.getName(), "crash"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldMatch("# A fatal error has been detected by the Java Runtime Environment:.*"); + + File hsErrFile = HsErrFileUtils.openHsErrFileFromOutput(output); + validateRegisterContent(hsErrFile, useAVX); + } + + static Pattern[] createRegisterPatterns(String regType, int count) { + Pattern[] patterns = new Pattern[count]; + for (int i = 0; i < count; i++) { + // Create regex pattern to match entire register line (e.g., "XMM[0]=0xHEX 0xHEX") + // Used with Matcher.matches() which requires matching the entire line + patterns[i] = Pattern.compile(regType + "\\[" + i + "\\]=.*"); + } + return patterns; + } + + static void validateRegisterContent(File hsErrFile, int useAVX) throws Exception { + if (useAVX == 1) { + validateRegistersUseAVX1(hsErrFile); + } else if (useAVX == 2) { + validateRegistersUseAVX2(hsErrFile); + } else if (useAVX == 3) { + validateRegistersUseAVX3(hsErrFile); + } + } + + static void validateRegistersUseAVX1(File hsErrFile) throws Exception { + // UseAVX=1: XMM registers only (0-15) + Pattern[] positivePatterns = createRegisterPatterns("XMM", 16); + Pattern[] negativePatterns = new Pattern[] { + Pattern.compile("YMM\\[.*\\]=.*"), + Pattern.compile("ZMM\\[.*\\]=.*"), + }; + HsErrFileUtils.checkHsErrFileContent(hsErrFile, positivePatterns, negativePatterns, false, false); + } + + static void validateRegistersUseAVX2(File hsErrFile) throws Exception { + // UseAVX=2: YMM registers only (0-15) + Pattern[] positivePatterns = createRegisterPatterns("YMM", 16); + Pattern[] negativePatterns = new Pattern[] { + Pattern.compile("XMM\\[.*\\]=.*"), + Pattern.compile("ZMM\\[.*\\]=.*"), + }; + HsErrFileUtils.checkHsErrFileContent(hsErrFile, positivePatterns, negativePatterns, false, false); + } + + static void validateRegistersUseAVX3(File hsErrFile) throws Exception { + // UseAVX=3: ZMM + K masks (if available) or fallback to YMM + // Try ZMM first, then fallback to YMM if CPU doesn't support AVX-512 + try { + Pattern[] zmmPatterns = createRegisterPatterns("ZMM", 32); + Pattern[] zmmNegativePatterns = new Pattern[] { + Pattern.compile("XMM\\[.*\\]=.*"), + }; + HsErrFileUtils.checkHsErrFileContent(hsErrFile, zmmPatterns, zmmNegativePatterns, false, false); + + Pattern[] kPatterns = createRegisterPatterns("K", 8); + HsErrFileUtils.checkHsErrFileContent(hsErrFile, kPatterns, null, false, false); + } catch (RuntimeException e) { + // If ZMM not found, try YMM + Pattern[] ymmPatterns = createRegisterPatterns("YMM", 16); + Pattern[] ymmNegativePatterns = new Pattern[] { + Pattern.compile("XMM\\[.*\\]=.*"), + }; + HsErrFileUtils.checkHsErrFileContent(hsErrFile, ymmPatterns, ymmNegativePatterns, false, false); + } + } +} From 57f988d31ceaec97ded8de08b777daff01840e88 Mon Sep 17 00:00:00 2001 From: Kelvin Nilsen Date: Mon, 29 Jun 2026 20:32:45 +0000 Subject: [PATCH 106/707] 8386910: Shenandoah: remove redundant logging of free set status Reviewed-by: wkemper, xpeng, ruili --- src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index 24748bdaab3..eddeca57fd1 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2021, 2022, Red Hat, Inc. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -204,8 +204,6 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { // we will not age young-gen objects in the case that we skip evacuation. entry_cleanup_early(); - heap->free_set()->log_status_under_lock(); - // Processing strong roots // This may be skipped if there is nothing to update/evacuate. // If so, strong_root_in_progress would be unset. From 299a42b3ce51e093a77ab4691e125bc1a12ec474 Mon Sep 17 00:00:00 2001 From: Jorn Vernee Date: Mon, 29 Jun 2026 21:50:33 +0000 Subject: [PATCH 107/707] 8383882: javac: incremental compilation using --module misses classes Reviewed-by: jlahoda, mcimadamore, vromero --- .../com/sun/tools/javac/main/Arguments.java | 35 +-- .../tools/javac/resources/javac.properties | 2 +- .../IncrementalComp/TestIncrementalComp.java | 293 ++++++++++++++++++ .../tools/javac/modules/MOptionTest.java | 36 +-- 4 files changed, 327 insertions(+), 39 deletions(-) create mode 100644 test/langtools/tools/javac/IncrementalComp/TestIncrementalComp.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java index 58beee78af2..9a0b75a3aa4 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java @@ -32,7 +32,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; -import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -278,12 +277,8 @@ public void init(String ownName) { */ public Set getFileObjects() { if (fileObjects == null) { - fileObjects = new LinkedHashSet<>(); - } - if (files != null) { - JavacFileManager jfm = (JavacFileManager) getFileManager(); - for (JavaFileObject fo: jfm.getJavaFileObjectsFromPaths(files)) - fileObjects.add(fo); + // see Arguments::validate + throw new IllegalStateException("file objects have not been initialized"); } return fileObjects; } @@ -421,6 +416,9 @@ private boolean doProcessArgs(Iterable args, */ public boolean validate() { JavaFileManager fm = getFileManager(); + if (fileObjects == null) { + fileObjects = new LinkedHashSet<>(); + } if (options.isSet(Option.MODULE)) { if (!fm.hasLocation(StandardLocation.CLASS_OUTPUT)) { log.error(Errors.OutputDirMustBeSpecifiedWithDashMOption); @@ -433,19 +431,10 @@ public boolean validate() { Location sourceLoc = fm.getLocationForModule(StandardLocation.MODULE_SOURCE_PATH, module); if (sourceLoc == null) { log.error(Errors.ModuleNotFoundInModuleSourcePath(module)); - } else { - Location classLoc = fm.getLocationForModule(StandardLocation.CLASS_OUTPUT, module); - - for (JavaFileObject file : fm.list(sourceLoc, "", EnumSet.of(JavaFileObject.Kind.SOURCE), true)) { - String className = fm.inferBinaryName(sourceLoc, file); - JavaFileObject classFile = fm.getJavaFileForInput(classLoc, className, Kind.CLASS); - - if (classFile == null || classFile.getLastModified() < file.getLastModified()) { - if (fileObjects == null) - fileObjects = new HashSet<>(); - fileObjects.add(file); - } - } + return false; + } + for (JavaFileObject file : fm.list(sourceLoc, "", EnumSet.of(Kind.SOURCE), true)) { + fileObjects.add(file); } } } catch (IOException ex) { @@ -455,6 +444,12 @@ public boolean validate() { } } } + if (files != null) { + JavacFileManager jfm = (JavacFileManager) getFileManager(); + for (JavaFileObject fo : jfm.getJavaFileObjectsFromPaths(files)){ + fileObjects.add(fo); + } + } if (isEmpty()) { // It is allowed to compile nothing if just asking for help or version info. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties index 7824772b1f3..d835c639827 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties @@ -48,7 +48,7 @@ javac.opt.modulepath=\ javac.opt.sourcepath=\ Specify where to find input source files javac.opt.m=\ - Compile only the specified module(s), check timestamps + Compile only the specified module(s) javac.opt.modulesourcepath=\ Specify where to find input source files for multiple modules javac.opt.bootclasspath=\ diff --git a/test/langtools/tools/javac/IncrementalComp/TestIncrementalComp.java b/test/langtools/tools/javac/IncrementalComp/TestIncrementalComp.java new file mode 100644 index 00000000000..2be04e98c2e --- /dev/null +++ b/test/langtools/tools/javac/IncrementalComp/TestIncrementalComp.java @@ -0,0 +1,293 @@ +/* + * 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 javac incremental compilation with modules + * @run junit TestIncrementalComp + */ + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.module.Configuration; +import java.lang.module.ModuleFinder; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.spi.ToolProvider; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.nio.file.StandardOpenOption.CREATE_NEW; +import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class TestIncrementalComp { + + static final ToolProvider JAVAC = ToolProvider.findFirst("javac") + .orElseThrow(); + + record TestCase(String srcDir, Map sources, Set modules, String mainModule, Map addReadsEdges) { + TestCase(String srcDir, Map sources, Set modules, String mainModule) { + this(srcDir, sources, modules, mainModule, Map.of()); + } + } + + @ParameterizedTest + @MethodSource("cases") + public void test(TestCase testCase) throws Throwable { + Path workDir = Path.of(testCase.srcDir()); + // set up test sources + Path localTestModules = workDir.resolve("test_modules"); + Path outDir = workDir.resolve("mods"); + for (Map.Entry sourceFile : testCase.sources().entrySet()) { + Path filePath = localTestModules.resolve(sourceFile.getKey()); + Files.createDirectories(filePath.getParent()); + Files.writeString(filePath, sourceFile.getValue(), CREATE_NEW); + } + + Path libPath = localTestModules.resolve(LIB_PATH); + Files.createDirectories(libPath.getParent()); + Files.writeString(libPath, ALT_LIB_INT, CREATE_NEW); + + List javacCommand = new ArrayList<>(List.of( + "-d", outDir.toString(), + "--module-source-path=" + localTestModules, + "--module", String.join(",", testCase.modules()) + )); + for (Map.Entry addReads : testCase.addReadsEdges().entrySet()) { + String reader = addReads.getKey(); + String read = addReads.getValue(); + javacCommand.add(String.format("--add-reads=%s=%s", reader, read)); + } + // compile both modules + compile(javacCommand); + + String mainClass = testCase.mainModule() + ".app.Main"; + invokeMainMethod(outDir, testCase.mainModule(), mainClass, testCase.addReadsEdges()); + + // modify sources. Dep is not modified + Files.writeString(libPath, ALT_LIB_LONG, TRUNCATE_EXISTING); + + // recompile. Any dependency on the changed file should be recompiled as well + compile(javacCommand); + + // should work + // if this fails because of incremental compilation issues, we can expect to see a NoSuchMethodError + invokeMainMethod(outDir, testCase.mainModule(), mainClass, testCase.addReadsEdges()); + } + + private static void invokeMainMethod(Path modulePath, String moduleName, String mainClassName, + Map addReadsEdges) + throws ReflectiveOperationException { + // define module layer + // note that we need to explicitly add any read module to the set of roots + ModuleLayer boot = ModuleLayer.boot(); + Set allRoots = Stream.concat(Stream.of(moduleName), addReadsEdges.values().stream()) + .collect(Collectors.toSet()); + Configuration config = boot.configuration() + .resolve(ModuleFinder.of(modulePath), ModuleFinder.of(), allRoots); + ModuleLayer.Controller controller = ModuleLayer.defineModulesWithOneLoader( + config, List.of(boot), ClassLoader.getSystemClassLoader()); + + // add extra reads edges + for (Map.Entry addReads : addReadsEdges.entrySet()) { + Module reader = controller.layer().findModule(addReads.getKey()).orElseThrow(); + Module read = controller.layer().findModule(addReads.getValue()).orElseThrow(); + controller.addReads(reader, read); + } + + // invoke main + Class main1 = controller.layer().findLoader(moduleName).loadClass(mainClassName); + Method m = main1.getMethod("main", String[].class); + m.invoke(null, new Object[]{ new String[0] }); + } + + private static void compile(List args) { + System.err.println("compile: " + args); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + int rc = JAVAC.run(pw, pw, args.toArray(String[]::new)); + pw.close(); + System.err.println(sw); + assertEquals(0, rc); + } + + private static final Path LIB_PATH = Path.of("org.moda/org/moda/lib/Lib.java"); + + private static final String ALT_LIB_INT = """ + package org.moda.lib; + public class Lib { + public static int getVal() { + return 42; + } + } + """; + + private static final String ALT_LIB_LONG = """ + package org.moda.lib; + public class Lib { + public static long getVal() { + return 42; + } + } + """; + + static Stream cases() { + return Stream.of( + new TestCase("single", Map.of( + Path.of("org.moda/module-info.java"), + """ + module org.moda { + // for reflective access + exports org.moda.app; + } + """, + Path.of("org.moda/org/moda/lib/Dep.java"), + """ + package org.moda.lib; + + public class Dep { + public static long getVal() { + return Lib.getVal(); + } + } + """, + Path.of("org.moda/org/moda/app/Main.java"), + """ + package org.moda.app; + + import org.moda.lib.Dep; + + public class Main { + public static void main(String[] args) { + System.out.println(Dep.getVal()); + } + } + """ + ), Set.of("org.moda"), "org.moda"), + new TestCase("multi", Map.of( + Path.of("org.moda/module-info.java"), + """ + module org.moda { + exports org.moda.lib; + } + """, + Path.of("org.modb/module-info.java"), + """ + module org.modb { + requires org.moda; + + // for reflective access + exports org.modb.app; + } + """, + Path.of("org.modb/org/modb/app/Main.java"), + """ + package org.modb.app; + + import org.moda.lib.Lib; + + public class Main { + public static void main(String[] args) { + System.out.println(Lib.getVal()); + } + } + """ + ), Set.of("org.moda", "org.modb"), "org.modb"), + new TestCase("transitive", Map.of( + Path.of("org.moda/module-info.java"), + """ + module org.moda { + exports org.moda.lib; + } + + """, + Path.of("org.modb/module-info.java"), + """ + module org.modb { + // for org.modc + requires transitive org.moda; + } + """, + Path.of("org.modc/module-info.java"), + """ + module org.modc { + requires org.modb; + + // for reflective access + exports org.modc.app; + } + """, + Path.of("org.modc/org/modc/app/Main.java"), + """ + package org.modc.app; + + import org.moda.lib.Lib; + + public class Main { + public static void main(String[] args) { + System.out.println(Lib.getVal()); + } + } + """ + ), Set.of("org.moda", "org.modb", "org.modc"), "org.modc"), + new TestCase("add_reads", Map.of( + Path.of("org.moda/module-info.java"), + """ + module org.moda { + exports org.moda.lib; + } + """, + Path.of("org.modb/module-info.java"), + """ + module org.modb { + // no explicit requires + + // for reflective access + exports org.modb.app; + } + """, + Path.of("org.modb/org/modb/app/Main.java"), + """ + package org.modb.app; + + import org.moda.lib.Lib; + + public class Main { + public static void main(String[] args) { + System.out.println(Lib.getVal()); + } + } + """ + ), Set.of("org.moda", "org.modb"), "org.modb", Map.of("org.modb", "org.moda")) + ); + } + } diff --git a/test/langtools/tools/javac/modules/MOptionTest.java b/test/langtools/tools/javac/modules/MOptionTest.java index ff08960b0bc..470815a9493 100644 --- a/test/langtools/tools/javac/modules/MOptionTest.java +++ b/test/langtools/tools/javac/modules/MOptionTest.java @@ -84,12 +84,12 @@ public void testOneModule(Path base) throws Exception { .run(Task.Expect.SUCCESS) .writeAll(); - if (!moduleInfoTimeStamp.equals(Files.getLastModifiedTime(moduleInfoClass))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(moduleInfoClass).compareTo(moduleInfoTimeStamp) <= 0) { + throw new AssertionError("Classfile too old!"); } - if (!testTestTimeStamp.equals(Files.getLastModifiedTime(testTestClass))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(testTestClass).compareTo(Files.getLastModifiedTime(testTest)) < 0) { + throw new AssertionError("Classfiles too old!"); } // Date back the source file by one second compared to the current time. @@ -102,8 +102,8 @@ public void testOneModule(Path base) throws Exception { .run(Task.Expect.SUCCESS) .writeAll(); - if (!moduleInfoTimeStamp.equals(Files.getLastModifiedTime(moduleInfoClass))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(moduleInfoClass).compareTo(moduleInfoTimeStamp) <= 0) { + throw new AssertionError("Classfile too old!"); } if (Files.getLastModifiedTime(testTestClass).compareTo(Files.getLastModifiedTime(testTest)) < 0) { @@ -219,20 +219,20 @@ public void testMultiModule(Path base) throws Exception { .run(Task.Expect.SUCCESS) .writeAll(); - if (!m1ModuleInfoTimeStamp.equals(Files.getLastModifiedTime(m1ModuleInfoClass))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(m1ModuleInfoClass).compareTo(m1ModuleInfoTimeStamp) <= 0) { + throw new AssertionError("Classfile too old!"); } - if (!m2ModuleInfoTimeStamp.equals(Files.getLastModifiedTime(m2ModuleInfoClass))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(m2ModuleInfoClass).compareTo(m2ModuleInfoTimeStamp) <= 0) { + throw new AssertionError("Classfile too old!"); } - if (!C1TimeStamp.equals(Files.getLastModifiedTime(classC1))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(classC1).compareTo(Files.getLastModifiedTime(C1Source)) < 0) { + throw new AssertionError("Classfiles too old!"); } - if (!C2TimeStamp.equals(Files.getLastModifiedTime(classC2))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(classC2).compareTo(Files.getLastModifiedTime(C2Source)) < 0) { + throw new AssertionError("Classfiles too old!"); } // Date back the source file by one second compared to the current time. @@ -246,12 +246,12 @@ public void testMultiModule(Path base) throws Exception { .run(Task.Expect.SUCCESS) .writeAll(); - if (!m1ModuleInfoTimeStamp.equals(Files.getLastModifiedTime(m1ModuleInfoClass))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(m1ModuleInfoClass).compareTo(m1ModuleInfoTimeStamp) <= 0) { + throw new AssertionError("Classfile too old!"); } - if (!m2ModuleInfoTimeStamp.equals(Files.getLastModifiedTime(m2ModuleInfoClass))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(m2ModuleInfoClass).compareTo(m2ModuleInfoTimeStamp) <= 0) { + throw new AssertionError("Classfile too old!"); } if (Files.getLastModifiedTime(classC1).compareTo(Files.getLastModifiedTime(C1Source)) < 0) { From e4cd94459082586237c998cece527163300a758c Mon Sep 17 00:00:00 2001 From: Jorn Vernee Date: Mon, 29 Jun 2026 21:52:37 +0000 Subject: [PATCH 108/707] 8387406: ProblemList java/foreign/normalize/TestNormalize.java Reviewed-by: liach, jpai --- test/jdk/ProblemList.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index fcde1d9c01d..5e730af92b0 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -648,6 +648,8 @@ jdk/jfr/event/oldobject/TestZ.java 8375615 generic- # jdk_foreign +java/foreign/normalize/TestNormalize.java 8386848 generic-all + ############################################################################ # Client manual tests From f232f552af2df3ef5190438a4bb22f0d7ee42dd2 Mon Sep 17 00:00:00 2001 From: Michael Reeves Date: Tue, 30 Jun 2026 04:39:30 +0000 Subject: [PATCH 109/707] 8379327: 128-bit multiplication uses two multiply instructions on x86_64 Reviewed-by: dlong, sviswanathan --- src/hotspot/cpu/aarch64/aarch64.ad | 14 +-- src/hotspot/cpu/arm/arm.ad | 16 +-- src/hotspot/cpu/ppc/ppc.ad | 16 +-- src/hotspot/cpu/riscv/riscv.ad | 14 +-- src/hotspot/cpu/s390/s390.ad | 16 +-- src/hotspot/cpu/x86/x86.ad | 44 +++++-- src/hotspot/share/opto/classes.hpp | 2 + src/hotspot/share/opto/compile.cpp | 30 ++++- src/hotspot/share/opto/compile.hpp | 1 + src/hotspot/share/opto/divnode.cpp | 54 ++++---- src/hotspot/share/opto/divnode.hpp | 28 +---- src/hotspot/share/opto/matcher.hpp | 18 +-- src/hotspot/share/opto/mulnode.cpp | 32 +++++ src/hotspot/share/opto/mulnode.hpp | 29 ++++- src/hotspot/share/opto/multnode.hpp | 28 +++++ src/hotspot/share/opto/node.cpp | 25 +++- src/hotspot/share/opto/node.hpp | 2 +- .../c2/TestMultiplyHighLowFusion.java | 117 ++++++++++++++++++ .../library/Operations.java | 4 + .../vm/compiler/MultiplyHighLowFusion.java | 116 +++++++++++++++++ 20 files changed, 491 insertions(+), 115 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/c2/TestMultiplyHighLowFusion.java create mode 100644 test/micro/org/openjdk/bench/vm/compiler/MultiplyHighLowFusion.java diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 49f3419dfb6..05e4321b663 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -2512,25 +2512,25 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? _FLOAT_REG_mask.size() : FLOATPRESSURE; } -const RegMask& Matcher::divI_proj_mask() { +const RegMask& Matcher::firstI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for MODI projection of divmodI. -const RegMask& Matcher::modI_proj_mask() { +// Register for the second projection of an int pair +const RegMask& Matcher::secondI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for DIVL projection of divmodL. -const RegMask& Matcher::divL_proj_mask() { +// Register for the first projection of a long pair +const RegMask& Matcher::firstL_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for MODL projection of divmodL. -const RegMask& Matcher::modL_proj_mask() { +// Register for the second projection of a long pair +const RegMask& Matcher::secondL_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } diff --git a/src/hotspot/cpu/arm/arm.ad b/src/hotspot/cpu/arm/arm.ad index 45ae283e05a..7ae3381600e 100644 --- a/src/hotspot/cpu/arm/arm.ad +++ b/src/hotspot/cpu/arm/arm.ad @@ -1112,26 +1112,26 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? 30 : FLOATPRESSURE; } -// Register for DIVI projection of divmodI -const RegMask& Matcher::divI_proj_mask() { +// Register for the first projection of an int pair +const RegMask& Matcher::firstI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for MODI projection of divmodI -const RegMask& Matcher::modI_proj_mask() { +// Register for the second projection of an int pair +const RegMask& Matcher::secondI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for DIVL projection of divmodL -const RegMask& Matcher::divL_proj_mask() { +// Register for the first projection of a long pair +const RegMask& Matcher::firstL_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for MODL projection of divmodL -const RegMask& Matcher::modL_proj_mask() { +// Register for the second projection of a long pair +const RegMask& Matcher::secondL_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 3cdc820b5f9..e7464feb4ab 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -2351,26 +2351,26 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? 28 : FLOATPRESSURE; } -// Register for DIVI projection of divmodI. -const RegMask& Matcher::divI_proj_mask() { +// Register for the first projection of an int pair +const RegMask& Matcher::firstI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for MODI projection of divmodI. -const RegMask& Matcher::modI_proj_mask() { +// Register for the second projection of an int pair +const RegMask& Matcher::secondI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for DIVL projection of divmodL. -const RegMask& Matcher::divL_proj_mask() { +// Register for the first projection of a long pair +const RegMask& Matcher::firstL_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for MODL projection of divmodL. -const RegMask& Matcher::modL_proj_mask() { +// Register for the second projection of a long pair +const RegMask& Matcher::secondL_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 0c077dc84a3..7bfff4b2086 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -2100,25 +2100,25 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? _FLOAT_REG_mask.size() : FLOATPRESSURE; } -const RegMask& Matcher::divI_proj_mask() { +const RegMask& Matcher::firstI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for MODI projection of divmodI. -const RegMask& Matcher::modI_proj_mask() { +// Register for the second projection of an int pair +const RegMask& Matcher::secondI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for DIVL projection of divmodL. -const RegMask& Matcher::divL_proj_mask() { +// Register for the first projection of a long pair +const RegMask& Matcher::firstL_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for MODL projection of divmodL. -const RegMask& Matcher::modL_proj_mask() { +// Register for the second projection of a long pair +const RegMask& Matcher::secondL_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index 2208a197ac9..c0e51bd2bfd 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -1929,23 +1929,23 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? 15 : FLOATPRESSURE; } -// Register for DIVI projection of divmodI -const RegMask& Matcher::divI_proj_mask() { +// Register for the first projection of an int pair +const RegMask& Matcher::firstI_proj_mask() { return _Z_RARG4_INT_REG_mask; } -// Register for MODI projection of divmodI -const RegMask& Matcher::modI_proj_mask() { +// Register for the second projection of an int pair +const RegMask& Matcher::secondI_proj_mask() { return _Z_RARG3_INT_REG_mask; } -// Register for DIVL projection of divmodL -const RegMask& Matcher::divL_proj_mask() { +// Register for the first projection of a long pair +const RegMask& Matcher::firstL_proj_mask() { return _Z_RARG4_LONG_REG_mask; } -// Register for MODL projection of divmodL -const RegMask& Matcher::modL_proj_mask() { +// Register for the second projection of a long pair +const RegMask& Matcher::secondL_proj_mask() { return _Z_RARG3_LONG_REG_mask; } diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index df035f39f58..3f953dbe725 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -2764,23 +2764,23 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? default_float_pressure_threshold : FLOATPRESSURE; } -// Register for DIVI projection of divmodI -const RegMask& Matcher::divI_proj_mask() { +// Register for the first projection of an int pair +const RegMask& Matcher::firstI_proj_mask() { return INT_RAX_REG_mask(); } -// Register for MODI projection of divmodI -const RegMask& Matcher::modI_proj_mask() { +// Register for the second projection of an int pair +const RegMask& Matcher::secondI_proj_mask() { return INT_RDX_REG_mask(); } -// Register for DIVL projection of divmodL -const RegMask& Matcher::divL_proj_mask() { +// Register for the first projection of a long pair +const RegMask& Matcher::firstL_proj_mask() { return LONG_RAX_REG_mask(); } -// Register for MODL projection of divmodL -const RegMask& Matcher::modL_proj_mask() { +// Register for the second projection of a long pair +const RegMask& Matcher::secondL_proj_mask() { return LONG_RDX_REG_mask(); } @@ -11379,6 +11379,34 @@ instruct mulL_mem_imm(rRegL dst, memory src, immL32 imm, rFlagsReg cr) ins_pipe(ialu_reg_mem_alu0); %} +instruct mulHiLoL_rReg(rax_RegL rax, rdx_RegL rdx, rRegL src, rFlagsReg cr) +%{ + match(MulHiLoL src rax); + match(MulHiLoL rax src); + effect(KILL cr); + + ins_cost(300); + format %{ "imulq RDX:RAX, RAX, $src\t# mulhilo" %} + ins_encode %{ + __ imulq($src$$Register); + %} + ins_pipe(ialu_reg_reg_alu0); +%} + +instruct umulHiLoL_rReg(rax_RegL rax, rdx_RegL rdx, rRegL src, rFlagsReg cr) +%{ + match(UMulHiLoL src rax); + match(UMulHiLoL rax src); + effect(KILL cr); + + ins_cost(300); + format %{ "mulq RDX:RAX, RAX, $src\t# umulhilo" %} + ins_encode %{ + __ mulq($src$$Register); + %} + ins_pipe(ialu_reg_reg_alu0); +%} + instruct mulHiL_rReg(rdx_RegL dst, rRegL src, rax_RegL rax, rFlagsReg cr) %{ match(Set dst (MulHiL src rax)); diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index 7033dad211c..4d06e20875a 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -268,6 +268,8 @@ macro(MulD) macro(MulF) macro(MulHiL) macro(UMulHiL) +macro(MulHiLoL) +macro(UMulHiLoL) macro(MulI) macro(MulL) macro(Multi) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index a273bb6053e..1f51cdc1d39 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -3276,8 +3276,8 @@ void Compile::handle_div_mod_op(Node* n, BasicType bt, bool is_unsigned) { // DivMod node so the dependency is not lost. divmod->add_prec_from(n); divmod->add_prec_from(d); - d->subsume_by(divmod->div_proj(), this); - n->subsume_by(divmod->mod_proj(), this); + d->subsume_by(divmod->first_proj(), this); + n->subsume_by(divmod->second_proj(), this); } else { // Replace "a % b" with "a - ((a / b) * b)" Node* mult = MulNode::make(d, d->in(2), bt); @@ -3286,6 +3286,24 @@ void Compile::handle_div_mod_op(Node* n, BasicType bt, bool is_unsigned) { } } +void Compile::handle_mulhi_mul_op(Node* n, bool is_unsigned) { + const int fused_opcode = is_unsigned ? Op_UMulHiLoL : Op_MulHiLoL; + if (!Matcher::has_match_rule(fused_opcode)) { + return; + } + + Node* mul = n->find_similar(Op_MulL, true); + + if (mul == nullptr) { + return; + } + + MulHiLoLNode* mul_hi_lo = is_unsigned ? static_cast(UMulHiLoLNode::make(n)) + : MulHiLoLNode::make(n); + mul->subsume_by(mul_hi_lo->first_proj(), this); + n->subsume_by(mul_hi_lo->second_proj(), this); +} + void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop, Unique_Node_List& dead_nodes) { switch( nop ) { case Op_Opaque1: // Remove Opaque Nodes before matching @@ -3721,6 +3739,14 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f handle_div_mod_op(n, T_LONG, true); break; + case Op_MulHiL: + handle_mulhi_mul_op(n, false); + break; + + case Op_UMulHiL: + handle_mulhi_mul_op(n, true); + break; + case Op_LoadVector: case Op_StoreVector: #ifdef ASSERT diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 3c2e1c64119..ab36f59a28f 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -1257,6 +1257,7 @@ class Compile : public Phase { void final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop, Unique_Node_List& dead_nodes); void final_graph_reshaping_walk(Node_Stack& nstack, Node* root, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes); void handle_div_mod_op(Node* n, BasicType bt, bool is_unsigned); + void handle_mulhi_mul_op(Node* n, bool is_unsigned); // Logic cone optimization. void optimize_logic_cones(PhaseIterGVN &igvn); diff --git a/src/hotspot/share/opto/divnode.cpp b/src/hotspot/share/opto/divnode.cpp index b398ec27b80..1687ff2cade 100644 --- a/src/hotspot/share/opto/divnode.cpp +++ b/src/hotspot/share/opto/divnode.cpp @@ -1614,12 +1614,6 @@ const Type* ModFloatingNode::Value(PhaseGVN* phase) const { //============================================================================= -DivModNode::DivModNode( Node *c, Node *dividend, Node *divisor ) : MultiNode(3) { - init_req(0, c); - init_req(1, dividend); - init_req(2, divisor); -} - DivModNode* DivModNode::make(Node* div_or_mod, BasicType bt, bool is_unsigned) { assert(bt == T_INT || bt == T_LONG, "only int or long input pattern accepted"); @@ -1645,8 +1639,8 @@ DivModINode* DivModINode::make(Node* div_or_mod) { "only div or mod input pattern accepted"); DivModINode* divmod = new DivModINode(n->in(0), n->in(1), n->in(2)); - Node* dproj = new ProjNode(divmod, DivModNode::div_proj_num); - Node* mproj = new ProjNode(divmod, DivModNode::mod_proj_num); + Node* dproj = new ProjNode(divmod, DivModNode::first_proj_num); + Node* mproj = new ProjNode(divmod, DivModNode::second_proj_num); return divmod; } @@ -1657,8 +1651,8 @@ DivModLNode* DivModLNode::make(Node* div_or_mod) { "only div or mod input pattern accepted"); DivModLNode* divmod = new DivModLNode(n->in(0), n->in(1), n->in(2)); - Node* dproj = new ProjNode(divmod, DivModNode::div_proj_num); - Node* mproj = new ProjNode(divmod, DivModNode::mod_proj_num); + Node* dproj = new ProjNode(divmod, DivModNode::first_proj_num); + Node* mproj = new ProjNode(divmod, DivModNode::second_proj_num); return divmod; } @@ -1667,11 +1661,11 @@ DivModLNode* DivModLNode::make(Node* div_or_mod) { Node *DivModINode::match( const ProjNode *proj, const Matcher *match ) { uint ideal_reg = proj->ideal_reg(); RegMask rm; - if (proj->_con == div_proj_num) { - rm.assignFrom(match->divI_proj_mask()); + if (proj->_con == first_proj_num) { + rm.assignFrom(match->firstI_proj_mask()); } else { - assert(proj->_con == mod_proj_num, "must be div or mod projection"); - rm.assignFrom(match->modI_proj_mask()); + assert(proj->_con == second_proj_num, "must be div or mod projection"); + rm.assignFrom(match->secondI_proj_mask()); } return new MachProjNode(this, proj->_con, rm, ideal_reg); } @@ -1682,11 +1676,11 @@ Node *DivModINode::match( const ProjNode *proj, const Matcher *match ) { Node *DivModLNode::match( const ProjNode *proj, const Matcher *match ) { uint ideal_reg = proj->ideal_reg(); RegMask rm; - if (proj->_con == div_proj_num) { - rm.assignFrom(match->divL_proj_mask()); + if (proj->_con == first_proj_num) { + rm.assignFrom(match->firstL_proj_mask()); } else { - assert(proj->_con == mod_proj_num, "must be div or mod projection"); - rm.assignFrom(match->modL_proj_mask()); + assert(proj->_con == second_proj_num, "must be div or mod projection"); + rm.assignFrom(match->secondL_proj_mask()); } return new MachProjNode(this, proj->_con, rm, ideal_reg); } @@ -1698,8 +1692,8 @@ UDivModINode* UDivModINode::make(Node* div_or_mod) { "only div or mod input pattern accepted"); UDivModINode* divmod = new UDivModINode(n->in(0), n->in(1), n->in(2)); - Node* dproj = new ProjNode(divmod, DivModNode::div_proj_num); - Node* mproj = new ProjNode(divmod, DivModNode::mod_proj_num); + Node* dproj = new ProjNode(divmod, DivModNode::first_proj_num); + Node* mproj = new ProjNode(divmod, DivModNode::second_proj_num); return divmod; } @@ -1710,8 +1704,8 @@ UDivModLNode* UDivModLNode::make(Node* div_or_mod) { "only div or mod input pattern accepted"); UDivModLNode* divmod = new UDivModLNode(n->in(0), n->in(1), n->in(2)); - Node* dproj = new ProjNode(divmod, DivModNode::div_proj_num); - Node* mproj = new ProjNode(divmod, DivModNode::mod_proj_num); + Node* dproj = new ProjNode(divmod, DivModNode::first_proj_num); + Node* mproj = new ProjNode(divmod, DivModNode::second_proj_num); return divmod; } @@ -1720,11 +1714,11 @@ UDivModLNode* UDivModLNode::make(Node* div_or_mod) { Node* UDivModINode::match( const ProjNode *proj, const Matcher *match ) { uint ideal_reg = proj->ideal_reg(); RegMask rm; - if (proj->_con == div_proj_num) { - rm.assignFrom(match->divI_proj_mask()); + if (proj->_con == first_proj_num) { + rm.assignFrom(match->firstI_proj_mask()); } else { - assert(proj->_con == mod_proj_num, "must be div or mod projection"); - rm.assignFrom(match->modI_proj_mask()); + assert(proj->_con == second_proj_num, "must be div or mod projection"); + rm.assignFrom(match->secondI_proj_mask()); } return new MachProjNode(this, proj->_con, rm, ideal_reg); } @@ -1735,11 +1729,11 @@ Node* UDivModINode::match( const ProjNode *proj, const Matcher *match ) { Node* UDivModLNode::match( const ProjNode *proj, const Matcher *match ) { uint ideal_reg = proj->ideal_reg(); RegMask rm; - if (proj->_con == div_proj_num) { - rm.assignFrom(match->divL_proj_mask()); + if (proj->_con == first_proj_num) { + rm.assignFrom(match->firstL_proj_mask()); } else { - assert(proj->_con == mod_proj_num, "must be div or mod projection"); - rm.assignFrom(match->modL_proj_mask()); + assert(proj->_con == second_proj_num, "must be div or mod projection"); + rm.assignFrom(match->secondL_proj_mask()); } return new MachProjNode(this, proj->_con, rm, ideal_reg); } diff --git a/src/hotspot/share/opto/divnode.hpp b/src/hotspot/share/opto/divnode.hpp index 2598429716f..366e3fb882d 100644 --- a/src/hotspot/share/opto/divnode.hpp +++ b/src/hotspot/share/opto/divnode.hpp @@ -239,36 +239,20 @@ class UModLNode : public DivModIntegerNode { //------------------------------DivModNode--------------------------------------- // Division with remainder result. -class DivModNode : public MultiNode { +class DivModNode : public BinaryMultiNode { protected: - DivModNode( Node *c, Node *dividend, Node *divisor ); + DivModNode(Node* ctrl, Node* dividend, Node* divisor) : BinaryMultiNode(ctrl, dividend, divisor) {} public: - enum { - div_proj_num = 0, // quotient - mod_proj_num = 1 // remainder - }; virtual int Opcode() const; - virtual Node* Identity(PhaseGVN* phase) { return this; } - virtual Node *Ideal(PhaseGVN *phase, bool can_reshape) { return nullptr; } - virtual const Type* Value(PhaseGVN* phase) const { return bottom_type(); } - virtual uint hash() const { return Node::hash(); } - virtual bool is_CFG() const { return false; } - virtual uint ideal_reg() const { return NotAMachineReg; } static DivModNode* make(Node* div_or_mod, BasicType bt, bool is_unsigned); - - ProjNode* div_proj() { return proj_out_or_null(div_proj_num); } - ProjNode* mod_proj() { return proj_out_or_null(mod_proj_num); } - -private: - virtual bool depends_only_on_test() const { return false; } }; //------------------------------DivModINode--------------------------------------- // Integer division with remainder result. class DivModINode : public DivModNode { public: - DivModINode( Node *c, Node *dividend, Node *divisor ) : DivModNode(c, dividend, divisor) {} + DivModINode(Node* ctrl, Node* dividend, Node* divisor) : DivModNode(ctrl, dividend, divisor) {} virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeTuple::INT_PAIR; } virtual Node *match( const ProjNode *proj, const Matcher *m ); @@ -281,7 +265,7 @@ class DivModINode : public DivModNode { // Long division with remainder result. class DivModLNode : public DivModNode { public: - DivModLNode( Node *c, Node *dividend, Node *divisor ) : DivModNode(c, dividend, divisor) {} + DivModLNode(Node* ctrl, Node* dividend, Node* divisor) : DivModNode(ctrl, dividend, divisor) {} virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeTuple::LONG_PAIR; } virtual Node *match( const ProjNode *proj, const Matcher *m ); @@ -295,7 +279,7 @@ class DivModLNode : public DivModNode { // Unsigend integer division with remainder result. class UDivModINode : public DivModNode { public: - UDivModINode( Node *c, Node *dividend, Node *divisor ) : DivModNode(c, dividend, divisor) {} + UDivModINode(Node* ctrl, Node* dividend, Node* divisor) : DivModNode(ctrl, dividend, divisor) {} virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeTuple::INT_PAIR; } virtual Node *match( const ProjNode *proj, const Matcher *m ); @@ -308,7 +292,7 @@ class UDivModINode : public DivModNode { // Unsigned long division with remainder result. class UDivModLNode : public DivModNode { public: - UDivModLNode( Node *c, Node *dividend, Node *divisor ) : DivModNode(c, dividend, divisor) {} + UDivModLNode(Node* ctrl, Node* dividend, Node* divisor) : DivModNode(ctrl, dividend, divisor) {} virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeTuple::LONG_PAIR; } virtual Node *match( const ProjNode *proj, const Matcher *m ); diff --git a/src/hotspot/share/opto/matcher.hpp b/src/hotspot/share/opto/matcher.hpp index 31f4a782247..2453a7ece4e 100644 --- a/src/hotspot/share/opto/matcher.hpp +++ b/src/hotspot/share/opto/matcher.hpp @@ -418,15 +418,15 @@ class Matcher : public PhaseTransform { static OptoReg::Name inline_cache_reg(); static int inline_cache_reg_encode(); - // Register for DIVI projection of divmodI - static const RegMask& divI_proj_mask(); - // Register for MODI projection of divmodI - static const RegMask& modI_proj_mask(); - - // Register for DIVL projection of divmodL - static const RegMask& divL_proj_mask(); - // Register for MODL projection of divmodL - static const RegMask& modL_proj_mask(); + // Register for the first projection of an int pair + static const RegMask& firstI_proj_mask(); + // Register for the second projection of an int pair + static const RegMask& secondI_proj_mask(); + + // Register for the first projection of a long pair + static const RegMask& firstL_proj_mask(); + // Register for the second projection of a long pair + static const RegMask& secondL_proj_mask(); // Java-Interpreter calling convention // (what you use when calling between compiled-Java and Interpreted-Java diff --git a/src/hotspot/share/opto/mulnode.cpp b/src/hotspot/share/opto/mulnode.cpp index e48acd23b87..eb24e31eee2 100644 --- a/src/hotspot/share/opto/mulnode.cpp +++ b/src/hotspot/share/opto/mulnode.cpp @@ -26,6 +26,8 @@ #include "opto/addnode.hpp" #include "opto/connode.hpp" #include "opto/convertnode.hpp" +#include "opto/machnode.hpp" +#include "opto/matcher.hpp" #include "opto/memnode.hpp" #include "opto/mulnode.hpp" #include "opto/phaseX.hpp" @@ -606,6 +608,36 @@ const Type* UMulHiLNode::Value(PhaseGVN* phase) const { return MulHiValue(t1, t2, bot); } +MulHiLoLNode* MulHiLoLNode::make(Node* mul_hi) { + assert(mul_hi->Opcode() == Op_MulHiL, "expected MulHiL"); + + MulHiLoLNode* mul_hi_lo = new MulHiLoLNode(mul_hi->in(0), mul_hi->in(1), mul_hi->in(2)); + [[maybe_unused]] Node* lo_proj = new ProjNode(mul_hi_lo, MulHiLoLNode::first_proj_num); + [[maybe_unused]] Node* hi_proj = new ProjNode(mul_hi_lo, MulHiLoLNode::second_proj_num); + return mul_hi_lo; +} + +UMulHiLoLNode* UMulHiLoLNode::make(Node* umul_hi) { + assert(umul_hi->Opcode() == Op_UMulHiL, "expected UMulHiL"); + + UMulHiLoLNode* umul_hi_lo = new UMulHiLoLNode(umul_hi->in(0), umul_hi->in(1), umul_hi->in(2)); + [[maybe_unused]] Node* lo_proj = new ProjNode(umul_hi_lo, MulHiLoLNode::first_proj_num); + [[maybe_unused]] Node* hi_proj = new ProjNode(umul_hi_lo, MulHiLoLNode::second_proj_num); + return umul_hi_lo; +} + +Node* MulHiLoLNode::match(const ProjNode* proj, const Matcher* match) { + uint ideal_reg = proj->ideal_reg(); + RegMask rm; + if (proj->_con == first_proj_num) { + rm.assignFrom(match->firstL_proj_mask()); + } else { + assert(proj->_con == second_proj_num, "must be lo or hi projection"); + rm.assignFrom(match->secondL_proj_mask()); + } + return new MachProjNode(this, proj->_con, rm, ideal_reg); +} + // A common routine used by UMulHiLNode and MulHiLNode const Type* MulHiValue(const Type *t1, const Type *t2, const Type *bot) { // Either input is TOP ==> the result is TOP diff --git a/src/hotspot/share/opto/mulnode.hpp b/src/hotspot/share/opto/mulnode.hpp index 1e19e8ec5cd..f26137dfe49 100644 --- a/src/hotspot/share/opto/mulnode.hpp +++ b/src/hotspot/share/opto/mulnode.hpp @@ -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. * 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,6 +25,7 @@ #ifndef SHARE_OPTO_MULNODE_HPP #define SHARE_OPTO_MULNODE_HPP +#include "opto/multnode.hpp" #include "opto/node.hpp" #include "opto/opcodes.hpp" #include "opto/type.hpp" @@ -32,6 +33,7 @@ // Portions of code courtesy of Clifford Click class PhaseTransform; +class Matcher; //------------------------------MulNode---------------------------------------- // Classic MULTIPLY functionality. This covers all the usual 'multiply' @@ -205,6 +207,31 @@ class UMulHiLNode : public Node { friend const Type* MulHiValue(const Type *t1, const Type *t2, const Type *bot); }; +//------------------------------MulHiLoLNode----------------------------------- +// Lower and upper 64-bit results of a signed 64x64->128 multiply. +class MulHiLoLNode : public BinaryMultiNode { +protected: + MulHiLoLNode(Node* ctrl, Node* in1, Node* in2) : BinaryMultiNode(ctrl, in1, in2) {} + +public: + virtual int Opcode() const; + virtual const Type* bottom_type() const { return TypeTuple::LONG_PAIR; } + + virtual Node* match(const ProjNode* proj, const Matcher* m); + + static MulHiLoLNode* make(Node* mul_hi); +}; + +//------------------------------UMulHiLoLNode---------------------------------- +// Lower and upper 64-bit results of an unsigned 64x64->128 multiply. +class UMulHiLoLNode : public MulHiLoLNode { +public: + UMulHiLoLNode(Node* ctrl, Node* in1, Node* in2) : MulHiLoLNode(ctrl, in1, in2) {} + virtual int Opcode() const; + + static UMulHiLoLNode* make(Node* umul_hi); +}; + //------------------------------AndINode--------------------------------------- // Logically AND 2 integers. Included with the MUL nodes because it inherits // all the behavior of multiplication on a ring. diff --git a/src/hotspot/share/opto/multnode.hpp b/src/hotspot/share/opto/multnode.hpp index b63d418b742..6a69eafb7ed 100644 --- a/src/hotspot/share/opto/multnode.hpp +++ b/src/hotspot/share/opto/multnode.hpp @@ -149,6 +149,34 @@ class MultiNode : public Node { ProjNode* find_first(uint which_proj, bool is_io_use) const; }; +class BinaryMultiNode : public MultiNode { +protected: + BinaryMultiNode(Node* ctrl, Node* in1, Node* in2) : MultiNode(3) { + init_req(0, ctrl); + init_req(1, in1); + init_req(2, in2); + } + +public: + enum { + first_proj_num = 0, + second_proj_num = 1 + }; + + virtual Node* Identity(PhaseGVN* phase) { return this; } + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape) { return nullptr; } + virtual const Type* Value(PhaseGVN* phase) const { return bottom_type(); } + virtual uint hash() const { return Node::hash(); } + virtual bool is_CFG() const { return false; } + virtual uint ideal_reg() const { return NotAMachineReg; } + + ProjNode* first_proj() const { return proj_out_or_null(first_proj_num); } + ProjNode* second_proj() const { return proj_out_or_null(second_proj_num); } + +private: + virtual bool depends_only_on_test() const { return false; } +}; + //------------------------------ProjNode--------------------------------------- // This class defines a Projection node. Projections project a single element // out of a tuple (or Signature) type. Only MultiNodes produce TypeTuple diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index 997ce92fe1c..2f7cc6d1c1d 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -2882,7 +2882,7 @@ bool Node::is_iteratively_computed() { //--------------------------find_similar------------------------------ // Return a node with opcode "opc" and same inputs as "this" if one can // be found; Otherwise return null; -Node* Node::find_similar(int opc) { +Node* Node::find_similar(int opc, bool is_commutative) { if (req() >= 2) { Node* def = in(1); if (def && def->outcnt() >= 2) { @@ -2890,9 +2890,26 @@ Node* Node::find_similar(int opc) { Node* use = def->fast_out(i); if (use != this && use->Opcode() == opc && - use->req() == req() && - has_same_inputs_as(use)) { - return use; + use->req() == req()) { + bool same = false; + if (!is_commutative || req() < 3) { + same = use->has_same_inputs_as(this); + } else { + if (use->in(0) == in(0) && + ((use->in(1) == in(1) && use->in(2) == in(2)) || + (use->in(1) == in(2) && use->in(2) == in(1)))) { + same = true; + for (uint j = 3; j < req(); j++) { + if (use->in(j) != in(j)) { + same = false; + break; + } + } + } + } + if (same) { + return use; + } } } } diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index 1ef4b5a51b6..443f4bfbe8a 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -1315,7 +1315,7 @@ class Node { // Return a node with opcode "opc" and same inputs as "this" if one can // be found; Otherwise return null; - Node* find_similar(int opc); + Node* find_similar(int opc, bool is_commutative = false); bool has_same_inputs_as(const Node* other) const; // Return the unique control out if only one. Null if none or more than one. diff --git a/test/hotspot/jtreg/compiler/c2/TestMultiplyHighLowFusion.java b/test/hotspot/jtreg/compiler/c2/TestMultiplyHighLowFusion.java new file mode 100644 index 00000000000..31dd52bd3f6 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestMultiplyHighLowFusion.java @@ -0,0 +1,117 @@ +/* + * 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 8379327 + * @summary Verify correctness for combined low/high 64-bit multiplication patterns. + * @library /test/lib / + * @run driver ${test.main.class} + */ + +package compiler.c2; + +import compiler.lib.generators.Generator; +import compiler.lib.generators.Generators; +import compiler.lib.ir_framework.*; +import java.math.BigInteger; + +public class TestMultiplyHighLowFusion { + private static final BigInteger MASK_64 = BigInteger.ONE.shiftLeft(64).subtract(BigInteger.ONE); + private static final Generator LONG_GEN = Generators.G.longs(); + + public static void main(String[] args) { + TestFramework.run(); + } + + @Test + @IR(applyIfPlatform = {"x64", "true"}, phase = CompilePhase.PRINT_IDEAL, counts = {"\\bMulHiLoL\\b", "1"}) + public static long doMath(long a, long b) { + long low = a * b; + long high = Math.multiplyHigh(a, b); + return low + high; + } + + @Test + @IR(applyIfPlatform = {"x64", "true"}, phase = CompilePhase.PRINT_IDEAL, counts = {"\\bMulHiLoL\\b", "1"}) + public static long doMathSwapped(long a, long b) { + long low = b * a; + long high = Math.multiplyHigh(b, a); + return low + high; + } + + @Test + @IR(applyIfPlatform = {"x64", "true"}, phase = CompilePhase.PRINT_IDEAL, counts = {"\\bUMulHiLoL\\b", "1"}) + public static long doUnsignedMath(long a, long b) { + long low = a * b; + long high = Math.unsignedMultiplyHigh(a, b); + return low + high; + } + + @Test + @IR(applyIfPlatform = {"x64", "true"}, phase = CompilePhase.PRINT_IDEAL, counts = {"\\bUMulHiLoL\\b", "1"}) + public static long doUnsignedMathSwapped(long a, long b) { + long low = b * a; + long high = Math.unsignedMultiplyHigh(b, a); + return low + high; + } + + @Run(test = {"doMath", "doMathSwapped", "doUnsignedMath", "doUnsignedMathSwapped"}) + public void runTests() { + verifyPair(LONG_GEN.next(), LONG_GEN.next()); + } + + private void verifyPair(long a, long b) { + long expectedSigned = expectedSigned(a, b); + long expectedUnsigned = expectedUnsigned(a, b); + + if (doMath(a, b) != expectedSigned) { + throw new RuntimeException("Signed mismatch for a=" + a + ", b=" + b); + } + if (doMathSwapped(a, b) != expectedSigned) { + throw new RuntimeException("Signed swapped mismatch for a=" + a + ", b=" + b); + } + if (doUnsignedMath(a, b) != expectedUnsigned) { + throw new RuntimeException("Unsigned mismatch for a=" + a + ", b=" + b); + } + if (doUnsignedMathSwapped(a, b) != expectedUnsigned) { + throw new RuntimeException("Unsigned swapped mismatch for a=" + a + ", b=" + b); + } + } + + private static long expectedSigned(long a, long b) { + BigInteger product = BigInteger.valueOf(a).multiply(BigInteger.valueOf(b)); + long low = product.longValue(); + long high = product.shiftRight(64).longValue(); + return low + high; + } + + private static long expectedUnsigned(long a, long b) { + BigInteger ua = BigInteger.valueOf(a).and(MASK_64); + BigInteger ub = BigInteger.valueOf(b).and(MASK_64); + BigInteger product = ua.multiply(ub); + long low = product.longValue(); + long high = product.shiftRight(64).longValue(); + return low + high; + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java index becda83a029..3dffa096525 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java @@ -264,6 +264,10 @@ private static List generatePrimitiveOperations() { ops.add(Expression.make(BOOLEANS, "Boolean.logicalOr(", BOOLEANS, ", ", BOOLEANS, ")")); ops.add(Expression.make(BOOLEANS, "Boolean.logicalXor(", BOOLEANS, ", ", BOOLEANS, ")")); + // ------------ Math ------------- + ops.add(Expression.make(LONGS, "Math.multiplyHigh(", LONGS, ", ", LONGS, ")")); + ops.add(Expression.make(LONGS, "Math.unsignedMultiplyHigh(", LONGS, ", ", LONGS, ")")); + // TODO: Math and other classes. // Note: Math.copySign is non-deterministic because of NaN having encoding with sign bit set and unset. diff --git a/test/micro/org/openjdk/bench/vm/compiler/MultiplyHighLowFusion.java b/test/micro/org/openjdk/bench/vm/compiler/MultiplyHighLowFusion.java new file mode 100644 index 00000000000..c4f4e522409 --- /dev/null +++ b/test/micro/org/openjdk/bench/vm/compiler/MultiplyHighLowFusion.java @@ -0,0 +1,116 @@ +/* + * 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 org.openjdk.bench.vm.compiler; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import java.util.Random; +import java.util.concurrent.TimeUnit; + +/** + * Benchmarks patterns that may fuse low/high 64-bit multiply operations. + */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 4, time = 2, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 4, time = 2, timeUnit = TimeUnit.SECONDS) +@Fork(value = 3) +public class MultiplyHighLowFusion { + + @Param("1024") + private int arraySize; + + private long[] lhs; + private long[] rhs; + + @Setup + public void setup() { + Random random = new Random(0x5EED); + lhs = new long[arraySize]; + rhs = new long[arraySize]; + for (int i = 0; i < arraySize; i++) { + lhs[i] = random.nextLong(); + rhs[i] = random.nextLong(); + } + } + + @Benchmark + public long signedLowOnly() { + long sum = 0; + for (int i = 0; i < arraySize; i++) { + sum += lhs[i] * rhs[i]; + } + return sum; + } + + @Benchmark + public long signedHighOnly() { + long sum = 0; + for (int i = 0; i < arraySize; i++) { + sum += Math.multiplyHigh(lhs[i], rhs[i]); + } + return sum; + } + + @Benchmark + public long signedLowPlusHigh() { + long sum = 0; + for (int i = 0; i < arraySize; i++) { + long a = lhs[i]; + long b = rhs[i]; + sum += (a * b) + Math.multiplyHigh(a, b); + } + return sum; + } + + @Benchmark + public long unsignedHighOnly() { + long sum = 0; + for (int i = 0; i < arraySize; i++) { + sum += Math.unsignedMultiplyHigh(lhs[i], rhs[i]); + } + return sum; + } + + @Benchmark + public long unsignedLowPlusHigh() { + long sum = 0; + for (int i = 0; i < arraySize; i++) { + long a = lhs[i]; + long b = rhs[i]; + sum += (a * b) + Math.unsignedMultiplyHigh(a, b); + } + return sum; + } +} From 08435e4861a348fade00673a91e61b83bacccbbf Mon Sep 17 00:00:00 2001 From: Serguei Spitsyn Date: Tue, 30 Jun 2026 05:05:22 +0000 Subject: [PATCH 110/707] 8379144: serviceability/jvmti/vthread/VThreadTest/VThreadTest.java timed out with --enable-preview Reviewed-by: lmesnik --- .../vthread/VThreadTest/VThreadTest.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadTest/VThreadTest.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadTest/VThreadTest.java index 7330f4c061d..115326567d3 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadTest/VThreadTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadTest/VThreadTest.java @@ -33,13 +33,13 @@ import java.util.concurrent.*; public class VThreadTest { - private static final String agentLib = "VThreadTest"; - static final int MSG_COUNT = 10*1000; static final SynchronousQueue QUEUE = new SynchronousQueue<>(); static native boolean check(); + static void log(String msg) { System.out.println(msg); } + static void producer(String msg) throws InterruptedException { int ii = 1; long ll = 2*(long)ii; @@ -54,7 +54,11 @@ static void producer(String msg) throws InterruptedException { for (int i = 0; i < MSG_COUNT; i++) { producer("msg: "); } - } catch (InterruptedException e) { } + } catch (Throwable t) { + t.printStackTrace(System.out); + log("VThreadTest failed: PRODUCER caught a throwable: " + t); + System.exit(1); + } }; static final Runnable CONSUMER = () -> { @@ -62,7 +66,11 @@ static void producer(String msg) throws InterruptedException { for (int i = 0; i < MSG_COUNT; i++) { String s = QUEUE.take(); } - } catch (InterruptedException e) { } + } catch (Throwable t) { + t.printStackTrace(System.out); + log("VThreadTest failed: CONSUMER caught a throwable: " + t); + System.exit(1); + } }; public static void test1() throws Exception { @@ -80,14 +88,6 @@ void runTest() throws Exception { } public static void main(String[] args) throws Exception { - try { - System.loadLibrary(agentLib); - } catch (UnsatisfiedLinkError ex) { - System.err.println("Failed to load " + agentLib + " lib"); - System.err.println("java.library.path: " + System.getProperty("java.library.path")); - throw ex; - } - VThreadTest obj = new VThreadTest(); obj.runTest(); } From ce87f11a1d2295fceb04c4b94b9a175d7807973a Mon Sep 17 00:00:00 2001 From: Ashay Rane Date: Tue, 30 Jun 2026 05:23:33 +0000 Subject: [PATCH 111/707] 8386685: CDS load on Windows/ARM64 using base address set to 0x5_0000_0000 causes a JVM crash Reviewed-by: iklam, stuefe --- src/hotspot/share/cds/aotMetaspace.cpp | 29 ++++++++++++++------------ src/hotspot/share/cds/aotMetaspace.hpp | 3 +++ src/hotspot/share/cds/cdsConfig.cpp | 7 +++++++ src/hotspot/share/cds/cds_globals.hpp | 6 ++++-- 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index fac320c3ed7..fbd12038c94 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -164,7 +164,7 @@ size_t AOTMetaspace::protection_zone_size() { return os::cds_core_region_alignment(); } -static bool shared_base_valid(char* shared_base) { +bool AOTMetaspace::shared_base_valid(char* shared_base) { // We check user input for SharedBaseAddress at dump time. // At CDS runtime, "shared_base" will be the (attempted) mapping start. It will also @@ -172,10 +172,15 @@ static bool shared_base_valid(char* shared_base) { // the prototype mark words) carry pre-computed narrow Klass IDs that refer to the mapping // start as base. // - // On AARCH64, The "shared_base" may not be later usable as encoding base, depending on the + // The "shared_base" may not be later usable as encoding base, depending on the // total size of the reserved area and the precomputed_narrow_klass_shift. This is checked // before reserving memory. Here we weed out values already known to be invalid later. - return AARCH64_ONLY(is_aligned(shared_base, 4 * G)) NOT_AARCH64(true); + // Since we cannot predict the range, we use the full maximum encoding range + // (4G). + constexpr size_t range = 4 * G; + address addr = (address)shared_base; + const int shift = ArchiveBuilder::precomputed_narrow_klass_shift(); + return CompressedKlassPointers::check_klass_decode_mode(addr, shift, range); } class DumpClassListCLDClosure : public CLDClosure { @@ -273,7 +278,7 @@ static char* compute_shared_base(size_t cds_max) { err = "too high"; } else if (shared_base_too_high(specified_base, aligned_base, cds_max)) { err = "too high"; - } else if (!shared_base_valid(aligned_base)) { + } else if (!AOTMetaspace::shared_base_valid(aligned_base)) { err = "invalid for this platform"; } else { return aligned_base; @@ -291,7 +296,7 @@ static char* compute_shared_base(size_t cds_max) { // Make sure the default value of SharedBaseAddress specified in globals.hpp is sane. assert(!shared_base_too_high(specified_base, aligned_base, cds_max), "Sanity"); - assert(shared_base_valid(aligned_base), "Sanity"); + assert(AOTMetaspace::shared_base_valid(aligned_base), "Sanity"); return aligned_base; } @@ -1971,14 +1976,12 @@ char* AOTMetaspace::reserve_address_space_for_archives(FileMapInfo* static_mapin const size_t total_range_size = archive_space_size + gap_size + class_space_size; - // Test that class space base address plus shift can be decoded by aarch64, when restored. - const int precomputed_narrow_klass_shift = ArchiveBuilder::precomputed_narrow_klass_shift(); - if (!CompressedKlassPointers::check_klass_decode_mode(base_address, precomputed_narrow_klass_shift, - total_range_size)) { - aot_log_info(aot)("CDS initialization: Cannot use SharedBaseAddress " PTR_FORMAT " with precomputed shift %d.", - p2i(base_address), precomputed_narrow_klass_shift); - use_archive_base_addr = false; - } + // The code for dumping the archive ensures that the base address is valid. + // Here we validate that the base address plus shift can be decoded when + // restored. + assert(shared_base_valid((char*)base_address), + "Cannot use SharedBaseAddress " PTR_FORMAT " with precomputed shift %d.", + p2i(base_address), ArchiveBuilder::precomputed_narrow_klass_shift()); assert(total_range_size > ccs_begin_offset, "must be"); if (use_windows_memory_mapping() && use_archive_base_addr) { diff --git a/src/hotspot/share/cds/aotMetaspace.hpp b/src/hotspot/share/cds/aotMetaspace.hpp index 975b6be76d7..cc90c9da3b0 100644 --- a/src/hotspot/share/cds/aotMetaspace.hpp +++ b/src/hotspot/share/cds/aotMetaspace.hpp @@ -188,6 +188,9 @@ class AOTMetaspace : AllStatic { static bool use_optimized_module_handling() { return NOT_CDS(false) CDS_ONLY(_use_optimized_module_handling); } static void disable_optimized_module_handling() { _use_optimized_module_handling = false; } + // Check if the supplied shared base address can be used as the encoding base. + static bool shared_base_valid(char* shared_base); + private: static void read_extra_data(JavaThread* current, const char* filename) NOT_CDS_RETURN; static void fork_and_dump_final_static_archive(TRAPS); diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index 2dd1d9d0824..63d1f4af4ae 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -41,6 +41,7 @@ #include "runtime/vmThread.hpp" #include "utilities/defaultStream.hpp" #include "utilities/formatBuffer.hpp" +#include "utilities/globalDefinitions.hpp" bool CDSConfig::_is_dumping_static_archive = false; bool CDSConfig::_is_dumping_preimage_static_archive = false; @@ -123,6 +124,12 @@ void CDSConfig::ergo_initialize() { // etc), there is usually no need to attach to this JVM. FLAG_SET_ERGO(DisableAttachMechanism, true); } + + if (!AOTMetaspace::shared_base_valid((char*)SharedBaseAddress)) { + log_warning(cds)("SharedBaseAddress " PTR_FORMAT " is invalid. Reverting to " PTR_FORMAT, + p2i((void*)SharedBaseAddress), p2i((void*)DEFAULT_SHARED_BASE_ADDRESS)); + FLAG_SET_ERGO(SharedBaseAddress, DEFAULT_SHARED_BASE_ADDRESS); + } } const char* CDSConfig::default_archive_path() { diff --git a/src/hotspot/share/cds/cds_globals.hpp b/src/hotspot/share/cds/cds_globals.hpp index 7df498ca5b9..640cde848b8 100644 --- a/src/hotspot/share/cds/cds_globals.hpp +++ b/src/hotspot/share/cds/cds_globals.hpp @@ -27,6 +27,9 @@ #include "runtime/globals_shared.hpp" +#define DEFAULT_SHARED_BASE_ADDRESS (LP64_ONLY(32*G) \ + NOT_LP64(LINUX_ONLY(2*G) NOT_LINUX(0))) + // // Defines all globals flags used by CDS. // @@ -51,8 +54,7 @@ product(bool, PrintSharedArchiveAndExit, false, \ "Print shared archive file contents") \ \ - product(size_t, SharedBaseAddress, LP64_ONLY(32*G) \ - NOT_LP64(LINUX_ONLY(2*G) NOT_LINUX(0)), \ + product(size_t, SharedBaseAddress, DEFAULT_SHARED_BASE_ADDRESS, \ "Address to allocate shared memory region for class data") \ range(0, SIZE_MAX) \ \ From c0df91c40aa15937e85825d5636c11cc9b4a52e8 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 30 Jun 2026 07:39:57 +0000 Subject: [PATCH 112/707] 8387262: Enum constant frame::pc_return_offset is always zero Reviewed-by: dholmes, coleenp --- src/hotspot/cpu/aarch64/frame_aarch64.hpp | 3 +-- src/hotspot/cpu/arm/frame_arm.hpp | 3 +-- src/hotspot/cpu/ppc/frame_ppc.hpp | 4 +--- src/hotspot/cpu/riscv/frame_riscv.hpp | 4 +--- src/hotspot/cpu/s390/frame_s390.hpp | 8 -------- src/hotspot/cpu/x86/frame_x86.hpp | 3 +-- src/hotspot/cpu/zero/frame_zero.hpp | 3 +-- src/hotspot/share/code/aotCodeCache.cpp | 3 --- src/hotspot/share/code/nmethod.cpp | 3 --- src/hotspot/share/code/oopRecorder.cpp | 5 +---- src/hotspot/share/code/relocInfo.cpp | 5 +---- src/hotspot/share/compiler/disassembler.cpp | 5 +---- src/hotspot/share/runtime/deoptimization.cpp | 2 +- src/hotspot/share/runtime/frame.cpp | 4 ++-- src/hotspot/share/runtime/sharedRuntime.cpp | 2 +- src/hotspot/share/runtime/vmStructs.cpp | 2 -- .../share/classes/sun/jvm/hotspot/runtime/Frame.java | 8 -------- 17 files changed, 13 insertions(+), 54 deletions(-) diff --git a/src/hotspot/cpu/aarch64/frame_aarch64.hpp b/src/hotspot/cpu/aarch64/frame_aarch64.hpp index 231710df7d7..ac4740645b8 100644 --- a/src/hotspot/cpu/aarch64/frame_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/frame_aarch64.hpp @@ -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, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -66,7 +66,6 @@ public: enum { - pc_return_offset = 0, // All frames link_offset = 0, return_addr_offset = 1, diff --git a/src/hotspot/cpu/arm/frame_arm.hpp b/src/hotspot/cpu/arm/frame_arm.hpp index 026bd993981..2ef44414e1c 100644 --- a/src/hotspot/cpu/arm/frame_arm.hpp +++ b/src/hotspot/cpu/arm/frame_arm.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -27,7 +27,6 @@ public: enum { - pc_return_offset = 0, // All frames link_offset = 0, return_addr_offset = 1, diff --git a/src/hotspot/cpu/ppc/frame_ppc.hpp b/src/hotspot/cpu/ppc/frame_ppc.hpp index 14743c7d75a..bf49bbb7e01 100644 --- a/src/hotspot/cpu/ppc/frame_ppc.hpp +++ b/src/hotspot/cpu/ppc/frame_ppc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2025 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -391,8 +391,6 @@ } enum { - // normal return address is 1 bundle past PC - pc_return_offset = 0, // size, in words, of frame metadata (e.g. pc and link) metadata_words = sizeof(java_abi) >> LogBytesPerWord, // size, in words, of metadata at frame bottom, i.e. it is not part of the diff --git a/src/hotspot/cpu/riscv/frame_riscv.hpp b/src/hotspot/cpu/riscv/frame_riscv.hpp index ce5a8dde230..d5f04ee3ff7 100644 --- a/src/hotspot/cpu/riscv/frame_riscv.hpp +++ b/src/hotspot/cpu/riscv/frame_riscv.hpp @@ -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) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -103,8 +103,6 @@ public: enum { - pc_return_offset = 0, - // All frames link_offset = -2, return_addr_offset = -1, diff --git a/src/hotspot/cpu/s390/frame_s390.hpp b/src/hotspot/cpu/s390/frame_s390.hpp index bcdeec43e1a..664a49fdd21 100644 --- a/src/hotspot/cpu/s390/frame_s390.hpp +++ b/src/hotspot/cpu/s390/frame_s390.hpp @@ -542,14 +542,6 @@ unsigned long flags, int max_frames = 0); enum { - // This enum value specifies the offset from the pc remembered by - // call instructions to the location where control returns to - // after a normal return. Most architectures remember the return - // location directly, i.e. the offset is zero. This is the case - // for z/Architecture, too. - // - // Normal return address is the instruction following the branch. - pc_return_offset = 0, metadata_words = 0, metadata_words_at_bottom = 0, metadata_words_at_top = 0, diff --git a/src/hotspot/cpu/x86/frame_x86.hpp b/src/hotspot/cpu/x86/frame_x86.hpp index 546c40fffe4..d97e6b847b4 100644 --- a/src/hotspot/cpu/x86/frame_x86.hpp +++ b/src/hotspot/cpu/x86/frame_x86.hpp @@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -54,7 +54,6 @@ public: enum { - pc_return_offset = 0, // All frames link_offset = 0, return_addr_offset = 1, diff --git a/src/hotspot/cpu/zero/frame_zero.hpp b/src/hotspot/cpu/zero/frame_zero.hpp index 19096615594..45d1cb82e82 100644 --- a/src/hotspot/cpu/zero/frame_zero.hpp +++ b/src/hotspot/cpu/zero/frame_zero.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright 2007, 2008, 2009, 2010 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -30,7 +30,6 @@ public: enum { - pc_return_offset = 0, metadata_words = 0, // size, in words, of metadata at frame bottom, i.e. it is not part of the // caller/callee overlap diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index 7e1391ed0f0..b70f89b2645 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -2424,9 +2424,6 @@ int AOTCodeAddressTable::id_for_address(address addr, RelocIterator reloc, CodeB id = search_address(addr, _stubs_addr, _stubs_max); if (id == BAD_ADDRESS_ID) { StubCodeDesc* desc = StubCodeDesc::desc_for(addr); - if (desc == nullptr) { - desc = StubCodeDesc::desc_for(addr + frame::pc_return_offset); - } 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 { diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index 27f01797d39..5d7df498102 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -3783,9 +3783,6 @@ const char* nmethod::reloc_string_for(u_char* begin, u_char* end) { address dest = r->destination(); if (StubRoutines::contains(dest)) { StubCodeDesc* desc = StubCodeDesc::desc_for(dest); - if (desc == nullptr) { - desc = StubCodeDesc::desc_for(dest + frame::pc_return_offset); - } if (desc != nullptr) { st.print(" Stub::%s", desc->name()); return st.as_string(); diff --git a/src/hotspot/share/code/oopRecorder.cpp b/src/hotspot/share/code/oopRecorder.cpp index c37651892cc..93c74be27b9 100644 --- a/src/hotspot/share/code/oopRecorder.cpp +++ b/src/hotspot/share/code/oopRecorder.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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -302,9 +302,6 @@ void ExternalsRecorder::print_statistics() { if (addr != nullptr) { if (StubRoutines::contains(addr)) { StubCodeDesc* desc = StubCodeDesc::desc_for(addr); - if (desc == nullptr) { - desc = StubCodeDesc::desc_for(addr + frame::pc_return_offset); - } const char* stub_name = (desc != nullptr) ? desc->name() : ""; tty->print(" stub: %s", stub_name); } else { diff --git a/src/hotspot/share/code/relocInfo.cpp b/src/hotspot/share/code/relocInfo.cpp index 73e4b6de7b4..5295dc0f287 100644 --- a/src/hotspot/share/code/relocInfo.cpp +++ b/src/hotspot/share/code/relocInfo.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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -922,9 +922,6 @@ void RelocIterator::print_current_on(outputStream* st) { st->print(" | [destination=" INTPTR_FORMAT "]", p2i(dest)); if (StubRoutines::contains(dest)) { StubCodeDesc* desc = StubCodeDesc::desc_for(dest); - if (desc == nullptr) { - desc = StubCodeDesc::desc_for(dest + frame::pc_return_offset); - } if (desc != nullptr) { st->print(" Stub::%s", desc->name()); } diff --git a/src/hotspot/share/compiler/disassembler.cpp b/src/hotspot/share/compiler/disassembler.cpp index 2c1ef235e07..9dc8956d98d 100644 --- a/src/hotspot/share/compiler/disassembler.cpp +++ b/src/hotspot/share/compiler/disassembler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -591,9 +591,6 @@ void decode_env::print_address(address adr) { if (Universe::is_fully_initialized()) { if (StubRoutines::contains(adr)) { StubCodeDesc* desc = StubCodeDesc::desc_for(adr); - if (desc == nullptr) { - desc = StubCodeDesc::desc_for(adr + frame::pc_return_offset); - } if (desc != nullptr) { st->print("Stub::%s", desc->name()); if (desc->begin() != adr) { diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index d5dccc820f3..e9143a3c4e3 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -673,7 +673,7 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread // as interpreted so the skeleton frame will be walkable // The correct pc will be set when the skeleton frame is completely filled out // The final pc we store in the loop is wrong and will be overwritten below - frame_pcs[number_of_frames - 1 - index ] = Interpreter::deopt_entry(vtos, 0) - frame::pc_return_offset; + frame_pcs[number_of_frames - 1 - index ] = Interpreter::deopt_entry(vtos, 0); callee_parameters = array->element(index)->method()->size_of_parameters(); callee_locals = array->element(index)->method()->max_locals(); diff --git a/src/hotspot/share/runtime/frame.cpp b/src/hotspot/share/runtime/frame.cpp index d99d36571ad..ae04d398043 100644 --- a/src/hotspot/share/runtime/frame.cpp +++ b/src/hotspot/share/runtime/frame.cpp @@ -206,9 +206,9 @@ address frame::raw_pc() const { if (is_deoptimized_frame()) { nmethod* nm = cb()->as_nmethod_or_null(); assert(nm != nullptr, "only nmethod is expected here"); - return nm->deopt_handler_entry() - pc_return_offset; + return nm->deopt_handler_entry(); } else { - return (pc() - pc_return_offset); + return pc(); } } diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index b799063d58e..5489735da39 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -1810,7 +1810,7 @@ JRT_LEAF(void, SharedRuntime::fixup_callers_callsite(Method* method, address cal nmethod* caller = cb->as_nmethod(); // Get the return PC for the passed caller PC. - address return_pc = caller_pc + frame::pc_return_offset; + address return_pc = caller_pc; if (!caller->is_in_use() || !NativeCall::is_call_before(return_pc)) { return; diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 856ff947dc4..3868510691a 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -1709,8 +1709,6 @@ /**********************/ \ NOT_ZERO(PPC64_ONLY(declare_constant(frame::entry_frame_locals_size))) \ \ - declare_constant(frame::pc_return_offset) \ - \ /*************/ \ /* vmSymbols */ \ /*************/ \ diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Frame.java index 978fb39ad1c..0258fea6808 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Frame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Frame.java @@ -75,12 +75,6 @@ public void update(Observable o, Object data) { /** Size of ConstMethod for computing BCI from BCP (FIXME: hack) */ private static long ConstMethodSize; - private static int pcReturnOffset; - - public static int pcReturnOffset() { - return pcReturnOffset; - } - protected void adjustForDeopt() { if (pc != null) { // Look for a deopt pc and if it is deopted convert to original pc @@ -104,8 +98,6 @@ private static synchronized void initialize(TypeDataBase db) { // FIXME: not sure whether alignment here is correct or how to // force it (round up to address size?) ConstMethodSize = ConstMethodType.getSize(); - - pcReturnOffset = db.lookupIntConstant("frame::pc_return_offset").intValue(); } protected int bcpToBci(Address bcp, ConstMethod cm) { From e5c0e0f4db80e689feb110d1b1d577adbd232a4d Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Tue, 30 Jun 2026 09:01:13 +0000 Subject: [PATCH 113/707] 8387391: hotspot_gc_shenandoah should include gtests Reviewed-by: xpeng, kdnilsen, wkemper --- test/hotspot/jtreg/TEST.groups | 3 +- .../hotspot/jtreg/gtest/ShenandoahGtests.java | 31 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 test/hotspot/jtreg/gtest/ShenandoahGtests.java diff --git a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups index e09235f6a39..f400aa22f0b 100644 --- a/test/hotspot/jtreg/TEST.groups +++ b/test/hotspot/jtreg/TEST.groups @@ -314,7 +314,8 @@ tier1_gc_shenandoah = \ gc/shenandoah/compiler/ \ gc/shenandoah/mxbeans/ \ gc/shenandoah/TestSmallHeap.java \ - gc/shenandoah/oom/ + gc/shenandoah/oom/ \ + gtest/ShenandoahGtests.java tier2_gc_shenandoah = \ runtime/MemberName/MemberNameLeak.java \ diff --git a/test/hotspot/jtreg/gtest/ShenandoahGtests.java b/test/hotspot/jtreg/gtest/ShenandoahGtests.java new file mode 100644 index 00000000000..1e8c404fc12 --- /dev/null +++ b/test/hotspot/jtreg/gtest/ShenandoahGtests.java @@ -0,0 +1,31 @@ +/* + * Copyright Amazon.com Inc. 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 Run Shenandoah gtests + * @library /test/lib + * @requires vm.gc.Shenandoah + * @requires vm.debug + * @run main/native GTestWrapper --gtest_filter=Shenandoah* + */ From 6432f5bec4014f9222d141a0af7043170e2f80ed Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Tue, 30 Jun 2026 09:01:38 +0000 Subject: [PATCH 114/707] 8387393: Problemlist compiler/escapeAnalysis/TestBCEscapeAnalyzerOverflow.java on Windows AArch64 Reviewed-by: ayang --- test/hotspot/jtreg/ProblemList.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 8d9de094323..4ac2843190c 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -70,6 +70,8 @@ compiler/c2/aarch64/TestStaticCallStub.java 8359963 generic-aarch64 compiler/unsafe/AlignmentGapAccess.java 8373487 generic-all +compiler/escapeAnalysis/TestBCEscapeAnalyzerOverflow.java 8387392 windows-aarch64 + ############################################################################# # :hotspot_gc From 9333d300aa02831ab78178449f04a4703a0b2082 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Tue, 30 Jun 2026 09:34:15 +0000 Subject: [PATCH 115/707] 8382213: Shenandoah: Drop weak root processing flags earlier Reviewed-by: kdnilsen, wkemper --- .../shenandoah/shenandoahClosures.inline.hpp | 2 +- .../gc/shenandoah/shenandoahCodeRoots.cpp | 2 +- .../gc/shenandoah/shenandoahConcurrentGC.cpp | 74 ++++++++----------- .../gc/shenandoah/shenandoahConcurrentGC.hpp | 10 +-- .../share/gc/shenandoah/shenandoahHeap.cpp | 32 +++----- .../share/gc/shenandoah/shenandoahHeap.hpp | 4 +- .../share/gc/shenandoah/shenandoahNMethod.cpp | 2 +- .../share/gc/shenandoah/shenandoahOldGC.cpp | 2 +- .../gc/shenandoah/shenandoahPhaseTimings.hpp | 4 +- .../shenandoah/shenandoahStackWatermark.cpp | 16 ++-- 10 files changed, 59 insertions(+), 89 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp index 0f2a5b48d84..f57a9b20957 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp @@ -144,7 +144,7 @@ void ShenandoahEvacuateUpdateRootClosureBase::do_oop( template template void ShenandoahEvacuateUpdateRootClosureBase::do_oop_work(T* p) { - assert(_heap->is_concurrent_weak_root_in_progress() || + assert((_heap->is_concurrent_weak_root_in_progress() && _heap->is_evacuation_in_progress()) || _heap->is_concurrent_strong_root_in_progress(), "Only do this in root processing phase"); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp index 3116ec30665..d1c25eb49b4 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp @@ -111,7 +111,7 @@ class ShenandoahNMethodUnlinkClosure : public NMethodClosure { return; } - { + if (_heap->is_evacuation_in_progress()) { ShenandoahNMethodLocker locker(nm_data->lock()); // Heal oops diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index eddeca57fd1..28f04de2f86 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -183,7 +183,7 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { assert(heap->is_concurrent_weak_root_in_progress(), "Must be doing weak roots now"); - // Concurrent stack processing + // Finish all thread/stack roots if needed. This completes stack watermark processing. if (heap->is_evacuation_in_progress()) { entry_thread_roots(); } @@ -211,6 +211,9 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { entry_strong_roots(); } + // Roots processing is complete, put the weak roots flag down. + entry_final_roots(); + // Continue the cycle with evacuation and optional update-refs. // This may be skipped if there is nothing to evacuate. // If so, evac_in_progress would be unset by collection set preparation code. @@ -249,9 +252,18 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { entry_cleanup_complete(); } else { _abbreviated = true; - if (!entry_final_roots()) { - assert(_degen_point != _degenerated_unset, "Need to know where to start degenerated cycle"); - return false; + + if (heap->mode()->is_generational()) { + entry_complete_abbreviated_cycle(); + + // If the promote-in-place operation was cancelled, we can have the degenerated + // cycle complete the operation. It will see that no evacuations are in progress, + // and that there are regions wanting promotion. The risk with not handling the + // cancellation would be failing to restore top for these regions and leaving + // them unable to serve allocations for the old generation. + if (check_cancellation_and_abort(ShenandoahDegenPoint::_degenerated_evac)) { + return false; + } } // In normal cycle, final-update-refs would verify at the end of the cycle. @@ -275,34 +287,34 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { return true; } -bool ShenandoahConcurrentGC::complete_abbreviated_cycle() { +void ShenandoahConcurrentGC::entry_complete_abbreviated_cycle() { shenandoah_assert_generational(); ShenandoahGenerationalHeap* const heap = ShenandoahGenerationalHeap::heap(); + TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); + static const char* msg = "Concurrent complete abbreviated cycle"; + ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::complete_abbreviated); + EventMark em("%s", msg); + + ShenandoahWorkerScope scope(heap->workers(), + ShenandoahWorkerPolicy::calc_workers_for_conc_evac(), + msg); + // We chose not to evacuate because we found sufficient immediate garbage. // However, there may still be regions to promote in place, so do that now. if (heap->old_generation()->has_in_place_promotions()) { - entry_promote_in_place(); - - // If the promote-in-place operation was cancelled, we can have the degenerated - // cycle complete the operation. It will see that no evacuations are in progress, - // and that there are regions wanting promotion. The risk with not handling the - // cancellation would be failing to restore top for these regions and leaving - // them unable to serve allocations for the old generation.This will leave the weak - // roots flag set (the degenerated cycle will unset it). - if (check_cancellation_and_abort(ShenandoahDegenPoint::_degenerated_evac)) { - return false; - } + ShenandoahTimingsTracker timing(ShenandoahPhaseTimings::complete_abbreviated_promote_in_place); + ShenandoahGCWorkerPhase worker_phase(ShenandoahPhaseTimings::complete_abbreviated_promote_in_place); + heap->promote_regions_in_place(_generation, true); } // At this point, the cycle is effectively complete. If the cycle has been cancelled here, // the control thread will detect it on its next iteration and run a degenerated young cycle. - if (!_generation->is_old()) { + if (!heap->cancelled_gc() && !_generation->is_old()) { + ShenandoahTimingsTracker tracker(ShenandoahPhaseTimings::complete_abbreviated_update_region_ages); heap->update_region_ages(_generation->complete_marking_context()); } - - return true; } void ShenandoahConcurrentGC::vmop_entry_init_mark() { @@ -582,16 +594,6 @@ void ShenandoahConcurrentGC::entry_evacuate() { op_evacuate(); } -void ShenandoahConcurrentGC::entry_promote_in_place() const { - shenandoah_assert_generational(); - - ShenandoahTimingsTracker timing(ShenandoahPhaseTimings::promote_in_place); - ShenandoahGCWorkerPhase worker_phase(ShenandoahPhaseTimings::promote_in_place); - EventMark em("%s", "Promote in place"); - - ShenandoahGenerationalHeap::heap()->promote_regions_in_place(_generation, true); -} - void ShenandoahConcurrentGC::entry_update_thread_roots() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); @@ -1227,26 +1229,14 @@ void ShenandoahConcurrentGC::op_final_update_refs() { } } -bool ShenandoahConcurrentGC::entry_final_roots() { +void ShenandoahConcurrentGC::entry_final_roots() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - - const char* msg = conc_final_roots_event_message(); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_final_roots); EventMark em("%s", msg); - ShenandoahWorkerScope scope(heap->workers(), - ShenandoahWorkerPolicy::calc_workers_for_conc_evac(), - msg); - - if (heap->mode()->is_generational()) { - if (!complete_abbreviated_cycle()) { - return false; - } - } heap->concurrent_final_roots(); - return true; } void ShenandoahConcurrentGC::op_verify_final() { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp index fde585b4aa9..e763d1853e3 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp @@ -91,6 +91,8 @@ class ShenandoahConcurrentGC : public ShenandoahGC { void entry_class_unloading(); void entry_strong_roots(); void entry_cleanup_early(); + void entry_complete_abbreviated_cycle(); + void entry_final_roots(); void entry_evacuate(); void entry_update_thread_roots(); void entry_update_card_table(); @@ -98,12 +100,6 @@ class ShenandoahConcurrentGC : public ShenandoahGC { void entry_update_refs(); void entry_cleanup_complete(); - // This is the last phase of a cycle which performs no evacuations - bool entry_final_roots(); - - // Called when the collection set is empty, but the generational mode has regions to promote in place - void entry_promote_in_place() const; - // Actual work for the phases void op_reset(); void op_init_mark(); @@ -135,8 +131,6 @@ class ShenandoahConcurrentGC : public ShenandoahGC { private: void start_mark(); - bool complete_abbreviated_cycle(); - static bool has_in_place_promotions(ShenandoahHeap* heap); // Messages for GC trace events, they have to be immortal for diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index e60db88974a..ae0c873fa58 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -1236,7 +1236,6 @@ void ShenandoahHeap::concurrent_prepare_for_update_refs() { // A cancellation at this point means the degenerated cycle must resume from update-refs. set_gc_state_concurrent(EVACUATION, false); - set_gc_state_concurrent(WEAK_ROOTS, false); set_gc_state_concurrent(UPDATE_REFS, true); } @@ -1252,35 +1251,24 @@ void ShenandoahHeap::concurrent_prepare_for_update_refs() { _update_refs_iterator.reset(); } -class ShenandoahCompositeHandshakeClosure : public HandshakeClosure { - HandshakeClosure* _handshake_1; - HandshakeClosure* _handshake_2; - public: - ShenandoahCompositeHandshakeClosure(HandshakeClosure* handshake_1, HandshakeClosure* handshake_2) : - HandshakeClosure(handshake_2->name()), - _handshake_1(handshake_1), _handshake_2(handshake_2) {} +void ShenandoahHeap::concurrent_final_roots() { + { + MutexLocker lock(Threads_lock); - void do_thread(Thread* thread) override { - _handshake_1->do_thread(thread); - _handshake_2->do_thread(thread); +#ifdef ASSERT + for (JavaThreadIteratorWithHandle jtiwh; JavaThread* jt = jtiwh.next();) { + StackWatermark* sw = StackWatermarkSet::get(jt, StackWatermarkKind::gc); + assert(sw == nullptr || sw->processing_completed(), + "Cannot turn off weak roots before stack watermark processing is complete"); } -}; +#endif -void ShenandoahHeap::concurrent_final_roots(HandshakeClosure* handshake_closure) { - { - assert(!is_evacuation_in_progress(), "Should not evacuate for abbreviated or old cycles"); - MutexLocker lock(Threads_lock); set_gc_state_concurrent(WEAK_ROOTS, false); } ShenandoahGCStatePropagatorHandshakeClosure propagator(_gc_state.raw_value()); Threads::non_java_threads_do(&propagator); - if (handshake_closure == nullptr) { - Handshake::execute(&propagator); - } else { - ShenandoahCompositeHandshakeClosure composite(&propagator, handshake_closure); - Handshake::execute(&composite); - } + Handshake::execute(&propagator); } oop ShenandoahHeap::evacuate_object(oop p, Thread* thread) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index 86707c7e831..9810b316c21 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -493,8 +493,8 @@ class ShenandoahHeap : public CollectedHeap { // Retires LABs used for evacuation void concurrent_prepare_for_update_refs(); - // Turn off weak roots flag, purge old satb buffers in generational mode - void concurrent_final_roots(HandshakeClosure* handshake_closure = nullptr); + // Turn off weak roots flag + void concurrent_final_roots(); virtual void update_heap_references(ShenandoahGeneration* generation, bool concurrent); // Final update region states diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp index 5b24cfc979a..b0573c3f677 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp @@ -124,7 +124,7 @@ void ShenandoahNMethod::heal_nmethod(nmethod* nm) { assert(data->lock()->owned_by_self(), "Must hold the lock"); ShenandoahHeap* const heap = ShenandoahHeap::heap(); - if (heap->is_concurrent_weak_root_in_progress() || + if ((heap->is_concurrent_weak_root_in_progress() && heap->is_evacuation_in_progress()) || heap->is_concurrent_strong_root_in_progress()) { heal_nmethod_metadata(data); } else if (heap->is_concurrent_mark_in_progress()) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp index ff441a0c868..df41069d922 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp @@ -134,7 +134,7 @@ bool ShenandoahOldGC::collect(GCCause::Cause cause) { // return from here with weak roots in progress. This is not a valid gc state // for any young collections (or allocation failures) that interrupt the old // collection. - heap->concurrent_final_roots(); + entry_final_roots(); // After concurrent old marking finishes, we reclaim immediate garbage. Further, we may also want to expand OLD in order // to make room for anticipated promotions and/or for mixed evacuations. Mixed evacuations are especially likely to diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp index bc52d755139..dfb42e0b76f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp @@ -106,8 +106,10 @@ class outputStream; " CE: ") \ SHENANDOAH_SIMPLE_PHASE_DEF(f, conc_update_card_table, "Concurrent Update Cards") \ SHENANDOAH_SIMPLE_PHASE_DEF(f, conc_final_roots, "Concurrent Final Roots") \ - SHENANDOAH_WORKER_PHASE_DEF(f, promote_in_place, " Promote Regions", \ + SHENANDOAH_SIMPLE_PHASE_DEF(f, complete_abbreviated, "Complete Abbreviated Cycle") \ + SHENANDOAH_WORKER_PHASE_DEF(f, complete_abbreviated_promote_in_place, " Promote Regions", \ " PIP: ") \ + SHENANDOAH_SIMPLE_PHASE_DEF(f, complete_abbreviated_update_region_ages, " Update Region Ages") \ SHENANDOAH_SIMPLE_PHASE_DEF(f, final_verify_gross, "Pause Final Verify (G)") \ SHENANDOAH_SIMPLE_PHASE_DEF(f, final_verify, "Pause Final Verify (N)") \ SHENANDOAH_SIMPLE_PHASE_DEF(f, init_update_refs_gross, "Pause Init Update Refs (G)") \ diff --git a/src/hotspot/share/gc/shenandoah/shenandoahStackWatermark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahStackWatermark.cpp index 81c584dfa37..8df2449f8b6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahStackWatermark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahStackWatermark.cpp @@ -67,14 +67,13 @@ ShenandoahStackWatermark::ShenandoahStackWatermark(JavaThread* jt) : OopClosure* ShenandoahStackWatermark::closure_from_context(void* context) { if (context != nullptr) { - assert(_heap->is_concurrent_weak_root_in_progress() || + assert((_heap->is_concurrent_weak_root_in_progress() && _heap->is_evacuation_in_progress()) || _heap->is_concurrent_mark_in_progress(), "Only these two phases"); assert(Thread::current()->is_Worker_thread(), "Unexpected thread passing in context: " PTR_FORMAT, p2i(context)); return reinterpret_cast(context); } else { - if (_heap->is_concurrent_weak_root_in_progress()) { - assert(_heap->is_evacuation_in_progress(), "Nothing to evacuate"); + if (_heap->is_concurrent_weak_root_in_progress() && _heap->is_evacuation_in_progress()) { return &_evac_update_oop_cl; } else if (_heap->is_concurrent_mark_in_progress()) { return &_keep_alive_cl; @@ -87,11 +86,9 @@ OopClosure* ShenandoahStackWatermark::closure_from_context(void* context) { void ShenandoahStackWatermark::start_processing_impl(void* context) { NoSafepointVerifier nsv; - ShenandoahHeap* const heap = ShenandoahHeap::heap(); // Process the non-frame part of the thread - if (heap->is_concurrent_weak_root_in_progress()) { - assert(heap->is_evacuation_in_progress(), "Should not be armed"); + if (_heap->is_concurrent_weak_root_in_progress() && _heap->is_evacuation_in_progress()) { // Retire the TLABs, which will force threads to reacquire their TLABs. // This is needed for two reasons. Strong one: new allocations would be with new freeset, // which would be outside the collection set, so no cset writes would happen there. @@ -100,7 +97,7 @@ void ShenandoahStackWatermark::start_processing_impl(void* context) { retire_tlab(); _jt->oops_do_no_frames(closure_from_context(context), &_nm_cl); - } else if (heap->is_concurrent_mark_in_progress()) { + } else if (_heap->is_concurrent_mark_in_progress()) { // We need to reset all TLABs because they might be below the TAMS, and we need to mark // the objects in them. Do not let mutators allocate any new objects in their current TLABs. // It is also a good place to resize the TLAB sizes for future allocations. @@ -129,9 +126,8 @@ void ShenandoahStackWatermark::retire_tlab() { void ShenandoahStackWatermark::process(const frame& fr, RegisterMap& register_map, void* context) { OopClosure* oops = closure_from_context(context); assert(oops != nullptr, "Should not get to here"); - ShenandoahHeap* const heap = ShenandoahHeap::heap(); - assert((heap->is_concurrent_weak_root_in_progress() && heap->is_evacuation_in_progress()) || - heap->is_concurrent_mark_in_progress(), + assert((_heap->is_concurrent_weak_root_in_progress() && _heap->is_evacuation_in_progress()) || + _heap->is_concurrent_mark_in_progress(), "Only these two phases"); fr.oops_do(oops, &_nm_cl, ®ister_map, DerivedPointerIterationMode::_directly); } From 88d111e24ec11910b19cc482b466df01bc03c30e Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 30 Jun 2026 12:21:51 +0000 Subject: [PATCH 116/707] 8387258: Test jdk/jfr/event/runtime/TestResidentSetSizeEvent.java failed on Windows: The size should be less than or equal to peak Reviewed-by: dholmes, mgronlun --- src/hotspot/os/windows/os_windows.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 2b74cccb072..0fc636483f5 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -6387,7 +6387,7 @@ void os::jfr_report_memory_info() { // Send the RSS JFR event EventResidentSetSize event; event.set_size(pmex.WorkingSetSize); - event.set_peak(pmex.PeakWorkingSetSize); + event.set_peak(MAX2(pmex.PeakWorkingSetSize, pmex.WorkingSetSize)); event.commit(); } else { // Log a warning From fa2ca3d087adaeb1bd5f449edf916887d817fb6d Mon Sep 17 00:00:00 2001 From: Martin Doerr Date: Tue, 30 Jun 2026 14:21:06 +0000 Subject: [PATCH 117/707] 8387184: [PPC64] C1 logic operations should support generic constants Reviewed-by: rrich, dbriemann --- src/hotspot/cpu/ppc/assembler_ppc.cpp | 47 ++++++++--- src/hotspot/cpu/ppc/assembler_ppc.hpp | 10 ++- src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp | 36 +++++--- src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp | 15 ++-- src/hotspot/cpu/ppc/ppc.ad | 92 +++------------------ 5 files changed, 85 insertions(+), 115 deletions(-) diff --git a/src/hotspot/cpu/ppc/assembler_ppc.cpp b/src/hotspot/cpu/ppc/assembler_ppc.cpp index ab16fc437e9..406d0b446a4 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.cpp @@ -75,23 +75,46 @@ int Assembler::branch_destination(int inst, int pos) { return r; } -// Low-level andi-one-instruction-macro. -void Assembler::andi(Register a, Register s, const long ui16) { - if (is_power_of_2(((unsigned long) ui16)+1)) { +// Low-level andi-one-instruction-macro. May clobber CR0. +void Assembler::andi(Register a, Register s, julong int_or_long_const) { + // Instructions which don't set CR0 are preferred. + if (int_or_long_const == 0) { + // should not be handled as pow2minus1 + li(a, 0); + } else if (is_power_of_2(int_or_long_const + 1)) { // pow2minus1 - clrldi(a, s, 64 - log2i_exact((((unsigned long) ui16)+1))); - } else if (is_power_of_2((jlong) ui16)) { - // pow2 - rlwinm(a, s, 0, 31 - log2i_exact((jlong) ui16), 31 - log2i_exact((jlong) ui16)); - } else if (is_power_of_2((jlong)-ui16)) { - // negpow2 - clrrdi(a, s, log2i_exact((jlong)-ui16)); + clrldi(a, s, 64 - log2i_exact(int_or_long_const + 1)); + } else if (is_power_of_2(-int_or_long_const)) { + // negpow2 (includes (julong)min_jlong) + clrrdi(a, s, log2i_exact(-int_or_long_const)); + } else if (is_uimm((jlong)int_or_long_const, 32) && has_consecutive_ones(int_or_long_const)) { + // consecutive ones + rlwinm(a, s, 0, count_leading_zeros((uint32_t)int_or_long_const), + 31 - count_trailing_zeros((uint32_t)int_or_long_const)); + } else if (is_uimm((jlong)int_or_long_const, 16)) { + // side effect: clobbers CR0 + andi_(a, s, int_or_long_const); } else { - assert(is_uimm(ui16, 16), "must be 16-bit unsigned immediate"); - andi_(a, s, ui16); + assert(is_uimm((jlong)int_or_long_const, 32) && (int_or_long_const & 0xFFFF) == 0, + "not encodable: " UINT64_FORMAT_X, int_or_long_const); + // side effect: clobbers CR0 + andis_(a, s, int_or_long_const >> 16); } } +// Check if int_or_long_const is supported by Assembler::andi. +bool Assembler::andi_supports(julong int_or_long_const) { + // 16 bit always possible by andi_ (but other instructions are preferred) + if (is_uimm((jlong)int_or_long_const, 16)) return true; + + // special cases 32 bit: higher 16 bit and consecutive ones are supported + if (is_uimm((jlong)int_or_long_const, 32) && + ((int_or_long_const & 0xFFFF) == 0 || has_consecutive_ones(int_or_long_const))) return true; + + // special cases 64 bit: clrldi, clrrdi + return is_power_of_2(int_or_long_const + 1) || is_power_of_2(-int_or_long_const); +} + // RegisterOrConstant version. void Assembler::ld(Register d, RegisterOrConstant roc, Register s1) { if (roc.is_constant()) { diff --git a/src/hotspot/cpu/ppc/assembler_ppc.hpp b/src/hotspot/cpu/ppc/assembler_ppc.hpp index f62c93e466c..77c7f63cd06 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.hpp @@ -1048,6 +1048,13 @@ class Assembler : public AbstractAssembler { return (julong)x < maxplus1; } + // Test if x has exactly one consecutive range of one bits (e.g. 00111000) + static bool has_consecutive_ones(julong x) { + if (x == max_julong) return true; + if (x == 0) return false; + return is_power_of_2((x >> count_trailing_zeros(x)) + 1); + } + protected: // helpers @@ -1606,7 +1613,8 @@ class Assembler : public AbstractAssembler { inline void isel_0( Register d, ConditionRegister cr, Condition cc, Register b = noreg); // PPC 1, section 3.3.11, Fixed-Point Logical Instructions - void andi( Register a, Register s, long ui16); // optimized version + void andi( Register a, Register s, julong int_or_long_const); // optimized version, may clobber CR0 + static bool andi_supports(julong int_or_long_const); inline void andi_( Register a, Register s, int ui16); inline void andis_( Register a, Register s, int ui16); inline void ori( Register a, Register s, int ui16); diff --git a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp index 78fae5c2677..1ec710aad29 100644 --- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp @@ -1669,26 +1669,40 @@ void LIR_Assembler::logic_op(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr d = dest->as_register_lo(); l = left->as_register_lo(); } - long uimms = (unsigned long)uimm >> 16, - uimmss = (unsigned long)uimm >> 32; + long uimms = (unsigned long)uimm >> 16; switch (code) { case lir_logic_and: - if (uimmss != 0 || (uimms != 0 && (uimm & 0xFFFF) != 0) || is_power_of_2(uimm)) { - __ andi(d, l, uimm); // special cases - } else if (uimms != 0) { __ andis_(d, l, uimms); } - else { __ andi_(d, l, uimm); } + if (Assembler::andi_supports(uimm)) { + __ andi(d, l, uimm); // includes andis_ and special cases + } else { // for operands which are not generated by LIRGenerator::do_LogicOp + __ load_const_optimized(R0, uimm); + __ andr(d, l, R0); + } break; case lir_logic_or: - if (uimms != 0) { assert((uimm & 0xFFFF) == 0, "sanity"); __ oris(d, l, uimms); } - else { __ ori(d, l, uimm); } + if (Assembler::is_uimm(uimm, 16)) { + __ ori(d, l, uimm); + } else if ((uimm & 0xFFFF) == 0 && Assembler::is_uimm(uimms, 16)) { + __ oris(d, l, uimms); + } else { // for operands which are not generated by LIRGenerator::do_LogicOp + __ load_const_optimized(R0, uimm); + __ orr(d, l, R0); + } break; case lir_logic_xor: - if (uimm == -1) { __ nand(d, l, l); } // special case - else if (uimms != 0) { assert((uimm & 0xFFFF) == 0, "sanity"); __ xoris(d, l, uimms); } - else { __ xori(d, l, uimm); } + if (Assembler::is_uimm(uimm, 16)) { + __ xori(d, l, uimm); + } else if ((uimm & 0xFFFF) == 0 && Assembler::is_uimm(uimms, 16)) { + __ xoris(d, l, uimms); + } else if (uimm == -1) { + __ nand(d, l, l); // special case + } else { // for operands which are not generated by LIRGenerator::do_LogicOp + __ load_const_optimized(R0, uimm); + __ xorr(d, l, R0); + } break; default: ShouldNotReachHere(); diff --git a/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp index a652a155f62..56c069053c6 100644 --- a/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp @@ -578,18 +578,13 @@ inline bool can_handle_logic_op_as_uimm(ValueType *type, Bytecodes::Code bc) { Assembler::is_uimm((jlong)((julong)int_or_long_const >> 16), 16)) return true; // see Assembler::andi - if (bc == Bytecodes::_iand && - (is_power_of_2(int_or_long_const+1) || - is_power_of_2(int_or_long_const) || - is_power_of_2(-int_or_long_const))) return true; - if (bc == Bytecodes::_land && - (is_power_of_2((unsigned long)int_or_long_const+1) || - (Assembler::is_uimm(int_or_long_const, 32) && is_power_of_2(int_or_long_const)) || - (int_or_long_const != min_jlong && is_power_of_2(-int_or_long_const)))) return true; + if ((bc == Bytecodes::_iand || bc == Bytecodes::_land)) + return Assembler::andi_supports(int_or_long_const); // special case: xor -1 - if ((bc == Bytecodes::_ixor || bc == Bytecodes::_lxor) && - int_or_long_const == -1) return true; + if ((bc == Bytecodes::_ixor || bc == Bytecodes::_lxor)) + return (int_or_long_const == -1); + return false; } diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index e7464feb4ab..896128f99cc 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -9155,61 +9155,14 @@ instruct andI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ ins_pipe(pipe_class_default); %} -// Left shifted Immediate And -instruct andI_reg_immIhi16(iRegIdst dst, iRegIsrc src1, immIhi16 src2, flagsRegCR0 cr0) %{ +instruct andI_reg_immI(iRegIdst dst, iRegIsrc src1, immI src2, flagsRegCR0 cr0) %{ match(Set dst (AndI src1 src2)); + predicate(Assembler::andi_supports((juint)(n->in(2)->get_int()))); effect(KILL cr0); - format %{ "ANDIS $dst, $src1, $src2.hi" %} - size(4); - ins_encode %{ - __ andis_($dst$$Register, $src1$$Register, (int)((unsigned short)(($src2$$constant & 0xFFFF0000) >> 16))); - %} - ins_pipe(pipe_class_default); -%} - -// Immediate And -instruct andI_reg_uimm16(iRegIdst dst, iRegIsrc src1, uimmI16 src2, flagsRegCR0 cr0) %{ - match(Set dst (AndI src1 src2)); - effect(KILL cr0); - format %{ "ANDI $dst, $src1, $src2" %} size(4); ins_encode %{ - // FIXME: avoid andi_ ? - __ andi_($dst$$Register, $src1$$Register, $src2$$constant); - %} - ins_pipe(pipe_class_default); -%} - -// Immediate And where the immediate is a negative power of 2. -instruct andI_reg_immInegpow2(iRegIdst dst, iRegIsrc src1, immInegpow2 src2) %{ - match(Set dst (AndI src1 src2)); - format %{ "ANDWI $dst, $src1, $src2" %} - size(4); - ins_encode %{ - __ clrrdi($dst$$Register, $src1$$Register, log2i_exact(-(juint)$src2$$constant)); - %} - ins_pipe(pipe_class_default); -%} - -instruct andI_reg_immIpow2minus1(iRegIdst dst, iRegIsrc src1, immIpow2minus1 src2) %{ - match(Set dst (AndI src1 src2)); - format %{ "ANDWI $dst, $src1, $src2" %} - size(4); - ins_encode %{ - __ clrldi($dst$$Register, $src1$$Register, 64 - log2i_exact((juint)$src2$$constant + 1u)); - %} - ins_pipe(pipe_class_default); -%} - -instruct andI_reg_immIpowerOf2(iRegIdst dst, iRegIsrc src1, immIpowerOf2 src2) %{ - match(Set dst (AndI src1 src2)); - predicate(UseRotateAndMaskInstructionsPPC64); - format %{ "ANDWI $dst, $src1, $src2" %} - size(4); - ins_encode %{ - int bitpos = 31 - log2i_exact((juint)$src2$$constant); - __ rlwinm($dst$$Register, $src1$$Register, 0, bitpos, bitpos); + __ andi($dst$$Register, $src1$$Register, (juint)$src2$$constant); // optimized version %} ins_pipe(pipe_class_default); %} @@ -9227,50 +9180,27 @@ instruct andL_reg_reg(iRegLdst dst, iRegLsrc src1, iRegLsrc src2) %{ ins_pipe(pipe_class_default); %} -// Immediate And long -instruct andL_reg_uimm16(iRegLdst dst, iRegLsrc src1, uimmL16 src2, flagsRegCR0 cr0) %{ +instruct andL_reg_immL(iRegLdst dst, iRegLsrc src1, immL src2, flagsRegCR0 cr0) %{ match(Set dst (AndL src1 src2)); + predicate(Assembler::andi_supports(n->in(2)->get_long())); effect(KILL cr0); - format %{ "ANDI $dst, $src1, $src2 \t// long" %} size(4); ins_encode %{ - // FIXME: avoid andi_ ? - __ andi_($dst$$Register, $src1$$Register, $src2$$constant); - %} - ins_pipe(pipe_class_default); -%} - -// Immediate And Long where the immediate is a negative power of 2. -instruct andL_reg_immLnegpow2(iRegLdst dst, iRegLsrc src1, immLnegpow2 src2) %{ - match(Set dst (AndL src1 src2)); - format %{ "ANDDI $dst, $src1, $src2" %} - size(4); - ins_encode %{ - __ clrrdi($dst$$Register, $src1$$Register, log2i_exact(-(julong)$src2$$constant)); - %} - ins_pipe(pipe_class_default); -%} - -instruct andL_reg_immLpow2minus1(iRegLdst dst, iRegLsrc src1, immLpow2minus1 src2) %{ - match(Set dst (AndL src1 src2)); - format %{ "ANDDI $dst, $src1, $src2" %} - size(4); - ins_encode %{ - __ clrldi($dst$$Register, $src1$$Register, 64 - log2i_exact((julong)$src2$$constant + 1ull)); + __ andi($dst$$Register, $src1$$Register, $src2$$constant); // optimized version %} ins_pipe(pipe_class_default); %} // AndL + ConvL2I. -instruct convL2I_andL_reg_immLpow2minus1(iRegIdst dst, iRegLsrc src1, immLpow2minus1 src2) %{ +instruct convL2I_andL_reg_immL(iRegIdst dst, iRegLsrc src1, immL src2, flagsRegCR0 cr0) %{ match(Set dst (ConvL2I (AndL src1 src2))); - ins_cost(DEFAULT_COST); - - format %{ "ANDDI $dst, $src1, $src2 \t// long + l2i" %} + predicate(Assembler::andi_supports(n->in(1)->in(2)->get_long())); + effect(KILL cr0); + format %{ "ANDI $dst, $src1, $src2 \t// long + l2i" %} size(4); ins_encode %{ - __ clrldi($dst$$Register, $src1$$Register, 64 - log2i_exact((julong)$src2$$constant + 1ull)); + __ andi($dst$$Register, $src1$$Register, $src2$$constant); // optimized version %} ins_pipe(pipe_class_default); %} From 45d3532a2ce7e2d0cc1c6c65b0cf7301569af1b6 Mon Sep 17 00:00:00 2001 From: Matias Saavedra Silva Date: Tue, 30 Jun 2026 15:28:53 +0000 Subject: [PATCH 118/707] 8380750: Test runtime/cds/appcds/TestSerialGCWithCDS.java#id1 failed: StringIndexOutOfBoundsException Reviewed-by: coleenp, iklam --- test/hotspot/jtreg/runtime/cds/TestCDSVMCrash.java | 2 +- test/lib/jdk/test/lib/cds/CDSTestUtils.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/runtime/cds/TestCDSVMCrash.java b/test/hotspot/jtreg/runtime/cds/TestCDSVMCrash.java index 8241b0f9a2e..2c796243cbf 100644 --- a/test/hotspot/jtreg/runtime/cds/TestCDSVMCrash.java +++ b/test/hotspot/jtreg/runtime/cds/TestCDSVMCrash.java @@ -62,7 +62,7 @@ public static void main(String[] args) throws Exception { throw new Error("Expected VM to crash"); } catch(RuntimeException e) { if (!e.getMessage().contains("A fatal error has been detected")) { - throw new Error("Expected message: A fatal error has been detected"); + throw new Error("Expected message: A fatal error has been detected. Instead message is: " + e.getMessage()); } } System.out.println("PASSED"); diff --git a/test/lib/jdk/test/lib/cds/CDSTestUtils.java b/test/lib/jdk/test/lib/cds/CDSTestUtils.java index 59e4a1bbbde..8060eb92a87 100644 --- a/test/lib/jdk/test/lib/cds/CDSTestUtils.java +++ b/test/lib/jdk/test/lib/cds/CDSTestUtils.java @@ -703,7 +703,7 @@ public static OutputAnalyzer executeAndLog(Process process, String logName) thro static String getCrashMessage(String stdOut) { int start = stdOut.indexOf("# A fatal error has been detected by the Java Runtime Environment:"); - int end = stdOut.indexOf(".log", start) + 4; + int end = stdOut.indexOf("# JRE version", start); return stdOut.substring(start, end); } From 4bf4a60f76a9980fdce54fa85cec223ebd5e025e Mon Sep 17 00:00:00 2001 From: Mark Powers Date: Tue, 30 Jun 2026 18:07:11 +0000 Subject: [PATCH 119/707] 8387123: Remove LuxTrust Global Root CA Reviewed-by: mullan, rhalade --- .../share/data/cacerts/luxtrustglobalrootca | 28 ------------------- .../security/lib/cacerts/VerifyCACerts.java | 10 ++----- 2 files changed, 3 insertions(+), 35 deletions(-) delete mode 100644 src/java.base/share/data/cacerts/luxtrustglobalrootca diff --git a/src/java.base/share/data/cacerts/luxtrustglobalrootca b/src/java.base/share/data/cacerts/luxtrustglobalrootca deleted file mode 100644 index 7fb3d818f80..00000000000 --- a/src/java.base/share/data/cacerts/luxtrustglobalrootca +++ /dev/null @@ -1,28 +0,0 @@ -Owner: CN=LuxTrust Global Root, O=LuxTrust s.a., C=LU -Issuer: CN=LuxTrust Global Root, O=LuxTrust s.a., C=LU -Serial number: bb8 -Valid from: Thu Mar 17 09:51:37 GMT 2011 until: Wed Mar 17 09:51:37 GMT 2021 -Signature algorithm name: SHA256withRSA -Subject Public Key Algorithm: 2048-bit RSA key -Version: 3 ------BEGIN CERTIFICATE----- -MIIDZDCCAkygAwIBAgICC7gwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCTFUx -FjAUBgNVBAoTDUx1eFRydXN0IHMuYS4xHTAbBgNVBAMTFEx1eFRydXN0IEdsb2Jh -bCBSb290MB4XDTExMDMxNzA5NTEzN1oXDTIxMDMxNzA5NTEzN1owRDELMAkGA1UE -BhMCTFUxFjAUBgNVBAoTDUx1eFRydXN0IHMuYS4xHTAbBgNVBAMTFEx1eFRydXN0 -IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsn+n -QPAiygz267Hxyw6VV0B1r6A/Ps7sqjJX5hmxZ0OYWmt8s7j6eJyqpoSyYBuAQc5j -zR8XCJmk9e8+EsdMsFeaXHhAePxFjdqRZ9w6Ubltc+a3OY52OrQfBfVpVfmTz3iI -Sr6qm9d7R1tGBEyCFqY19vx039a0r9jitScRdFmiwmYsaArhmIiIPIoFdRTjuK7z -CISbasE/MRivJ6VLm6T9eTHemD0OYcqHmMH4ijCc+j4z1aXEAwfh95Z0GAAnOCfR -K6qq4UFFi2/xJcLcopeVx0IUM115hCNq52XAV6DYXaljAeew5Ivo+MVjuOVsdJA9 -x3f8K7p56aTGEnin/wIDAQABo2AwXjAMBgNVHRMEBTADAQH/MA4GA1UdDwEB/wQE -AwIBBjAfBgNVHSMEGDAWgBQXFYWJCS8kh28/HRvk8pZ5g0gTzjAdBgNVHQ4EFgQU -FxWFiQkvJIdvPx0b5PKWeYNIE84wDQYJKoZIhvcNAQELBQADggEBAFrwHNDUUM9B -fua4nX3DcNBeNv9ujnov3kgR1TQuPLdFwlQlp+HBHjeDtpSutkVIA+qVvuucarQ3 -XB8u02uCgUNbCj8RVWOs+nwIAjegPDkEM/6XMshS5dklTbDG7mgfcKpzzlcD3H0K -DTPy0lrfCmw7zBFRlxqkIaKFNQLXgCLShLL4wKpov9XrqsMLq6F8K/f1O4fhVFfs -BSTveUJO84ton+Ruy4KZycwq3FPCH3CDqyEPVrRI/98HIrOM+R2mBN8tAza53W/+ -MYhm/2xtRDSvCHc+JtJy9LtHVpM8mGPhM7uZI5K1g3noHZ9nrWLWidb2/CfeMifL -hNp3hSGhEiE= ------END CERTIFICATE----- diff --git a/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java b/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java index c2c58b36c38..82b6a6c257e 100644 --- a/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java +++ b/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java @@ -28,7 +28,7 @@ * 8223499 8225392 8232019 8234245 8233223 8225068 8225069 8243321 8243320 * 8243559 8225072 8258630 8259312 8256421 8225081 8225082 8225083 8245654 * 8305975 8304760 8307134 8295894 8314960 8317373 8317374 8318759 8319187 - * 8321408 8316138 8341057 8303770 8350498 8359170 8361212 8372351 + * 8321408 8316138 8341057 8303770 8350498 8359170 8361212 8372351 8387123 * @summary Check root CA entries in cacerts file */ import java.io.ByteArrayInputStream; @@ -47,12 +47,12 @@ public class VerifyCACerts { + File.separator + "security" + File.separator + "cacerts"; // The numbers of certs now. - private static final int COUNT = 111; + private static final int COUNT = 110; // SHA-256 of cacerts, can be generated with // shasum -a 256 cacerts | sed -e 's/../&:/g' | tr '[:lower:]' '[:upper:]' | cut -c1-95 private static final String CHECKSUM - = "26:75:A0:AA:6E:7C:15:8B:BC:CF:11:81:38:3E:E7:94:31:9E:36:2D:F9:A6:BC:88:E1:A5:F8:46:9A:4C:1D:D7"; + = "AA:C2:64:41:28:06:1F:83:92:54:7C:DD:95:82:61:4C:8F:FA:09:7B:17:64:A7:A8:7C:A9:F6:27:25:95:2D:BB"; // Hex formatter to upper case with ":" delimiter private static final HexFormat HEX = HexFormat.ofDelimiter(":").withUpperCase(); @@ -143,8 +143,6 @@ public class VerifyCACerts { "96:BC:EC:06:26:49:76:F3:74:60:77:9A:CF:28:C5:A7:CF:E8:A3:C0:AA:E1:1A:8F:FC:EE:05:C0:BD:DF:08:C6"); put("letsencryptisrgx2 [jdk]", "69:72:9B:8E:15:A8:6E:FC:17:7A:57:AF:B7:17:1D:FC:64:AD:D2:8C:2F:CA:8C:F1:50:7E:34:45:3C:CB:14:70"); - put("luxtrustglobalrootca [jdk]", - "A1:B2:DB:EB:64:E7:06:C6:16:9E:3C:41:18:B2:3B:AA:09:01:8A:84:27:66:6D:8B:F0:E2:88:91:EC:05:19:50"); put("quovadisrootca [jdk]", "A4:5E:DE:3B:BB:F0:9C:8A:E1:5C:72:EF:C0:72:68:D6:93:A2:1C:99:6F:D5:1E:67:CA:07:94:60:FD:6D:88:73"); put("quovadisrootca1g3 [jdk]", @@ -296,8 +294,6 @@ public class VerifyCACerts { add("addtrustexternalca [jdk]"); // Valid until: Sat May 30 10:44:50 GMT 2020 add("addtrustqualifiedca [jdk]"); - // Valid until: Wed Mar 17 02:51:37 PDT 2021 - add("luxtrustglobalrootca [jdk]"); // Valid until: Wed Mar 17 11:33:33 PDT 2021 add("quovadisrootca [jdk]"); // Valid until: Sat May 21 04:00:00 GMT 2022 From c7816b0b444019aef047b7f0d8281cbf3b8d17fb Mon Sep 17 00:00:00 2001 From: Chad Rakoczy Date: Tue, 30 Jun 2026 19:44:04 +0000 Subject: [PATCH 120/707] 8382135: AArch64: HotCodeCollectorMoveFunction.java fails intermittently Reviewed-by: eastigeevich, aph --- src/hotspot/share/runtime/hotCodeSampler.cpp | 16 +++++++--------- .../hotcode/HotCodeCollectorMoveFunction.java | 12 +++++++++++- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/hotspot/share/runtime/hotCodeSampler.cpp b/src/hotspot/share/runtime/hotCodeSampler.cpp index 94242b718a5..e033765c1f2 100644 --- a/src/hotspot/share/runtime/hotCodeSampler.cpp +++ b/src/hotspot/share/runtime/hotCodeSampler.cpp @@ -61,15 +61,13 @@ bool ThreadSampler::sample_all_java_threads() { continue; } - if (CodeCache::contains(pc)) { - nmethod* nm = CodeCache::find_blob(pc)->as_nmethod_or_null(); - if (nm != nullptr) { - bool created = false; - int *count = _samples.put_if_absent(nm, 0, &created); - (*count)++; - if (created) { - _samples.maybe_grow(); - } + CodeBlob* cb = CodeCache::find_blob(pc); + if (cb != nullptr && cb->is_nmethod()) { + bool created = false; + int *count = _samples.put_if_absent(cb->as_nmethod(), 0, &created); + (*count)++; + if (created) { + _samples.maybe_grow(); } } } diff --git a/test/hotspot/jtreg/compiler/hotcode/HotCodeCollectorMoveFunction.java b/test/hotspot/jtreg/compiler/hotcode/HotCodeCollectorMoveFunction.java index 5677ca88eb2..2b93c24e255 100644 --- a/test/hotspot/jtreg/compiler/hotcode/HotCodeCollectorMoveFunction.java +++ b/test/hotspot/jtreg/compiler/hotcode/HotCodeCollectorMoveFunction.java @@ -79,6 +79,8 @@ static class Runner { private static final int C2_LEVEL = 4; private static final int FUNC_RUN_MILLIS = 60_000; + private static volatile int blackholeCount = 0; + static { try { method = Runner.class.getMethod("func"); @@ -111,7 +113,15 @@ private static void compileFunc() { public static void func() { long start = System.currentTimeMillis(); - while (System.currentTimeMillis() - start < FUNC_RUN_MILLIS) {} + while (System.currentTimeMillis() - start < FUNC_RUN_MILLIS) { + // Perform multiplicative LCG to ensure the compiler does not optimize away the code. + // Integer overflow is used for the modulus so the loop terminates after (2^32)/4 iterations + int num = 1; + do { + blackholeCount++; + num *= 69069; + } while (num != 1); + } } } } From db357f7e089127d550e6ea872d533b8ce22e7992 Mon Sep 17 00:00:00 2001 From: "Daniel D. Daugherty" Date: Tue, 30 Jun 2026 23:25:45 +0000 Subject: [PATCH 121/707] 8387554: ProblemList vmTestbase/nsk/jvmti/unit/functions/Dispose/JvmtiTest/TestDescription.java in virtual thread configs 8387557: ProblemList vmTestbase/nsk/jvmti/scenarios/capability/CM02/cm02t001/TestDescription.java in virtual thread configs 8387558: ProblemList vmTestbase/nsk/jvmti/unit/timers/JvmtiTest/TestDescription.java on windows 8387560: ProblemList vmTestbase/nsk/jdi/EventRequestManager/threadStartRequests/thrstartreq002/TestDescription.java in virtual thread configs Reviewed-by: sspitsyn --- test/hotspot/jtreg/ProblemList-Virtual.txt | 5 +++++ test/hotspot/jtreg/ProblemList.txt | 1 + 2 files changed, 6 insertions(+) diff --git a/test/hotspot/jtreg/ProblemList-Virtual.txt b/test/hotspot/jtreg/ProblemList-Virtual.txt index 705cded007a..b30a09a7710 100644 --- a/test/hotspot/jtreg/ProblemList-Virtual.txt +++ b/test/hotspot/jtreg/ProblemList-Virtual.txt @@ -29,6 +29,11 @@ serviceability/AsyncGetCallTrace/MyPackage/ASGCTBaseTest.java 8308026 generic-al serviceability/jvmti/Heap/IterateHeapWithEscapeAnalysisEnabled.java 8264699 generic-all vmTestbase/vm/mlvm/indy/func/jvmti/mergeCP_indy2manyDiff_a/TestDescription.java 8308367 generic-all +vmTestbase/nsk/jvmti/unit/functions/Dispose/JvmtiTest/TestDescription.java 8387429 generic-all +vmTestbase/nsk/jvmti/scenarios/capability/CM02/cm02t001/TestDescription.java 8299217 generic-all +vmTestbase/nsk/jdi/EventRequestManager/threadStartRequests/thrstartreq002/TestDescription.java 8327967 generic-all + + #### ## Classes not unloaded as expected (TODO, need to check if FJ keeps a reference) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 4ac2843190c..a9f70fc97a4 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -162,6 +162,7 @@ vmTestbase/metaspace/gc/firstGC_default/TestDescription.java 8208250 generic-all vmTestbase/nsk/jvmti/scenarios/capability/CM03/cm03t001/TestDescription.java 8073470 linux-all vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t006/TestDescription.java 8372206 generic-all vmTestbase/nsk/jvmti/InterruptThread/intrpthrd003/TestDescription.java 8288911 macosx-all +vmTestbase/nsk/jvmti/unit/timers/JvmtiTest/TestDescription.java 8235348 windows-x64 vmTestbase/jit/escape/LockCoarsening/LockCoarsening001.java 8148743 generic-all vmTestbase/jit/escape/LockCoarsening/LockCoarsening002.java 8208259 generic-all From aa17cf560835706351f2ce69886b3e23049f6bb1 Mon Sep 17 00:00:00 2001 From: Gui Cao Date: Wed, 1 Jul 2026 02:34:42 +0000 Subject: [PATCH 122/707] 8387381: RISC-V: assert failed with fastdebug build on systems with different core types Reviewed-by: dzhang, fyang --- src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp index f48df178ce6..3ede62e14cd 100644 --- a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp +++ b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp @@ -261,13 +261,16 @@ void RiscvHwprobe::add_features_from_query_result() { // ====== non-extensions ====== // - if (is_valid(RISCV_HWPROBE_KEY_MARCHID)) { + // For value-type keys, the kernel returns (uint64_t)-1 when CPUs in the + // query set disagree (different core types). Skip these as the value is + // not meaningful for the system as a whole. + if (is_valid(RISCV_HWPROBE_KEY_MARCHID) && query[RISCV_HWPROBE_KEY_MARCHID].value != (uint64_t)-1) { VM_Version::marchid.enable_feature(query[RISCV_HWPROBE_KEY_MARCHID].value); } - if (is_valid(RISCV_HWPROBE_KEY_MIMPID)) { + if (is_valid(RISCV_HWPROBE_KEY_MIMPID) && query[RISCV_HWPROBE_KEY_MIMPID].value != (uint64_t)-1) { VM_Version::mimpid.enable_feature(query[RISCV_HWPROBE_KEY_MIMPID].value); } - if (is_valid(RISCV_HWPROBE_KEY_MVENDORID)) { + if (is_valid(RISCV_HWPROBE_KEY_MVENDORID) && query[RISCV_HWPROBE_KEY_MVENDORID].value != (uint64_t)-1) { VM_Version::mvendorid.enable_feature(query[RISCV_HWPROBE_KEY_MVENDORID].value); } // RISCV_HWPROBE_KEY_CPUPERF_0 is deprecated and returns similar values From 64ae319b5cd457aeb23d910d5ce09541028593fb Mon Sep 17 00:00:00 2001 From: Tobias Hartmann Date: Wed, 1 Jul 2026 05:55:55 +0000 Subject: [PATCH 123/707] 8387334: IR Framework tests should run in jtreg driver mode Reviewed-by: epeter, shade, chagedorn, mchevalier --- .../compiler/c2/ReachabilityFenceTest.java | 2 +- .../jtreg/compiler/c2/TestMergeStores.java | 8 +- .../c2/irTests/ConstructorBarriers.java | 2 +- .../TestVectorizationMismatchedAccess.java | 8 +- .../c2/riscv64/TestIntegerReverse.java | 4 +- .../compiler/c2/riscv64/TestLongReverse.java | 4 +- .../TestDebugDuringExceptionCatching.java | 2 +- .../CmpDisjointButNonOrderedRangesLong.java | 4 +- .../TestShortRunningLongCountedLoop.java | 10 +-- .../loopopts/TestHasTruncationWrap.java | 4 +- .../TestRedundantSafepointElimination.java | 2 +- .../rangechecks/TestFoldCompares.java | 6 +- .../compiler/stable/LazyConstantsIrTest.java | 2 +- .../TestRotateByteAndShortVector.java | 2 +- .../TestRoundVectorDoubleRandom.java | 2 +- .../TestRoundVectorFloatRandom.java | 2 +- .../vectorization/runner/ArrayCopyTest.java | 12 +-- .../runner/ArrayIndexFillTest.java | 14 +--- .../runner/ArrayInvariantFillTest.java | 37 +++++---- .../runner/ArrayShiftOpTest.java | 14 +--- .../runner/ArrayTypeConvertTest.java | 29 +------ .../runner/ArrayUnsafeOpTest.java | 12 +-- .../runner/BasicBooleanOpTest.java | 14 +--- .../vectorization/runner/BasicByteOpTest.java | 22 +++--- .../vectorization/runner/BasicCharOpTest.java | 12 +-- .../runner/BasicDoubleOpTest.java | 14 +--- .../runner/BasicFloatOpTest.java | 12 +-- .../vectorization/runner/BasicIntOpTest.java | 14 +--- .../vectorization/runner/BasicLongOpTest.java | 14 +--- .../runner/BasicShortOpTest.java | 12 +-- .../runner/LoopArrayIndexComputeTest.java | 33 +++----- .../runner/LoopCombinedOpTest.java | 31 ++------ .../runner/LoopControlFlowTest.java | 12 +-- .../runner/LoopLiveOutNodesTest.java | 14 +--- .../runner/LoopRangeStrideTest.java | 14 +--- .../runner/LoopReductionOpTest.java | 11 +-- .../runner/MultipleLoopsTest.java | 14 +--- .../runner/StripMinedLoopTest.java | 20 +++-- .../runner/VectorizationTestRunner.java | 75 ++++++++++++++----- 39 files changed, 180 insertions(+), 340 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/ReachabilityFenceTest.java b/test/hotspot/jtreg/compiler/c2/ReachabilityFenceTest.java index d0bce024696..14c4f7b5a48 100644 --- a/test/hotspot/jtreg/compiler/c2/ReachabilityFenceTest.java +++ b/test/hotspot/jtreg/compiler/c2/ReachabilityFenceTest.java @@ -38,7 +38,7 @@ * @summary Tests to ensure that reachabilityFence() correctly keeps objects from being collected prematurely. * @modules java.base/jdk.internal.misc * @library /test/lib / - * @run main/othervm -Xbatch compiler.c2.ReachabilityFenceTest + * @run driver ${test.main.class} */ public class ReachabilityFenceTest { private static final int SIZE = 100; diff --git a/test/hotspot/jtreg/compiler/c2/TestMergeStores.java b/test/hotspot/jtreg/compiler/c2/TestMergeStores.java index 5e6a757dd5f..99143f04dcd 100644 --- a/test/hotspot/jtreg/compiler/c2/TestMergeStores.java +++ b/test/hotspot/jtreg/compiler/c2/TestMergeStores.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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,7 +38,7 @@ * @summary Test merging of consecutive stores * @modules java.base/jdk.internal.misc * @library /test/lib / - * @run main/timeout=480 compiler.c2.TestMergeStores aligned + * @run driver/timeout=480 ${test.main.class} aligned */ /* @@ -48,7 +48,7 @@ * @summary Test merging of consecutive stores * @modules java.base/jdk.internal.misc * @library /test/lib / - * @run main/timeout=480 compiler.c2.TestMergeStores unaligned + * @run driver/timeout=480 ${test.main.class} unaligned */ /* @@ -58,7 +58,7 @@ * @summary Test merging of consecutive stores * @modules java.base/jdk.internal.misc * @library /test/lib / - * @run main/timeout=480 compiler.c2.TestMergeStores StressIGVN + * @run driver/timeout=480 ${test.main.class} StressIGVN */ public class TestMergeStores { diff --git a/test/hotspot/jtreg/compiler/c2/irTests/ConstructorBarriers.java b/test/hotspot/jtreg/compiler/c2/irTests/ConstructorBarriers.java index ba7e7d851b0..66dabcebf80 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/ConstructorBarriers.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/ConstructorBarriers.java @@ -31,7 +31,7 @@ * @summary Test barriers emitted in constructors * @library /test/lib / * @requires os.arch=="aarch64" | os.arch=="riscv64" | os.arch=="x86_64" | os.arch=="amd64" - * @run main compiler.c2.irTests.ConstructorBarriers + * @run driver ${test.main.class} */ public class ConstructorBarriers { public static void main(String[] args) { diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java b/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java index 5524b5d7b6c..9556fce988d 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2023, Red Hat, Inc. All rights reserved. - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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,7 +26,6 @@ import compiler.lib.ir_framework.*; import jdk.test.lib.Utils; -import jdk.test.whitebox.WhiteBox; import jdk.internal.misc.Unsafe; import java.util.Random; import java.util.Arrays; @@ -40,15 +39,12 @@ * @summary C2: vectorization fails on simple ByteBuffer loop * @modules java.base/jdk.internal.misc * @library /test/lib / - * @build jdk.test.whitebox.WhiteBox - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.c2.irTests.TestVectorizationMismatchedAccess + * @run driver ${test.main.class} */ public class TestVectorizationMismatchedAccess { private static final Unsafe UNSAFE = Unsafe.getUnsafe(); private static final Random RANDOM = Utils.getRandomInstance(); - private final static WhiteBox wb = WhiteBox.getWhiteBox(); public static void main(String[] args) { TestFramework framework = new TestFramework(); diff --git a/test/hotspot/jtreg/compiler/c2/riscv64/TestIntegerReverse.java b/test/hotspot/jtreg/compiler/c2/riscv64/TestIntegerReverse.java index 8b3abbb0300..82bb79d3c1f 100644 --- a/test/hotspot/jtreg/compiler/c2/riscv64/TestIntegerReverse.java +++ b/test/hotspot/jtreg/compiler/c2/riscv64/TestIntegerReverse.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, Rivos Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -30,7 +30,7 @@ * * @library /test/lib / * @requires os.arch == "riscv64" & vm.cpu.features ~= ".*zbkb.*" - * @run main/othervm compiler.c2.riscv64.TestIntegerReverse + * @run driver ${test.main.class} */ package compiler.c2.riscv64; diff --git a/test/hotspot/jtreg/compiler/c2/riscv64/TestLongReverse.java b/test/hotspot/jtreg/compiler/c2/riscv64/TestLongReverse.java index 01c3b871ffa..807a58a18f3 100644 --- a/test/hotspot/jtreg/compiler/c2/riscv64/TestLongReverse.java +++ b/test/hotspot/jtreg/compiler/c2/riscv64/TestLongReverse.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, Rivos Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -30,7 +30,7 @@ * * @library /test/lib / * @requires os.arch == "riscv64" & vm.cpu.features ~= ".*zbkb.*" - * @run main/othervm compiler.c2.riscv64.TestLongReverse + * @run driver ${test.main.class} */ package compiler.c2.riscv64; diff --git a/test/hotspot/jtreg/compiler/exceptions/TestDebugDuringExceptionCatching.java b/test/hotspot/jtreg/compiler/exceptions/TestDebugDuringExceptionCatching.java index 9be192d1f55..026b2d15b77 100644 --- a/test/hotspot/jtreg/compiler/exceptions/TestDebugDuringExceptionCatching.java +++ b/test/hotspot/jtreg/compiler/exceptions/TestDebugDuringExceptionCatching.java @@ -43,7 +43,7 @@ * @library /test/lib /test/jdk/java/lang/invoke/common / * @build test.java.lang.invoke.lib.InstructionHelper * - * @run main/othervm ${test.main.class} + * @run driver ${test.main.class} */ public class TestDebugDuringExceptionCatching { diff --git a/test/hotspot/jtreg/compiler/igvn/CmpDisjointButNonOrderedRangesLong.java b/test/hotspot/jtreg/compiler/igvn/CmpDisjointButNonOrderedRangesLong.java index c5ef1640721..ab40a2ea234 100644 --- a/test/hotspot/jtreg/compiler/igvn/CmpDisjointButNonOrderedRangesLong.java +++ b/test/hotspot/jtreg/compiler/igvn/CmpDisjointButNonOrderedRangesLong.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 @@ -27,7 +27,7 @@ * @summary Ranges can be proven to be disjoint but not orderable (thanks to unsigned range) * Comparing such values in such range with != should always be true. * @library /test/lib / - * @run main compiler.igvn.CmpDisjointButNonOrderedRangesLong + * @run driver ${test.main.class} */ package compiler.igvn; diff --git a/test/hotspot/jtreg/compiler/longcountedloops/TestShortRunningLongCountedLoop.java b/test/hotspot/jtreg/compiler/longcountedloops/TestShortRunningLongCountedLoop.java index 7e55353e0f7..ed65deb6c85 100644 --- a/test/hotspot/jtreg/compiler/longcountedloops/TestShortRunningLongCountedLoop.java +++ b/test/hotspot/jtreg/compiler/longcountedloops/TestShortRunningLongCountedLoop.java @@ -32,14 +32,11 @@ * @bug 8342692 * @summary C2: long counted loop/long range checks: don't create loop-nest for short running loops * @library /test/lib / - * @build jdk.test.whitebox.WhiteBox - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.longcountedloops.TestShortRunningLongCountedLoop + * @run driver ${test.main.class} */ public class TestShortRunningLongCountedLoop { private static volatile int volatileField; - private final static WhiteBox wb = WhiteBox.getWhiteBox(); public static void main(String[] args) { // IR rules expect a single loop so disable unrolling @@ -351,8 +348,9 @@ public static void testLongLoopUnknownBoundsShortLoopFailedSpeculation_runner(Ru throw new RuntimeException("incorrect result: " + res); } } - wb.enqueueMethodForCompilation(info.getTest(), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); - if (!wb.isMethodCompiled(info.getTest())) { + WhiteBox whitebox = WhiteBox.getWhiteBox(); + whitebox.enqueueMethodForCompilation(info.getTest(), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); + if (!whitebox.isMethodCompiled(info.getTest())) { throw new RuntimeException("Should be compiled now"); } for (int i = 0; i < 10; i++) { diff --git a/test/hotspot/jtreg/compiler/loopopts/TestHasTruncationWrap.java b/test/hotspot/jtreg/compiler/loopopts/TestHasTruncationWrap.java index 9a68a2fcb77..143933ed6ea 100644 --- a/test/hotspot/jtreg/compiler/loopopts/TestHasTruncationWrap.java +++ b/test/hotspot/jtreg/compiler/loopopts/TestHasTruncationWrap.java @@ -27,14 +27,14 @@ * @summary Test CountedLoopConverter::has_truncation_wrap logic that checks if * a truncated iv (e.g. byte or char iv) is still a valid counted loop. * @library /test/lib / - * @run main ${test.main.class} + * @run driver ${test.main.class} */ /* * @test id=Xcomp * @bug 8385855 * @library /test/lib / - * @run main ${test.main.class} -Xcomp -XX:-TieredCompilation -XX:CompileCommand=compileonly,${test.main.class}::test* + * @run driver ${test.main.class} -Xcomp -XX:-TieredCompilation -XX:CompileCommand=compileonly,${test.main.class}::test* */ package compiler.loopopts; diff --git a/test/hotspot/jtreg/compiler/loopopts/TestRedundantSafepointElimination.java b/test/hotspot/jtreg/compiler/loopopts/TestRedundantSafepointElimination.java index 69f86a2bf1d..f557a491160 100644 --- a/test/hotspot/jtreg/compiler/loopopts/TestRedundantSafepointElimination.java +++ b/test/hotspot/jtreg/compiler/loopopts/TestRedundantSafepointElimination.java @@ -30,7 +30,7 @@ * @bug 8347499 * @summary Tests that redundant safepoints can be eliminated in loops. * @library /test/lib / - * @run main compiler.loopopts.TestRedundantSafepointElimination + * @run driver ${test.main.class} */ public class TestRedundantSafepointElimination { public static void main(String[] args) { diff --git a/test/hotspot/jtreg/compiler/rangechecks/TestFoldCompares.java b/test/hotspot/jtreg/compiler/rangechecks/TestFoldCompares.java index bec3e442403..b0df68b209a 100644 --- a/test/hotspot/jtreg/compiler/rangechecks/TestFoldCompares.java +++ b/test/hotspot/jtreg/compiler/rangechecks/TestFoldCompares.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 @@ -27,14 +27,14 @@ * @summary Test logic in IfNode::fold_compares, which folds 2 signed comparisons * into a single comparison. * @library /test/lib / - * @run main ${test.main.class} + * @run driver ${test.main.class} */ /* * @test id=Xcomp * @bug 8346420 * @library /test/lib / - * @run main ${test.main.class} -Xcomp -XX:-TieredCompilation -XX:CompileCommand=compileonly,${test.main.class}::test* + * @run driver ${test.main.class} -Xcomp -XX:-TieredCompilation -XX:CompileCommand=compileonly,${test.main.class}::test* */ package compiler.rangechecks; diff --git a/test/hotspot/jtreg/compiler/stable/LazyConstantsIrTest.java b/test/hotspot/jtreg/compiler/stable/LazyConstantsIrTest.java index b9f9343dd39..8f967fae560 100644 --- a/test/hotspot/jtreg/compiler/stable/LazyConstantsIrTest.java +++ b/test/hotspot/jtreg/compiler/stable/LazyConstantsIrTest.java @@ -27,7 +27,7 @@ * @modules java.base/jdk.internal.lang * @library /test/lib / * @enablePreview - * @run main ${test.main.class} + * @run driver ${test.main.class} */ package compiler.stable; diff --git a/test/hotspot/jtreg/compiler/vectorization/TestRotateByteAndShortVector.java b/test/hotspot/jtreg/compiler/vectorization/TestRotateByteAndShortVector.java index 79cde2f0d26..4c448564a87 100644 --- a/test/hotspot/jtreg/compiler/vectorization/TestRotateByteAndShortVector.java +++ b/test/hotspot/jtreg/compiler/vectorization/TestRotateByteAndShortVector.java @@ -29,7 +29,7 @@ * @key randomness * @summary Test vectorization of rotate byte and short * @library /test/lib / - * @run main/othervm TestRotateByteAndShortVector + * @run driver ${test.main.class} */ import java.util.Random; diff --git a/test/hotspot/jtreg/compiler/vectorization/TestRoundVectorDoubleRandom.java b/test/hotspot/jtreg/compiler/vectorization/TestRoundVectorDoubleRandom.java index 78dd4f50a06..e5a6966cdcf 100644 --- a/test/hotspot/jtreg/compiler/vectorization/TestRoundVectorDoubleRandom.java +++ b/test/hotspot/jtreg/compiler/vectorization/TestRoundVectorDoubleRandom.java @@ -31,7 +31,7 @@ * @library /test/lib / * @modules java.base/jdk.internal.math * @requires os.arch == "riscv64" & vm.cpu.features ~= ".*rvv.*" - * @run main compiler.vectorization.TestRoundVectorDoubleRandom + * @run driver ${test.main.class} */ package compiler.vectorization; diff --git a/test/hotspot/jtreg/compiler/vectorization/TestRoundVectorFloatRandom.java b/test/hotspot/jtreg/compiler/vectorization/TestRoundVectorFloatRandom.java index 474601346e8..92b6d3b9840 100644 --- a/test/hotspot/jtreg/compiler/vectorization/TestRoundVectorFloatRandom.java +++ b/test/hotspot/jtreg/compiler/vectorization/TestRoundVectorFloatRandom.java @@ -31,7 +31,7 @@ * @library /test/lib / * @modules java.base/jdk.internal.math * @requires os.arch == "riscv64" & vm.cpu.features ~= ".*rvv.*" - * @run main compiler.vectorization.TestRoundVectorFloatRandom + * @run driver ${test.main.class} */ package compiler.vectorization; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayCopyTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayCopyTest.java index 48b2ff754ad..f1140533d25 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayCopyTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayCopyTest.java @@ -24,18 +24,10 @@ /* * @test * @summary Vectorization test on array copy + * @requires vm.compiler2.enabled * @library /test/lib / * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.ArrayCopyTest - * - * @requires vm.compiler2.enabled + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayIndexFillTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayIndexFillTest.java index 8d0ba2be589..3708fc87f29 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayIndexFillTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayIndexFillTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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,18 +26,10 @@ * @test * @summary Vectorization test on array index fill * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.ArrayIndexFillTest - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayInvariantFillTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayInvariantFillTest.java index b7044b1c79d..90e4955bee3 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayInvariantFillTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayInvariantFillTest.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. + * 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 @@ -25,24 +26,11 @@ * @test * @summary Vectorization test on array invariant fill * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * -XX:-OptimizeFill - * compiler.vectorization.runner.ArrayInvariantFillTest - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * -XX:+OptimizeFill - * compiler.vectorization.runner.ArrayInvariantFillTest - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} NoOptimizeFill + * @run driver ${test.main.class} OptimizeFill */ package compiler.vectorization.runner; @@ -68,11 +56,22 @@ public ArrayInvariantFillTest() { doubleInv = ran.nextDouble(); } + // We must pass the flags directly to the Test VM, and not the Driver VM in the @run above. + @Override + protected String[] testVMFlags(String[] args) { + return switch (args[0]) { + case "NoOptimizeFill" -> new String[]{"-XX:-OptimizeFill"}; + case "OptimizeFill" -> new String[]{"-XX:+OptimizeFill"}; + default -> throw new RuntimeException("Test argument not recognized: " + args[0]); + }; + } + // ---------------- Simple Fill ---------------- @Test - @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true", "rvv", "true"}, - applyIf = {"OptimizeFill", "false"}, - counts = {IRNode.REPLICATE_B, ">0"}) + // TODO 8387402 + //@IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true", "rvv", "true"}, + // applyIf = {"OptimizeFill", "false"}, + // counts = {IRNode.REPLICATE_B, ">0"}) @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true", "rvv", "true"}, applyIf = {"OptimizeFill", "true"}, counts = {IRNode.REPLICATE_B, "0"}) diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayShiftOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayShiftOpTest.java index e2d28cbf083..2699afda5cc 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayShiftOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayShiftOpTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, Rivos Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -28,18 +28,10 @@ * @bug 8183390 8332905 * @summary Vectorization test on bug-prone shift operation * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.ArrayShiftOpTest - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayTypeConvertTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayTypeConvertTest.java index f9c5f6199f1..d6f2febb06f 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayTypeConvertTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayTypeConvertTest.java @@ -27,33 +27,12 @@ * @bug 8183390 8340010 8342095 * @summary Vectorization test on array type conversions * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * * @requires vm.compiler2.enabled * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.ArrayTypeConvertTest nCOH_nAV - * - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.ArrayTypeConvertTest nCOH_yAV - * - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.ArrayTypeConvertTest yCOH_nAV - * - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.ArrayTypeConvertTest yCOH_yAV + * @run driver ${test.main.class} nCOH_nAV + * @run driver ${test.main.class} nCOH_yAV + * @run driver ${test.main.class} yCOH_nAV + * @run driver ${test.main.class} yCOH_yAV */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayUnsafeOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayUnsafeOpTest.java index 8b4513b8490..f6874a03ffb 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayUnsafeOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayUnsafeOpTest.java @@ -25,17 +25,9 @@ * @test * @summary Vectorization test on array unsafe operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.ArrayUnsafeOpTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java index ba82013e182..3a61b365800 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -27,17 +27,9 @@ * @summary Vectorization test on basic boolean operations * @requires vm.opt.StressUnstableIfTraps == null | !vm.opt.StressUnstableIfTraps * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.BasicBooleanOpTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicByteOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicByteOpTest.java index a336b32f7b9..acbf44c471c 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicByteOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicByteOpTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2023, 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 @@ -26,19 +26,9 @@ * @test * @summary Vectorization test on basic byte operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * -XX:CompileCommand=CompileOnly,compiler.vectorization.runner.BasicByteOpTest::* - * -XX:LoopUnrollLimit=1000 - * compiler.vectorization.runner.BasicByteOpTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; @@ -64,6 +54,12 @@ public BasicByteOpTest() { } } + // We must pass the flags directly to the test-VM, and not the driver vm in the @run above. + @Override + protected String[] testVMFlags(String[] args) { + return new String[]{"-XX:CompileCommand=CompileOnly,compiler.vectorization.runner.BasicByteOpTest::*", "-XX:LoopUnrollLimit=1000"}; + } + // ---------------- Arithmetic ---------------- @Test @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true", "rvv", "true"}, diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicCharOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicCharOpTest.java index 4211d5eec5e..be462f0be16 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicCharOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicCharOpTest.java @@ -25,17 +25,9 @@ * @test * @summary Vectorization test on basic char operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.BasicCharOpTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicDoubleOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicDoubleOpTest.java index 8d5925ec8c6..1adb89591a5 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicDoubleOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicDoubleOpTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, Rivos Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -27,18 +27,10 @@ * @test * @summary Vectorization test on basic double operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.BasicDoubleOpTest - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicFloatOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicFloatOpTest.java index b89d068d8af..870b8746baf 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicFloatOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicFloatOpTest.java @@ -25,18 +25,10 @@ * @test * @summary Vectorization test on basic float operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.BasicFloatOpTest - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicIntOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicIntOpTest.java index e096f7878ab..8849418e609 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicIntOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicIntOpTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2023, 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 @@ -26,17 +26,9 @@ * @test * @summary Vectorization test on basic int operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.BasicIntOpTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicLongOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicLongOpTest.java index a6767054958..5404d943bbc 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicLongOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicLongOpTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2023, 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 @@ -26,18 +26,10 @@ * @test * @summary Vectorization test on basic long operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.BasicLongOpTest - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicShortOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicShortOpTest.java index b957a00278a..4c7221dea52 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicShortOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicShortOpTest.java @@ -27,17 +27,9 @@ * @bug 8183390 8342095 * @summary Vectorization test on basic short operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.BasicShortOpTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/LoopArrayIndexComputeTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/LoopArrayIndexComputeTest.java index c8a3c71bdee..27058012f36 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/LoopArrayIndexComputeTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/LoopArrayIndexComputeTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2023, 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 @@ -26,26 +26,13 @@ * @test * @summary Vectorization test on loop array index computation * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopArrayIndexComputeTest nAV_ySAC - * - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopArrayIndexComputeTest yAV_ySAC - * - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopArrayIndexComputeTest nAV_nSAC - * - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopArrayIndexComputeTest yAV_nSAC - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} nAV_ySAC + * @run driver ${test.main.class} yAV_ySAC + * @run driver ${test.main.class} nAV_nSAC + * @run driver ${test.main.class} yAV_nSAC */ package compiler.vectorization.runner; @@ -60,10 +47,10 @@ public class LoopArrayIndexComputeTest extends VectorizationTestRunner { @Override protected String[] testVMFlags(String[] args) { return switch (args[0]) { - case "nAV_ySAC" -> new String[]{"-XX:-AlignVector", "-XX:+UseAutoVectorizationSpeculativeAliasingChecks"}; - case "yAV_ySAC" -> new String[]{"-XX:+AlignVector", "-XX:+UseAutoVectorizationSpeculativeAliasingChecks"}; - case "nAV_nSAC" -> new String[]{"-XX:-AlignVector", "-XX:-UseAutoVectorizationSpeculativeAliasingChecks"}; - case "yAV_nSAC" -> new String[]{"-XX:+AlignVector", "-XX:-UseAutoVectorizationSpeculativeAliasingChecks"}; + case "nAV_ySAC" -> new String[]{"-XX:+UnlockDiagnosticVMOptions", "-XX:-AlignVector", "-XX:+UseAutoVectorizationSpeculativeAliasingChecks"}; + case "yAV_ySAC" -> new String[]{"-XX:+UnlockDiagnosticVMOptions", "-XX:+AlignVector", "-XX:+UseAutoVectorizationSpeculativeAliasingChecks"}; + case "nAV_nSAC" -> new String[]{"-XX:+UnlockDiagnosticVMOptions", "-XX:-AlignVector", "-XX:-UseAutoVectorizationSpeculativeAliasingChecks"}; + case "yAV_nSAC" -> new String[]{"-XX:+UnlockDiagnosticVMOptions", "-XX:+AlignVector", "-XX:-UseAutoVectorizationSpeculativeAliasingChecks"}; default -> { throw new RuntimeException("Test argument not recognized: " + args[0]); } }; } diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/LoopCombinedOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/LoopCombinedOpTest.java index c46b2e11612..714de5b3c6b 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/LoopCombinedOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/LoopCombinedOpTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2023, 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 @@ -26,33 +26,12 @@ * @test * @summary Vectorization test on combined operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * * @requires vm.compiler2.enabled * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopCombinedOpTest nCOH_nAV - * - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopCombinedOpTest nCOH_yAV - * - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopCombinedOpTest yCOH_nAV - * - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopCombinedOpTest yCOH_yAV + * @run driver ${test.main.class} nCOH_nAV + * @run driver ${test.main.class} nCOH_yAV + * @run driver ${test.main.class} yCOH_nAV + * @run driver ${test.main.class} yCOH_yAV */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/LoopControlFlowTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/LoopControlFlowTest.java index e36e4097813..51326956983 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/LoopControlFlowTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/LoopControlFlowTest.java @@ -25,17 +25,9 @@ * @test * @summary Vectorization test on simple control flow in loop * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopControlFlowTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/LoopLiveOutNodesTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/LoopLiveOutNodesTest.java index 06a3eb33bc3..cad2af04a9b 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/LoopLiveOutNodesTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/LoopLiveOutNodesTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2023, 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 @@ -26,17 +26,9 @@ * @test * @summary Vectorization test on loops with live out nodes * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopLiveOutNodesTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/LoopRangeStrideTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/LoopRangeStrideTest.java index 2db565461ac..a36d11198e7 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/LoopRangeStrideTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/LoopRangeStrideTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2023, 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 @@ -26,18 +26,10 @@ * @test * @summary Vectorization test on different loop ranges and strides * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopRangeStrideTest - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/LoopReductionOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/LoopReductionOpTest.java index 546d99f5cce..9b9dcb03f6e 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/LoopReductionOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/LoopReductionOpTest.java @@ -25,19 +25,10 @@ * @test * @summary Vectorization test on reduction operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopReductionOpTest - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/MultipleLoopsTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/MultipleLoopsTest.java index 4dbfba02a43..4be74d20733 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/MultipleLoopsTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/MultipleLoopsTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2023, 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 @@ -26,17 +26,9 @@ * @test * @summary Vectorization test on multiple loops in a method * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.MultipleLoopsTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/StripMinedLoopTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/StripMinedLoopTest.java index dbc999647ad..347571fc95b 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/StripMinedLoopTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/StripMinedLoopTest.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. + * 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 @@ -25,18 +26,9 @@ * @test * @summary Vectorization test with small strip mining iterations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * -XX:LoopStripMiningIter=10 - * compiler.vectorization.runner.StripMinedLoopTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; @@ -59,6 +51,12 @@ public StripMinedLoopTest() { } } + // We must pass the flags directly to the Test VM, and not the Driver VM in the @run above. + @Override + protected String[] testVMFlags(String[] args) { + return new String[]{"-XX:LoopStripMiningIter=10"}; + } + @Test @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true", "rvv", "true"}, counts = {IRNode.STORE_VECTOR, ">0"}) diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/VectorizationTestRunner.java b/test/hotspot/jtreg/compiler/vectorization/runner/VectorizationTestRunner.java index 7f8e4ec3b39..9adebf30d31 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/VectorizationTestRunner.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/VectorizationTestRunner.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. + * 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 @@ -29,14 +30,23 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import jdk.test.lib.Utils; +import jdk.test.lib.helpers.ClassFileInstaller; +import jdk.test.lib.process.ProcessTools; import jdk.test.whitebox.WhiteBox; public class VectorizationTestRunner { - private static final WhiteBox WB = WhiteBox.getWhiteBox(); + private static final String VERIFY_CORRECTNESS_ARG = "--verify-vectorization-correctness"; + + private static class Flags { + private static final WhiteBox WHITEBOX = WhiteBox.getWhiteBox(); + } private static final int COMP_LEVEL_INTP = 0; private static final int COMP_LEVEL_C2 = 4; @@ -52,6 +62,35 @@ protected void run(String[] args) { // invokes it twice - first time in the interpreter and second time compiled // by C2. Then this runner compares the two return values. Hence we require // each test method returning a primitive value or an array of primitive type. + runCorrectnessTestsInTestVM(args); + + // 2) Vectorization ability test + // To test vectorizability, invoke the IR test framework to check existence of + // expected C2 IR node. + TestFramework irTest = new TestFramework(klass); + irTest.addFlags(testVMFlags(args)); + irTest.start(); + } + + private void runCorrectnessTestsInTestVM(String[] args) { + List command = new ArrayList<>(); + command.addAll(Arrays.asList(testVMFlags(args))); + command.add("-Xbootclasspath/a:."); + command.add("-XX:+UnlockDiagnosticVMOptions"); + command.add("-XX:+WhiteBoxAPI"); + command.add(getClass().getName()); + command.add(VERIFY_CORRECTNESS_ARG); + command.add(getClass().getName()); + try { + ClassFileInstaller.main("jdk.test.whitebox.WhiteBox"); + ProcessTools.executeTestJava(command).shouldHaveExitValue(0); + } catch (Exception e) { + throw new RuntimeException("Vectorization correctness test failed", e); + } + } + + private void runCorrectnessTests() { + Class klass = getClass(); for (Method method : klass.getDeclaredMethods()) { try { if (method.isAnnotationPresent(Test.class)) { @@ -63,13 +102,6 @@ protected void run(String[] args) { "." + method.getName() + ": " + e.getMessage()); } } - - // 2) Vectorization ability test - // To test vectorizability, invoke the IR test framework to check existence of - // expected C2 IR node. - TestFramework irTest = new TestFramework(klass); - irTest.addFlags(testVMFlags(args)); - irTest.start(); } // Override this to add extra flags. @@ -111,20 +143,20 @@ private void runTestOnMethod(Method method) throws InterruptedException { // Temporarily disable the compiler and invoke the method to get reference // result from the interpreter - WB.setBooleanVMFlag("UseCompiler", false); + Flags.WHITEBOX.setBooleanVMFlag("UseCompiler", false); try { expected = method.invoke(this); } catch (Exception e) { e.printStackTrace(); fail("Exception is thrown in test method invocation (interpreter)."); } - assert(WB.getMethodCompilationLevel(method) == COMP_LEVEL_INTP); - WB.setBooleanVMFlag("UseCompiler", true); + assert(Flags.WHITEBOX.getMethodCompilationLevel(method) == COMP_LEVEL_INTP); + Flags.WHITEBOX.setBooleanVMFlag("UseCompiler", true); // Compile the method and invoke it again long enqueueTime = System.currentTimeMillis(); - WB.enqueueMethodForCompilation(method, COMP_LEVEL_C2); - while (WB.getMethodCompilationLevel(method) != COMP_LEVEL_C2) { + Flags.WHITEBOX.enqueueMethodForCompilation(method, COMP_LEVEL_C2); + while (Flags.WHITEBOX.getMethodCompilationLevel(method) != COMP_LEVEL_C2) { Thread.sleep(100 /*ms*/); } try { @@ -133,7 +165,7 @@ private void runTestOnMethod(Method method) throws InterruptedException { e.printStackTrace(); fail("Exception is thrown in test method invocation (C2)."); } - assert(WB.getMethodCompilationLevel(method) == COMP_LEVEL_C2); + assert(Flags.WHITEBOX.getMethodCompilationLevel(method) == COMP_LEVEL_C2); // Check if two invocations return the same Class retType = method.getReturnType(); @@ -172,11 +204,10 @@ private void runTestOnMethod(Method method) throws InterruptedException { } private static VectorizationTestRunner createTestInstance(String testName) { - if (!testName.toLowerCase().endsWith(".java")) { - fail("Invalid test file name " + testName); + if (testName.toLowerCase().endsWith(".java")) { + testName = testName.substring(0, testName.length() - 5); + testName = testName.replace('/', '.'); } - testName = testName.substring(0, testName.length() - 5); - testName = testName.replace('/', '.'); VectorizationTestRunner instance = null; try { @@ -196,7 +227,13 @@ private static void fail(String reason) { } public static void main(String[] args) { - VectorizationTestRunner testObj = createTestInstance(Utils.TEST_NAME); + VectorizationTestRunner testObj; + if (args.length > 0 && args[0].equals(VERIFY_CORRECTNESS_ARG)) { + testObj = createTestInstance(args[1]); + testObj.runCorrectnessTests(); + return; + } + testObj = createTestInstance(Utils.TEST_NAME); testObj.run(args); } } From b186074751bbd5b34dc92a1119e7b93e91cd8c7c Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Wed, 1 Jul 2026 06:02:23 +0000 Subject: [PATCH 124/707] 8387387: Parallel: Clean up startup allocation locking Co-authored-by: Axel Boldt-Christmas Reviewed-by: tschatzl, aboldtch --- .../gc/parallel/parallelScavengeHeap.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index 7aa88110fc8..ea3a85861b8 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -315,17 +315,16 @@ HeapWord* ParallelScavengeHeap::mem_allocate_work(size_t size, bool is_tlab) { return result; } + // Ensure that is_init_completed() does not transition while expanding the heap. + ConditionalMutexLocker ml_init(InitCompleted_lock, !is_init_completed(), Mutex::_no_safepoint_check_flag); if (!is_init_completed()) { - // Double checked locking, this ensure that is_init_completed() does not - // transition while expanding the heap. - MonitorLocker ml(InitCompleted_lock, Monitor::_no_safepoint_check_flag); - if (!is_init_completed()) { - result = expand_heap_and_allocate(size, is_tlab); - // Return the result if it's tlab-allocation. If the result is null, callers will retry - // non-tlab allocation. - if (result != nullptr || is_tlab) { - return result; - } + // Rechecked !is_init_completed() implies we have mutual exclusion via + // `Heap_lock` and `InitCompleted_lock` + result = expand_heap_and_allocate(size, is_tlab); + // Return the result if it's tlab-allocation. If the result is null, + // callers will retry non-tlab allocation. + if (result != nullptr || is_tlab) { + return result; } } } From 28c79eb79222f304a4bc7233f90a255bf66d4d91 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Wed, 1 Jul 2026 07:17:48 +0000 Subject: [PATCH 125/707] 8387395: [REDO] C2: SIGSEGV in compiled code due to missing ctrl Reviewed-by: dlong, kvn, vlivanov --- src/hotspot/share/opto/compile.cpp | 36 +++++++++---- src/hotspot/share/opto/node.cpp | 21 ++++++++ src/hotspot/share/opto/node.hpp | 1 + .../TestRemoveCastPPWithCMoveUse.java | 53 +++++++++++++++++++ 4 files changed, 101 insertions(+), 10 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/controldependency/TestRemoveCastPPWithCMoveUse.java diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 1f51cdc1d39..e5f91875516 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -3497,22 +3497,38 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f ResourceMark rm; Unique_Node_List wq; wq.push(n); + + + // When we remove a CastPP, we need to pin all of its transitive users under the control of + // the removed node. The simplest approach is to pin all of the uses of the removed CastPP, + // but it is overly conservative, as an AddP does not really need pinning. As a result, we + // look through those nodes that do not need pinning and only pin memory access nodes under + // n->in(0). for (uint next = 0; next < wq.size(); ++next) { Node *m = wq.at(next); for (DUIterator_Fast imax, i = m->fast_outs(imax); i < imax; i++) { Node* use = m->fast_out(i); - if (use->is_Mem() || use->is_EncodeNarrowPtr()) { + int use_op = use->Opcode(); + if (use->is_CFG() || use->pinned() || // already pinned at the exact control + use->is_Cmp() || use_op == Op_CastP2X || use_op == Op_Conv2B) { // pure computations + continue; + } else if (use->is_EncodeNarrowPtr() || // EncodeP remembers whether its input is nullable, so it must be pinned + use_op == Op_PartialSubtypeCheck || // This accesses its pointer inputs, so it must depend on them being not-null + use->is_Mem() || use->is_memory_access_intrinsic()) { use->ensure_control_or_add_prec(n->in(0)); + } else if (use_op == Op_AddP || + use_op == Op_CastPP || use_op == Op_CheckCastPP || + use_op == Op_CMoveP || use_op == Op_CMoveN || + use_op == Op_DecodeN || use_op == Op_DecodeNKlass || + use_op == Op_VerifyVectorAlignment) { + // Look through use to find memory accesses if use does not need pinning + wq.push(use); } else { - switch(use->Opcode()) { - case Op_AddP: - case Op_DecodeN: - case Op_DecodeNKlass: - case Op_CheckCastPP: - case Op_CastPP: - wq.push(use); - break; - } + // Should have handled all kinds of nodes, verify that we do not unexpectedly arrive + // here + assert(false, "unexpected node %s", use->Name()); + // Be conservative in product and pin the unexpected use + use->ensure_control_or_add_prec(n->in(0)); } } } diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index 2f7cc6d1c1d..726a3ea1b55 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -3018,6 +3018,27 @@ bool Node::is_data_proj_of_pure_function(const Node* maybe_pure_function) const return Opcode() == Op_Proj && as_Proj()->_con == TypeFunc::Parms && maybe_pure_function->is_CallLeafPure(); } +// Whether this is an intrinsic node that accesses memory and has a memory input, such as array +// equal intrinsic. Some nodes do access memory but do not have a memory input, such as +// PartialSubTypeCheck, they are not included here. +bool Node::is_memory_access_intrinsic() const { + switch (Opcode()) { + case Op_StrComp: + case Op_StrEquals: + case Op_StrIndexOf: + case Op_StrIndexOfChar: + case Op_StrCompressedCopy: + case Op_StrInflatedCopy: + case Op_AryEq: + case Op_CountPositives: + case Op_VectorizedHashCode: + case Op_EncodeISOArray: + return true; + default: + return false; + } +} + //--------------------------has_non_debug_uses------------------------------ // Checks whether the node has any non-debug uses or not. bool Node::has_non_debug_uses() const { diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index 443f4bfbe8a..b3de7498e50 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -1069,6 +1069,7 @@ class Node { uint is_Copy() const { return (_flags & Flag_is_Copy); } virtual bool is_CFG() const { return false; } + bool is_memory_access_intrinsic() const; // If this node is control-dependent on a test, can it be rerouted to a dominating equivalent // test? This means that the node can be executed safely as long as it happens after the test diff --git a/test/hotspot/jtreg/compiler/controldependency/TestRemoveCastPPWithCMoveUse.java b/test/hotspot/jtreg/compiler/controldependency/TestRemoveCastPPWithCMoveUse.java new file mode 100644 index 00000000000..3d752cc74f5 --- /dev/null +++ b/test/hotspot/jtreg/compiler/controldependency/TestRemoveCastPPWithCMoveUse.java @@ -0,0 +1,53 @@ +/* + * 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.controldependency; + +/* + * @test + * @bug 8385420 + * @summary C2 correctly handles the case when the removed CastPPNode has a CMove use. + * @run main ${test.main.class} + * @run main/othervm -Xbatch -XX:CompileOnly=${test.main.class}::test + * -XX:+UnlockDiagnosticVMOptions -XX:+StressGCM ${test.main.class} + * + */ +public class TestRemoveCastPPWithCMoveUse { + public static void main(String[] args) { + for (int i = 0; i < 10_000; i++) { + test(null, false); + test(null, true); + test("", false); + test("", true); + } + } + + static int test(String a, boolean flag) { + StringBuilder sb = new StringBuilder(); + if (a == null) { + sb.append(""); + } else { + sb.append(flag ? a : ""); + } + return sb.length(); + } +} From fcfd6ad141e27e77beba7a6c3b82f9c5ac113550 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Wed, 1 Jul 2026 07:57:41 +0000 Subject: [PATCH 126/707] 8386846: G1: Crash in ~ThreadTotalCPUTimeClosure inside G1ServiceThread during CDS abort Reviewed-by: shade, dholmes --- src/hotspot/share/runtime/cpuTimeCounters.cpp | 11 ++++++++++- src/hotspot/share/runtime/cpuTimeCounters.hpp | 5 +++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/runtime/cpuTimeCounters.cpp b/src/hotspot/share/runtime/cpuTimeCounters.cpp index e174407089c..3374a1c5db3 100644 --- a/src/hotspot/share/runtime/cpuTimeCounters.cpp +++ b/src/hotspot/share/runtime/cpuTimeCounters.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2023 Google LLC. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -25,6 +25,7 @@ #include "runtime/atomicAccess.hpp" #include "runtime/cpuTimeCounters.hpp" +#include "utilities/globalCounter.inline.hpp" const char* CPUTimeGroups::to_string(CPUTimeType val) { switch (val) { @@ -77,6 +78,10 @@ void CPUTimeCounters::inc_gc_total_cpu_time(jlong diff) { } void CPUTimeCounters::publish_gc_total_cpu_time() { + GlobalCounter::CriticalSection cs(Thread::current()); + if (!UsePerfData || !PerfDataManager::has_PerfData()) { + return; + } CPUTimeCounters* instance = CPUTimeCounters::get_instance(); // Atomically fetch the current _gc_total_cpu_time_diff and reset it to zero. jlong new_value = 0; @@ -103,6 +108,10 @@ PerfCounter* CPUTimeCounters::get_counter(CPUTimeGroups::CPUTimeType name) { } void CPUTimeCounters::update_counter(CPUTimeGroups::CPUTimeType name, jlong total) { + GlobalCounter::CriticalSection cs(Thread::current()); + if (!UsePerfData || !PerfDataManager::has_PerfData()) { + return; + } CPUTimeCounters* instance = CPUTimeCounters::get_instance(); PerfCounter* counter = instance->get_counter(name); jlong prev_value = counter->get_value(); diff --git a/src/hotspot/share/runtime/cpuTimeCounters.hpp b/src/hotspot/share/runtime/cpuTimeCounters.hpp index c2e636bdb1d..15f680c06e1 100644 --- a/src/hotspot/share/runtime/cpuTimeCounters.hpp +++ b/src/hotspot/share/runtime/cpuTimeCounters.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2023 Google LLC. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -79,6 +79,8 @@ class CPUTimeCounters: public CHeapObj { static void inc_gc_total_cpu_time(jlong diff); + static PerfCounter* get_counter(CPUTimeGroups::CPUTimeType name); + public: static void initialize() { assert(_instance == nullptr, "we can only allocate one CPUTimeCounters object"); @@ -91,7 +93,6 @@ class CPUTimeCounters: public CHeapObj { } static void create_counter(CPUTimeGroups::CPUTimeType name); - static PerfCounter* get_counter(CPUTimeGroups::CPUTimeType name); static void update_counter(CPUTimeGroups::CPUTimeType name, jlong total); static void publish_gc_total_cpu_time(); From 867b4f42c0eacb7758a7615a1f56fc7c7dc56371 Mon Sep 17 00:00:00 2001 From: Ruben Ayrapetyan Date: Wed, 1 Jul 2026 08:36:46 +0000 Subject: [PATCH 127/707] 8387081: AArch64: Refactor MacroAssembler::cmpxchg Reviewed-by: qamai, aph --- src/hotspot/cpu/aarch64/aarch64_atomic.ad | 132 +++++------------- src/hotspot/cpu/aarch64/aarch64_atomic_ad.m4 | 14 +- .../cpu/aarch64/c1_LIRAssembler_aarch64.cpp | 4 +- .../cpu/aarch64/c2_MacroAssembler_aarch64.cpp | 9 +- src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.ad | 20 ++- src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.m4 | 26 ++-- .../shenandoahBarrierSetAssembler_aarch64.cpp | 8 +- .../gc/z/zBarrierSetAssembler_aarch64.cpp | 5 +- src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad | 10 +- .../cpu/aarch64/macroAssembler_aarch64.cpp | 35 ++++- .../cpu/aarch64/macroAssembler_aarch64.hpp | 20 ++- 11 files changed, 122 insertions(+), 161 deletions(-) diff --git a/src/hotspot/cpu/aarch64/aarch64_atomic.ad b/src/hotspot/cpu/aarch64/aarch64_atomic.ad index 3b05a637215..13fbe781518 100644 --- a/src/hotspot/cpu/aarch64/aarch64_atomic.ad +++ b/src/hotspot/cpu/aarch64/aarch64_atomic.ad @@ -43,8 +43,7 @@ instruct compareAndExchangeB(iRegINoSp res, indirect mem, iRegI oldval, iRegI ne %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::byte, /*acquire*/ false, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::byte, memory_order_release, $res$$Register); __ sxtbw($res$$Register, $res$$Register); %} ins_pipe(pipe_slow); @@ -59,8 +58,7 @@ instruct compareAndExchangeS(iRegINoSp res, indirect mem, iRegI oldval, iRegI ne %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::halfword, /*acquire*/ false, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::halfword, memory_order_release, $res$$Register); __ sxthw($res$$Register, $res$$Register); %} ins_pipe(pipe_slow); @@ -75,8 +73,7 @@ instruct compareAndExchangeI(iRegINoSp res, indirect mem, iRegI oldval, iRegI ne %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ false, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::word, memory_order_release, $res$$Register); %} ins_pipe(pipe_slow); %} @@ -90,8 +87,7 @@ instruct compareAndExchangeL(iRegLNoSp res, indirect mem, iRegL oldval, iRegL ne %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ false, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::xword, memory_order_release, $res$$Register); %} ins_pipe(pipe_slow); %} @@ -106,8 +102,7 @@ instruct compareAndExchangeN(iRegNNoSp res, indirect mem, iRegN oldval, iRegN ne %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ false, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::word, memory_order_release, $res$$Register); %} ins_pipe(pipe_slow); %} @@ -122,8 +117,7 @@ instruct compareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP ne %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ false, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::xword, memory_order_release, $res$$Register); %} ins_pipe(pipe_slow); %} @@ -138,8 +132,7 @@ instruct compareAndExchangeBAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::byte, /*acquire*/ true, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::byte, memory_order_seq_cst, $res$$Register); __ sxtbw($res$$Register, $res$$Register); %} ins_pipe(pipe_slow); @@ -155,8 +148,7 @@ instruct compareAndExchangeSAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::halfword, /*acquire*/ true, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::halfword, memory_order_seq_cst, $res$$Register); __ sxthw($res$$Register, $res$$Register); %} ins_pipe(pipe_slow); @@ -172,8 +164,7 @@ instruct compareAndExchangeIAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ true, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::word, memory_order_seq_cst, $res$$Register); %} ins_pipe(pipe_slow); %} @@ -188,8 +179,7 @@ instruct compareAndExchangeLAcq(iRegLNoSp res, indirect mem, iRegL oldval, iRegL %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ true, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::xword, memory_order_seq_cst, $res$$Register); %} ins_pipe(pipe_slow); %} @@ -204,8 +194,7 @@ instruct compareAndExchangeNAcq(iRegNNoSp res, indirect mem, iRegN oldval, iRegN %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ true, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::word, memory_order_seq_cst, $res$$Register); %} ins_pipe(pipe_slow); %} @@ -220,8 +209,7 @@ instruct compareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iRegP %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ true, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::xword, memory_order_seq_cst, $res$$Register); %} ins_pipe(pipe_slow); %} @@ -235,9 +223,7 @@ instruct compareAndSwapB(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::byte, /*acquire*/ false, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::byte, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -252,9 +238,7 @@ instruct compareAndSwapS(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::halfword, /*acquire*/ false, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::halfword, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -269,9 +253,7 @@ instruct compareAndSwapI(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ false, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -286,9 +268,7 @@ instruct compareAndSwapL(iRegINoSp res, indirect mem, iRegL oldval, iRegL newval "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ false, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -304,9 +284,7 @@ instruct compareAndSwapN(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ false, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -322,9 +300,7 @@ instruct compareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ false, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -340,9 +316,7 @@ instruct compareAndSwapBAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI new "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::byte, /*acquire*/ true, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::byte, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -358,9 +332,7 @@ instruct compareAndSwapSAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI new "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::halfword, /*acquire*/ true, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::halfword, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -376,9 +348,7 @@ instruct compareAndSwapIAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI new "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ true, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -394,9 +364,7 @@ instruct compareAndSwapLAcq(iRegINoSp res, indirect mem, iRegL oldval, iRegL new "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ true, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -412,9 +380,7 @@ instruct compareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN oldval, iRegN new "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ true, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -430,9 +396,7 @@ instruct compareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP new "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ true, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -447,9 +411,7 @@ instruct weakCompareAndSwapB(iRegINoSp res, indirect mem, iRegI oldval, iRegI ne "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::byte, /*acquire*/ false, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::byte, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -464,9 +426,7 @@ instruct weakCompareAndSwapS(iRegINoSp res, indirect mem, iRegI oldval, iRegI ne "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::halfword, /*acquire*/ false, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::halfword, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -481,9 +441,7 @@ instruct weakCompareAndSwapI(iRegINoSp res, indirect mem, iRegI oldval, iRegI ne "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ false, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -498,9 +456,7 @@ instruct weakCompareAndSwapL(iRegINoSp res, indirect mem, iRegL oldval, iRegL ne "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ false, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -516,9 +472,7 @@ instruct weakCompareAndSwapN(iRegINoSp res, indirect mem, iRegN oldval, iRegN ne "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ false, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -534,9 +488,7 @@ instruct weakCompareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP ne "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ false, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -552,9 +504,7 @@ instruct weakCompareAndSwapBAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::byte, /*acquire*/ true, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::byte, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -570,9 +520,7 @@ instruct weakCompareAndSwapSAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::halfword, /*acquire*/ true, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::halfword, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -588,9 +536,7 @@ instruct weakCompareAndSwapIAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ true, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -606,9 +552,7 @@ instruct weakCompareAndSwapLAcq(iRegINoSp res, indirect mem, iRegL oldval, iRegL "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ true, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -624,9 +568,7 @@ instruct weakCompareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN oldval, iRegN "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ true, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -642,9 +584,7 @@ instruct weakCompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ true, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); diff --git a/src/hotspot/cpu/aarch64/aarch64_atomic_ad.m4 b/src/hotspot/cpu/aarch64/aarch64_atomic_ad.m4 index dc51754e7f9..d6b3abd1e6f 100644 --- a/src/hotspot/cpu/aarch64/aarch64_atomic_ad.m4 +++ b/src/hotspot/cpu/aarch64/aarch64_atomic_ad.m4 @@ -53,8 +53,7 @@ ifelse($7,Acq,INDENT(predicate(needs_acquiring_load_exclusive(n));),`dnl') %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::$4, /*acquire*/ ifelse($7,Acq,true,false), /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::$4, ifelse($7,Acq,memory_order_seq_cst,memory_order_release), $res$$Register); __ $6($res$$Register, $res$$Register); %} ins_pipe(pipe_slow); @@ -76,8 +75,7 @@ ifelse($1$6,PAcq,INDENT(predicate(needs_acquiring_load_exclusive(n) && (n->as_Lo %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::$4, /*acquire*/ ifelse($6,Acq,true,false), /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::$4, ifelse($6,Acq,memory_order_seq_cst,memory_order_release), $res$$Register); %} ins_pipe(pipe_slow); %}')dnl @@ -112,9 +110,7 @@ ifelse($6,Acq,INDENT(predicate(needs_acquiring_load_exclusive(n));),`dnl') "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::$4, /*acquire*/ ifelse($6,Acq,true,false), /*release*/ true, - /*weak*/ ifelse($7,Weak,true,false), noreg); + __ ifelse($7,Weak,cmpxchg_weak,cmpxchg)($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::$4, ifelse($6,Acq,memory_order_seq_cst,memory_order_release)); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -137,9 +133,7 @@ ifelse($1$6,PAcq,INDENT(predicate(needs_acquiring_load_exclusive(n) && (n->as_Lo "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::$4, /*acquire*/ ifelse($6,Acq,true,false), /*release*/ true, - /*weak*/ ifelse($7,Weak,true,false), noreg); + __ ifelse($7,Weak,cmpxchg_weak,cmpxchg)($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::$4, ifelse($6,Acq,memory_order_seq_cst,memory_order_release)); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index 87451b5a07a..202f3227e2d 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -1492,12 +1492,12 @@ void LIR_Assembler::emit_opTypeCheck(LIR_OpTypeCheck* op) { } void LIR_Assembler::casw(Register addr, Register newval, Register cmpval) { - __ cmpxchg(addr, cmpval, newval, Assembler::word, /* acquire*/ true, /* release*/ true, /* weak*/ false, rscratch1); + __ cmpxchg(addr, cmpval, newval, Assembler::word, memory_order_seq_cst, rscratch1); __ cset(rscratch1, Assembler::NE); } void LIR_Assembler::casl(Register addr, Register newval, Register cmpval) { - __ cmpxchg(addr, cmpval, newval, Assembler::xword, /* acquire*/ true, /* release*/ true, /* weak*/ false, rscratch1); + __ cmpxchg(addr, cmpval, newval, Assembler::xword, memory_order_seq_cst, rscratch1); __ cset(rscratch1, Assembler::NE); } diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index cb9e308197e..e46a338e649 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -204,8 +204,7 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register t1, // Try to lock. Transition lock-bits 0b01 => 0b00 orr(t1_mark, t1_mark, markWord::unlocked_value); eor(t3_t, t1_mark, markWord::unlocked_value); - cmpxchg(/*addr*/ obj, /*expected*/ t1_mark, /*new*/ t3_t, Assembler::xword, - /*acquire*/ true, /*release*/ false, /*weak*/ false, noreg); + cmpxchg(/*addr*/ obj, /*expected*/ t1_mark, /*new*/ t3_t, Assembler::xword, memory_order_acquire); br(Assembler::NE, slow_path); bind(push); @@ -285,8 +284,7 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register t1, // Try to CAS owner (no owner => current thread's _monitor_owner_id). ldr(rscratch2, Address(rthread, JavaThread::monitor_owner_id_offset())); - cmpxchg(t2_owner_addr, zr, rscratch2, Assembler::xword, /*acquire*/ true, - /*release*/ false, /*weak*/ false, t3_owner); + cmpxchg(t2_owner_addr, zr, rscratch2, Assembler::xword, memory_order_acquire, t3_owner); br(Assembler::EQ, monitor_locked); // Check if recursive. @@ -371,8 +369,7 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register box, Register t1, // Try to unlock. Transition lock bits 0b00 => 0b01 assert(oopDesc::mark_offset_in_bytes() == 0, "required to avoid lea"); orr(t3_t, t1_mark, markWord::unlocked_value); - cmpxchg(/*addr*/ obj, /*expected*/ t1_mark, /*new*/ t3_t, Assembler::xword, - /*acquire*/ false, /*release*/ true, /*weak*/ false, noreg); + cmpxchg(/*addr*/ obj, /*expected*/ t1_mark, /*new*/ t3_t, Assembler::xword, memory_order_release); br(Assembler::EQ, unlocked); bind(push_and_slow_path); diff --git a/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.ad b/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.ad index 18fc27a4af4..375a0a89760 100644 --- a/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.ad +++ b/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.ad @@ -283,7 +283,7 @@ instruct g1CompareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, - false /* acquire */, true /* release */, false /* weak */, $res$$Register); + memory_order_release, $res$$Register); write_barrier_post(masm, this, $mem$$Register /* store_addr */, $newval$$Register /* new_val */, @@ -316,7 +316,7 @@ instruct g1CompareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iRe RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, - true /* acquire */, true /* release */, false /* weak */, $res$$Register); + memory_order_seq_cst, $res$$Register); write_barrier_post(masm, this, $mem$$Register /* store_addr */, $newval$$Register /* new_val */, @@ -346,7 +346,7 @@ instruct g1CompareAndExchangeN(iRegNNoSp res, indirect mem, iRegN oldval, iRegN RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, - false /* acquire */, true /* release */, false /* weak */, $res$$Register); + memory_order_release, $res$$Register); __ decode_heap_oop($tmp1$$Register, $newval$$Register); write_barrier_post(masm, this, $mem$$Register /* store_addr */, @@ -377,7 +377,7 @@ instruct g1CompareAndExchangeNAcq(iRegNNoSp res, indirect mem, iRegN oldval, iRe RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, - true /* acquire */, true /* release */, false /* weak */, $res$$Register); + memory_order_seq_cst, $res$$Register); __ decode_heap_oop($tmp1$$Register, $newval$$Register); write_barrier_post(masm, this, $mem$$Register /* store_addr */, @@ -409,8 +409,7 @@ instruct g1CompareAndSwapP(iRegINoSp res, indirect mem, iRegP newval, iRegPNoSp $tmp2$$Register /* tmp2 */, RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, - false /* acquire */, true /* release */, false /* weak */, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_release); __ cset($res$$Register, Assembler::EQ); write_barrier_post(masm, this, $mem$$Register /* store_addr */, @@ -442,8 +441,7 @@ instruct g1CompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP newval, iRegPNo $tmp2$$Register /* tmp2 */, RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, - true /* acquire */, true /* release */, false /* weak */, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_seq_cst); __ cset($res$$Register, Assembler::EQ); write_barrier_post(masm, this, $mem$$Register /* store_addr */, @@ -475,8 +473,7 @@ instruct g1CompareAndSwapN(iRegINoSp res, indirect mem, iRegN newval, iRegPNoSp $tmp3$$Register /* tmp2 */, RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, - false /* acquire */, true /* release */, false /* weak */, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_release); __ cset($res$$Register, Assembler::EQ); __ decode_heap_oop($tmp1$$Register, $newval$$Register); write_barrier_post(masm, this, @@ -509,8 +506,7 @@ instruct g1CompareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN newval, iRegPNo $tmp3$$Register /* tmp2 */, RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, - true /* acquire */, true /* release */, false /* weak */, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_seq_cst); __ cset($res$$Register, Assembler::EQ); __ decode_heap_oop($tmp1$$Register, $newval$$Register); write_barrier_post(masm, this, diff --git a/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.m4 b/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.m4 index 8fb1f7e8e42..63b464ceb8c 100644 --- a/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.m4 +++ b/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.m4 @@ -151,7 +151,7 @@ instruct g1CompareAndExchangeP$1(iRegPNoSp res, indirect mem, iRegP oldval, iReg RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, - $3 /* acquire */, true /* release */, false /* weak */, $res$$Register); + ifelse($1,Acq,memory_order_seq_cst,memory_order_release), $res$$Register); write_barrier_post(masm, this, $mem$$Register /* store_addr */, $newval$$Register /* new_val */, @@ -160,8 +160,8 @@ instruct g1CompareAndExchangeP$1(iRegPNoSp res, indirect mem, iRegP oldval, iReg %} ins_pipe(pipe_slow); %}')dnl -CAEP_INSN(,,false) -CAEP_INSN(Acq,_acq,true) +CAEP_INSN(,) +CAEP_INSN(Acq,_acq) dnl define(`CAEN_INSN', ` @@ -185,7 +185,7 @@ instruct g1CompareAndExchangeN$1(iRegNNoSp res, indirect mem, iRegN oldval, iReg RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, - $3 /* acquire */, true /* release */, false /* weak */, $res$$Register); + ifelse($1,Acq,memory_order_seq_cst,memory_order_release), $res$$Register); __ decode_heap_oop($tmp1$$Register, $newval$$Register); write_barrier_post(masm, this, $mem$$Register /* store_addr */, @@ -195,8 +195,8 @@ instruct g1CompareAndExchangeN$1(iRegNNoSp res, indirect mem, iRegN oldval, iReg %} ins_pipe(pipe_slow); %}')dnl -CAEN_INSN(,,false) -CAEN_INSN(Acq,_acq,true) +CAEN_INSN(,) +CAEN_INSN(Acq,_acq) dnl define(`CASP_INSN', ` @@ -221,8 +221,7 @@ instruct g1CompareAndSwapP$1(iRegINoSp res, indirect mem, iRegP newval, iRegPNoS $tmp2$$Register /* tmp2 */, RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, - $3 /* acquire */, true /* release */, false /* weak */, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, ifelse($1,Acq,memory_order_seq_cst,memory_order_release)); __ cset($res$$Register, Assembler::EQ); write_barrier_post(masm, this, $mem$$Register /* store_addr */, @@ -232,8 +231,8 @@ instruct g1CompareAndSwapP$1(iRegINoSp res, indirect mem, iRegP newval, iRegPNoS %} ins_pipe(pipe_slow); %}')dnl -CASP_INSN(,,false) -CASP_INSN(Acq,_acq,true) +CASP_INSN(,) +CASP_INSN(Acq,_acq) dnl define(`CASN_INSN', ` @@ -258,8 +257,7 @@ instruct g1CompareAndSwapN$1(iRegINoSp res, indirect mem, iRegN newval, iRegPNoS $tmp3$$Register /* tmp2 */, RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, - $3 /* acquire */, true /* release */, false /* weak */, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, ifelse($1,Acq,memory_order_seq_cst,memory_order_release)); __ cset($res$$Register, Assembler::EQ); __ decode_heap_oop($tmp1$$Register, $newval$$Register); write_barrier_post(masm, this, @@ -270,8 +268,8 @@ instruct g1CompareAndSwapN$1(iRegINoSp res, indirect mem, iRegN newval, iRegPNoS %} ins_pipe(pipe_slow); %}')dnl -CASN_INSN(,,false) -CASN_INSN(Acq,_acq,true) +CASN_INSN(,) +CASN_INSN(Acq,_acq) dnl define(`XCHGP_INSN', ` diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp index bc8af2354c8..7406aa0c1c4 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp @@ -597,8 +597,14 @@ void ShenandoahBarrierSetAssembler::compare_and_set_c2(const MachNode* node, Mac ShenandoahBarrierStubC2::load_store_pre(masm, node, addr, tmp1, tmp2, tmp3, narrow); + atomic_memory_order order = acquire ? memory_order_seq_cst : memory_order_release; + // CAS! - __ cmpxchg(addr, oldval, newval, op_size, acquire, /* release */ true, weak, exchange ? res : noreg); + if (weak) { + __ cmpxchg_weak(addr, oldval, newval, op_size, order, exchange ? res : noreg); + } else { + __ cmpxchg(addr, oldval, newval, op_size, order, exchange ? res : noreg); + } // If we need a boolean result out of CAS, set the flag appropriately and promote the result. if (!exchange) { diff --git a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp index 1eb96cdb6e7..7c320d835e7 100644 --- a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp @@ -283,10 +283,7 @@ void ZBarrierSetAssembler::store_barrier_medium(MacroAssembler* masm, // If we get this far, we know there is a young raw null value in the field. __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatStoreGoodBeforeMov); __ movzw(rtmp1, barrier_Relocation::unpatched); - __ cmpxchg(rtmp2, zr, rtmp1, - Assembler::xword, - false /* acquire */, false /* release */, true /* weak */, - rtmp3); + __ cmpxchg_weak(rtmp2, zr, rtmp1, Assembler::xword, memory_order_relaxed, rtmp3); __ br(Assembler::NE, slow_path); __ bind(slow_path_continuation); diff --git a/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad b/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad index ad2e9243823..74e0395c81e 100644 --- a/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad +++ b/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad @@ -207,8 +207,7 @@ instruct zCompareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newva Address ref_addr($mem$$Register); z_store_barrier(masm, this, ref_addr, $newval$$Register, $newval_tmp$$Register, rscratch2, true /* is_atomic */); z_color(masm, this, $oldval_tmp$$Register, $oldval$$Register); - __ cmpxchg($mem$$Register, $oldval_tmp$$Register, $newval_tmp$$Register, Assembler::xword, - false /* acquire */, true /* release */, false /* weak */, noreg); + __ cmpxchg($mem$$Register, $oldval_tmp$$Register, $newval_tmp$$Register, Assembler::xword, memory_order_release); __ cset($res$$Register, Assembler::EQ); %} @@ -231,8 +230,7 @@ instruct zCompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP ne Address ref_addr($mem$$Register); z_store_barrier(masm, this, ref_addr, $newval$$Register, $newval_tmp$$Register, rscratch2, true /* is_atomic */); z_color(masm, this, $oldval_tmp$$Register, $oldval$$Register); - __ cmpxchg($mem$$Register, $oldval_tmp$$Register, $newval_tmp$$Register, Assembler::xword, - true /* acquire */, true /* release */, false /* weak */, noreg); + __ cmpxchg($mem$$Register, $oldval_tmp$$Register, $newval_tmp$$Register, Assembler::xword, memory_order_seq_cst); __ cset($res$$Register, Assembler::EQ); %} @@ -255,7 +253,7 @@ instruct zCompareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP n z_store_barrier(masm, this, ref_addr, $newval$$Register, $newval_tmp$$Register, rscratch2, true /* is_atomic */); z_color(masm, this, $oldval_tmp$$Register, $oldval$$Register); __ cmpxchg($mem$$Register, $oldval_tmp$$Register, $newval_tmp$$Register, Assembler::xword, - false /* acquire */, true /* release */, false /* weak */, $res$$Register); + memory_order_release, $res$$Register); z_uncolor(masm, this, $res$$Register); %} @@ -278,7 +276,7 @@ instruct zCompareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iReg z_store_barrier(masm, this, ref_addr, $newval$$Register, $newval_tmp$$Register, rscratch2, true /* is_atomic */); z_color(masm, this, $oldval_tmp$$Register, $oldval$$Register); __ cmpxchg($mem$$Register, $oldval_tmp$$Register, $newval_tmp$$Register, Assembler::xword, - true /* acquire */, true /* release */, false /* weak */, $res$$Register); + memory_order_seq_cst, $res$$Register); z_uncolor(masm, this, $res$$Register); %} diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 1c052b67503..d5e220fd4a3 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -2231,8 +2231,7 @@ void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_ // offset is no longer needed after the address is computed. lea(rscratch2, Address(mdp, offset)); - cmpxchg(/*addr*/ rscratch2, /*expected*/ zr, /*new*/ recv, Assembler::xword, - /*acquire*/ false, /*release*/ false, /*weak*/ true, noreg); + cmpxchg_weak(/*addr*/ rscratch2, /*expected*/ zr, /*new*/ recv, Assembler::xword, memory_order_relaxed); // CAS success means the slot now has the receiver we want. CAS failure means // something had claimed the slot concurrently: it can be the same receiver we want, @@ -3494,9 +3493,33 @@ void MacroAssembler::reinit_heapbase() void MacroAssembler::cmpxchg(Register addr, Register expected, Register new_val, enum operand_size size, - bool acquire, bool release, + enum atomic_memory_order order, bool weak, Register result) { + bool acquire, release; + + switch (order) { + case memory_order_relaxed: + acquire = false; + release = false; + break; + case memory_order_acquire: + acquire = true; + release = false; + break; + case memory_order_release: + acquire = false; + release = true; + break; + case memory_order_acq_rel: + case memory_order_seq_cst: + acquire = true; + release = true; + break; + default: + ShouldNotReachHere(); + } + if (result == noreg) result = rscratch1; BLOCK_COMMENT("cmpxchg {"); if (UseLSE) { @@ -7180,8 +7203,7 @@ void MacroAssembler::fast_lock(Register basic_lock, Register obj, Register t1, R assert(oopDesc::mark_offset_in_bytes() == 0, "required to avoid lea"); orr(mark, mark, markWord::unlocked_value); eor(t, mark, markWord::unlocked_value); - cmpxchg(/*addr*/ obj, /*expected*/ mark, /*new*/ t, Assembler::xword, - /*acquire*/ true, /*release*/ false, /*weak*/ false, noreg); + cmpxchg(/*addr*/ obj, /*expected*/ mark, /*new*/ t, Assembler::xword, memory_order_acquire); br(Assembler::NE, slow); bind(push); @@ -7249,8 +7271,7 @@ void MacroAssembler::fast_unlock(Register obj, Register t1, Register t2, Registe // Try to unlock. Transition lock bits 0b00 => 0b01 assert(oopDesc::mark_offset_in_bytes() == 0, "required to avoid lea"); orr(t, mark, markWord::unlocked_value); - cmpxchg(obj, mark, t, Assembler::xword, - /*acquire*/ false, /*release*/ true, /*weak*/ false, noreg); + cmpxchg(obj, mark, t, Assembler::xword, memory_order_release); br(Assembler::EQ, unlocked); bind(push_and_slow); diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index 8f1e662765e..9c722cd297e 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -32,6 +32,7 @@ #include "metaprogramming/enableIf.hpp" #include "oops/compressedOops.hpp" #include "oops/compressedKlass.hpp" +#include "runtime/atomicAccess.hpp" #include "runtime/vm_version.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/powerOfTwo.hpp" @@ -1239,12 +1240,25 @@ class MacroAssembler: public Assembler { str(rscratch1, adr); } +private: // A generic CAS; success or failure is in the EQ flag. // Clobbers rscratch1 void cmpxchg(Register addr, Register expected, Register new_val, - enum operand_size size, - bool acquire, bool release, bool weak, - Register result); + enum operand_size size, enum atomic_memory_order order, + bool weak, Register result); + +public: + void cmpxchg(Register addr, Register expected, Register new_val, + enum operand_size size, enum atomic_memory_order order, + Register result = noreg) { + cmpxchg(addr, expected, new_val, size, order, /* weak */ false, result); + } + + void cmpxchg_weak(Register addr, Register expected, Register new_val, + enum operand_size size, enum atomic_memory_order order, + Register result = noreg) { + cmpxchg(addr, expected, new_val, size, order, /* weak */ true, result); + } #ifdef ASSERT // Template short-hand support to clean-up after a failed call to trampoline From 0c209afd4f9f669490ef6e07ac582fbc0a6cb649 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Wed, 1 Jul 2026 12:44:51 +0000 Subject: [PATCH 128/707] 8387265: G1: Shutdown during concurrent cycle leaves SATB queues in inconsistent state Reviewed-by: aboldtch, ayang --- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 3 ++ src/hotspot/share/gc/g1/g1ConcurrentMark.cpp | 20 +++++++++++++ src/hotspot/share/gc/g1/g1ConcurrentMark.hpp | 2 ++ .../share/gc/g1/g1ConcurrentMarkThread.hpp | 3 +- .../gc/g1/g1ConcurrentMarkThread.inline.hpp | 10 +++---- .../share/gc/g1/g1SATBMarkQueueSet.cpp | 9 +----- src/hotspot/share/gc/g1/g1VMOperations.cpp | 30 +++++++++++++++---- src/hotspot/share/gc/g1/g1VMOperations.hpp | 14 ++++++++- src/hotspot/share/runtime/vmOperation.hpp | 1 + 9 files changed, 71 insertions(+), 21 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 7396c1ee9ce..eaa6afb5efa 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -1654,6 +1654,9 @@ void G1CollectedHeap::stop() { // that are destroyed during shutdown. _cr->stop(); _service_thread->stop(); + VM_G1StopMarking op; + VMThread::execute(&op); + _cm->stop(); } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 11c93b092b1..6f9e4e2e9cf 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -2036,6 +2036,26 @@ void G1ConcurrentMark::print_stats() { } } +bool G1ConcurrentMark::shutdown_cleanup_needed() const { + // Cleanup (aborting threads, setting abort flags) is needed throughout the whole cycle before + // stopping the CM thread. + return is_fully_initialized() && is_in_concurrent_cycle(); +} + +void G1ConcurrentMark::shutdown_concurrent_cycle() { + assert_at_safepoint_on_vm_thread(); + + abort_root_region_scan_at_safepoint(); + abort_marking_threads(); + + SATBMarkQueueSet& satb_mq_set = G1BarrierSet::satb_mark_queue_set(); + satb_mq_set.abandon_partial_marking(); + // This can be called either during or outside marking, we'll read + // the expected_active value from the SATB queue set. + satb_mq_set.set_active_all_threads(false, /* new active value */ + satb_mq_set.is_active() /* expected_active */); +} + bool G1ConcurrentMark::concurrent_cycle_abort() { assert_at_safepoint_on_vm_thread(); assert(_g1h->collector_state()->is_in_full_gc(), "must be"); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index 21518423957..73dabc12863 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -608,6 +608,8 @@ class G1ConcurrentMark : public CHeapObj { bool mark_stack_empty() const { return _global_mark_stack.is_empty(); } void concurrent_cycle_start(); + bool shutdown_cleanup_needed() const; + void shutdown_concurrent_cycle(); // Abandon current marking iteration due to a Full GC. bool concurrent_cycle_abort(); void concurrent_cycle_end(bool mark_cycle_completed); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp index a22442c2b7f..a1c684ecf59 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp @@ -50,7 +50,8 @@ class G1ConcurrentMarkThread: public ConcurrentGCThread { Atomic _state; - ServiceState state() const { return _state.load_relaxed(); } + ServiceState state() const { return _state.load_acquire(); } + void set_state(ServiceState new_state) { _state.release_store(new_state); } // Returns whether we are in a "Full" cycle. bool is_in_full_concurrent_cycle() const; diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp index bea6fe4e451..3225c253dbb 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp @@ -48,31 +48,31 @@ inline bool G1ConcurrentMarkThread::is_in_full_concurrent_cycle() const { inline void G1ConcurrentMarkThread::set_idle() { // Concurrent cycle may be aborted any time. assert(!is_idle(), "must not be idle"); - _state.store_relaxed(Idle); + set_state(Idle); } inline void G1ConcurrentMarkThread::start_full_cycle() { assert(SafepointSynchronize::is_at_safepoint(), "must be"); assert(is_idle(), "cycle in progress"); - _state.store_relaxed(FullCycleMarking); + set_state(FullCycleMarking); } inline void G1ConcurrentMarkThread::start_undo_cycle() { assert(SafepointSynchronize::is_at_safepoint(), "must be"); assert(is_idle(), "cycle in progress"); - _state.store_relaxed(UndoCycleResetForNextCycle); + set_state(UndoCycleResetForNextCycle); } inline void G1ConcurrentMarkThread::set_full_cycle_rebuild_and_scrub() { assert(SafepointSynchronize::is_at_safepoint(), "must be"); assert(state() == FullCycleMarking, "must be"); - _state.store_relaxed(FullCycleRebuildOrScrub); + set_state(FullCycleRebuildOrScrub); } inline void G1ConcurrentMarkThread::set_full_cycle_reset_for_next_cycle() { assert(SafepointSynchronize::is_at_safepoint(), "must be"); assert(state() == FullCycleRebuildOrScrub, "must be"); - _state.store_relaxed(FullCycleResetForNextCycle); + set_state(FullCycleResetForNextCycle); } inline bool G1ConcurrentMarkThread::is_in_marking() const { diff --git a/src/hotspot/share/gc/g1/g1SATBMarkQueueSet.cpp b/src/hotspot/share/gc/g1/g1SATBMarkQueueSet.cpp index 7553936bb26..b913bdc2525 100644 --- a/src/hotspot/share/gc/g1/g1SATBMarkQueueSet.cpp +++ b/src/hotspot/share/gc/g1/g1SATBMarkQueueSet.cpp @@ -114,12 +114,5 @@ class G1SATBMarkQueueFilterFn { }; void G1SATBMarkQueueSet::filter(SATBMarkQueue& queue) { - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - if (g1h->collector_state()->is_in_marking()) { - apply_filter(G1SATBMarkQueueFilterFn(), queue); - } else { - // is_in_marking() covers both the concurrent marking and the Remark pause. Outside - // of that, there can be no entry that requires SATB marking. - queue.set_empty(); - } + apply_filter(G1SATBMarkQueueFilterFn(), queue); } diff --git a/src/hotspot/share/gc/g1/g1VMOperations.cpp b/src/hotspot/share/gc/g1/g1VMOperations.cpp index 86e55e8ac4f..373ec9660da 100644 --- a/src/hotspot/share/gc/g1/g1VMOperations.cpp +++ b/src/hotspot/share/gc/g1/g1VMOperations.cpp @@ -130,8 +130,13 @@ void VM_G1CollectForAllocation::doit() { } void VM_G1PauseConcurrent::doit() { - GCIdMark gc_id_mark(_gc_id); G1CollectedHeap* g1h = G1CollectedHeap::heap(); + if (_is_shutting_down) { + g1h->concurrent_mark()->shutdown_concurrent_cycle(); + return; + } + + GCIdMark gc_id_mark(_gc_id); GCTraceCPUTime tcpu(g1h->concurrent_mark()->gc_tracer_cm()); // GCTraceTime(...) only supports sub-phases, so a more verbose version @@ -150,12 +155,9 @@ void VM_G1PauseConcurrent::doit() { bool VM_G1PauseConcurrent::doit_prologue() { Heap_lock->lock(); G1CollectedHeap* g1h = G1CollectedHeap::heap(); - if (g1h->is_shutting_down()) { + _is_shutting_down = g1h->is_shutting_down(); + if (_is_shutting_down && !g1h->concurrent_mark()->shutdown_cleanup_needed()) { Heap_lock->unlock(); - // JVM shutdown has started. Abort concurrent marking to ensure that any further - // concurrent VM operations will not try to start and interfere with the shutdown - // process. - g1h->concurrent_mark()->abort_marking_threads(); return false; } return true; @@ -177,3 +179,19 @@ void VM_G1PauseCleanup::work() { G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); cm->cleanup(); } + +bool VM_G1StopMarking::doit_prologue() { + G1CollectedHeap* g1h = G1CollectedHeap::heap(); +#ifdef ASSERT + { + MutexLocker ml(Heap_lock); + assert(g1h->is_shutting_down(), "must be"); + } +#endif + return g1h->concurrent_mark()->shutdown_cleanup_needed(); +} + +void VM_G1StopMarking::doit() { + G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); + cm->shutdown_concurrent_cycle(); +} diff --git a/src/hotspot/share/gc/g1/g1VMOperations.hpp b/src/hotspot/share/gc/g1/g1VMOperations.hpp index 458d638b04e..7d56ea1916f 100644 --- a/src/hotspot/share/gc/g1/g1VMOperations.hpp +++ b/src/hotspot/share/gc/g1/g1VMOperations.hpp @@ -80,11 +80,12 @@ class VM_G1CollectForAllocation : public VM_CollectForAllocation { // Concurrent G1 stop-the-world operations such as remark and cleanup. class VM_G1PauseConcurrent : public VM_Operation { uint _gc_id; + bool _is_shutting_down; const char* _message; protected: VM_G1PauseConcurrent(const char* message) : - _gc_id(GCId::current()), _message(message) { } + _gc_id(GCId::current()), _is_shutting_down(false), _message(message) { } virtual void work() = 0; // Does this concurrent pause affect the memory pools? If so, update the collectionUsage() @@ -116,4 +117,15 @@ class VM_G1PauseCleanup : public VM_G1PauseConcurrent { void work() override; }; +class VM_G1StopMarking : public VM_Operation { +public: + VM_G1StopMarking() : VM_Operation() { } + VMOp_Type type() const override { return VMOp_G1StopMarking; } + + bool doit_prologue() override; + void doit() override; + + bool is_gc_operation() const override { return true; } +}; + #endif // SHARE_GC_G1_G1VMOPERATIONS_HPP diff --git a/src/hotspot/share/runtime/vmOperation.hpp b/src/hotspot/share/runtime/vmOperation.hpp index e22d11cf1a8..af9aa68c7ec 100644 --- a/src/hotspot/share/runtime/vmOperation.hpp +++ b/src/hotspot/share/runtime/vmOperation.hpp @@ -59,6 +59,7 @@ template(G1PauseCleanup) \ template(G1TryInitiateConcMark) \ template(G1RendezvousGCThreads) \ + template(G1StopMarking) \ template(ZMarkEndOld) \ template(ZMarkEndYoung) \ template(ZMarkFlushOperation) \ From 3f52251c9d82991b14f0bbf34c81627911b0fdbf Mon Sep 17 00:00:00 2001 From: Andreas Chmielewski Date: Wed, 1 Jul 2026 20:32:46 +0000 Subject: [PATCH 129/707] 8387124: Incomplete algorithm decomposition for TLS 1.3 cipher suites in SSLAlgorithmDecomposer Reviewed-by: abarashev, mullan --- .../security/ssl/SSLAlgorithmDecomposer.java | 10 + .../BulkCipherDisabledAlgorithms.java | 218 ++++++++++++++++++ .../TLS13BulkCipherDisabledCipherSuite.java | 79 +++++++ 3 files changed, 307 insertions(+) create mode 100644 test/jdk/javax/net/ssl/ciphersuites/BulkCipherDisabledAlgorithms.java create mode 100644 test/jdk/sun/security/ssl/CipherSuite/TLS13BulkCipherDisabledCipherSuite.java diff --git a/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmDecomposer.java b/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmDecomposer.java index 565ed8f6128..61b1236e9bc 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmDecomposer.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmDecomposer.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, IBM Corporation. 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 @@ -172,9 +173,18 @@ private Set decomposes(SSLCipher bulkCipher) { case B_AES_128_GCM: components.add("AES_128_GCM"); break; + case B_AES_128_GCM_IV: + components.add("AES_128_GCM"); + break; case B_AES_256_GCM: components.add("AES_256_GCM"); break; + case B_AES_256_GCM_IV: + components.add("AES_256_GCM"); + break; + case B_CC20_P1305: + components.add("CHACHA20_POLY1305"); + break; } return components; diff --git a/test/jdk/javax/net/ssl/ciphersuites/BulkCipherDisabledAlgorithms.java b/test/jdk/javax/net/ssl/ciphersuites/BulkCipherDisabledAlgorithms.java new file mode 100644 index 00000000000..11f3efb1518 --- /dev/null +++ b/test/jdk/javax/net/ssl/ciphersuites/BulkCipherDisabledAlgorithms.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2026, IBM Corporation. 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 8387124 + * @summary Test TLS cipher suite disabling via jdk.tls.disabledAlgorithms, + * including matching on bulk cipher components, covering both + * visibility and handshake behavior. + * @library /test/lib + * /javax/net/ssl/TLSCommon + * /javax/net/ssl/templates + * @run main/othervm BulkCipherDisabledAlgorithms visibility + * @run main/othervm BulkCipherDisabledAlgorithms handshake + */ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.net.ssl.*; + +import jdk.test.lib.process.Proc; + +import java.security.NoSuchAlgorithmException; +import java.security.Security; + +public class BulkCipherDisabledAlgorithms { + + public static void main(String[] args) throws Exception { + if (args.length == 0) { + throw new RuntimeException("Missing mode argument"); + } + + String mode = args[0]; + boolean isVisibilityTest = "visibility".equals(mode); + boolean isHandshakeTest = "handshake".equals(mode); + + if (args.length == 1) { + List tests = buildTests(isVisibilityTest); + + for (String[] test : tests) { + String suite = test[0]; + String disabled = test[1]; + String expected = test[2]; + + System.out.println("================================================="); + System.out.println("Testing: " + mode + + ", suite=" + suite + + ", disabled=" + disabled + + ", expected=" + expected); + + Proc p = Proc.create( + BulkCipherDisabledAlgorithms.class.getName()) + .args(mode, suite, expected) + .secprop("jdk.tls.disabledAlgorithms", disabled) + .inheritIO(); + + p.start().waitFor(0); + } + + System.out.println("TEST PASS - OK"); + return; + } + + String suite = args[1]; + String expected = args[2]; + boolean expectedDisabled = "disabled".equals(expected); + + if (isVisibilityTest) { + testCipherSuiteVisibility(suite, expectedDisabled); + } + + if (isHandshakeTest) { + testHandshake(suite, expectedDisabled); + } + } + + // Returns cipher suites for testing. + // - true: use all supported suites (independent of disabledAlgorithms) + // - false: use default enabled suites (candidates for handshake) + private static CipherSuite[] getCipherSuites(boolean useSupportedSuites) + throws NoSuchAlgorithmException { + SSLEngine engine = SSLContext.getDefault().createSSLEngine(); + String[] suites = useSupportedSuites + ? engine.getSupportedCipherSuites() + : engine.getEnabledCipherSuites(); + + return Arrays.stream(suites) + .map(CipherSuite::cipherSuite) + .filter(cs -> cs != CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) + .toArray(CipherSuite[]::new); + } + + private static List buildTests(boolean useSupportedSuites) + throws NoSuchAlgorithmException { + if (useSupportedSuites) { + // disabledAlgorithms limits supported suites; clear to list all + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + } + + List tests = new ArrayList<>(); + CipherSuite[] suites = getCipherSuites(useSupportedSuites); + + for (CipherSuite suite : suites) { + String suiteName = suite.name(); + String bulk = extractBulkCipher(suiteName); + + tests.add(new String[] { suiteName, suiteName, "disabled" }); + tests.add(new String[] { suiteName, bulk, "disabled" }); + + for (CipherSuite other : suites) { + // Negative test case: disable a different bulk cipher than the one + // used by the current suite. This ensures that the suite remains + // enabled and a successful TLS handshake can still be negotiated. + if (other == suite) { + continue; + } + + String otherBulk = extractBulkCipher(other.name()); + + if (!bulk.equals(otherBulk) + && !suiteName.contains(otherBulk)) { + tests.add(new String[] { suiteName, otherBulk, "enabled" }); + break; + } + } + } + + return tests; + } + + /** + * Separator used in TLS cipher suite names to mark the start of + * the bulk cipher component (e.g. TLS_RSA_WITH_AES_128_CBC_SHA). + */ + private static final String WITH = "_WITH_"; + + private static String extractBulkCipher(String suite) { + if (suite.contains(WITH)) { + String after = suite.substring(suite.indexOf(WITH) + WITH.length()); + int last = after.lastIndexOf('_'); + return after.substring(0, last); + } else { + int first = suite.indexOf('_'); + int last = suite.lastIndexOf('_'); + return suite.substring(first + 1, last); + } + } + + private static void testCipherSuiteVisibility(String suite, boolean expectedDisabled) + throws NoSuchAlgorithmException { + boolean visible = Arrays.asList(getCipherSuites(true)) + .contains(CipherSuite.cipherSuite(suite)); + + if (!expectedDisabled && !visible) { + throw new RuntimeException( + "Cipher suite '" + suite + "' not visible but expected to be enabled"); + } else if (expectedDisabled && visible) { + throw new RuntimeException( + "Cipher suite '" + suite + "' visible but expected to be disabled"); + } + } + + private static void testHandshake(String suite, boolean expectedDisabled) throws Exception { + try { + new TLSHandshakeTest(suite).run(); + + if (expectedDisabled) { + throw new RuntimeException( + "Handshake succeeded but should fail: " + suite); + } + } catch (SSLHandshakeException e) { + if (!expectedDisabled) { + throw new RuntimeException( + "Handshake failed unexpectedly: " + suite, e); + } + } + } + + private static class TLSHandshakeTest extends SSLSocketTemplate { + private final String suite; + + TLSHandshakeTest(String suite) { + this.suite = suite; + } + + @Override + protected void configureClientSocket(SSLSocket socket) { + socket.setEnabledCipherSuites(new String[] { suite }); + } + + @Override + protected void configureServerSocket(SSLServerSocket socket) { + socket.setEnabledCipherSuites(new String[] { suite }); + } + } +} diff --git a/test/jdk/sun/security/ssl/CipherSuite/TLS13BulkCipherDisabledCipherSuite.java b/test/jdk/sun/security/ssl/CipherSuite/TLS13BulkCipherDisabledCipherSuite.java new file mode 100644 index 00000000000..87a6f156152 --- /dev/null +++ b/test/jdk/sun/security/ssl/CipherSuite/TLS13BulkCipherDisabledCipherSuite.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2026, IBM Corporation. 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 8387124 + * @summary Test disabling TLS 1.3 cipher suites with bulk ciphers names + * @run testng/othervm TLS13BulkCipherDisabledCipherSuite + */ + +import static org.testng.AssertJUnit.assertTrue; + +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.security.Security; +import java.util.List; + +public class TLS13BulkCipherDisabledCipherSuite extends AbstractDisableCipherSuites { + + private static final String SECURITY_PROPERTY = "jdk.tls.disabledAlgorithms"; + private static final String TEST_ALGORITHMS = "AES_256_GCM," + + " AES_128_GCM," + + " CHACHA20_POLY1305"; + private static final String[] CIPHER_SUITES = new String[] { + "TLS_AES_256_GCM_SHA384", + "TLS_AES_128_GCM_SHA256", + "TLS_CHACHA20_POLY1305_SHA256" + }; + static final List CIPHER_SUITES_IDS = List.of( + 0x1301, + 0x1302, + 0x1303); + + @Override + protected String getProtocol() { + return "TLSv1.3"; + } + + @BeforeTest + void setUp() throws Exception { + Security.setProperty(SECURITY_PROPERTY, TEST_ALGORITHMS); + } + + @Test + public void testDefault() throws Exception { + assertTrue(testDefaultCase(CIPHER_SUITES_IDS)); + } + + @Test + public void testAddDisabled() throws Exception { + assertTrue(testEngAddDisabled(CIPHER_SUITES, CIPHER_SUITES_IDS)); + } + + @Test + public void testOnlyDisabled() throws Exception { + assertTrue(testEngOnlyDisabled(CIPHER_SUITES)); + } +} From 568bb44750e5a967c233277525c975aec2e88bd3 Mon Sep 17 00:00:00 2001 From: Ashay Rane Date: Thu, 2 Jul 2026 02:21:09 +0000 Subject: [PATCH 130/707] 8386807: com/sun/jndi/ldap/Connection.java references the wrong exception Reviewed-by: dfuchs --- .../share/classes/com/sun/jndi/ldap/Connection.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java b/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java index 1e0a924f12c..3bbebf5f9d7 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java @@ -1173,8 +1173,14 @@ public void handshakeCompleted(HandshakeCompletedEvent event) { tlsHandshakeCompleted.complete(tlsServerCert); } catch (SSLPeerUnverifiedException ex) { CommunicationException ce = new CommunicationException(); - ce.setRootCause(closureReason); - tlsHandshakeCompleted.completeExceptionally(ex); + IOException priorFailure = closureReason; + if (priorFailure != null) { + ce.setRootCause(priorFailure); + ce.addSuppressed(ex); + } else { + ce.setRootCause(ex); + } + tlsHandshakeCompleted.completeExceptionally(ce); } } } From a301709aba74d2a7ca744b38b45232fd88cbfbc7 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Thu, 2 Jul 2026 04:32:10 +0000 Subject: [PATCH 131/707] 8385924: GZIPInputStream.read() behaves differently on some Java versions Reviewed-by: lancea, alanb, simonis --- .../java/util/zip/GZIPInputStream.java | 166 +++++++++++++----- .../GZIP/GZIPInputStreamCallsAvailable.java | 124 +++++++++++++ .../zip/GZIP/GZIPOverBlockingStreams.java | 3 + 3 files changed, 251 insertions(+), 42 deletions(-) create mode 100644 test/jdk/java/util/zip/GZIP/GZIPInputStreamCallsAvailable.java diff --git a/src/java.base/share/classes/java/util/zip/GZIPInputStream.java b/src/java.base/share/classes/java/util/zip/GZIPInputStream.java index 88d08386e8c..8586dc7f63b 100644 --- a/src/java.base/share/classes/java/util/zip/GZIPInputStream.java +++ b/src/java.base/share/classes/java/util/zip/GZIPInputStream.java @@ -58,6 +58,19 @@ * The {@link #close} method should be called to release resources used by this * stream, either directly, or with the {@code try}-with-resources statement. * + * @implNote + * After reading a member trailer, the {@linkplain #read(byte[], int, int) read} method calls + * {@link InputStream#available()} on the underlying stream to determine whether additional + * bytes are available that may represent a subsequent member. If the + * {@systemProperty jdk.util.gzip.tryReadAheadAfterTrailer} system property is set + * to {@code true}, then the call to {@code InputStream.available()} is skipped and the + * implementation instead attempts to read a subsequent member in the stream. + * {@code GZIPInputStream} depends on the return value of {@code InputStream.available()} + * to reliably process a stream with a series of members. Consequently, it may be necessary + * to set this property in environments that process streams with a series of members. By default, + * the {@code jdk.util.gzip.tryReadAheadAfterTrailer} system property is not set, and + * {@code InputStream.available()} gets called. + * * @spec https://www.rfc-editor.org/info/rfc1952 * RFC 1952: GZIP file format specification version 4.3 * @@ -66,6 +79,12 @@ * @since 1.1 */ public class GZIPInputStream extends InflaterInputStream { + + // system property which configures whether we skip the call to InputStream.available() + // when checking for additional GZIP members in a stream + private static final boolean alwaysReadNextMember = + Boolean.getBoolean("jdk.util.gzip.tryReadAheadAfterTrailer"); + /** * GZIP header magic number. */ @@ -119,7 +138,11 @@ public GZIPInputStream(InputStream in, int size) throws IOException { super(in, createInflater(in, size), size); usesDefaultInflater = true; try { - readHeader(in); + // we don't expect the stream to be at EOF + // and if it is, then we want readHeader to + // raise an exception, so we pass "true" for + // the "failOnEOF" param. + readHeader(in, true); } catch (IOException ioe) { this.inf.end(); throw ioe; @@ -194,10 +217,15 @@ public int read(byte[] buf, int off, int len) throws IOException { } int n = super.read(buf, off, len); if (n == -1) { - if (readTrailer()) + if (hasNoMoreMembers()) { eos = true; - else + } else { + // When a next member is available, hasNoMoreMembers() will read + // its header and will position the stream at the next member's + // deflated data. We now decompress and return that member's + // decompressed data. return this.read(buf, off, len); + } } else { crc.update(buf, off, n); } @@ -221,12 +249,40 @@ public void close() throws IOException { /* * Reads GZIP member header and returns the total byte number * of this member header. + * If failOnEOF is false and if the given InputStream has already + * reached EOF when this method was invoked, then this method returns + * -1 (indicating that there's no GZIP member header). + * In all other cases of malformed header or EOF being detected + * when reading the header, this method will throw an IOException. */ - private int readHeader(InputStream this_in) throws IOException { - CheckedInputStream in = new CheckedInputStream(this_in, crc); + private int readHeader(InputStream stream, boolean failOnEOF) throws IOException { + CheckedInputStream in = new CheckedInputStream(stream, crc); crc.reset(); + + int magic; + if (!failOnEOF) { + // read an unsigned short value representing the GZIP magic header. + // this is the same as calling readUShort(in), except that here, + // when reading the first byte, we don't raise an EOFException + // if the stream has already reached EOF. + + // read unsigned byte + int b = in.read(); + if (b == -1) { // EOF + crc.reset(); + return -1; // represents no header bytes available + } + checkUnexpectedByte(b); + // read the next unsigned byte to form the unsigned + // short. we throw the usual EOFException/ZipException + // from this point on if there is no more data or + // the data doesn't represent a header. + magic = (readUByte(in) << 8) | b; + } else { + magic = readUShort(in); + } // Check header magic - if (readUShort(in) != GZIP_MAGIC) { + if (magic != GZIP_MAGIC) { throw new ZipException("Not in GZIP format"); } // Check compression method @@ -268,44 +324,66 @@ private int readHeader(InputStream this_in) throws IOException { return n; } - /* - * Reads GZIP member trailer and returns true if the eos - * reached, false if there are more (concatenated gzip - * data set) + /** + * Reads the current GZIP member's trailer and returns true if the end-of-stream is + * reached. After reading the current member's trailer, if the stream has a subsequent + * GZIP member, then this method reads that member's header and returns false indicating + * that there is another member in the stream. */ - private boolean readTrailer() throws IOException { - InputStream in = this.in; - int n = inf.getRemaining(); - if (n > 0) { - in = new SequenceInputStream( - new ByteArrayInputStream(buf, len - n, n), - new FilterInputStream(in) { - public void close() throws IOException {} - }); + private boolean hasNoMoreMembers() throws IOException { + final int numRemainingInInflater = inf.getRemaining(); + InputStream stream = this.in; + if (numRemainingInInflater > 0) { + stream = new SequenceInputStream( + new ByteArrayInputStream(buf, len - numRemainingInInflater, numRemainingInInflater), + new FilterInputStream(stream) { + public void close() {} + }); } + // first read the current member's trailer + readTrailer(stream); + // decide whether to read next member's header + final boolean readNextMember = alwaysReadNextMember + || this.in.available() > 0 + || numRemainingInInflater > 26; // current member's trailer == 8 bytes + // + minimum of 10 bytes header for next member + // + mandatory 8 bytes from next member's trailer + // == at least 26 bytes needed for next member to + // be present + if (!readNextMember) { + return true; // no need to read next member + } + // read next member's header + int m = 8; // this.trailer + try { + int numNextHeaderBytes = readHeader(stream, false); // next.header (if available) + if (numNextHeaderBytes == -1) { + return true; // end of stream reached, no more members + } + m += numNextHeaderBytes; + } catch (IOException ze) { + return true; // ignore any malformed, consider it as no more members in the stream + } + inf.reset(); // reset the inflater for fresh input data from the next member + if (numRemainingInInflater > m) { + // position the inflater's input buffer to the start of next member's deflated data + inf.setInput(buf, len - numRemainingInInflater + m, numRemainingInInflater - m); + } + return false; // next member exists + } + + /** + * Reads the current member's trailer + * + * @param stream the InputStream containing the trailer + */ + private void readTrailer(final InputStream stream) throws IOException { // Uses left-to-right evaluation order - if ((readUInt(in) != crc.getValue()) || - // rfc1952; ISIZE is the input size modulo 2^32 - (readUInt(in) != (inf.getBytesWritten() & 0xffffffffL))) + if ((readUInt(stream) != crc.getValue()) || + // rfc1952; ISIZE is the input size modulo 2^32 + (readUInt(stream) != (inf.getBytesWritten() & 0xffffffffL))) { throw new ZipException("Corrupt GZIP trailer"); - - // If there are more bytes available in "in" or - // the leftover in the "inf" is > 26 bytes: - // this.trailer(8) + next.header.min(10) + next.trailer(8) - // try concatenated case - if (this.in.available() > 0 || n > 26) { - int m = 8; // this.trailer - try { - m += readHeader(in); // next.header - } catch (IOException ze) { - return true; // ignore any malformed, do nothing - } - inf.reset(); - if (n > m) - inf.setInput(buf, len - n + m, n - m); - return false; } - return true; } /* @@ -332,12 +410,16 @@ private int readUByte(InputStream in) throws IOException { if (b == -1) { throw new EOFException(); } + checkUnexpectedByte(b); + return b; + } + + private void checkUnexpectedByte(final int b) throws IOException { if (b < -1 || b > 255) { - // Report on this.in, not argument in; see read{Header, Trailer}. + // report the InputStream type which returned this unexpected byte throw new IOException(this.in.getClass().getName() - + ".read() returned value out of range -1..255: " + b); + + ".read() returned value out of range -1..255: " + b); } - return b; } /* diff --git a/test/jdk/java/util/zip/GZIP/GZIPInputStreamCallsAvailable.java b/test/jdk/java/util/zip/GZIP/GZIPInputStreamCallsAvailable.java new file mode 100644 index 00000000000..e39b47dfc8e --- /dev/null +++ b/test/jdk/java/util/zip/GZIP/GZIPInputStreamCallsAvailable.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. + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.List; +import java.util.Random; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +import jdk.test.lib.RandomFactory; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static java.nio.charset.StandardCharsets.US_ASCII; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +/* + * @test + * @summary Verify the behaviour of GZIPInputStream when dealing with InputStream.available() + * on the underlying stream and the jdk.util.gzip.tryReadAheadAfterTrailer + * system property being enabled/disabled + * @key randomness + * @library /test/lib + * @build jdk.test.lib.RandomFactory + * @run junit/othervm -Djdk.util.gzip.tryReadAheadAfterTrailer=true GZIPInputStreamCallsAvailable + * @run junit/othervm -Djdk.util.gzip.tryReadAheadAfterTrailer=false GZIPInputStreamCallsAvailable + * @run junit GZIPInputStreamCallsAvailable + */ +class GZIPInputStreamCallsAvailable { + + private static final boolean AVAILABLE_METHOD_INVOCATION_SKIPPED = + Boolean.getBoolean("jdk.util.gzip.tryReadAheadAfterTrailer"); + private static final Random random = RandomFactory.getRandom(); + + private record TestData(byte[] uncompressed, byte[] compressed) { + } + + static List numGZIPMembers() { + return List.of(1, + 33, + random.nextInt(2, 1001) // a reasonably large number of members + ); + } + + /* + * Verify that GZIPInputStream reads and returns the correct decompressed data when: + * - the underlying InputStream.available() returns an accurate value + * - and when the GZIPInputStream isn't expected to call the underlying InputStream.available() + * method + */ + @ParameterizedTest + @MethodSource("numGZIPMembers") + void testMultipleMembers(final int numMembers) throws IOException { + final TestData testData = createGZIPStream(numMembers); + final InputStream underlyingStream = AVAILABLE_METHOD_INVOCATION_SKIPPED + // stream whose available() method isn't expected to be invoked + ? new AlwaysThrowFromAvailable(new ByteArrayInputStream(testData.compressed)) + // stream whose available() will be invoked and returns an accurate value + : new ByteArrayInputStream(testData.compressed); + try (GZIPInputStream gzip = new GZIPInputStream(underlyingStream)) { + final byte[] decompressed = gzip.readAllBytes(); + assertArrayEquals(testData.uncompressed, decompressed, "unexpected decompressed data"); + } + } + + /* + * Creates and returns bytes representing a GZIP stream consisting of the given number of + * members. + */ + private static TestData createGZIPStream(final int numMembers) throws IOException { + final String content = "foo bar hello world from " + GZIPInputStreamCallsAvailable.class; + final ByteArrayOutputStream uncompressed = new ByteArrayOutputStream(); + final ByteArrayOutputStream gzipped = new ByteArrayOutputStream(); + for (int i = 1; i <= numMembers; i++) { + final ByteArrayOutputStream member = new ByteArrayOutputStream(); + try (final OutputStream gzip = new GZIPOutputStream(member)) { + final byte[] memberRawBytes = ("member-" + i + " " + content).getBytes(US_ASCII); + gzip.write(memberRawBytes); + // keep track of the uncompressed content too so that it can be compared for + // equality with the decompressed content + uncompressed.write(memberRawBytes); + } + // write out the GZIP member to the stream which accumulates all the members + gzipped.write(member.toByteArray()); + } + return new TestData(uncompressed.toByteArray(), gzipped.toByteArray()); + } + + private static class AlwaysThrowFromAvailable extends FilterInputStream { + public AlwaysThrowFromAvailable(InputStream in) { + super(in); + } + + @Override + public int available() { + throw new AssertionError(this.getClass().getName() + + ".available() wasn't expected to be invoked"); + } + } +} diff --git a/test/jdk/java/util/zip/GZIP/GZIPOverBlockingStreams.java b/test/jdk/java/util/zip/GZIP/GZIPOverBlockingStreams.java index 81f55f2f0dd..b6dea98c28d 100644 --- a/test/jdk/java/util/zip/GZIP/GZIPOverBlockingStreams.java +++ b/test/jdk/java/util/zip/GZIP/GZIPOverBlockingStreams.java @@ -63,6 +63,9 @@ * @library /test/lib * @build jdk.test.lib.net.URIBuilder jdk.test.lib.RandomFactory * @run junit GZIPOverBlockingStreams + * @comment verify it behaves the same when jdk.util.gzip.tryReadAheadAfterTrailer system property + * is set to false + * @run junit/othervm -Djdk.util.gzip.tryReadAheadAfterTrailer=false GZIPOverBlockingStreams */ class GZIPOverBlockingStreams { From e1a3967870c0fc5171e41ebe686eb94c8158dfb2 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 2 Jul 2026 05:35:46 +0000 Subject: [PATCH 132/707] 8364322: (fs) fchmodat support for AT_SYMLINK_NOFOLLOW flag too pessimistic on Linux Reviewed-by: alanb --- .../sun/nio/fs/UnixNativeDispatcher.java | 10 ++++---- .../native/libnio/fs/UnixNativeDispatcher.c | 17 +++++++------ .../nio/file/DirectoryStream/SecureDS.java | 24 ++++++++++++++----- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java b/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java index 2d72aeb2ee9..ed28e1a1fe7 100644 --- a/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java +++ b/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java @@ -555,9 +555,10 @@ static native int flistxattr(int filedes, long listAddress, int size) /** * Capabilities */ - private static final int SUPPORTS_OPENAT = 1 << 1; // syscalls - private static final int SUPPORTS_XATTR = 1 << 3; - private static final int SUPPORTS_BIRTHTIME = 1 << 16; // other features + private static final int SUPPORTS_OPENAT = 1 << 1; // syscalls + private static final int SUPPORTS_FCHMODAT_NOFOLLOW = 1 << 2; + private static final int SUPPORTS_XATTR = 1 << 3; + private static final int SUPPORTS_BIRTHTIME = 1 << 16; // other features private static final int capabilities; /** @@ -585,9 +586,8 @@ static boolean xattrSupported() { * Supports fchmodat with AT_SYMLINK_NOFOLLOW flag */ static boolean fchmodatNoFollowSupported() { - return fchmodatNoFollowSupported0(); + return (capabilities & SUPPORTS_FCHMODAT_NOFOLLOW) != 0; } - private static native boolean fchmodatNoFollowSupported0(); private static native int init(); static { diff --git a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c index 4b5cfabebfb..aba16118988 100644 --- a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c +++ b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -388,17 +388,16 @@ Java_sun_nio_fs_UnixNativeDispatcher_init(JNIEnv* env, jclass this) capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_XATTR; #endif - return capabilities; -} - -JNIEXPORT jboolean JNICALL -Java_sun_nio_fs_UnixNativeDispatcher_fchmodatNoFollowSupported0(JNIEnv* env, jclass this) { #if defined(__linux__) - // Linux recognizes but does not support the AT_SYMLINK_NOFOLLOW flag - return JNI_FALSE; + // Linux 6.6+ supports AT_SYMLINK_NOFOLLOW. glibc 2.32+ also provides emulation for older kernels. + if (fchmodat(AT_FDCWD, "", 0, AT_SYMLINK_NOFOLLOW) == 0 || errno != ENOTSUP) { + capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_FCHMODAT_NOFOLLOW; + } #else - return JNI_TRUE; + capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_FCHMODAT_NOFOLLOW; #endif + + return capabilities; } JNIEXPORT jbyteArray JNICALL diff --git a/test/jdk/java/nio/file/DirectoryStream/SecureDS.java b/test/jdk/java/nio/file/DirectoryStream/SecureDS.java index 870a84a8927..f3321a8c04d 100644 --- a/test/jdk/java/nio/file/DirectoryStream/SecureDS.java +++ b/test/jdk/java/nio/file/DirectoryStream/SecureDS.java @@ -212,6 +212,13 @@ static void doSetPermissions(Path dir) throws IOException { Path link = createSymbolicLink(aDir.resolve(linkEntry), fileEntry); Set permsLink = getPosixFilePermissions(link, NOFOLLOW_LINKS); + // Test setting permissions on a regular file through the no-follow view + view = stream.getFileAttributeView(fileEntry, PosixFileAttributeView.class, NOFOLLOW_LINKS); + view.setPermissions(noperms); + assertEquals(noperms, getPosixFilePermissions(file)); + view.setPermissions(permsFile); + assertEquals(permsFile, getPosixFilePermissions(file)); + // Test following link to file view = stream.getFileAttributeView(link, PosixFileAttributeView.class); view.setPermissions(noperms); @@ -220,14 +227,19 @@ static void doSetPermissions(Path dir) throws IOException { view.setPermissions(permsFile); assertEquals(permsFile, getPosixFilePermissions(file)); assertEquals(permsLink, getPosixFilePermissions(link, NOFOLLOW_LINKS)); - // Symbolic link permissions do not apply on Linux - if (!Platform.isLinux()) { - // Test not following link to file - view = stream.getFileAttributeView(link, PosixFileAttributeView.class, NOFOLLOW_LINKS); - view.setPermissions(noperms); + + // Test not following link to file + var linkView = stream.getFileAttributeView(link, PosixFileAttributeView.class, NOFOLLOW_LINKS); + if (Platform.isLinux()) { + // Symbolic link permissions do not apply on Linux + assertThrows(IOException.class, () -> linkView.setPermissions(noperms)); + assertEquals(permsFile, getPosixFilePermissions(file)); + assertThrows(IOException.class, () -> linkView.setPermissions(permsLink)); + } else { + linkView.setPermissions(noperms); assertEquals(permsFile, getPosixFilePermissions(file)); assertEquals(noperms, getPosixFilePermissions(link, NOFOLLOW_LINKS)); - view.setPermissions(permsLink); + linkView.setPermissions(permsLink); assertEquals(permsFile, getPosixFilePermissions(file)); assertEquals(permsLink, getPosixFilePermissions(link, NOFOLLOW_LINKS)); } From db987b1e378cb337a5043a8c051f724461405296 Mon Sep 17 00:00:00 2001 From: Ivan Bereziuk Date: Thu, 2 Jul 2026 08:11:09 +0000 Subject: [PATCH 133/707] 8386474: Aarch64: Correct static_assert((N & (N - 1)) == 0 Co-authored-by: Ferenc Rakoczi Reviewed-by: adinn, semery --- src/hotspot/cpu/aarch64/register_aarch64.hpp | 9 +++++---- src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp | 12 ++++++++---- src/hotspot/share/utilities/globalDefinitions.hpp | 4 ++-- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/hotspot/cpu/aarch64/register_aarch64.hpp b/src/hotspot/cpu/aarch64/register_aarch64.hpp index ab83307d526..8d8856d3cf9 100644 --- a/src/hotspot/cpu/aarch64/register_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/register_aarch64.hpp @@ -28,6 +28,7 @@ #include "asm/register.hpp" #include "utilities/checkedCast.hpp" +#include "utilities/globalDefinitions.hpp" #include "utilities/powerOfTwo.hpp" class VMRegImpl; @@ -513,25 +514,25 @@ template bool vs_write_before_read(const VSeq& vout, const VSeq& vi template VSeq vs_front(const VSeq& v) { - static_assert(N > 0 && ((N & 1) == 0), "sequence length must be even"); + static_assert(N > 0 && is_even(N), "sequence length must be even"); return VSeq(v.base(), v.delta()); } template VSeq vs_back(const VSeq& v) { - static_assert(N > 0 && ((N & 1) == 0), "sequence length must be even"); + static_assert(N > 0 && is_even(N), "sequence length must be even"); return VSeq(v.base() + N / 2 * v.delta(), v.delta()); } template VSeq vs_even(const VSeq& v) { - static_assert(N > 0 && ((N & 1) == 0), "sequence length must be even"); + static_assert(N > 0 && is_even(N), "sequence length must be even"); return VSeq(v.base(), v.delta() * 2); } template VSeq vs_odd(const VSeq& v) { - static_assert(N > 0 && ((N & 1) == 0), "sequence length must be even"); + static_assert(N > 0 && is_even(N), "sequence length must be even"); return VSeq(v.base() + v.delta(), v.delta() * 2); } diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index cae69ac4621..2ad7e00817c 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -5442,6 +5442,7 @@ class StubGenerator: public StubCodeGenerator { // address supplied in base. template void vs_ldpq(const VSeq& v, Register base) { + static_assert(N > 0 && is_even(N), "sequence length must be even"); for (int i = 0; i < N; i += 2) { __ ldpq(v[i], v[i+1], Address(base, 16 * i)); } @@ -5452,7 +5453,7 @@ class StubGenerator: public StubCodeGenerator { // in base using post-increment addressing template void vs_ldpq_post(const VSeq& v, Register base) { - static_assert((N & (N - 1)) == 0, "sequence length must be even"); + static_assert(N > 0 && is_even(N), "sequence length must be even"); for (int i = 0; i < N; i += 2) { __ ldpq(v[i], v[i+1], __ post(base, 32)); } @@ -5463,7 +5464,7 @@ class StubGenerator: public StubCodeGenerator { // supplied in base using post-increment addressing template void vs_stpq_post(const VSeq& v, Register base) { - static_assert((N & (N - 1)) == 0, "sequence length must be even"); + static_assert(N > 0 && is_even(N), "sequence length must be even"); for (int i = 0; i < N; i += 2) { __ stpq(v[i], v[i+1], __ post(base, 32)); } @@ -5474,7 +5475,7 @@ class StubGenerator: public StubCodeGenerator { // using post-increment addressing. template void vs_ld2_post(const VSeq& v, Assembler::SIMD_Arrangement T, Register base) { - static_assert((N & (N - 1)) == 0, "sequence length must be even"); + static_assert(N > 0 && is_even(N), "sequence length must be even"); for (int i = 0; i < N; i += 2) { __ ld2(v[i], v[i+1], T, __ post(base, 32)); } @@ -5485,7 +5486,7 @@ class StubGenerator: public StubCodeGenerator { // post-increment addressing. template void vs_st2_post(const VSeq& v, Assembler::SIMD_Arrangement T, Register base) { - static_assert((N & (N - 1)) == 0, "sequence length must be even"); + static_assert(N > 0 && is_even(N), "sequence length must be even"); for (int i = 0; i < N; i += 2) { __ st2(v[i], v[i+1], T, __ post(base, 32)); } @@ -5530,6 +5531,7 @@ class StubGenerator: public StubCodeGenerator { // offsets array template void vs_ldpq_indexed(const VSeq& v, Register base, int start, int (&offsets)[N/2]) { + static_assert(N > 0 && is_even(N), "sequence length must be even"); for (int i = 0; i < N/2; i++) { __ ldpq(v[2*i], v[2*i+1], Address(base, start + offsets[i])); } @@ -5577,6 +5579,7 @@ class StubGenerator: public StubCodeGenerator { template void vs_ld2_indexed(const VSeq& v, Assembler::SIMD_Arrangement T, Register base, Register tmp, int start, int (&offsets)[N/2]) { + static_assert(N > 0 && is_even(N), "sequence length must be even"); for (int i = 0; i < N/2; i++) { __ add(tmp, base, start + offsets[i]); __ ld2(v[2*i], v[2*i+1], T, tmp); @@ -5590,6 +5593,7 @@ class StubGenerator: public StubCodeGenerator { template void vs_st2_indexed(const VSeq& v, Assembler::SIMD_Arrangement T, Register base, Register tmp, int start, int (&offsets)[N/2]) { + static_assert(N > 0 && is_even(N), "sequence length must be even"); for (int i = 0; i < N/2; i++) { __ add(tmp, base, start + offsets[i]); __ st2(v[2*i], v[2*i+1], T, tmp); diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 40691de518e..5e5a57c3780 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -1157,8 +1157,8 @@ inline T clamp(T value, T min, T max) { return MIN2(MAX2(value, min), max); } -inline bool is_odd (intx x) { return x & 1; } -inline bool is_even(intx x) { return !is_odd(x); } +constexpr bool is_odd (intx x) { return x & 1; } +constexpr bool is_even(intx x) { return !is_odd(x); } // abs methods which cannot overflow and so are well-defined across // the entire domain of integer types. From 157cca780278fd276f807ca1c5cf88e73894b936 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Thu, 2 Jul 2026 09:13:16 +0000 Subject: [PATCH 134/707] 8355412: com/sun/net/httpserver/Test9a.java failed on windows trying to delete file: java.nio.file.FileSystemException: The process cannot access the file because it is being used by another process Reviewed-by: dfuchs --- test/jdk/com/sun/net/httpserver/Test9a.java | 204 -------------------- 1 file changed, 204 deletions(-) delete mode 100644 test/jdk/com/sun/net/httpserver/Test9a.java diff --git a/test/jdk/com/sun/net/httpserver/Test9a.java b/test/jdk/com/sun/net/httpserver/Test9a.java deleted file mode 100644 index 56fbf9953a3..00000000000 --- a/test/jdk/com/sun/net/httpserver/Test9a.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @bug 6270015 - * @library /test/lib - * @build jdk.test.lib.Utils - * jdk.test.lib.net.SimpleSSLContext - * jdk.test.lib.net.URIBuilder - * @run main/othervm Test9a - * @run main/othervm -Djava.net.preferIPv6Addresses=true Test9a - * @summary Light weight HTTP server - */ - -import com.sun.net.httpserver.*; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.concurrent.*; -import java.io.*; -import java.net.*; -import javax.net.ssl.*; -import jdk.test.lib.net.SimpleSSLContext; -import jdk.test.lib.net.URIBuilder; - -import static jdk.test.lib.Asserts.assertEquals; -import static jdk.test.lib.Asserts.assertFileContentsEqual; -import static jdk.test.lib.Utils.createTempFileOfSize; - -/* Same as Test1 but requests run in parallel. - */ - -public class Test9a extends Test { - - private static final String TEMP_FILE_PREFIX = - HttpServer.class.getPackageName() + '-' + Test9a.class.getSimpleName() + '-'; - - private static final SSLContext serverCtx = SimpleSSLContext.findSSLContext(); - private static final SSLContext clientCtx = SimpleSSLContext.findSSLContext(); - static volatile boolean error = false; - - public static void main (String[] args) throws Exception { - HttpsServer server = null; - ExecutorService executor=null; - Path smallFilePath = createTempFileOfSize(TEMP_FILE_PREFIX, null, 23); - Path largeFilePath = createTempFileOfSize(TEMP_FILE_PREFIX, null, 2730088); - try { - System.out.print ("Test9a: "); - InetAddress loopback = InetAddress.getLoopbackAddress(); - InetSocketAddress addr = new InetSocketAddress(loopback, 0); - server = HttpsServer.create (addr, 0); - // Assert that both files share the same parent and can be served from the same `FileServerHandler` - assertEquals(smallFilePath.getParent(), largeFilePath.getParent()); - HttpHandler h = new FileServerHandler (smallFilePath.getParent().toString()); - HttpContext c1 = server.createContext ("/", h); - executor = Executors.newCachedThreadPool(); - server.setExecutor (executor); - server.setHttpsConfigurator(new HttpsConfigurator (serverCtx)); - server.start(); - - int port = server.getAddress().getPort(); - error = false; - Thread[] t = new Thread[100]; - - t[0] = test (true, "https", port, smallFilePath); - t[1] = test (true, "https", port, largeFilePath); - t[2] = test (true, "https", port, smallFilePath); - t[3] = test (true, "https", port, largeFilePath); - t[4] = test (true, "https", port, smallFilePath); - t[5] = test (true, "https", port, largeFilePath); - t[6] = test (true, "https", port, smallFilePath); - t[7] = test (true, "https", port, largeFilePath); - t[8] = test (true, "https", port, smallFilePath); - t[9] = test (true, "https", port, largeFilePath); - t[10] = test (true, "https", port, smallFilePath); - t[11] = test (true, "https", port, largeFilePath); - t[12] = test (true, "https", port, smallFilePath); - t[13] = test (true, "https", port, largeFilePath); - t[14] = test (true, "https", port, smallFilePath); - t[15] = test (true, "https", port, largeFilePath); - for (int i=0; i<16; i++) { - t[i].join(); - } - if (error) { - throw new RuntimeException ("error"); - } - - System.out.println ("OK"); - } finally { - if (server != null) - server.stop(0); - if (executor != null) - executor.shutdown(); - Files.delete(smallFilePath); - Files.delete(largeFilePath); - } - } - - static int foo = 1; - - static ClientThread test (boolean fixedLen, String protocol, int port, Path filePath) throws Exception { - ClientThread t = new ClientThread (fixedLen, protocol, port, filePath); - t.start(); - return t; - } - - static Object fileLock = new Object(); - - static class ClientThread extends Thread { - - boolean fixedLen; - String protocol; - int port; - private final Path filePath; - - ClientThread (boolean fixedLen, String protocol, int port, Path filePath) { - this.fixedLen = fixedLen; - this.protocol = protocol; - this.port = port; - this.filePath = filePath; - } - - public void run () { - try { - URL url = URIBuilder.newBuilder() - .scheme(protocol) - .loopback() - .port(port) - .path("/" + filePath.getFileName()) - .toURL(); - - HttpURLConnection urlc = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY); - if (urlc instanceof HttpsURLConnection) { - HttpsURLConnection urlcs = (HttpsURLConnection) urlc; - urlcs.setHostnameVerifier (new HostnameVerifier () { - public boolean verify (String s, SSLSession s1) { - return true; - } - }); - urlcs.setSSLSocketFactory (clientCtx.getSocketFactory()); - } - byte [] buf = new byte [4096]; - - String s = "chunk"; - if (fixedLen) { - urlc.setRequestProperty ("XFixed", "yes"); - s = "fixed"; - } - InputStream is = urlc.getInputStream(); - File temp; - synchronized (fileLock) { - temp = File.createTempFile (s, null); - temp.deleteOnExit(); - } - OutputStream fout = new BufferedOutputStream (new FileOutputStream(temp)); - int c, count = 0; - while ((c=is.read(buf)) != -1) { - count += c; - fout.write (buf, 0, c); - } - is.close(); - fout.close(); - - if (count != filePath.toFile().length()) { - System.out.println ("wrong amount of data returned"); - System.out.println ("fixedLen = "+fixedLen); - System.out.println ("protocol = "+protocol); - System.out.println ("port = "+port); - System.out.println ("file = " + filePath); - System.out.println ("temp = "+temp); - System.out.println ("count = "+count); - error = true; - } - assertFileContentsEqual(filePath, temp.toPath()); - temp.delete(); - } catch (Exception e) { - e.printStackTrace(); - error = true; - } - } - } - -} From c042ad289d10e4c2dba4371630ff82318b0aa23b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Du=EF=BF=BDan=20B=EF=BF=BDlek?= Date: Thu, 2 Jul 2026 09:32:44 +0000 Subject: [PATCH 135/707] 8386842: Preview files in root directory not recognized in system image Reviewed-by: alanb, sherman, liach --- .../jdk/internal/jimage/ImageReader.java | 48 +++++++++++++++-- .../jdk/internal/jimage/ImageReaderTest.java | 52 ++++++++++++++++++- 2 files changed, 95 insertions(+), 5 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java b/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java index 2cf28b835ce..59c2392dea9 100644 --- a/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java +++ b/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java @@ -486,7 +486,7 @@ Node findResourceNode(String moduleName, String resourcePath) { ImageLocation loc = null; if (isPreviewEnabled) { // We must test preview location first (if in preview mode). - loc = findLocation(moduleName, PREVIEW_RESOURCE_PREFIX + resourcePath); + loc = findLocation(moduleName, PREVIEW_RESOURCE_PREFIX + "/" + resourcePath); } if (loc == null) { loc = findLocation(moduleName, resourcePath); @@ -531,7 +531,7 @@ boolean containsResource(String moduleName, String resourcePath) { return node.isResource(); } } - loc = findLocation(moduleName, PREVIEW_RESOURCE_PREFIX + resourcePath); + loc = findLocation(moduleName, PREVIEW_RESOURCE_PREFIX + "/" + resourcePath); } if (loc == null) { loc = findLocation(moduleName, resourcePath); @@ -561,7 +561,19 @@ private Node buildAndCacheModulesNode(String name) { // Now try the non-prefixed resource name, but be careful to avoid false // positives for names like "/modules/modules/xxx" which could return a // location of a directory entry. - loc = findLocation(name.substring(MODULES_PREFIX.length())); + String resourceName = name.substring(MODULES_PREFIX.length()); + if (isPreviewEnabled) { + // Root-level preview resources are not pre-cached when an image + // is opened, so check for them first. + int pathStart = resourceName.indexOf('/', 1); + if (pathStart > 1 && resourceName.indexOf('/', pathStart + 1) < 0) { + loc = findLocation(resourceName.substring(0, pathStart) + + PREVIEW_INFIX + "/" + resourceName.substring(pathStart + 1)); + } + } + if (loc == null) { + loc = findLocation(resourceName); + } return loc != null && loc.getType() == RESOURCE ? ensureCached(newResource(name, loc)) : null; @@ -649,6 +661,36 @@ private void completeDirectory(Directory dir) { private Directory completeModuleDirectory(Directory dir, ImageLocation loc) { assert dir.getName().equals(loc.getFullName()) : "Mismatched location for directory: " + dir; List previewOnlyNodes = getPreviewNodesToMerge(dir); + if (isPreviewEnabled && previewOnlyNodes.isEmpty()) { + // When opening an image in preview mode, packages that have preview + // content are eagerly processed, caching preview resources and + // preview-only directories for direct lookup. Root-level preview + // resources are omitted during this process, since they have no + // package path and the empty package is not represented under + // "/packages", and must be processed separately. + int moduleStart = MODULES_PREFIX.length() + 1; + if (dir.getName().indexOf('/', moduleStart) < 0) { + ImageLocation previewLoc = findLocation(dir.getName() + PREVIEW_INFIX); + if (previewLoc != null) { + previewOnlyNodes = createChildNodes(previewLoc, 0, childLoc -> { + String baseName = getBaseName(childLoc); + String nonPreviewChildName = dir.getName() + "/" + baseName; + boolean isPreviewOnly = ImageLocation.isPreviewOnly(childLoc.getFlags()); + LocationType type = childLoc.getType(); + if (type == RESOURCE) { + Node childNode = nodes.computeIfAbsent(nonPreviewChildName, n -> newResource(n, childLoc)); + return isPreviewOnly ? childNode : null; + } else { + assert type == MODULES_DIR : "Invalid location type: " + childLoc; + Node childNode = nodes.get(nonPreviewChildName); + assert !(isPreviewOnly && childNode == null) : + "Inconsistent child node: " + nonPreviewChildName; + return isPreviewOnly ? childNode : null; + } + }); + } + } + } // We hide preview names from direct lookup, but must also prevent // the preview directory from appearing in any META-INF directories. boolean parentIsMetaInfDir = isMetaInf(dir); diff --git a/test/jdk/jdk/internal/jimage/ImageReaderTest.java b/test/jdk/jdk/internal/jimage/ImageReaderTest.java index 5104bb97f95..0fadc6eec12 100644 --- a/test/jdk/jdk/internal/jimage/ImageReaderTest.java +++ b/test/jdk/jdk/internal/jimage/ImageReaderTest.java @@ -46,6 +46,7 @@ import java.util.stream.Collectors; import static java.util.stream.Collectors.toSet; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -59,7 +60,7 @@ /* * @test - * @bug 8385355 + * @bug 8385355 8386842 * @summary Tests for ImageReader. * @modules java.base/jdk.internal.jimage * jdk.jlink/jdk.tools.jlink.internal @@ -86,6 +87,11 @@ public class ImageReaderTest { "!META-INF/z", "!META-INF/collision/child.properties", "!META-INF/collision", + // Non-class resource in top level directory + "!fileA.txt", + "!fileB.txt", + "!META-INF/preview/fileA.txt", + "!META-INF/preview/fileB.txt", // Replaces original class in preview mode. "@com.foo.HasPreviewVersion", // New class in existing package in preview mode. @@ -96,6 +102,11 @@ public class ImageReaderTest { // Two new packages in preview mode (new symbolic links). "@com.bar.preview.stuff.Foo", "@com.bar.preview.stuff.Bar"), + "modbaz", Arrays.asList( + "!file.txt", + "!normal.txt", + "!META-INF/preview/file.txt", + "!META-INF/preview/previewOnly.txt"), "modgus", Arrays.asList( // A second module with a preview-only empty package (preview). "@com.bar.preview.other.Gus")); @@ -270,6 +281,18 @@ public void testPreviewResources_disabled() throws IOException { assertAbsent(reader, "/modules/modfoo/com/foo/bar/IsPreviewOnly.class"); assertDirContents(reader, "/modules/modfoo/com/foo", "HasPreviewVersion.class", "NormalFoo.class", "bar"); assertDirContents(reader, "/modules/modfoo/com/foo/bar", "NormalBar.class"); + + // Non-class resource in top level directory + assertResource(reader, "modfoo", "fileA.txt"); + assertNonPreviewResourceVersion(reader, "modfoo", "fileA.txt"); + assertNode(reader, "/modules/modfoo/fileB.txt"); + assertNonPreviewResourceVersion(reader, "modfoo", "fileB.txt"); + assertDirContents(reader, "/modules/modfoo", "META-INF", "module-info.class", "fileA.txt", "fileB.txt", "com"); + + assertAbsent(reader, "/modules/modbaz/previewOnly.txt"); + assertDirContents(reader, "/modules/modbaz", "META-INF", "module-info.class", "file.txt", "normal.txt"); + assertNonPreviewResourceVersion(reader, "modbaz", "file.txt"); + assertNonPreviewResourceVersion(reader, "modbaz", "normal.txt"); } } @@ -289,6 +312,20 @@ public void testPreviewResources_enabled() throws IOException { assertResource(reader, "modfoo", "com/foo/bar/IsPreviewOnly.class"); assertDirContents(reader, "/modules/modfoo/com/foo", "HasPreviewVersion.class", "NormalFoo.class", "bar"); assertDirContents(reader, "/modules/modfoo/com/foo/bar", "NormalBar.class", "IsPreviewOnly.class"); + + // Non-class resource in top level directory + assertResource(reader, "modfoo", "fileA.txt"); + assertPreviewResourceVersion(reader, "modfoo", "fileA.txt"); + assertNode(reader, "/modules/modfoo/fileB.txt"); + assertPreviewResourceVersion(reader, "modfoo", "fileB.txt"); + assertDirContents(reader, "/modules/modfoo/com", "foo"); + assertDirContents(reader, "/modules/modfoo", "META-INF", "module-info.class", "fileA.txt", "fileB.txt", "com"); + + assertNode(reader, "/modules/modbaz/previewOnly.txt"); + assertDirContents(reader, "/modules/modbaz", "META-INF", "module-info.class", "file.txt", "normal.txt", "previewOnly.txt"); + assertPreviewResourceVersion(reader, "modbaz", "file.txt"); + assertNonPreviewResourceVersion(reader, "modbaz", "normal.txt"); + assertPreviewResourceVersion(reader, "modbaz", "previewOnly.txt"); } } @@ -405,6 +442,17 @@ private static void assertResource(ImageReader reader, String modName, String re assertSame(resNode, reader.findNode(nodeName)); } + private static void assertNonPreviewResourceVersion(ImageReader reader, String modName, String resPath) throws IOException { + Node resNode = reader.findResourceNode(modName, resPath); + assertArrayEquals(resPath.getBytes(StandardCharsets.UTF_8), reader.getResource(resNode)); + } + + private static void assertPreviewResourceVersion(ImageReader reader, String modName, String resPath) throws IOException { + Node resNode = reader.findResourceNode(modName, resPath); + String name = "META-INF/preview/" + resPath; + assertArrayEquals(name.getBytes(StandardCharsets.UTF_8), reader.getResource(resNode)); + } + private static void assertNonPreviewVersion(ImageClassLoader loader, String module, String fqn) throws IOException { assertEquals("Class: " + fqn, loader.loadAndGetToString(module, fqn)); } @@ -441,7 +489,7 @@ public static Path buildJImage(Map> entries) { classes.forEach(fqn -> { if (fqn.startsWith("!")) { - jar.addEntry(fqn.substring(1), "resource".getBytes(StandardCharsets.UTF_8)); + jar.addEntry(fqn.substring(1), fqn.substring(1).getBytes(StandardCharsets.UTF_8)); return; } boolean isPreviewEntry = fqn.startsWith("@"); From 36ca5bbc82f3fe3855016bc6e74e169cb3f2857a Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Thu, 2 Jul 2026 11:28:39 +0000 Subject: [PATCH 136/707] 8387129: Parallel: Wrong TaskTerminator in ParallelScavengeRefProcProxyTask Reviewed-by: jsikstro, tschatzl --- src/hotspot/share/gc/parallel/psCompactionManager.hpp | 1 - src/hotspot/share/gc/parallel/psPromotionManager.hpp | 1 + src/hotspot/share/gc/parallel/psScavenge.cpp | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/gc/parallel/psCompactionManager.hpp b/src/hotspot/share/gc/parallel/psCompactionManager.hpp index ee8ab3f7df0..cd56bf1c91e 100644 --- a/src/hotspot/share/gc/parallel/psCompactionManager.hpp +++ b/src/hotspot/share/gc/parallel/psCompactionManager.hpp @@ -60,7 +60,6 @@ class PCMarkAndPushClosure: public ClaimMetadataVisitingOopIterateClosure { class ParCompactionManager : public CHeapObj { friend class MarkFromRootsTask; friend class ParallelCompactRefProcProxyTask; - friend class ParallelScavengeRefProcProxyTask; friend class ParMarkBitMap; friend class PSParallelCompact; friend class FillDensePrefixAndCompactionTask; diff --git a/src/hotspot/share/gc/parallel/psPromotionManager.hpp b/src/hotspot/share/gc/parallel/psPromotionManager.hpp index edce4861d4d..287808429d3 100644 --- a/src/hotspot/share/gc/parallel/psPromotionManager.hpp +++ b/src/hotspot/share/gc/parallel/psPromotionManager.hpp @@ -55,6 +55,7 @@ class ParCompactionManager; class PSPromotionManager { friend class PSScavenge; + friend class ParallelScavengeRefProcProxyTask; friend class ScavengeRootsTask; private: diff --git a/src/hotspot/share/gc/parallel/psScavenge.cpp b/src/hotspot/share/gc/parallel/psScavenge.cpp index 8dbd2485e76..883bcb81a50 100644 --- a/src/hotspot/share/gc/parallel/psScavenge.cpp +++ b/src/hotspot/share/gc/parallel/psScavenge.cpp @@ -193,7 +193,7 @@ class ParallelScavengeRefProcProxyTask : public RefProcProxyTask { public: ParallelScavengeRefProcProxyTask(uint max_workers) : RefProcProxyTask("ParallelScavengeRefProcProxyTask", max_workers), - _terminator(max_workers, ParCompactionManager::marking_stacks()) {} + _terminator(max_workers, PSPromotionManager::vm_thread_promotion_manager()->stack_array_depth()) {} void work(uint worker_id) override { assert(worker_id < _max_workers, "sanity"); From e68f5ec8352b226de0c59c30e19ee9a762444048 Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Thu, 2 Jul 2026 11:37:20 +0000 Subject: [PATCH 137/707] 8387633: Remove UnlockExperimentalVMOptions for COH in CDS build Reviewed-by: iklam, erikj, shade --- make/Images.gmk | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/make/Images.gmk b/make/Images.gmk index 8008cfa6779..a09ac7e3bc6 100644 --- a/make/Images.gmk +++ b/make/Images.gmk @@ -142,8 +142,7 @@ define CreateCDSArchive $1_$2_COOPS_OPTION := $(if $(findstring _nocoops, $2),-XX:-UseCompressedOops) # enable and also explicitly disable coh as needed. ifeq ($(call isTargetCpuBits, 64), true) - $1_$2_NOCOH_OPTION := -XX:+UnlockExperimentalVMOptions \ - $(if $(findstring _nocoh, $2),-XX:-UseCompactObjectHeaders,-XX:+UseCompactObjectHeaders) + $1_$2_NOCOH_OPTION := $(if $(findstring _nocoh, $2),-XX:-UseCompactObjectHeaders,-XX:+UseCompactObjectHeaders) endif $1_$2_DUMP_EXTRA_ARG := $$($1_$2_COOPS_OPTION) $$($1_$2_NOCOH_OPTION) $1_$2_DUMP_TYPE := $(if $(findstring _nocoops, $2),-NOCOOPS,)$(if $(findstring _nocoh, $2),-NOCOH,) From afe05fc47e3b77964c1ab6a2a8621ac9184e9e5a Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Thu, 2 Jul 2026 11:51:42 +0000 Subject: [PATCH 138/707] 8387596: DEVKIT_LIB_DIR is unused Reviewed-by: erikj --- make/autoconf/basic.m4 | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/make/autoconf/basic.m4 b/make/autoconf/basic.m4 index bb6908d9194..1591df46a91 100644 --- a/make/autoconf/basic.m4 +++ b/make/autoconf/basic.m4 @@ -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 @@ -327,14 +327,6 @@ AC_DEFUN_ONCE([BASIC_SETUP_DEVKIT], elif test -d "$DEVKIT_ROOT/$host/sys-root"; then SYSROOT="$DEVKIT_ROOT/$host/sys-root" fi - - if test "x$DEVKIT_ROOT" != x; then - DEVKIT_LIB_DIR="$DEVKIT_ROOT/lib" - if test "x$OPENJDK_TARGET_CPU_BITS" = x64; then - DEVKIT_LIB_DIR="$DEVKIT_ROOT/lib64" - fi - AC_SUBST(DEVKIT_LIB_DIR) - fi fi # You can force the sysroot if the sysroot encoded into the compiler tools From 052bd362d9538a18a48338ee5d63db457be9b1d2 Mon Sep 17 00:00:00 2001 From: Alexey Ivanov Date: Thu, 2 Jul 2026 12:44:18 +0000 Subject: [PATCH 139/707] 8386056: Test JFileChooser/HTMLFileName.java doesn't run in Nimbus and Motif Reviewed-by: psadhukhan, azvegint --- .../swing/JFileChooser/HTMLFileName.java | 67 +++++++++++++++---- 1 file changed, 53 insertions(+), 14 deletions(-) diff --git a/test/jdk/javax/swing/JFileChooser/HTMLFileName.java b/test/jdk/javax/swing/JFileChooser/HTMLFileName.java index a8bc9525cca..d22d8e207dd 100644 --- a/test/jdk/javax/swing/JFileChooser/HTMLFileName.java +++ b/test/jdk/javax/swing/JFileChooser/HTMLFileName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 @@ -33,7 +33,7 @@ /* * @test id=metal * @bug 8139228 - * @summary JFileChooser should not render Directory names in HTML format + * @summary JFileChooser should not render directory names in HTML format * @library /java/awt/regtesthelpers * @build PassFailJFrame * @run main/manual HTMLFileName metal @@ -42,15 +42,32 @@ /* * @test id=system * @bug 8139228 8358532 - * @summary JFileChooser should not render Directory names in HTML format + * @summary JFileChooser should not render directory names in HTML format * @library /java/awt/regtesthelpers * @build PassFailJFrame * @run main/manual HTMLFileName system */ +/* + * @test id=nimbus + * @bug 8139228 + * @summary JFileChooser should not render directory names in HTML format + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual HTMLFileName nimbus + */ + +/* + * @test id=motif + * @bug 8139228 + * @summary JFileChooser should not render directory names in HTML format + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual HTMLFileName motif + */ + public class HTMLFileName { private static final String INSTRUCTIONS = """ -
  1. JFileChooser shows a virtual directory. The first file in the list has the following name: @@ -86,31 +103,52 @@ public class HTMLFileName { """; - public static void main(String[] args) throws Exception { - if (args.length < 1) { - throw new IllegalArgumentException("Look-and-Feel keyword is required"); - } + private static final String MOTIF_INSTRUCTIONS = + "

    Note: there's no navigation combo box in Motif. " + + "Ignore it in the instructions.

    \n"; + + private static volatile String lafName; + + private static String getLafClassName(String lafKey) { final String lafClassName; - switch (args[0]) { + switch (lafKey) { case "metal" -> lafClassName = UIManager.getCrossPlatformLookAndFeelClassName(); case "system" -> lafClassName = UIManager.getSystemLookAndFeelClassName(); - default -> throw new IllegalArgumentException("Unsupported Look-and-Feel keyword: " + args[0]); + case "nimbus" -> lafClassName = "javax.swing.plaf.nimbus.NimbusLookAndFeel"; + case "motif" -> lafClassName = "com.sun.java.swing.plaf.motif.MotifLookAndFeel"; + default -> throw new IllegalArgumentException("Unsupported Look-and-Feel keyword: " + lafKey); } + return lafClassName; + } + + public static void main(String[] args) throws Exception { + if (args.length < 1) { + throw new IllegalArgumentException("Look-and-Feel keyword is required"); + } + + final String lafClassName = getLafClassName(args[0]); SwingUtilities.invokeAndWait(() -> { try { UIManager.setLookAndFeel(lafClassName); + lafName = UIManager.getLookAndFeel().getName(); } catch (Exception e) { throw new RuntimeException(e); } }); + final boolean motif = "CDE/Motif".equals(lafName); + System.out.println("Test for LookAndFeel " + lafClassName); PassFailJFrame.builder() - .instructions(INSTRUCTIONS) - .columns(45) - .rows(20) + .instructions("\n" + + "

    Look and Feel: " + + lafName + "

    \n" + + (motif ? MOTIF_INSTRUCTIONS : "") + + INSTRUCTIONS) + .columns(motif ? 70 : 45) + .rows(25) .testUI(HTMLFileName::initialize) .positionTestUIBottomRowCentered() .build() @@ -127,7 +165,8 @@ private static JFrame createFileChooser(boolean htmlDisabled) { jfc.putClientProperty("html.disable", htmlDisabled); jfc.setControlButtonsAreShown(false); - JFrame frame = new JFrame(htmlDisabled ? "HTML disabled" : "HTML enabled"); + JFrame frame = new JFrame((htmlDisabled ? "HTML disabled" : "HTML enabled") + + " - " + lafName); frame.add(jfc); frame.pack(); return frame; From 9b59c2dc766a71f8a042a6d5b3a8d14b2835df2f Mon Sep 17 00:00:00 2001 From: Srinivas Vamsi Parasa Date: Thu, 2 Jul 2026 12:44:36 +0000 Subject: [PATCH 140/707] 8369020: Test compiler/intrinsics/TestLongUnsignedDivMod.java completed and timed out Reviewed-by: mhaessig, thartmann --- .../jtreg/compiler/intrinsics/TestLongUnsignedDivMod.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/hotspot/jtreg/compiler/intrinsics/TestLongUnsignedDivMod.java b/test/hotspot/jtreg/compiler/intrinsics/TestLongUnsignedDivMod.java index ce9444823da..393d33f62a6 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/TestLongUnsignedDivMod.java +++ b/test/hotspot/jtreg/compiler/intrinsics/TestLongUnsignedDivMod.java @@ -110,7 +110,6 @@ public TestLongUnsignedDivMod() { } @Test // needs to be run in (fast) debug mode - @Warmup(10000) @IR(counts = {IRNode.UDIV_L, ">= 1"}) // At least one UDivL node is generated if intrinsic is used public void testDivideUnsigned() { for (int i = 0; i < BUFFER_SIZE; i++) { @@ -124,7 +123,6 @@ public void testDivideUnsigned() { } @Test // needs to be run in (fast) debug mode - @Warmup(10000) @IR(counts = {IRNode.UMOD_L, ">= 1"}) // At least one UModL node is generated if intrinsic is used public void testRemainderUnsigned() { for (int i = 0; i < BUFFER_SIZE; i++) { @@ -139,7 +137,6 @@ public void testRemainderUnsigned() { @Test // needs to be run in (fast) debug mode - @Warmup(10000) @IR(applyIfPlatform = {"x64", "true"}, counts = {IRNode.UDIV_MOD_L, ">= 1"}) // At least one UDivModL node is generated if intrinsic is used public void testDivModUnsigned() { From 10af769eb06427e37ae943a21964ae51de4526c6 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Thu, 2 Jul 2026 13:47:53 +0000 Subject: [PATCH 141/707] 8387298: IS_WIN2000 and IS_WINXP macros are obsolete Reviewed-by: aivanov, dgredler --- .../classes/sun/awt/windows/WToolkit.java | 4 +- .../native/libawt/windows/ComCtl32Util.cpp | 28 ++------ .../windows/native/libawt/windows/awt.h | 6 +- .../native/libawt/windows/awt_Choice.cpp | 8 +-- .../libawt/windows/awt_DesktopProperties.cpp | 65 ++++++------------- .../native/libawt/windows/awt_MenuItem.cpp | 10 +-- .../native/libawt/windows/awt_Toolkit.cpp | 35 ---------- 7 files changed, 32 insertions(+), 124 deletions(-) diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WToolkit.java b/src/java.desktop/windows/classes/sun/awt/windows/WToolkit.java index 4ed3e6b7e68..c60a2d6a362 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WToolkit.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WToolkit.java @@ -173,15 +173,13 @@ public static void loadLibraries() { } } - private static native String getWindowsVersion(); - static { loadLibraries(); initIDs(); // Print out which version of Windows is running if (log.isLoggable(PlatformLogger.Level.FINE)) { - log.fine("Win version: " + getWindowsVersion()); + log.fine("Win version: " + System.getProperty("os.version")); } } diff --git a/src/java.desktop/windows/native/libawt/windows/ComCtl32Util.cpp b/src/java.desktop/windows/native/libawt/windows/ComCtl32Util.cpp index acb3315d1e6..66e9d0c1a89 100644 --- a/src/java.desktop/windows/native/libawt/windows/ComCtl32Util.cpp +++ b/src/java.desktop/windows/native/libawt/windows/ComCtl32Util.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2008, 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 @@ -42,32 +42,18 @@ void ComCtl32Util::InitLibraries() { } WNDPROC ComCtl32Util::SubclassHWND(HWND hwnd, WNDPROC _WindowProc) { - if (IS_WINXP) { - const SUBCLASSPROC p = SharedWindowProc; // let compiler check type of SharedWindowProc - ::SetWindowSubclass(hwnd, p, (UINT_PTR)_WindowProc, NULL); // _WindowProc is used as subclass ID - return NULL; - } else { - return (WNDPROC)::SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)_WindowProc); - } + const SUBCLASSPROC p = SharedWindowProc; // let compiler check type of SharedWindowProc + ::SetWindowSubclass(hwnd, p, (UINT_PTR)_WindowProc, NULL); // _WindowProc is used as subclass ID + return NULL; } void ComCtl32Util::UnsubclassHWND(HWND hwnd, WNDPROC _WindowProc, WNDPROC _DefWindowProc) { - if (IS_WINXP) { - const SUBCLASSPROC p = SharedWindowProc; // let compiler check type of SharedWindowProc - ::RemoveWindowSubclass(hwnd, p, (UINT_PTR)_WindowProc); // _WindowProc is used as subclass ID - } else { - ::SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)_DefWindowProc); - } + const SUBCLASSPROC p = SharedWindowProc; // let compiler check type of SharedWindowProc + ::RemoveWindowSubclass(hwnd, p, (UINT_PTR)_WindowProc); // _WindowProc is used as subclass ID } LRESULT ComCtl32Util::DefWindowProc(WNDPROC _DefWindowProc, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - if (IS_WINXP) { - return ::DefSubclassProc(hwnd, msg, wParam, lParam); - } else if (_DefWindowProc != NULL) { - return ::CallWindowProc(_DefWindowProc, hwnd, msg, wParam, lParam); - } else { - return ::DefWindowProc(hwnd, msg, wParam, lParam); - } + return ::DefSubclassProc(hwnd, msg, wParam, lParam); } LRESULT ComCtl32Util::SharedWindowProc(HWND hwnd, UINT msg, diff --git a/src/java.desktop/windows/native/libawt/windows/awt.h b/src/java.desktop/windows/native/libawt/windows/awt.h index b6289dcae68..c367471afa9 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt.h +++ b/src/java.desktop/windows/native/libawt/windows/awt.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -155,12 +155,8 @@ typedef AwtObject* PDATA; /* /NEW JNI */ /* - * IS_WIN2000 returns TRUE on 2000, XP and Vista - * IS_WINXP returns TRUE on XP and Vista * IS_WINVISTA returns TRUE on Vista */ -#define IS_WIN2000 (LOBYTE(LOWORD(::GetVersion())) >= 5) -#define IS_WINXP ((IS_WIN2000 && HIBYTE(LOWORD(::GetVersion())) >= 1) || LOBYTE(LOWORD(::GetVersion())) > 5) #define IS_WINVISTA (LOBYTE(LOWORD(::GetVersion())) >= 6) #define IS_WIN8 ( \ (IS_WINVISTA && (HIBYTE(LOWORD(::GetVersion())) >= 2)) || \ diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Choice.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Choice.cpp index 125065c92fe..b7c45463c5a 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Choice.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Choice.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -179,11 +179,7 @@ AwtChoice* AwtChoice::Create(jobject peer, jobject parent) { ::GetClientRect(c->GetHWnd(), &rc); env->SetIntField(target, AwtComponent::widthID, c->ScaleDownX(rc.right)); env->SetIntField(target, AwtComponent::heightID, c->ScaleDownY(rc.bottom)); - - if (IS_WINXP) { - ::SendMessage(c->GetHWnd(), CB_SETMINVISIBLE, (WPARAM) MINIMUM_NUMBER_OF_VISIBLE_ITEMS, 0); - } - + ::SendMessage(c->GetHWnd(), CB_SETMINVISIBLE, (WPARAM) MINIMUM_NUMBER_OF_VISIBLE_ITEMS, 0); env->DeleteLocalRef(dimension); } } catch (...) { diff --git a/src/java.desktop/windows/native/libawt/windows/awt_DesktopProperties.cpp b/src/java.desktop/windows/native/libawt/windows/awt_DesktopProperties.cpp index 502433a13aa..d5ad022c1e0 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_DesktopProperties.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_DesktopProperties.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -76,9 +76,7 @@ void AwtDesktopProperties::GetWindowsParameters() { GetOtherParameters(); GetSoundEvents(); GetSystemProperties(); - if (IS_WINXP) { - GetXPStyleProperties(); - } + GetXPStyleProperties(); } void getInvScale(float &invScaleX, float &invScaleY) { @@ -423,12 +421,8 @@ void CheckFontSmoothingSettings(HWND hWnd) { if (firstTime) { SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fontSmoothing, 0); - if (IS_WINXP) { - SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, - &fontSmoothingType, 0); - SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, - &fontSmoothingContrast, 0); - } + SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &fontSmoothingType, 0); + SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &fontSmoothingContrast, 0); lastFontSmoothing = fontSmoothing; lastFontSmoothingType = fontSmoothingType; lastFontSmoothingContrast = fontSmoothingContrast; @@ -441,28 +435,18 @@ void CheckFontSmoothingSettings(HWND hWnd) { /* no need to check the other settings in this case. */ return; } - if (IS_WINXP) { - SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, - &fontSmoothingType, 0); - settingsChanged |= fontSmoothingType != lastFontSmoothingType; - if (!settingsChanged && - fontSmoothingType == FONTSMOOTHING_STANDARD) { - /* No need to check any LCD specific settings */ - return; - } else { - SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, - &fontSmoothingContrast, 0); - settingsChanged |= - fontSmoothingContrast != lastFontSmoothingContrast; - if (fontSmoothingType == FONTSMOOTHING_LCD) { - // Order is a registry entry so more expensive to check.x - subPixelOrder = GetLCDSubPixelOrder(); - settingsChanged |= subPixelOrder != lastSubpixelOrder; - } - } + SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &fontSmoothingType, 0); + settingsChanged |= fontSmoothingType != lastFontSmoothingType; + if (!settingsChanged && fontSmoothingType == FONTSMOOTHING_STANDARD) { + /* No need to check any LCD specific settings */ + return; } else { - if (settingsChanged && fontSmoothing == FONTSMOOTHING_ON) { - fontSmoothingType = FONTSMOOTHING_STANDARD; + SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &fontSmoothingContrast, 0); + settingsChanged |= fontSmoothingContrast != lastFontSmoothingContrast; + if (fontSmoothingType == FONTSMOOTHING_LCD) { + // Order is a registry entry so more expensive to check.x + subPixelOrder = GetLCDSubPixelOrder(); + settingsChanged |= subPixelOrder != lastSubpixelOrder; } } } @@ -519,13 +503,7 @@ void AwtDesktopProperties::GetColorParameters() { SetColorProperty(TEXT("win.mdi.backgroundColor"), GetSysColor(COLOR_APPWORKSPACE)); SetColorProperty(TEXT("win.menu.backgroundColor"), GetSysColor(COLOR_MENU)); SetColorProperty(TEXT("win.menu.textColor"), GetSysColor(COLOR_MENUTEXT)); - // COLOR_MENUBAR is only defined on WindowsXP. Our binaries are - // built on NT, hence the below ifdef. -#ifndef COLOR_MENUBAR -#define COLOR_MENUBAR 30 -#endif - SetColorProperty(TEXT("win.menubar.backgroundColor"), - GetSysColor(IS_WINXP ? COLOR_MENUBAR : COLOR_MENU)); + SetColorProperty(TEXT("win.menubar.backgroundColor"), GetSysColor(COLOR_MENUBAR)); SetColorProperty(TEXT("win.scrollbar.backgroundColor"), GetSysColor(COLOR_SCROLLBAR)); SetColorProperty(TEXT("win.text.grayedTextColor"), GetSysColor(COLOR_GRAYTEXT)); SetColorProperty(TEXT("win.tooltip.backgroundColor"), GetSysColor(COLOR_INFOBK)); @@ -540,14 +518,9 @@ void AwtDesktopProperties::GetOtherParameters() { SetBooleanProperty(TEXT("win.text.fontSmoothingOn"), GetBooleanParameter(SPI_GETFONTSMOOTHING)); // TODO END - if (IS_WINXP) { - SetIntegerProperty(TEXT("win.text.fontSmoothingType"), - GetIntegerParameter(SPI_GETFONTSMOOTHINGTYPE)); - SetIntegerProperty(TEXT("win.text.fontSmoothingContrast"), - GetIntegerParameter(SPI_GETFONTSMOOTHINGCONTRAST)); - SetIntegerProperty(TEXT("win.text.fontSmoothingOrientation"), - GetLCDSubPixelOrder()); - } + SetIntegerProperty(TEXT("win.text.fontSmoothingType"), GetIntegerParameter(SPI_GETFONTSMOOTHINGTYPE)); + SetIntegerProperty(TEXT("win.text.fontSmoothingContrast"), GetIntegerParameter(SPI_GETFONTSMOOTHINGCONTRAST)); + SetIntegerProperty(TEXT("win.text.fontSmoothingOrientation"), GetLCDSubPixelOrder()); int cxdrag = GetSystemMetrics(SM_CXDRAG); int cydrag = GetSystemMetrics(SM_CYDRAG); diff --git a/src/java.desktop/windows/native/libawt/windows/awt_MenuItem.cpp b/src/java.desktop/windows/native/libawt/windows/awt_MenuItem.cpp index ace140593f6..ff7e01df3e8 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_MenuItem.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_MenuItem.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -369,14 +369,8 @@ AwtMenuItem::DrawSelf(DRAWITEMSTRUCT& drawInfo) // Disabled text must be drawn in gray. crText = ::GetSysColor(bEnabled? COLOR_HIGHLIGHTTEXT : COLOR_GRAYTEXT); } else { - // COLOR_MENUBAR is only defined on WindowsXP. Our binaries are - // built on NT, hence the below ifdef. - -#ifndef COLOR_MENUBAR -#define COLOR_MENUBAR 30 -#endif // Set background and text colors for unselected item - if (IS_WINXP && IsTopMenu() && AwtDesktopProperties::IsXPStyle()) { + if (IsTopMenu() && AwtDesktopProperties::IsXPStyle()) { crBack = ::GetSysColor (COLOR_MENUBAR); } else { crBack = ::GetSysColor (COLOR_MENU); diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp index a94c96c58c5..c91ff821cba 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp @@ -2848,41 +2848,6 @@ Java_sun_awt_windows_WToolkit_isDynamicLayoutSupportedNative(JNIEnv *env, CATCH_BAD_ALLOC_RET(FALSE); } -/* - * Class: sun_awt_windows_WToolkit - * Method: printWindowsVersion - * Signature: ()Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL -Java_sun_awt_windows_WToolkit_getWindowsVersion(JNIEnv *env, jclass cls) -{ - TRY; - - WCHAR szVer[128]; - - DWORD version = ::GetVersion(); - swprintf(szVer, 128, L"0x%x = %ld", version, version); - int l = lstrlen(szVer); - - if (IS_WIN2000) { - if (IS_WINXP) { - if (IS_WINVISTA) { - swprintf(szVer + l, 128, L" (Windows Vista)"); - } else { - swprintf(szVer + l, 128, L" (Windows XP)"); - } - } else { - swprintf(szVer + l, 128, L" (Windows 2000)"); - } - } else { - swprintf(szVer + l, 128, L" (Unknown)"); - } - - return JNU_NewStringPlatform(env, szVer); - - CATCH_BAD_ALLOC_RET(NULL); -} - JNIEXPORT void JNICALL Java_sun_awt_windows_WToolkit_showTouchKeyboard(JNIEnv *env, jobject self, jboolean causedByTouchEvent) From 43838f04c35cc3f2ca1131f236d6a3af5a76ff2f Mon Sep 17 00:00:00 2001 From: Saint Wesonga Date: Thu, 2 Jul 2026 14:41:17 +0000 Subject: [PATCH 142/707] 8387302: Disable reserved stack areas for critical sections on Windows AArch64 Reviewed-by: dlong, shade --- src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp | 2 ++ src/hotspot/cpu/aarch64/globals_aarch64.hpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp b/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp index 1e788590b64..30aa30aede9 100644 --- a/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp @@ -61,7 +61,9 @@ const bool CCallingConventionRequiresIntsAsLongs = false; // evidence that it's worth doing. #define DEOPTIMIZE_WHEN_PATCHING +#if !defined(_WINDOWS) #define SUPPORT_RESERVED_STACK_AREA +#endif #if defined(__APPLE__) || defined(_WIN64) #define R18_RESERVED diff --git a/src/hotspot/cpu/aarch64/globals_aarch64.hpp b/src/hotspot/cpu/aarch64/globals_aarch64.hpp index 59c7e44b0e5..1db73ff0306 100644 --- a/src/hotspot/cpu/aarch64/globals_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/globals_aarch64.hpp @@ -48,7 +48,7 @@ define_pd_global(intx, OptoLoopAlignment, 16); // stack if compiled for unix and LP64. To pass stack overflow tests we need // 20 shadow pages. #define DEFAULT_STACK_SHADOW_PAGES (20 DEBUG_ONLY(+5)) -#define DEFAULT_STACK_RESERVED_PAGES (1) +#define DEFAULT_STACK_RESERVED_PAGES (NOT_WINDOWS(1) WINDOWS_ONLY(0)) #define MIN_STACK_YELLOW_PAGES DEFAULT_STACK_YELLOW_PAGES #define MIN_STACK_RED_PAGES DEFAULT_STACK_RED_PAGES From 5a781620b81ecbd699e3a32f95ef417d49d4deec Mon Sep 17 00:00:00 2001 From: Chris Plummer Date: Thu, 2 Jul 2026 19:56:42 +0000 Subject: [PATCH 143/707] 8327967: vmTestbase/nsk/jdi/EventRequestManager/threadStartRequests/thrstartreq002/TestDescription.java fails intermittently Reviewed-by: sspitsyn, dholmes --- test/hotspot/jtreg/ProblemList-Virtual.txt | 1 - test/hotspot/jtreg/vmTestbase/nsk/share/jdi/JDIBase.java | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/ProblemList-Virtual.txt b/test/hotspot/jtreg/ProblemList-Virtual.txt index b30a09a7710..14601f2ba6f 100644 --- a/test/hotspot/jtreg/ProblemList-Virtual.txt +++ b/test/hotspot/jtreg/ProblemList-Virtual.txt @@ -31,7 +31,6 @@ vmTestbase/vm/mlvm/indy/func/jvmti/mergeCP_indy2manyDiff_a/TestDescription.java vmTestbase/nsk/jvmti/unit/functions/Dispose/JvmtiTest/TestDescription.java 8387429 generic-all vmTestbase/nsk/jvmti/scenarios/capability/CM02/cm02t001/TestDescription.java 8299217 generic-all -vmTestbase/nsk/jdi/EventRequestManager/threadStartRequests/thrstartreq002/TestDescription.java 8327967 generic-all #### diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/jdi/JDIBase.java b/test/hotspot/jtreg/vmTestbase/nsk/share/jdi/JDIBase.java index 88b1de413dd..91a06a426c0 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/jdi/JDIBase.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/jdi/JDIBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 @@ -242,6 +242,7 @@ protected void breakpointForCommunication() throws JDITestRuntimeException { ThreadStartEvent tse = (ThreadStartEvent) event; log2("ThreadStartEvent is received while waiting for a breakpoint" + " event, thread: : " + tse.thread().name()); + eventSet.resume(); continue; } From 41a6eee8756ccd2ae8c511f1aacf7454aa5731db Mon Sep 17 00:00:00 2001 From: Ashay Rane Date: Fri, 3 Jul 2026 04:33:51 +0000 Subject: [PATCH 144/707] 8387625: Add "dt_socket" to `CheckedFeatures.notImplemented` for Windows/ARM64 Reviewed-by: cjplummer, shade --- .../jtreg/vmTestbase/nsk/share/jdi/ArgumentHandler.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/jdi/ArgumentHandler.java b/test/hotspot/jtreg/vmTestbase/nsk/share/jdi/ArgumentHandler.java index 08bfad05a04..11ccfc9447c 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/jdi/ArgumentHandler.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/jdi/ArgumentHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -597,6 +597,9 @@ class CheckedFeatures { {"windows-x64", "com.sun.jdi.CommandLineLaunch", "dt_socket"}, {"windows-x64", "com.sun.jdi.RawCommandLineLaunch", "dt_socket"}, + {"windows-aarch64", "com.sun.jdi.CommandLineLaunch", "dt_socket"}, + {"windows-aarch64", "com.sun.jdi.RawCommandLineLaunch", "dt_socket"}, + {"macosx-amd64", "com.sun.jdi.CommandLineLaunch", "dt_shmem"}, {"macosx-amd64", "com.sun.jdi.RawCommandLineLaunch", "dt_shmem"}, From 23d1e859c04e71a42d93862e5fab5d223608fb2b Mon Sep 17 00:00:00 2001 From: Tobias Hartmann Date: Fri, 3 Jul 2026 06:06:00 +0000 Subject: [PATCH 145/707] 8387622: Tests producing many small output writes are extremely slow on Windows Reviewed-by: chagedorn, mchevalier, kvn --- .../jtreg/compiler/arguments/TestC1Globals.java | 5 +++-- .../compiler/arguments/TestTraceTypeProfile.java | 3 ++- .../jtreg/compiler/c1/TestCITimeCountLinearScan.java | 4 +++- .../compiler/c1/TestPrintIRDuringConstruction.java | 6 ++++-- .../jtreg/compiler/c1/TestTraceLinearScanLevel.java | 5 +++-- test/hotspot/jtreg/compiler/c2/TestFindNode.java | 5 +++-- .../jtreg/compiler/c2/TestPrintIdealNodeCount.java | 5 +++-- .../c2/TestReduceAllocationAndNonExactAllocate.java | 3 +-- .../scalarReplacement/AllocationMergesTests.java | 3 --- .../jtreg/compiler/debug/TestCountCompiledCalls.java | 2 +- .../jtreg/compiler/debug/TestLogStackAssert.java | 6 ++++-- .../jtreg/compiler/debug/TestTracePhaseCCP.java | 9 +++++---- .../jtreg/compiler/debug/TraceIterativeGVN.java | 4 ++-- .../loopopts/TestBadlyFormedCountedLoop.java | 5 +++-- .../jtreg/compiler/loopopts/TestCMoveLimitType.java | 4 ++-- .../jtreg/compiler/print/PrintCompileQueue.java | 4 +++- test/hotspot/jtreg/compiler/print/PrintInlining.java | 12 ++++++------ .../compiler/print/TestPrintAssemblyDeoptRace.java | 5 +++-- .../compiler/print/TestPrintInliningLateMHCall.java | 5 ++++- .../print/TestPrintInliningLateVirtualCall.java | 5 ++++- .../print/TestProfileReturnTypePrinting.java | 5 +++-- .../jtreg/compiler/print/TestTraceOptoParse.java | 4 +++- .../compiler/relocations/TestPrintRelocations.java | 6 ++++-- .../uncommontrap/TestDeoptDetailsLockRank.java | 6 ++++-- .../jtreg/compiler/uncommontrap/TestDeoptOOM.java | 3 ++- .../TestPrintDiagnosticsWithoutProfileTraps.java | 4 ++-- .../uncommontrap/TraceDeoptimizationNoRealloc.java | 5 +++-- 27 files changed, 80 insertions(+), 53 deletions(-) diff --git a/test/hotspot/jtreg/compiler/arguments/TestC1Globals.java b/test/hotspot/jtreg/compiler/arguments/TestC1Globals.java index ba3d8aef191..b41b99b391b 100644 --- a/test/hotspot/jtreg/compiler/arguments/TestC1Globals.java +++ b/test/hotspot/jtreg/compiler/arguments/TestC1Globals.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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 @@ -27,7 +27,8 @@ * @requires vm.debug * @summary Test flag with c1 value numbering * - * @run main/othervm -XX:+PrintValueNumbering -XX:+Verbose -XX:-UseLocalValueNumbering + * @run main/othervm -XX:-DisplayVMOutput + * -XX:+PrintValueNumbering -XX:+Verbose -XX:-UseLocalValueNumbering * -Xcomp -XX:TieredStopAtLevel=1 * compiler.arguments.TestC1Globals */ diff --git a/test/hotspot/jtreg/compiler/arguments/TestTraceTypeProfile.java b/test/hotspot/jtreg/compiler/arguments/TestTraceTypeProfile.java index df1c253b689..f61018738ae 100644 --- a/test/hotspot/jtreg/compiler/arguments/TestTraceTypeProfile.java +++ b/test/hotspot/jtreg/compiler/arguments/TestTraceTypeProfile.java @@ -25,7 +25,8 @@ * @test * @summary Test running TraceTypeProfile enabled. * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions - * -XX:+TraceTypeProfile compiler.arguments.TestTraceTypeProfile + * -XX:-DisplayVMOutput -XX:+TraceTypeProfile + * compiler.arguments.TestTraceTypeProfile */ package compiler.arguments; diff --git a/test/hotspot/jtreg/compiler/c1/TestCITimeCountLinearScan.java b/test/hotspot/jtreg/compiler/c1/TestCITimeCountLinearScan.java index e67a3679758..81069210ee1 100644 --- a/test/hotspot/jtreg/compiler/c1/TestCITimeCountLinearScan.java +++ b/test/hotspot/jtreg/compiler/c1/TestCITimeCountLinearScan.java @@ -25,7 +25,9 @@ * @test * @bug 8374518 * @summary Sanity check the flag -XX:+CITime and -XX:+CountLinearScan - * @run main/othervm -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:+CITime -XX:+CountLinearScan ${test.main.class} + * @run main/othervm -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions + * -XX:-DisplayVMOutput -XX:+CITime -XX:+CountLinearScan + * ${test.main.class} */ package compiler.c1; diff --git a/test/hotspot/jtreg/compiler/c1/TestPrintIRDuringConstruction.java b/test/hotspot/jtreg/compiler/c1/TestPrintIRDuringConstruction.java index d406438d39f..ba2e1b081f5 100644 --- a/test/hotspot/jtreg/compiler/c1/TestPrintIRDuringConstruction.java +++ b/test/hotspot/jtreg/compiler/c1/TestPrintIRDuringConstruction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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,7 +26,9 @@ * @summary load/store elimination will print out instructions without bcis. * @bug 8235383 * @requires vm.debug == true & vm.compiler1.enabled - * @run main/othervm -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -Xcomp -XX:+PrintIRDuringConstruction -XX:+Verbose compiler.c1.TestPrintIRDuringConstruction + * @run main/othervm -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -Xcomp + * -XX:-DisplayVMOutput -XX:+PrintIRDuringConstruction -XX:+Verbose + * compiler.c1.TestPrintIRDuringConstruction */ package compiler.c1; diff --git a/test/hotspot/jtreg/compiler/c1/TestTraceLinearScanLevel.java b/test/hotspot/jtreg/compiler/c1/TestTraceLinearScanLevel.java index 233498d3c04..7eafb5b1c50 100644 --- a/test/hotspot/jtreg/compiler/c1/TestTraceLinearScanLevel.java +++ b/test/hotspot/jtreg/compiler/c1/TestTraceLinearScanLevel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 @@ -27,7 +27,8 @@ * @summary Sanity check the flag TraceLinearScanLevel with the highest level in a silent HelloWorld program. * * @requires vm.debug == true & vm.compiler1.enabled & vm.compMode != "Xcomp" - * @run main/othervm -Xbatch -XX:TraceLinearScanLevel=4 compiler.c1.TestTraceLinearScanLevel + * @run main/othervm -Xbatch -XX:-DisplayVMOutput -XX:TraceLinearScanLevel=4 + * compiler.c1.TestTraceLinearScanLevel */ package compiler.c1; diff --git a/test/hotspot/jtreg/compiler/c2/TestFindNode.java b/test/hotspot/jtreg/compiler/c2/TestFindNode.java index fa545da7e58..09f94a18f69 100644 --- a/test/hotspot/jtreg/compiler/c2/TestFindNode.java +++ b/test/hotspot/jtreg/compiler/c2/TestFindNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 @@ -27,7 +27,8 @@ * @requires vm.debug == true & vm.flavor == "server" * @summary Test which uses some special flags in order to test Node::find() in debug builds which could result in an endless loop or a stack overflow crash. * - * @run main/othervm -Xbatch -XX:CompileCommand=option,*::*,bool,Vectorize,true + * @run main/othervm -Xbatch -XX:-DisplayVMOutput + * -XX:CompileCommand=option,*::*,bool,Vectorize,true * -XX:+PrintOpto -XX:+TraceLoopOpts compiler.c2.TestFindNode */ package compiler.c2; diff --git a/test/hotspot/jtreg/compiler/c2/TestPrintIdealNodeCount.java b/test/hotspot/jtreg/compiler/c2/TestPrintIdealNodeCount.java index af252265b76..aeb391ea15f 100644 --- a/test/hotspot/jtreg/compiler/c2/TestPrintIdealNodeCount.java +++ b/test/hotspot/jtreg/compiler/c2/TestPrintIdealNodeCount.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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 @@ -27,7 +27,8 @@ * @requires vm.debug == true & vm.compiler2.enabled * @summary Run with -Xcomp -XX:-TieredCompilation to force C2 compilations to test -XX:+PrintIdealNodeCount in debug builds. * - * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:+PrintIdealNodeCount compiler.c2.TestPrintIdealNodeCount + * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:-DisplayVMOutput + * -XX:+PrintIdealNodeCount compiler.c2.TestPrintIdealNodeCount */ package compiler.c2; diff --git a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndNonExactAllocate.java b/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndNonExactAllocate.java index 1146d189ce2..ccb00c635c5 100644 --- a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndNonExactAllocate.java +++ b/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndNonExactAllocate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -32,7 +32,6 @@ * -XX:CompileCommand=compileonly,*::allocateInstance * -XX:CompileCommand=dontinline,*TestReduceAllocationAndNonExactAllocate*::* * -XX:+UnlockDiagnosticVMOptions - * -XX:+TraceReduceAllocationMerges * -XX:-TieredCompilation * -Xbatch * -Xcomp diff --git a/test/hotspot/jtreg/compiler/c2/irTests/scalarReplacement/AllocationMergesTests.java b/test/hotspot/jtreg/compiler/c2/irTests/scalarReplacement/AllocationMergesTests.java index 2c84ad2676e..8f24cb46e20 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/scalarReplacement/AllocationMergesTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/scalarReplacement/AllocationMergesTests.java @@ -43,7 +43,6 @@ public static void main(String[] args) { Scenario scenario0 = new Scenario(0, "-XX:+UnlockDiagnosticVMOptions", "-XX:+ReduceAllocationMerges", - "-XX:+TraceReduceAllocationMerges", "-XX:+DeoptimizeALot", "-XX:+UseCompressedOops", "-XX:CompileCommand=inline,*::charAt*", @@ -54,7 +53,6 @@ public static void main(String[] args) { Scenario scenario1 = new Scenario(2, "-XX:+UnlockDiagnosticVMOptions", "-XX:+ReduceAllocationMerges", - "-XX:+TraceReduceAllocationMerges", "-XX:+DeoptimizeALot", "-XX:-UseCompressedOops", "-XX:CompileCommand=inline,*::charAt*", @@ -65,7 +63,6 @@ public static void main(String[] args) { Scenario scenario2 = new Scenario(3, "-XX:+UnlockDiagnosticVMOptions", "-XX:+ReduceAllocationMerges", - "-XX:+TraceReduceAllocationMerges", "-XX:+DeoptimizeALot", "-XX:+UseCompressedOops", "-XX:-OptimizePtrCompare", diff --git a/test/hotspot/jtreg/compiler/debug/TestCountCompiledCalls.java b/test/hotspot/jtreg/compiler/debug/TestCountCompiledCalls.java index 1a3fdf6e9d6..551c22c1377 100644 --- a/test/hotspot/jtreg/compiler/debug/TestCountCompiledCalls.java +++ b/test/hotspot/jtreg/compiler/debug/TestCountCompiledCalls.java @@ -26,7 +26,7 @@ * @bug 8382057 * @requires vm.debug == true * - * @run main/othervm -Xbatch -XX:+CountCompiledCalls ${test.main.class} + * @run main/othervm -Xbatch -XX:-DisplayVMOutput -XX:+CountCompiledCalls ${test.main.class} */ package compiler.debug; diff --git a/test/hotspot/jtreg/compiler/debug/TestLogStackAssert.java b/test/hotspot/jtreg/compiler/debug/TestLogStackAssert.java index 042abc23fcc..8ad971bc68d 100644 --- a/test/hotspot/jtreg/compiler/debug/TestLogStackAssert.java +++ b/test/hotspot/jtreg/compiler/debug/TestLogStackAssert.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -28,7 +28,9 @@ * @bug 8344013 * @requires vm.debug == true & vm.compiler2.enabled * @summary Verify the xmlStream log stack is not left in a bad state - * @run main/othervm -XX:+LogCompilation -XX:CompileCommand=log,*.* -XX:+CITimeVerbose -Xcomp compiler.debug.TestLogStackAssert + * @run main/othervm -XX:-DisplayVMOutput -XX:+LogCompilation + * -XX:CompileCommand=log,*.* -XX:+CITimeVerbose -Xcomp + * compiler.debug.TestLogStackAssert */ public class TestLogStackAssert { diff --git a/test/hotspot/jtreg/compiler/debug/TestTracePhaseCCP.java b/test/hotspot/jtreg/compiler/debug/TestTracePhaseCCP.java index b46aac9a824..6de08c97329 100644 --- a/test/hotspot/jtreg/compiler/debug/TestTracePhaseCCP.java +++ b/test/hotspot/jtreg/compiler/debug/TestTracePhaseCCP.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -27,9 +27,10 @@ * @requires vm.debug == true & vm.compiler2.enabled * @modules java.base/jdk.internal.misc * - * @run main/othervm -Xbatch -XX:CompileCommand=dontinline,compiler.debug.TestTracePhaseCCP::test - * -XX:CompileCommand=compileonly,compiler.debug.TestTracePhaseCCP::test -XX:+TracePhaseCCP - * compiler.debug.TestTracePhaseCCP + * @run main/othervm -Xbatch -XX:-DisplayVMOutput + * -XX:CompileCommand=dontinline,compiler.debug.TestTracePhaseCCP::test + * -XX:CompileCommand=compileonly,compiler.debug.TestTracePhaseCCP::test + * -XX:+TracePhaseCCP compiler.debug.TestTracePhaseCCP */ package compiler.debug; diff --git a/test/hotspot/jtreg/compiler/debug/TraceIterativeGVN.java b/test/hotspot/jtreg/compiler/debug/TraceIterativeGVN.java index 8e6169f07dc..9d31cabd825 100644 --- a/test/hotspot/jtreg/compiler/debug/TraceIterativeGVN.java +++ b/test/hotspot/jtreg/compiler/debug/TraceIterativeGVN.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (C) 2021, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -25,7 +25,7 @@ /* * @test * @requires vm.debug == true & vm.compiler2.enabled - * @run main/othervm -Xbatch -XX:-TieredCompilation + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:-DisplayVMOutput * -XX:+IgnoreUnrecognizedVMOptions -XX:+TraceIterativeGVN * compiler.debug.TraceIterativeGVN */ diff --git a/test/hotspot/jtreg/compiler/loopopts/TestBadlyFormedCountedLoop.java b/test/hotspot/jtreg/compiler/loopopts/TestBadlyFormedCountedLoop.java index d8b6fc3fdfb..9375e7b7f40 100644 --- a/test/hotspot/jtreg/compiler/loopopts/TestBadlyFormedCountedLoop.java +++ b/test/hotspot/jtreg/compiler/loopopts/TestBadlyFormedCountedLoop.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2022, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,7 +26,8 @@ * @test * @bug 8273115 * @summary CountedLoopEndNode::stride_con crash in debug build with -XX:+TraceLoopOpts - * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+TraceLoopOpts -Xcomp -XX:-TieredCompilation + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions + * -XX:-DisplayVMOutput -XX:+TraceLoopOpts -Xcomp -XX:-TieredCompilation * -XX:CompileOnly=TestBadlyFormedCountedLoop::main TestBadlyFormedCountedLoop */ diff --git a/test/hotspot/jtreg/compiler/loopopts/TestCMoveLimitType.java b/test/hotspot/jtreg/compiler/loopopts/TestCMoveLimitType.java index 3b2c82afc46..10f2e0c113a 100644 --- a/test/hotspot/jtreg/compiler/loopopts/TestCMoveLimitType.java +++ b/test/hotspot/jtreg/compiler/loopopts/TestCMoveLimitType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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 @@ -39,7 +39,7 @@ * @key stress randomness * @bug 8299975 * @summary Limit underflow protection CMoveINode in PhaseIdealLoop::do_unroll must also protect type from underflow - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:-TieredCompilation + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:-DisplayVMOutput -XX:-TieredCompilation * -XX:CompileCommand=compileonly,compiler.loopopts.TestCMoveLimitType::test* * -XX:CompileCommand=dontinline,compiler.loopopts.TestCMoveLimitType::dontInline * -XX:RepeatCompilation=50 -XX:+StressIGVN diff --git a/test/hotspot/jtreg/compiler/print/PrintCompileQueue.java b/test/hotspot/jtreg/compiler/print/PrintCompileQueue.java index ee368b54bea..b633cfa46ca 100644 --- a/test/hotspot/jtreg/compiler/print/PrintCompileQueue.java +++ b/test/hotspot/jtreg/compiler/print/PrintCompileQueue.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2019, Loongson Technology Co. Ltd. All rights reserved. + * 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 @@ -25,7 +26,8 @@ * @test * @bug 8230943 * @summary possible deadlock was detected when ran with -XX:+CIPrintCompileQueue - * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+CIPrintCompileQueue + * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:-DisplayVMOutput + * -XX:+CIPrintCompileQueue * compiler.print.PrintCompileQueue * */ diff --git a/test/hotspot/jtreg/compiler/print/PrintInlining.java b/test/hotspot/jtreg/compiler/print/PrintInlining.java index 4b45a32949f..486231cc50f 100644 --- a/test/hotspot/jtreg/compiler/print/PrintInlining.java +++ b/test/hotspot/jtreg/compiler/print/PrintInlining.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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,12 +25,12 @@ * @test * @bug 8022585 8277055 * @summary VM crashes when ran with -XX:+PrintInlining - * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining - * compiler.print.PrintInlining - * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining - * compiler.print.PrintInlining - * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintIntrinsics + * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:-DisplayVMOutput -XX:+PrintInlining * compiler.print.PrintInlining + * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:+UnlockDiagnosticVMOptions + * -XX:-DisplayVMOutput -XX:+PrintInlining compiler.print.PrintInlining + * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:+UnlockDiagnosticVMOptions + * -XX:-DisplayVMOutput -XX:+PrintIntrinsics compiler.print.PrintInlining */ package compiler.print; diff --git a/test/hotspot/jtreg/compiler/print/TestPrintAssemblyDeoptRace.java b/test/hotspot/jtreg/compiler/print/TestPrintAssemblyDeoptRace.java index 22ce12f9641..726f7820797 100644 --- a/test/hotspot/jtreg/compiler/print/TestPrintAssemblyDeoptRace.java +++ b/test/hotspot/jtreg/compiler/print/TestPrintAssemblyDeoptRace.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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,7 +25,8 @@ * @test * @bug 8258229 * @summary If a method is made not entrant while printing the assembly, hotspot crashes due to mismatched relocation information. - * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-TieredCompilation -XX:+DeoptimizeALot + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions + * -XX:-DisplayVMOutput -XX:-TieredCompilation -XX:+DeoptimizeALot * -XX:CompileCommand=print,java/math/BitSieve.bit compiler.print.TestPrintAssemblyDeoptRace */ diff --git a/test/hotspot/jtreg/compiler/print/TestPrintInliningLateMHCall.java b/test/hotspot/jtreg/compiler/print/TestPrintInliningLateMHCall.java index 529469e3a95..85d3e19504c 100644 --- a/test/hotspot/jtreg/compiler/print/TestPrintInliningLateMHCall.java +++ b/test/hotspot/jtreg/compiler/print/TestPrintInliningLateMHCall.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2024, Red Hat and/or its affiliates. All rights reserved. + * 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 @@ -25,7 +26,9 @@ * @test * @bug 8335843 * @summary C2 hits assert(_print_inlining_stream->size() > 0) failed: missing inlining msg - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:-BackgroundCompilation -XX:+PrintCompilation -XX:+PrintInlining TestPrintInliningLateMHCall + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:-DisplayVMOutput + * -XX:-BackgroundCompilation -XX:+PrintCompilation -XX:+PrintInlining + * TestPrintInliningLateMHCall */ import java.lang.invoke.MethodHandle; diff --git a/test/hotspot/jtreg/compiler/print/TestPrintInliningLateVirtualCall.java b/test/hotspot/jtreg/compiler/print/TestPrintInliningLateVirtualCall.java index f73e30badcb..63a7215bf5b 100644 --- a/test/hotspot/jtreg/compiler/print/TestPrintInliningLateVirtualCall.java +++ b/test/hotspot/jtreg/compiler/print/TestPrintInliningLateVirtualCall.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2024, Red Hat and/or its affiliates. All rights reserved. + * 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 @@ -25,7 +26,9 @@ * @test * @bug 8327741 * @summary JVM crash in hotspot/share/opto/compile.cpp - failed: missing inlining msg - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:-BackgroundCompilation -XX:+PrintCompilation -XX:+PrintInlining TestPrintInliningLateVirtualCall + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:-DisplayVMOutput + * -XX:-BackgroundCompilation -XX:+PrintCompilation -XX:+PrintInlining + * TestPrintInliningLateVirtualCall */ public class TestPrintInliningLateVirtualCall { diff --git a/test/hotspot/jtreg/compiler/print/TestProfileReturnTypePrinting.java b/test/hotspot/jtreg/compiler/print/TestProfileReturnTypePrinting.java index 15f60ac3e77..cb5f67bc61c 100644 --- a/test/hotspot/jtreg/compiler/print/TestProfileReturnTypePrinting.java +++ b/test/hotspot/jtreg/compiler/print/TestProfileReturnTypePrinting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, 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,7 +26,8 @@ * @bug 8073154 * @run main/othervm -XX:TypeProfileLevel=020 * -XX:CompileCommand=compileonly,compiler.print.TestProfileReturnTypePrinting::testMethod - * -XX:+IgnoreUnrecognizedVMOptions -XX:+PrintLIR + * -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions + * -XX:-DisplayVMOutput -XX:+PrintLIR * compiler.print.TestProfileReturnTypePrinting * @summary Verify that c1's LIR that contains ProfileType node could be dumped * without a crash disregard to an exact class knowledge. diff --git a/test/hotspot/jtreg/compiler/print/TestTraceOptoParse.java b/test/hotspot/jtreg/compiler/print/TestTraceOptoParse.java index 52a7aba1a7e..3d3b242b4ce 100644 --- a/test/hotspot/jtreg/compiler/print/TestTraceOptoParse.java +++ b/test/hotspot/jtreg/compiler/print/TestTraceOptoParse.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2022, Tencent. All rights reserved. + * 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 @@ -26,7 +27,8 @@ * @bug 8293785 * @summary test for -XX:+TraceOptoParse * @requires vm.debug & vm.compiler2.enabled - * @run main/othervm -XX:+TraceOptoParse compiler.print.TestTraceOptoParse + * @run main/othervm -XX:-DisplayVMOutput -XX:+TraceOptoParse + * compiler.print.TestTraceOptoParse * */ diff --git a/test/hotspot/jtreg/compiler/relocations/TestPrintRelocations.java b/test/hotspot/jtreg/compiler/relocations/TestPrintRelocations.java index 7c84450c778..29508cf9092 100644 --- a/test/hotspot/jtreg/compiler/relocations/TestPrintRelocations.java +++ b/test/hotspot/jtreg/compiler/relocations/TestPrintRelocations.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -26,7 +26,9 @@ * @bug 8044538 * @summary assert hit while printing relocations for jump table entries * - * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -Xcomp -XX:CompileCommand=compileonly,java.lang.String*::* -XX:+PrintRelocations + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions + * -XX:-DisplayVMOutput -Xcomp + * -XX:CompileCommand=compileonly,java.lang.String*::* -XX:+PrintRelocations * compiler.relocations.TestPrintRelocations */ /** diff --git a/test/hotspot/jtreg/compiler/uncommontrap/TestDeoptDetailsLockRank.java b/test/hotspot/jtreg/compiler/uncommontrap/TestDeoptDetailsLockRank.java index 2866a84ba46..7bbe82c7311 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/TestDeoptDetailsLockRank.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/TestDeoptDetailsLockRank.java @@ -26,7 +26,9 @@ * @bug 8374862 * @summary Regression test for -XX:+Verbose -XX:+WizardMode -XX:+PrintDeoptimizationDetails crash * @requires vm.debug - * @run main/othervm -XX:+Verbose -XX:+WizardMode -XX:+PrintDeoptimizationDetails compiler.uncommontrap.TestDeoptDetailsLockRank + * @run main/othervm -XX:-DisplayVMOutput + * -XX:+Verbose -XX:+WizardMode -XX:+PrintDeoptimizationDetails + * compiler.uncommontrap.TestDeoptDetailsLockRank */ package compiler.uncommontrap; @@ -36,4 +38,4 @@ public class TestDeoptDetailsLockRank { public static void main(String[] args) { System.out.println("passed"); } -} \ No newline at end of file +} diff --git a/test/hotspot/jtreg/compiler/uncommontrap/TestDeoptOOM.java b/test/hotspot/jtreg/compiler/uncommontrap/TestDeoptOOM.java index 7a4f15d6461..21a3c08b665 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/TestDeoptOOM.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/TestDeoptOOM.java @@ -41,7 +41,8 @@ * -XX:CompileCommand=exclude,compiler.uncommontrap.TestDeoptOOM::main * -XX:CompileCommand=exclude,compiler.uncommontrap.TestDeoptOOM::m9_1 * -XX:+UnlockDiagnosticVMOptions - * -XX:+UseZGC -XX:+LogCompilation -XX:+PrintDeoptimizationDetails -XX:+TraceDeoptimization -XX:+Verbose + * -XX:-DisplayVMOutput -XX:+UseZGC -XX:+LogCompilation + * -XX:+PrintDeoptimizationDetails -XX:+TraceDeoptimization -XX:+Verbose * compiler.uncommontrap.TestDeoptOOM */ diff --git a/test/hotspot/jtreg/compiler/uncommontrap/TestPrintDiagnosticsWithoutProfileTraps.java b/test/hotspot/jtreg/compiler/uncommontrap/TestPrintDiagnosticsWithoutProfileTraps.java index 51b30219aca..6fb22ec0759 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/TestPrintDiagnosticsWithoutProfileTraps.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/TestPrintDiagnosticsWithoutProfileTraps.java @@ -28,7 +28,7 @@ * -XX:-TieredCompilation -Xcomp crash * @modules java.base/jdk.internal.misc * @requires vm.debug - * @run main/othervm -XX:+TraceDeoptimization -XX:-ProfileTraps + * @run main/othervm -XX:-DisplayVMOutput -XX:+TraceDeoptimization -XX:-ProfileTraps * -XX:-TieredCompilation -Xcomp -Xbatch * -XX:CompileCommand=compileonly,compiler.uncommontrap.TestPrintDiagnosticsWithoutProfileTraps::test * compiler.uncommontrap.TestPrintDiagnosticsWithoutProfileTraps @@ -55,4 +55,4 @@ public static void main(String[] args) { test(); System.out.println("passed"); } -} \ No newline at end of file +} diff --git a/test/hotspot/jtreg/compiler/uncommontrap/TraceDeoptimizationNoRealloc.java b/test/hotspot/jtreg/compiler/uncommontrap/TraceDeoptimizationNoRealloc.java index 4cd10a1a63e..abac84cdf23 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/TraceDeoptimizationNoRealloc.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/TraceDeoptimizationNoRealloc.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2021, 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 @@ -27,7 +27,8 @@ * @summary -XX:+TraceDeoptimization tries to print realloc'ed objects even when there are none * * @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement - * -XX:+UnlockDiagnosticVMOptions -XX:+TraceDeoptimization + * -XX:+UnlockDiagnosticVMOptions -XX:-DisplayVMOutput + * -XX:+TraceDeoptimization * compiler.uncommontrap.TraceDeoptimizationNoRealloc */ From 298965828c2ba45e7264c8560f36a3edb7449331 Mon Sep 17 00:00:00 2001 From: zifeihan Date: Fri, 3 Jul 2026 06:37:54 +0000 Subject: [PATCH 146/707] 8387078: RISC-V: x27 can be allocated in CompressedOops mode Reviewed-by: dzhang, fyang --- src/hotspot/cpu/riscv/riscv.ad | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 7bfff4b2086..e022dcb4262 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -1105,8 +1105,9 @@ void reg_mask_init() { _NO_SPECIAL_PTR_REG_mask.assignFrom(_ALL_REG_mask); _NO_SPECIAL_PTR_REG_mask.subtract(_NON_ALLOCATABLE_REG_mask); - // x27 is not allocatable when compressed oops is on - if (UseCompressedOops) { + // x27 is not allocatable when compressed oops is on and heapbase is not zero, + // compressed klass pointers doesn't use x27 when heapbase is zero. + if (UseCompressedOops && (CompressedOops::base() != nullptr)) { _NO_SPECIAL_REG32_mask.remove(OptoReg::as_OptoReg(x27->as_VMReg())); _NO_SPECIAL_REG_mask.remove(OptoReg::as_OptoReg(x27->as_VMReg())); _NO_SPECIAL_PTR_REG_mask.remove(OptoReg::as_OptoReg(x27->as_VMReg())); From 16da9173a7a0fb0511a6924e407e7125a9725f93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Fri, 3 Jul 2026 06:38:10 +0000 Subject: [PATCH 147/707] 8381880: Test compiler/c1/TestTooManyVirtualRegistersMain.java uses wrong class in CompileCommand Reviewed-by: chagedorn, shade --- .../jtreg/compiler/c1/TestTooManyVirtualRegistersMain.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c1/TestTooManyVirtualRegistersMain.java b/test/hotspot/jtreg/compiler/c1/TestTooManyVirtualRegistersMain.java index eff52cce7bb..b712fd2bdf3 100644 --- a/test/hotspot/jtreg/compiler/c1/TestTooManyVirtualRegistersMain.java +++ b/test/hotspot/jtreg/compiler/c1/TestTooManyVirtualRegistersMain.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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,8 +29,8 @@ * The test should bail out in C1. * * @compile TestTooManyVirtualRegisters.jasm - * @run main/othervm -Xbatch -XX:CompileCommand=dontinline,compiler.c1.TestExceptionBlockWithPredecessors::* - * compiler.c1.TestTooManyVirtualRegistersMain + * @run main/othervm -Xbatch -XX:CompileCommand=dontinline,compiler.c1.TestTooManyVirtualRegisters::* + * ${test.main.class} */ package compiler.c1; From 79be204abffe59bcd76e8d998cbc08c02a6dd6fa Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Fri, 3 Jul 2026 12:35:21 +0000 Subject: [PATCH 148/707] 8387465: Remove isXP() function from WPathGraphics.java Reviewed-by: aivanov, mdoerr --- .../classes/sun/awt/windows/WPathGraphics.java | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WPathGraphics.java b/src/java.desktop/windows/classes/sun/awt/windows/WPathGraphics.java index 87b1591c0eb..6be79ccaadf 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WPathGraphics.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WPathGraphics.java @@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -312,16 +312,6 @@ protected int platformFontCount(Font font, String str) { } } - private static boolean isXP() { - String osVersion = System.getProperty("os.version"); - if (osVersion != null) { - float version = Float.parseFloat(osVersion); - return version >= 5.1f; - } else { - return false; - } - } - /* In case GDI doesn't handle shaping or BIDI consistently with * 2D's TextLayout, we can detect these cases and redelegate up to * be drawn via TextLayout, which in is rendered as runs of @@ -335,8 +325,7 @@ private boolean strNeedsTextLayout(String str, Font font) { } else if (!useGDITextLayout) { return true; } else { - if (preferGDITextLayout || - (isXP() && FontUtilities.textLayoutIsCompatible(font))) { + if (preferGDITextLayout || FontUtilities.textLayoutIsCompatible(font)) { return false; } else { return true; From 0d84d84ad29824147ab000c9284928980473fcb7 Mon Sep 17 00:00:00 2001 From: Thomas Stuefe Date: Sat, 4 Jul 2026 04:53:08 +0000 Subject: [PATCH 149/707] 8387660: Oop verification is sometimes wrong Reviewed-by: shade, kvn --- .../gc/shared/barrierSetAssembler_aarch64.cpp | 4 ++-- .../aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp | 5 ++--- src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp | 9 +++++++-- src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp | 1 + src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp | 2 +- .../riscv/gc/shared/barrierSetAssembler_riscv.cpp | 4 ++-- .../cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp | 2 +- src/hotspot/cpu/riscv/macroAssembler_riscv.cpp | 13 ++++++++----- src/hotspot/cpu/riscv/macroAssembler_riscv.hpp | 1 + src/hotspot/cpu/riscv/stubGenerator_riscv.cpp | 2 +- .../cpu/x86/gc/shared/barrierSetAssembler_x86.cpp | 4 ++-- .../cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp | 4 ++-- src/hotspot/cpu/x86/macroAssembler_x86.cpp | 14 ++++++++------ src/hotspot/cpu/x86/macroAssembler_x86.hpp | 1 + .../cpu/x86/stubGenerator_x86_64_arraycopy.cpp | 4 ++-- 15 files changed, 41 insertions(+), 29 deletions(-) diff --git a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp index 38efcf80650..93781bb14bf 100644 --- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp @@ -389,8 +389,8 @@ void BarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Register __ cbnz(tmp1, error); // make sure klass is 'reasonable', which is not zero. - __ load_klass(obj, obj); // get klass - __ cbz(obj, error); // if klass is null it is broken + __ load_narrow_klass(tmp1, obj); // get klass + __ cbz(tmp1, error); // if klass is null it is broken } void BarrierSetAssembler::try_peek_weak_handle_in_nmethod(MacroAssembler* masm, Register weak_handle, Register obj, Register tmp, Label& slow_path) { diff --git a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp index 7c320d835e7..f07f899e869 100644 --- a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp @@ -1385,9 +1385,8 @@ void ZBarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Registe __ bind(check_oop); // make sure klass is 'reasonable', which is not zero. - __ load_klass(tmp1, obj); // get klass - __ tst(tmp1, tmp1); - __ br(Assembler::EQ, error); // if klass is null it is broken + __ load_narrow_klass(tmp1, obj); // get narrow klass + __ cbz(tmp1, error); // if klass is null it is broken __ bind(check_zaddress); // Check if the oop is in the right area of memory diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index d5e220fd4a3..62a6f61599c 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -5097,7 +5097,7 @@ void MacroAssembler::load_method_holder(Register holder, Register method) { ldr(holder, Address(holder, ConstantPool::pool_holder_offset())); // InstanceKlass* } -// Loads the obj's Klass* into dst. +// Loads the obj's narrow Klass from a compact object header (+COH) into dst. // Preserves all registers (incl src, rscratch1 and rscratch2). // Input: // src - the oop we want to load the klass from. @@ -5108,12 +5108,17 @@ void MacroAssembler::load_narrow_klass_compact(Register dst, Register src) { lsr(dst, dst, markWord::klass_shift); } -void MacroAssembler::load_klass(Register dst, Register src) { +// Loads the obj's narrow Klass from any header (compact or not) into dst. +void MacroAssembler::load_narrow_klass(Register dst, Register src) { if (UseCompactObjectHeaders) { load_narrow_klass_compact(dst, src); } else { ldrw(dst, Address(src, oopDesc::klass_offset_in_bytes())); } +} + +void MacroAssembler::load_klass(Register dst, Register src) { + load_narrow_klass(dst, src); decode_klass_not_null(dst); } diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index 9c722cd297e..740b783cbd4 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -924,6 +924,7 @@ class MacroAssembler: public Assembler { // oop manipulations void load_narrow_klass_compact(Register dst, Register src); + void load_narrow_klass(Register dst, Register src); void load_klass(Register dst, Register src); void store_klass(Register dst, Register src); void cmp_klass(Register obj, Register klass, Register tmp); diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index 2ad7e00817c..f6ed5c2862a 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -2578,7 +2578,7 @@ class StubGenerator: public StubCodeGenerator { __ bind(L1); __ stop("broken null klass"); __ bind(L2); - __ load_klass(rscratch1, dst); + __ load_narrow_klass(rscratch1, dst); __ cbz(rscratch1, L1); // this would be broken also BLOCK_COMMENT("} assert klasses not null done"); } diff --git a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp index fd78b429ee4..f16b22e5575 100644 --- a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp @@ -352,8 +352,8 @@ void BarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Register __ bne(tmp1, tmp2, error); // Make sure klass is 'reasonable', which is not zero. - __ load_klass(obj, obj, tmp1); // get klass - __ beqz(obj, error); // if klass is null it is broken + __ load_narrow_klass(tmp1, obj); // get klass + __ beqz(tmp1, error); // if klass is null it is broken } void BarrierSetAssembler::try_peek_weak_handle_in_nmethod(MacroAssembler* masm, Register weak_handle, Register obj, diff --git a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp index bf37ccf64e2..2f8491dd592 100644 --- a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp @@ -1039,7 +1039,7 @@ void ZBarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Registe __ bind(check_oop); // Make sure klass is 'reasonable', which is not zero - __ load_klass(tmp1, obj, tmp2); + __ load_narrow_klass(tmp1, obj); __ beqz(tmp1, error); __ bind(check_zaddress); diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index d93329544a7..7a339d83d25 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -3767,18 +3767,21 @@ void MacroAssembler::load_narrow_klass_compact(Register dst, Register src) { srli(dst, dst, markWord::klass_shift); } -void MacroAssembler::load_klass(Register dst, Register src, Register tmp) { - assert_different_registers(dst, tmp); - assert_different_registers(src, tmp); +void MacroAssembler::load_narrow_klass(Register dst, Register src) { if (UseCompactObjectHeaders) { load_narrow_klass_compact(dst, src); - decode_klass_not_null(dst, tmp); } else { lwu(dst, Address(src, oopDesc::klass_offset_in_bytes())); - decode_klass_not_null(dst, tmp); } } +void MacroAssembler::load_klass(Register dst, Register src, Register tmp) { + assert_different_registers(dst, tmp); + assert_different_registers(src, tmp); + load_narrow_klass(dst, src); + decode_klass_not_null(dst, tmp); +} + void MacroAssembler::store_klass(Register dst, Register src, Register tmp) { // FIXME: Should this be a store release? concurrent gcs assumes // klass length is valid if klass field is not null. diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index a5ad7eeaa5f..f28e828fb65 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -197,6 +197,7 @@ class MacroAssembler: public Assembler { Register val, Register tmp1, Register tmp2, Register tmp3); void load_klass(Register dst, Register src, Register tmp = t0); void load_narrow_klass_compact(Register dst, Register src); + void load_narrow_klass(Register dst, Register src); void store_klass(Register dst, Register src, Register tmp = t0); void cmp_klass_beq(Register obj, Register klass, Register tmp1, Register tmp2, diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp index 13f3ed4de89..82e5a49faf0 100644 --- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp @@ -1898,7 +1898,7 @@ class StubGenerator: public StubCodeGenerator { __ bind(L1); __ stop("broken null klass"); __ bind(L2); - __ load_klass(t0, dst, t1); + __ load_narrow_klass(t0, dst); __ beqz(t0, L1); // this would be broken also BLOCK_COMMENT("} assert klasses not null done"); } diff --git a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp index 731eef09c37..265d9b16397 100644 --- a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp @@ -357,8 +357,8 @@ void BarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Register __ jcc(Assembler::notZero, error); // make sure klass is 'reasonable', which is not zero. - __ load_klass(obj, obj, tmp1); // get klass - __ testptr(obj, obj); + __ load_narrow_klass(tmp1, obj); // get narrow Klass + __ testl(tmp1, tmp1); __ jcc(Assembler::zero, error); // if klass is null it is broken } diff --git a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp index 3301d6ace49..12e9cfa4573 100644 --- a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp @@ -1551,8 +1551,8 @@ void ZBarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Registe __ bind(check_oop); // make sure klass is 'reasonable', which is not zero. - __ load_klass(tmp1, obj, tmp2); // get klass - __ testptr(tmp1, tmp1); + __ load_narrow_klass(tmp1, obj); // get narrow klass + __ testl(tmp1, tmp1); __ jcc(Assembler::zero, error); // if klass is null it is broken __ bind(check_zaddress); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index 80dd7ccfbca..d1250f0820f 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -5434,19 +5434,21 @@ void MacroAssembler::load_narrow_klass_compact(Register dst, Register src) { shrq(dst, markWord::klass_shift); } -void MacroAssembler::load_klass(Register dst, Register src, Register tmp) { - assert_different_registers(src, tmp); - assert_different_registers(dst, tmp); - +void MacroAssembler::load_narrow_klass(Register dst, Register src) { if (UseCompactObjectHeaders) { load_narrow_klass_compact(dst, src); - decode_klass_not_null(dst, tmp); } else { movl(dst, Address(src, oopDesc::klass_offset_in_bytes())); - decode_klass_not_null(dst, tmp); } } +void MacroAssembler::load_klass(Register dst, Register src, Register tmp) { + assert_different_registers(src, tmp); + assert_different_registers(dst, tmp); + load_narrow_klass(dst, src); + decode_klass_not_null(dst, tmp); +} + void MacroAssembler::store_klass(Register dst, Register src, Register tmp) { assert(!UseCompactObjectHeaders, "not with compact headers"); assert_different_registers(src, tmp); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp index de5ec02fe43..a74c7b16f3e 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp @@ -350,6 +350,7 @@ class MacroAssembler: public Assembler { // oop manipulations void load_narrow_klass_compact(Register dst, Register src); + void load_narrow_klass(Register dst, Register src); void load_klass(Register dst, Register src, Register tmp); void store_klass(Register dst, Register src, 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 e7dc416a961..a45340b8800 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp @@ -3571,8 +3571,8 @@ address StubGenerator::generate_generic_copy(address byte_copy_entry, address sh __ bind(L1); __ stop("broken null klass"); __ bind(L2); - __ load_klass(rax, dst, rklass_tmp); - __ cmpq(rax, 0); + __ load_narrow_klass(rax, dst); + __ testl(rax, rax); __ jcc(Assembler::equal, L1); // this would be broken also BLOCK_COMMENT("} assert klasses not null done"); } From cb511b64e981a0fb32b777d9ac8bf5a08cdc694f Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 4 Jul 2026 18:10:55 +0000 Subject: [PATCH 150/707] 8387704: java/nio/file/DirectoryStream/SecureDS.java failing with AccessDeniedException Reviewed-by: alanb --- test/jdk/java/nio/file/DirectoryStream/SecureDS.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/jdk/java/nio/file/DirectoryStream/SecureDS.java b/test/jdk/java/nio/file/DirectoryStream/SecureDS.java index f3321a8c04d..e481f6f1594 100644 --- a/test/jdk/java/nio/file/DirectoryStream/SecureDS.java +++ b/test/jdk/java/nio/file/DirectoryStream/SecureDS.java @@ -216,8 +216,13 @@ static void doSetPermissions(Path dir) throws IOException { view = stream.getFileAttributeView(fileEntry, PosixFileAttributeView.class, NOFOLLOW_LINKS); view.setPermissions(noperms); assertEquals(noperms, getPosixFilePermissions(file)); - view.setPermissions(permsFile); - assertEquals(permsFile, getPosixFilePermissions(file)); + try { + view.setPermissions(permsFile); + assertEquals(permsFile, getPosixFilePermissions(file)); + } catch (AccessDeniedException e) { + // Fails on older Linux systems without fchmodat AT_SYMLINK_NOFOLLOW support + setPosixFilePermissions(file, permsFile); + } // Test following link to file view = stream.getFileAttributeView(link, PosixFileAttributeView.class); From 9a8592117745193ff90ccf510ad0344a18b2d3d5 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Sun, 5 Jul 2026 06:09:54 +0000 Subject: [PATCH 151/707] 8387693: Remove unused method Reviewed-by: azvegint --- .../swing/plaf/basic/BasicProgressBarUI.java | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicProgressBarUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicProgressBarUI.java index d249bf0bc9d..6f58fd9cdb6 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicProgressBarUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicProgressBarUI.java @@ -1165,24 +1165,6 @@ private int initRepaintInterval() { return repaintInterval; } - /** - * Returns the number of milliseconds per animation cycle. - * This value is meaningful - * only if the progress bar is in indeterminate mode. - * The cycle time is used by the default indeterminate progress bar - * painting code when determining - * how far to move the bouncing box per frame. - * The cycle time is specified by - * the "ProgressBar.cycleTime" UI default - * and adjusted, if necessary, - * by the initIndeterminateDefaults method. - * - * @return the cycle time, in milliseconds - */ - private int getCycleTime() { - return cycleTime; - } - private int initCycleTime() { cycleTime = DefaultLookup.getInt(progressBar, this, "ProgressBar.cycleTime", 3000); From 92b0565b00fcdae354bd101c762974b47e075f40 Mon Sep 17 00:00:00 2001 From: April Ivy Date: Sun, 5 Jul 2026 07:22:35 +0000 Subject: [PATCH 152/707] 8386802: ClassFile Util.entryList should consider non-RandomAccess lists Reviewed-by: liach --- .../classes/jdk/internal/classfile/impl/Util.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java b/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java index 6411c939549..d19dd202432 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -189,16 +189,18 @@ public int size() { public static List entryList(List list) { var result = new Object[list.size()]; // null check - for (int i = 0; i < result.length; i++) { - result[i] = TemporaryConstantPool.INSTANCE.classEntry(list.get(i)); + int i = 0; + for (var entry : list) { + result[i++] = TemporaryConstantPool.INSTANCE.classEntry(entry); } return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(result); } public static List moduleEntryList(List list) { var result = new Object[list.size()]; // null check - for (int i = 0; i < result.length; i++) { - result[i] = TemporaryConstantPool.INSTANCE.moduleEntry(TemporaryConstantPool.INSTANCE.utf8Entry(list.get(i).name())); + int i = 0; + for (var entry : list) { + result[i++] = TemporaryConstantPool.INSTANCE.moduleEntry(entry); } return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(result); } From 6e7e6f0bbf60b67c2e4f533a0edd8e867d954d31 Mon Sep 17 00:00:00 2001 From: Eric Fang Date: Mon, 6 Jul 2026 05:41:02 +0000 Subject: [PATCH 153/707] 8383905: AArch64: Improve code generation for long vector multiply Reviewed-by: aph, xgong --- src/hotspot/cpu/aarch64/aarch64_vector.ad | 92 ++++++++++- src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 | 59 ++++++- src/hotspot/cpu/aarch64/assembler_aarch64.hpp | 16 ++ test/hotspot/gtest/aarch64/aarch64-asmtest.py | 4 + test/hotspot/gtest/aarch64/asmtest.out.h | 49 +++--- .../compiler/lib/ir_framework/IRNode.java | 20 +++ .../TestVectorMulLongToSignedUnsignedInt.java | 153 +++++++++++++++--- .../compiler/vectorapi/VectorMultiplyOpt.java | 107 ++++++++++-- 8 files changed, 441 insertions(+), 59 deletions(-) diff --git a/src/hotspot/cpu/aarch64/aarch64_vector.ad b/src/hotspot/cpu/aarch64/aarch64_vector.ad index 12f98bb8549..c06c8b856b7 100644 --- a/src/hotspot/cpu/aarch64/aarch64_vector.ad +++ b/src/hotspot/cpu/aarch64/aarch64_vector.ad @@ -1157,7 +1157,8 @@ instruct vmulI_sve(vReg dst_src1, vReg src2) %{ // vector mul - LONG instruct vmulL_neon(vReg dst, vReg src1, vReg src2) %{ - predicate(UseSVE == 0); + predicate(UseSVE == 0 && !n->as_MulVL()->has_int_inputs() && + !n->as_MulVL()->has_uint_inputs()); match(Set dst (MulVL src1 src2)); format %{ "vmulL_neon $dst, $src1, $src2\t# 2L" %} ins_encode %{ @@ -1175,8 +1176,75 @@ instruct vmulL_neon(vReg dst, vReg src1, vReg src2) %{ ins_pipe(pipe_slow); %} +// Specialization of vmulL_int_neon when both inputs are the same IR node +// (e.g. v * v). Avoids one redundant xtn and saves one temporary register. +instruct vmulL_int_neon_same(vReg dst, vReg src, vReg tmp) %{ + predicate(UseSVE == 0 && n->as_MulVL()->has_int_inputs() && + n->in(1) == n->in(2)); + match(Set dst (MulVL src src)); + effect(TEMP tmp); + format %{ "vmulL_int_neon_same $dst, $src, $src\t# 2L. KILL $tmp" %} + ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this); + assert(length_in_bytes == 16, "must be"); + __ xtn($tmp$$FloatRegister, __ T2S, $src$$FloatRegister, __ T2D); + __ smullv($dst$$FloatRegister, __ T2S, $tmp$$FloatRegister, $tmp$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + +instruct vmulL_int_neon(vReg dst, vReg src1, vReg src2, vReg tmp1, vReg tmp2) %{ + predicate(UseSVE == 0 && n->as_MulVL()->has_int_inputs() && + n->in(1) != n->in(2)); + match(Set dst (MulVL src1 src2)); + effect(TEMP tmp1, TEMP tmp2); + format %{ "vmulL_int_neon $dst, $src1, $src2\t# 2L. KILL $tmp1, $tmp2" %} + ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this); + assert(length_in_bytes == 16, "must be"); + __ xtn($tmp1$$FloatRegister, __ T2S, $src1$$FloatRegister, __ T2D); + __ xtn($tmp2$$FloatRegister, __ T2S, $src2$$FloatRegister, __ T2D); + __ smullv($dst$$FloatRegister, __ T2S, $tmp1$$FloatRegister, $tmp2$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + +// Specialization of vmulL_uint_neon when both inputs are the same IR node +// (e.g. v * v). Avoids one redundant xtn and saves one temporary register. +instruct vmulL_uint_neon_same(vReg dst, vReg src, vReg tmp) %{ + predicate(UseSVE == 0 && n->as_MulVL()->has_uint_inputs() && + n->in(1) == n->in(2)); + match(Set dst (MulVL src src)); + effect(TEMP tmp); + format %{ "vmulL_uint_neon_same $dst, $src, $src\t# 2L. KILL $tmp" %} + ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this); + assert(length_in_bytes == 16, "must be"); + __ xtn($tmp$$FloatRegister, __ T2S, $src$$FloatRegister, __ T2D); + __ umullv($dst$$FloatRegister, __ T2S, $tmp$$FloatRegister, $tmp$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + +instruct vmulL_uint_neon(vReg dst, vReg src1, vReg src2, vReg tmp1, vReg tmp2) %{ + predicate(UseSVE == 0 && n->as_MulVL()->has_uint_inputs() && + n->in(1) != n->in(2)); + match(Set dst (MulVL src1 src2)); + effect(TEMP tmp1, TEMP tmp2); + format %{ "vmulL_uint_neon $dst, $src1, $src2\t# 2L. KILL $tmp1, $tmp2" %} + ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this); + assert(length_in_bytes == 16, "must be"); + __ xtn($tmp1$$FloatRegister, __ T2S, $src1$$FloatRegister, __ T2D); + __ xtn($tmp2$$FloatRegister, __ T2S, $src2$$FloatRegister, __ T2D); + __ umullv($dst$$FloatRegister, __ T2S, $tmp1$$FloatRegister, $tmp2$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + instruct vmulL_sve(vReg dst_src1, vReg src2) %{ - predicate(UseSVE > 0); + predicate(UseSVE == 1 || (UseSVE == 2 && !n->as_MulVL()->has_int_inputs() && + !n->as_MulVL()->has_uint_inputs())); match(Set dst_src1 (MulVL dst_src1 src2)); format %{ "vmulL_sve $dst_src1, $dst_src1, $src2" %} ins_encode %{ @@ -1185,6 +1253,26 @@ instruct vmulL_sve(vReg dst_src1, vReg src2) %{ ins_pipe(pipe_slow); %} +instruct vmulL_int_sve2(vReg dst, vReg src1, vReg src2) %{ + predicate(UseSVE == 2 && n->as_MulVL()->has_int_inputs()); + match(Set dst (MulVL src1 src2)); + format %{ "vmulL_int_sve2 $dst, $src1, $src2" %} + ins_encode %{ + __ sve_smullb($dst$$FloatRegister, __ D, $src1$$FloatRegister, $src2$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + +instruct vmulL_uint_sve2(vReg dst, vReg src1, vReg src2) %{ + predicate(UseSVE == 2 && n->as_MulVL()->has_uint_inputs()); + match(Set dst (MulVL src1 src2)); + format %{ "vmulL_uint_sve2 $dst, $src1, $src2" %} + ins_encode %{ + __ sve_umullb($dst$$FloatRegister, __ D, $src1$$FloatRegister, $src2$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + // vector mul - floating-point instruct vmulHF(vReg dst, vReg src1, vReg src2) %{ diff --git a/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 b/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 index 68c407bc9af..b749647ae1e 100644 --- a/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 +++ b/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 @@ -736,7 +736,8 @@ BINARY_OP_NEON_SVE_PAIRWISE(vmulI, MulVI, mulv, sve_mul, S) // vector mul - LONG instruct vmulL_neon(vReg dst, vReg src1, vReg src2) %{ - predicate(UseSVE == 0); + predicate(UseSVE == 0 && !n->as_MulVL()->has_int_inputs() && + !n->as_MulVL()->has_uint_inputs()); match(Set dst (MulVL src1 src2)); format %{ "vmulL_neon $dst, $src1, $src2\t# 2L" %} ins_encode %{ @@ -754,8 +755,47 @@ instruct vmulL_neon(vReg dst, vReg src1, vReg src2) %{ ins_pipe(pipe_slow); %} +dnl VMUL_L_NEON($1, $2 ) +dnl VMUL_L_NEON(kind, insn ) +define(`VMUL_L_NEON', `dnl +// Specialization of vmulL_$1_neon when both inputs are the same IR node +// (e.g. v * v). Avoids one redundant xtn and saves one temporary register. +instruct vmulL_$1_neon_same(vReg dst, vReg src, vReg tmp) %{ + predicate(UseSVE == 0 && n->as_MulVL()->has_$1_inputs() && + n->in(1) == n->in(2)); + match(Set dst (MulVL src src)); + effect(TEMP tmp); + format %{ "vmulL_$1_neon_same $dst, $src, $src\t# 2L. KILL $tmp" %} + ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this); + assert(length_in_bytes == 16, "must be"); + __ xtn($tmp$$FloatRegister, __ T2S, $src$$FloatRegister, __ T2D); + __ $2($dst$$FloatRegister, __ T2S, $tmp$$FloatRegister, $tmp$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + +instruct vmulL_$1_neon(vReg dst, vReg src1, vReg src2, vReg tmp1, vReg tmp2) %{ + predicate(UseSVE == 0 && n->as_MulVL()->has_$1_inputs() && + n->in(1) != n->in(2)); + match(Set dst (MulVL src1 src2)); + effect(TEMP tmp1, TEMP tmp2); + format %{ "vmulL_$1_neon $dst, $src1, $src2\t# 2L. KILL $tmp1, $tmp2" %} + ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this); + assert(length_in_bytes == 16, "must be"); + __ xtn($tmp1$$FloatRegister, __ T2S, $src1$$FloatRegister, __ T2D); + __ xtn($tmp2$$FloatRegister, __ T2S, $src2$$FloatRegister, __ T2D); + __ $2($dst$$FloatRegister, __ T2S, $tmp1$$FloatRegister, $tmp2$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} +')dnl +VMUL_L_NEON(int, smullv) +VMUL_L_NEON(uint, umullv) instruct vmulL_sve(vReg dst_src1, vReg src2) %{ - predicate(UseSVE > 0); + predicate(UseSVE == 1 || (UseSVE == 2 && !n->as_MulVL()->has_int_inputs() && + !n->as_MulVL()->has_uint_inputs())); match(Set dst_src1 (MulVL dst_src1 src2)); format %{ "vmulL_sve $dst_src1, $dst_src1, $src2" %} ins_encode %{ @@ -764,6 +804,21 @@ instruct vmulL_sve(vReg dst_src1, vReg src2) %{ ins_pipe(pipe_slow); %} +dnl VMUL_L_SVE2($1, $2 ) +dnl VMUL_L_SVE2(kind, sve2_insn ) +define(`VMUL_L_SVE2', `dnl +instruct vmulL_$1_sve2(vReg dst, vReg src1, vReg src2) %{ + predicate(UseSVE == 2 && n->as_MulVL()->has_$1_inputs()); + match(Set dst (MulVL src1 src2)); + format %{ "vmulL_$1_sve2 $dst, $src1, $src2" %} + ins_encode %{ + __ $2($dst$$FloatRegister, __ D, $src1$$FloatRegister, $src2$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} +')dnl +VMUL_L_SVE2(int, sve_smullb) +VMUL_L_SVE2(uint, sve_umullb) // vector mul - floating-point BINARY_OP(vmulHF, MulVHF, fmul, sve_fmul, H) BINARY_OP(vmulF, MulVF, fmul, sve_fmul, S) diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp index ae2b9ac9bf7..a81213c5ae4 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp @@ -4331,6 +4331,22 @@ template INSN(sve_bsl, 0b001, 0b1); // Bitwise select #undef INSN +// SVE2 widening integer multiply - vector +#define INSN(NAME, is_unsigned, is_top) \ + void NAME(FloatRegister Zd, SIMD_RegVariant T, FloatRegister Zn, FloatRegister Zm) { \ + starti; \ + assert(T != B && T != Q, "invalid size"); \ + int op = 0b011100 | (is_unsigned ? 0b10 : 0) | (is_top ? 0b1 : 0); \ + f(0b01000101, 31, 24), f(T, 23, 22), f(0, 21), rf(Zm, 16); \ + f(op, 15, 10), rf(Zn, 5), rf(Zd, 0); \ + } + + INSN(sve_umullb, /* is_unsigned */ true, /* is_top */ false); // Unsigned widening multiply of bottom elements + INSN(sve_umullt, /* is_unsigned */ true, /* is_top */ true ); // Unsigned widening multiply of top elements + INSN(sve_smullb, /* is_unsigned */ false, /* is_top */ false); // Signed widening multiply of bottom elements + INSN(sve_smullt, /* is_unsigned */ false, /* is_top */ true ); // Signed widening multiply of top elements +#undef INSN + // SVE2 saturating operations - predicate #define INSN(NAME, op1, op2) \ void NAME(FloatRegister Zdn, SIMD_RegVariant T, PRegister Pg, FloatRegister Znm) { \ diff --git a/test/hotspot/gtest/aarch64/aarch64-asmtest.py b/test/hotspot/gtest/aarch64/aarch64-asmtest.py index 04088bb0dc8..b5386d47a73 100644 --- a/test/hotspot/gtest/aarch64/aarch64-asmtest.py +++ b/test/hotspot/gtest/aarch64/aarch64-asmtest.py @@ -2163,6 +2163,10 @@ def generate(kind, names): # SVE2 instructions ["histcnt", "__ sve_histcnt(z16, __ S, p0, z16, z16);", "histcnt\tz16.s, p0/z, z16.s, z16.s"], ["histcnt", "__ sve_histcnt(z17, __ D, p0, z17, z17);", "histcnt\tz17.d, p0/z, z17.d, z17.d"], + ["umullb", "__ sve_umullb(z16, __ H, z17, z18);", "umullb\tz16.h, z17.b, z18.b"], + ["umullt", "__ sve_umullt(z19, __ S, z20, z21);", "umullt\tz19.s, z20.h, z21.h"], + ["smullb", "__ sve_smullb(z22, __ D, z23, z24);", "smullb\tz22.d, z23.s, z24.s"], + ["smullt", "__ sve_smullt(z25, __ H, z26, z27);", "smullt\tz25.h, z26.b, z27.b"], ]) print "\n// FloatImmediateOp" diff --git a/test/hotspot/gtest/aarch64/asmtest.out.h b/test/hotspot/gtest/aarch64/asmtest.out.h index bad9825af9b..95832a1faf6 100644 --- a/test/hotspot/gtest/aarch64/asmtest.out.h +++ b/test/hotspot/gtest/aarch64/asmtest.out.h @@ -1180,6 +1180,10 @@ __ sve_splice(z0, __ D, p0, z1); // splice z0.d, p0, z0.d, z1.d __ sve_histcnt(z16, __ S, p0, z16, z16); // histcnt z16.s, p0/z, z16.s, z16.s __ sve_histcnt(z17, __ D, p0, z17, z17); // histcnt z17.d, p0/z, z17.d, z17.d + __ sve_umullb(z16, __ H, z17, z18); // umullb z16.h, z17.b, z18.b + __ sve_umullt(z19, __ S, z20, z21); // umullt z19.s, z20.h, z21.h + __ sve_smullb(z22, __ D, z23, z24); // smullb z22.d, z23.s, z24.s + __ sve_smullt(z25, __ H, z26, z27); // smullt z25.h, z26.b, z27.b // FloatImmediateOp __ fmovd(v0, 2.0); // fmov d0, #2.0 @@ -1470,30 +1474,30 @@ 0x9101a1a0, 0xb10a5cc8, 0xd10810aa, 0xf10fd061, 0x120cb166, 0x321764bc, 0x52174681, 0x720c0227, 0x9241018e, 0xb25a2969, 0xd278b411, 0xf26aad01, - 0x14000000, 0x17ffffd7, 0x140004cc, 0x94000000, - 0x97ffffd4, 0x940004c9, 0x3400000a, 0x34fffa2a, - 0x340098ca, 0x35000008, 0x35fff9c8, 0x35009868, - 0xb400000b, 0xb4fff96b, 0xb400980b, 0xb500001d, - 0xb5fff91d, 0xb50097bd, 0x10000013, 0x10fff8b3, - 0x10009753, 0x90000013, 0x36300016, 0x3637f836, - 0x363096d6, 0x3758000c, 0x375ff7cc, 0x3758966c, + 0x14000000, 0x17ffffd7, 0x140004d0, 0x94000000, + 0x97ffffd4, 0x940004cd, 0x3400000a, 0x34fffa2a, + 0x3400994a, 0x35000008, 0x35fff9c8, 0x350098e8, + 0xb400000b, 0xb4fff96b, 0xb400988b, 0xb500001d, + 0xb5fff91d, 0xb500983d, 0x10000013, 0x10fff8b3, + 0x100097d3, 0x90000013, 0x36300016, 0x3637f836, + 0x36309756, 0x3758000c, 0x375ff7cc, 0x375896ec, 0x128313a0, 0x528a32c7, 0x7289173b, 0x92ab3acc, 0xd2a0bf94, 0xf2c285e8, 0x9358722f, 0x330e652f, 0x53067f3b, 0x93577c53, 0xb34a1aac, 0xd35a4016, 0x13946c63, 0x93c3dbc8, 0x54000000, 0x54fff5a0, - 0x54009440, 0x54000001, 0x54fff541, 0x540093e1, - 0x54000002, 0x54fff4e2, 0x54009382, 0x54000002, - 0x54fff482, 0x54009322, 0x54000003, 0x54fff423, - 0x540092c3, 0x54000003, 0x54fff3c3, 0x54009263, - 0x54000004, 0x54fff364, 0x54009204, 0x54000005, - 0x54fff305, 0x540091a5, 0x54000006, 0x54fff2a6, - 0x54009146, 0x54000007, 0x54fff247, 0x540090e7, - 0x54000008, 0x54fff1e8, 0x54009088, 0x54000009, - 0x54fff189, 0x54009029, 0x5400000a, 0x54fff12a, - 0x54008fca, 0x5400000b, 0x54fff0cb, 0x54008f6b, - 0x5400000c, 0x54fff06c, 0x54008f0c, 0x5400000d, - 0x54fff00d, 0x54008ead, 0x5400000e, 0x54ffefae, - 0x54008e4e, 0x5400000f, 0x54ffef4f, 0x54008def, + 0x540094c0, 0x54000001, 0x54fff541, 0x54009461, + 0x54000002, 0x54fff4e2, 0x54009402, 0x54000002, + 0x54fff482, 0x540093a2, 0x54000003, 0x54fff423, + 0x54009343, 0x54000003, 0x54fff3c3, 0x540092e3, + 0x54000004, 0x54fff364, 0x54009284, 0x54000005, + 0x54fff305, 0x54009225, 0x54000006, 0x54fff2a6, + 0x540091c6, 0x54000007, 0x54fff247, 0x54009167, + 0x54000008, 0x54fff1e8, 0x54009108, 0x54000009, + 0x54fff189, 0x540090a9, 0x5400000a, 0x54fff12a, + 0x5400904a, 0x5400000b, 0x54fff0cb, 0x54008feb, + 0x5400000c, 0x54fff06c, 0x54008f8c, 0x5400000d, + 0x54fff00d, 0x54008f2d, 0x5400000e, 0x54ffefae, + 0x54008ece, 0x5400000f, 0x54ffef4f, 0x54008e6f, 0xd40658e1, 0xd4014d22, 0xd4046543, 0xd4273f60, 0xd44cad80, 0xd503201f, 0xd503203f, 0xd503205f, 0xd503209f, 0xd50320bf, 0xd503219f, 0xd50323bf, @@ -1536,7 +1540,7 @@ 0x39598921, 0x795d3077, 0x399d0675, 0x7998d8f3, 0x79dbd02a, 0xb99d068a, 0xfd5d11a0, 0xbd58d76b, 0xfd1ac72d, 0xbd1d9c14, 0x5800001a, 0x18ffda33, - 0xf8991100, 0xd80078a0, 0xf8a758e0, 0xf9989d80, + 0xf8991100, 0xd8007920, 0xf8a758e0, 0xf9989d80, 0x1a0b0298, 0x3a1c01a0, 0x5a0400ea, 0x7a02020f, 0x9a1d028c, 0xba0e01ad, 0xda140186, 0xfa19022c, 0x0b2b877e, 0x2b21c8ee, 0xcb3ba47d, 0x6b3ae9a0, @@ -1719,7 +1723,8 @@ 0x0420bc31, 0x05271e11, 0x6545e891, 0x6585e891, 0x65c5e891, 0x6545c891, 0x6585c891, 0x65c5c891, 0x052c8020, 0x056c8020, 0x05ac8020, 0x05ec8020, - 0x45b0c210, 0x45f1c231, 0x1e601000, 0x1e603000, + 0x45b0c210, 0x45f1c231, 0x45527a30, 0x45957e93, + 0x45d872f6, 0x455b7759, 0x1e601000, 0x1e603000, 0x1e621000, 0x1e623000, 0x1e641000, 0x1e643000, 0x1e661000, 0x1e663000, 0x1e681000, 0x1e683000, 0x1e6a1000, 0x1e6a3000, 0x1e6c1000, 0x1e6c3000, diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index a76853016d9..249e73fa54b 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -2863,6 +2863,26 @@ public class IRNode { machOnlyNameRegex(X86_VMULDQ_REG, "vmuldq_reg"); } + public static final String AARCH64_VMULL_UINT_SVE2 = PREFIX + "AARCH64_VMULL_UINT_SVE2" + POSTFIX; + static { + machOnlyNameRegex(AARCH64_VMULL_UINT_SVE2, "vmulL_uint_sve2"); + } + + public static final String AARCH64_VMULL_INT_SVE2 = PREFIX + "AARCH64_VMULL_INT_SVE2" + POSTFIX; + static { + machOnlyNameRegex(AARCH64_VMULL_INT_SVE2, "vmulL_int_sve2"); + } + + public static final String AARCH64_VMULL_UINT_NEON = PREFIX + "AARCH64_VMULL_UINT_NEON" + POSTFIX; + static { + machOnlyNameRegex(AARCH64_VMULL_UINT_NEON, "vmulL_uint_neon"); + } + + public static final String AARCH64_VMULL_INT_NEON = PREFIX + "AARCH64_VMULL_INT_NEON" + POSTFIX; + static { + machOnlyNameRegex(AARCH64_VMULL_INT_NEON, "vmulL_int_neon"); + } + public static final String X86_SCONV_D2I = PREFIX + "X86_SCONV_D2I" + POSTFIX; static { machOnlyNameRegex(X86_SCONV_D2I, "convD2I_reg_reg"); diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestVectorMulLongToSignedUnsignedInt.java b/test/hotspot/jtreg/compiler/vectorapi/TestVectorMulLongToSignedUnsignedInt.java index d5b4771d3e1..e7745b5e88c 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/TestVectorMulLongToSignedUnsignedInt.java +++ b/test/hotspot/jtreg/compiler/vectorapi/TestVectorMulLongToSignedUnsignedInt.java @@ -34,7 +34,7 @@ /* * @test - * @bug 8384963 + * @bug 8384963 8383905 * @key randomness * @summary C2: Incorrect uint constant match mishandles negative values in vectors * @modules jdk.incubator.vector @@ -87,8 +87,18 @@ public static void main(String[] args) { // Case 1: Negative mask (-2L = 0xFFFF_FFFF_FFFF_FFFE). @Test - @IR(counts = {IRNode.AND_VL, " >0 ", IRNode.MUL_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) - @IR(failOn = {IRNode.X86_VMULUDQ_REG}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.AND_VL, " >0 ", + IRNode.MUL_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + @IR(failOn = {IRNode.X86_VMULUDQ_REG}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"avx", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_SVE2}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"sve2", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_NEON}, + phase = CompilePhase.MATCHING, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) public static void testNegativeMask() { LongVector v1 = LongVector.fromArray(SPECIES, src1, 0); LongVector v2 = LongVector.fromArray(SPECIES, src2, 0); @@ -107,8 +117,16 @@ public void runNegativeMask() { // Case 3: Mask = 0x1_0000_0000L (bit 32 set, exceeds uint range). @Test - @IR(counts = {IRNode.AND_VL, " >0 ", IRNode.MUL_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) - @IR(failOn = {IRNode.X86_VMULUDQ_REG}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.AND_VL, " >0 ", + IRNode.MUL_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + @IR(failOn = {IRNode.X86_VMULUDQ_REG}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"avx", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_SVE2}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"sve2", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_NEON}, phase = CompilePhase.MATCHING, applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) public static void testBit32SetMask() { LongVector v1 = LongVector.fromArray(SPECIES, src1, 0); LongVector v2 = LongVector.fromArray(SPECIES, src2, 0); @@ -127,8 +145,18 @@ public void runBit32SetMask() { // Case 4: Mask = Long.MIN_VALUE (0x8000_0000_0000_0000). @Test - @IR(counts = {IRNode.AND_VL, " >0 ", IRNode.MUL_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) - @IR(failOn = {IRNode.X86_VMULUDQ_REG}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.AND_VL, " >0 ", + IRNode.MUL_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + @IR(failOn = {IRNode.X86_VMULUDQ_REG}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"avx", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_SVE2}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"sve2", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_NEON}, + phase = CompilePhase.MATCHING, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) public static void testMinValueMask() { LongVector v1 = LongVector.fromArray(SPECIES, src1, 0); LongVector v2 = LongVector.fromArray(SPECIES, src2, 0); @@ -147,8 +175,17 @@ public void runMinValueMask() { // Case 5: Mask = 0xFFFF_FFFFL (exactly uint max, boundary valid case). @Test - @IR(counts = {IRNode.MUL_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) - @IR(counts = {IRNode.X86_VMULUDQ_REG, " >0 "}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.MUL_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + @IR(counts = {IRNode.X86_VMULUDQ_REG, " >0 "}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.AARCH64_VMULL_UINT_SVE2, " >0 "}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"sve2", "true"}) + @IR(counts = {IRNode.AARCH64_VMULL_UINT_NEON, " >0 "}, + phase = CompilePhase.MATCHING, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) public static void testUintMaxMask() { LongVector v1 = LongVector.fromArray(SPECIES, src1, 0); LongVector v2 = LongVector.fromArray(SPECIES, src2, 0); @@ -167,8 +204,17 @@ public void runUintMaxMask() { // Case 6: Small mask (0xFFFFL), clearly fits in uint. @Test - @IR(counts = {IRNode.MUL_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) - @IR(counts = {IRNode.X86_VMULUDQ_REG, " >0 "}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.MUL_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + @IR(counts = {IRNode.X86_VMULUDQ_REG, " >0 "}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.AARCH64_VMULL_UINT_SVE2, " >0 "}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"sve2", "true"}) + @IR(counts = {IRNode.AARCH64_VMULL_UINT_NEON, " >0 "}, + phase = CompilePhase.MATCHING, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) public static void testSmallMask() { LongVector v1 = LongVector.fromArray(SPECIES, src1, 0); LongVector v2 = LongVector.fromArray(SPECIES, src2, 0); @@ -187,8 +233,18 @@ public void runSmallMask() { // Case 7: URShift by 32 clears upper doubleword. @Test - @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.URSHIFT_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) - @IR(counts = {IRNode.X86_VMULUDQ_REG, " >0 "}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.MUL_VL, " >0 ", + IRNode.URSHIFT_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + @IR(counts = {IRNode.X86_VMULUDQ_REG, " >0 "}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.AARCH64_VMULL_UINT_SVE2, " >0 "}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"sve2", "true"}) + @IR(counts = {IRNode.AARCH64_VMULL_UINT_NEON, " >0 "}, + phase = CompilePhase.MATCHING, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) public static void testURShift32() { LongVector v1 = LongVector.fromArray(SPECIES, src1, 0); LongVector v2 = LongVector.fromArray(SPECIES, src2, 0); @@ -207,8 +263,18 @@ public void runURShift32() { // Case 8: Asymmetric — one input valid uint mask, other negative mask. @Test - @IR(counts = {IRNode.AND_VL, " >0 ", IRNode.MUL_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) - @IR(failOn = {IRNode.X86_VMULUDQ_REG}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.AND_VL, " >0 ", + IRNode.MUL_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + @IR(failOn = {IRNode.X86_VMULUDQ_REG}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"avx", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_SVE2}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"sve2", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_NEON}, + phase = CompilePhase.MATCHING, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) public static void testAsymmetricMask() { LongVector v1 = LongVector.fromArray(SPECIES, src1, 0); LongVector v2 = LongVector.fromArray(SPECIES, src2, 0); @@ -228,8 +294,19 @@ public void runAsymmetricMask() { // Case 9: Mixed — one input URShift (valid), other negative mask (invalid). // Note: -2L is used (not -1L) since AND with -1L is identity and gets folded. @Test - @IR(counts = {IRNode.URSHIFT_VL, " >0 ", IRNode.AND_VL, " >0 ", IRNode.MUL_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) - @IR(failOn = {IRNode.X86_VMULUDQ_REG}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.URSHIFT_VL, " >0 ", + IRNode.AND_VL, " >0 ", + IRNode.MUL_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + @IR(failOn = {IRNode.X86_VMULUDQ_REG}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"avx", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_SVE2}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"sve2", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_NEON}, + phase = CompilePhase.MATCHING, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) public static void testMixedURShiftAndNegMask() { LongVector v1 = LongVector.fromArray(SPECIES, src1, 0); LongVector v2 = LongVector.fromArray(SPECIES, src2, 0); @@ -248,8 +325,18 @@ public void runMixedURShiftAndNegMask() { // Case 10: Predicated AndV (uint path). Inactive lanes preserves destination with non-zero upper 32 bits. @Test - @IR(counts = {IRNode.AND_VL, " >0 ", IRNode.MUL_VL, " >0 "}, applyIfCPUFeature = {"avx512f", "true"}) - @IR(failOn = {IRNode.X86_VMULUDQ_REG}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx512f", "true"}) + @IR(counts = {IRNode.AND_VL, " >0 ", + IRNode.MUL_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx512f", "true", "sve", "true"}) + @IR(failOn = {IRNode.X86_VMULUDQ_REG}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"avx512f", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_SVE2}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"sve2", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_NEON}, + phase = CompilePhase.MATCHING, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) public static void testPredicatedAndMask() { LongVector v1 = LongVector.fromArray(SPECIES, src1, 0); LongVector v2 = LongVector.fromArray(SPECIES, src2, 0); @@ -270,8 +357,18 @@ public void runPredicatedAndMask() { // Case 11: Predicated URShiftVL by 32 (uint path). Inactive lanes preserves destination with non-zero upper 32 bits. @Test - @IR(counts = {IRNode.URSHIFT_VL, " >0 ", IRNode.MUL_VL, " >0 "}, applyIfCPUFeature = {"avx512f", "true"}) - @IR(failOn = {IRNode.X86_VMULUDQ_REG}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx512f", "true"}) + @IR(counts = {IRNode.URSHIFT_VL, " >0 ", + IRNode.MUL_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx512f", "true", "sve", "true"}) + @IR(failOn = {IRNode.X86_VMULUDQ_REG}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"avx512f", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_SVE2}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"sve2", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_UINT_NEON}, + phase = CompilePhase.MATCHING, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) public static void testPredicatedURShift32() { LongVector v1 = LongVector.fromArray(SPECIES, src1, 0); LongVector v2 = LongVector.fromArray(SPECIES, src2, 0); @@ -292,8 +389,18 @@ public void runPredicatedURShift32() { // Case 12: Predicated RShiftVL (arithmetic) by 32. @Test - @IR(counts = {IRNode.RSHIFT_VL, " >0 ", IRNode.MUL_VL, " >0 "}, applyIfCPUFeature = {"avx512f", "true"}) - @IR(failOn = {IRNode.X86_VMULDQ_REG}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx512f", "true"}) + @IR(counts = {IRNode.RSHIFT_VL, " >0 ", + IRNode.MUL_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx512f", "true", "sve", "true"}) + @IR(failOn = {IRNode.X86_VMULDQ_REG}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"avx512f", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_INT_SVE2}, + phase = CompilePhase.MATCHING, + applyIfCPUFeature = {"sve2", "true"}) + @IR(failOn = {IRNode.AARCH64_VMULL_INT_NEON}, + phase = CompilePhase.MATCHING, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) public static void testPredicatedRShift32() { LongVector v1 = LongVector.fromArray(SPECIES, src1, 0); LongVector v2 = LongVector.fromArray(SPECIES, src2, 0); diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorMultiplyOpt.java b/test/hotspot/jtreg/compiler/vectorapi/VectorMultiplyOpt.java index a8394f41f8a..68ac9249ebf 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/VectorMultiplyOpt.java +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorMultiplyOpt.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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,9 +31,9 @@ /** * @test - * @bug 8341137 + * @bug 8341137 8383905 * @key randomness - * @summary Optimize long vector multiplication using x86 VPMUL[U]DQ instruction. + * @summary Optimize long vector multiplication. * @modules jdk.incubator.vector * @library /test/lib / * @run driver compiler.vectorapi.VectorMultiplyOpt @@ -80,7 +80,7 @@ public VectorMultiplyOpt() { public static void main(String[] args) { TestFramework testFramework = new TestFramework(); - testFramework.setDefaultWarmup(5000) + testFramework.setDefaultWarmup(10000) .addFlags("--add-modules=jdk.incubator.vector") .start(); System.out.println("PASSED"); @@ -109,7 +109,12 @@ public static void validate(String msg, long[] actual, Object src1, Object src2, @Test @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.AND_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) @IR(counts = {"vmuludq", " >0 "}, phase = CompilePhase.FINAL_CODE, applyIfCPUFeature = {"avx", "true"}) - @Warmup(value = 10000) + @IR(counts = {"vmulL_uint_sve2", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeature = {"sve2", "true"}) + @IR(counts = {"vmulL_sve", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"sve", "true", "sve2", "false"}) + @IR(counts = {"vmulL_uint_neon", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) public static void test_pattern1() { int i = 0; for (; i < LSP.loopBound(res.length); i += LSP.length()) { @@ -132,7 +137,12 @@ public void test_pattern1_validate() { @Test @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.AND_VL, " >0 ", IRNode.URSHIFT_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) @IR(counts = {"vmuludq", " >0 "}, phase = CompilePhase.FINAL_CODE, applyIfCPUFeature = {"avx", "true"}) - @Warmup(value = 10000) + @IR(counts = {"vmulL_uint_sve2", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeature = {"sve2", "true"}) + @IR(counts = {"vmulL_sve", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"sve", "true", "sve2", "false"}) + @IR(counts = {"vmulL_uint_neon", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) public static void test_pattern2() { int i = 0; for (; i < LSP.loopBound(res.length); i += LSP.length()) { @@ -155,7 +165,12 @@ public void test_pattern2_validate() { @Test @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.URSHIFT_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) @IR(counts = {"vmuludq", " >0 "}, phase = CompilePhase.FINAL_CODE, applyIfCPUFeature = {"avx", "true"}) - @Warmup(value = 10000) + @IR(counts = {"vmulL_uint_sve2", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeature = {"sve2", "true"}) + @IR(counts = {"vmulL_sve", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"sve", "true", "sve2", "false"}) + @IR(counts = {"vmulL_uint_neon", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) public static void test_pattern3() { int i = 0; for (; i < LSP.loopBound(res.length); i += LSP.length()) { @@ -178,7 +193,12 @@ public void test_pattern3_validate() { @Test @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.URSHIFT_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) @IR(counts = {"vmuludq", " >0 "}, applyIfCPUFeature = {"avx", "true"}, phase = CompilePhase.FINAL_CODE) - @Warmup(value = 10000) + @IR(counts = {"vmulL_uint_sve2", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeature = {"sve2", "true"}) + @IR(counts = {"vmulL_sve", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"sve", "true", "sve2", "false"}) + @IR(counts = {"vmulL_uint_neon", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) public static void test_pattern4() { int i = 0; for (; i < LSP.loopBound(res.length); i += LSP.length()) { @@ -201,7 +221,12 @@ public void test_pattern4_validate() { @Test @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.VECTOR_CAST_I2L, " >0 "}, applyIfCPUFeature = {"avx", "true"}) @IR(counts = {"vmuldq", " >0 "}, applyIfCPUFeature = {"avx", "true"}, phase = CompilePhase.FINAL_CODE) - @Warmup(value = 10000) + @IR(counts = {"vmulL_int_sve2", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeature = {"sve2", "true"}) + @IR(counts = {"vmulL_sve", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"sve", "true", "sve2", "false"}) + @IR(counts = {"vmulL_int_neon", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) public static void test_pattern5() { int i = 0; for (; i < LSP.loopBound(res.length); i += LSP.length()) { @@ -227,7 +252,12 @@ public void test_pattern5_validate() { @Test @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.RSHIFT_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) @IR(counts = {"vmuldq", " >0 "}, applyIfCPUFeature = {"avx", "true"}, phase = CompilePhase.FINAL_CODE) - @Warmup(value = 10000) + @IR(counts = {"vmulL_int_sve2", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeature = {"sve2", "true"}) + @IR(counts = {"vmulL_sve", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"sve", "true", "sve2", "false"}) + @IR(counts = {"vmulL_int_neon", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) public static void test_pattern6() { int i = 0; for (; i < LSP.loopBound(res.length); i += LSP.length()) { @@ -247,4 +277,61 @@ public void test_pattern6_validate() { validate("pattern6 ", res, lsrc1, lsrc2, (l1, l2) -> (l1 >> shift5) * (l2 >> shift5)); } + // Same-operand multiplication (v * v) where v has zero-extended high bits. + // On NEON this should map to the dedicated rule that emits a single xtn. + @Test + @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.AND_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {"vmuludq", " >0 "}, phase = CompilePhase.FINAL_CODE, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {"vmulL_uint_sve2", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeature = {"sve2", "true"}) + @IR(counts = {"vmulL_sve", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"sve", "true", "sve2", "false"}) + @IR(counts = {"vmulL_uint_neon_same", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) + public static void test_pattern7() { + int i = 0; + for (; i < LSP.loopBound(res.length); i += LSP.length()) { + LongVector vsrc = LongVector.fromArray(LSP, lsrc1, i) + .lanewise(VectorOperators.AND, mask1); + vsrc.lanewise(VectorOperators.MUL, vsrc).intoArray(res, i); + } + for (; i < res.length; i++) { + long x = lsrc1[i] & mask1; + res[i] = x * x; + } + } + + @Check(test = "test_pattern7") + public void test_pattern7_validate() { + validate("pattern7 ", res, lsrc1, lsrc1, (l1, l2) -> { long x = l1 & mask1; return x * x; }); + } + + // Same-operand multiplication (v * v) where v has sign-extended high bits. + // On NEON this should map to the dedicated rule that emits a single xtn. + @Test + @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.VECTOR_CAST_I2L, " >0 "}, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {"vmuldq", " >0 "}, applyIfCPUFeature = {"avx", "true"}, phase = CompilePhase.FINAL_CODE) + @IR(counts = {"vmulL_int_sve2", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeature = {"sve2", "true"}) + @IR(counts = {"vmulL_sve", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"sve", "true", "sve2", "false"}) + @IR(counts = {"vmulL_int_neon_same", " >0 "}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) + public static void test_pattern8() { + int i = 0; + for (; i < LSP.loopBound(res.length); i += LSP.length()) { + LongVector vsrc = IntVector.fromArray(ISP, isrc1, i) + .convert(VectorOperators.I2L, 0) + .reinterpretAsLongs(); + vsrc.lanewise(VectorOperators.MUL, vsrc).intoArray(res, i); + } + for (; i < res.length; i++) { + res[i] = Math.multiplyFull(isrc1[i], isrc1[i]); + } + } + + @Check(test = "test_pattern8") + public void test_pattern8_validate() { + validate("pattern8 ", res, isrc1, isrc1, (i1, i2) -> Math.multiplyFull((int)i1, (int)i1)); + } } From 631b675d7949a0e6312d8d6f45e2515d53b12f05 Mon Sep 17 00:00:00 2001 From: Eric Fang Date: Mon, 6 Jul 2026 05:45:30 +0000 Subject: [PATCH 154/707] 8387388: AArch64: Optimize reduceLanes MUL op with ext instruction Reviewed-by: aph, xgong --- .../cpu/aarch64/c2_MacroAssembler_aarch64.cpp | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index e46a338e649..eacfef9618a 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -1806,19 +1806,19 @@ void C2_MacroAssembler::neon_reduce_mul_integral(Register dst, BasicType bt, if (isQ) { // Multiply the lower half and higher half of vector iteratively. // vtmp1 = vsrc[8:15] - ins(vtmp1, D, vsrc, 0, 1); + ext(vtmp1, T16B, vsrc, vsrc, 8); // vtmp1[n] = vsrc[n] * vsrc[n + 8], where n=[0, 7] mulv(vtmp1, T8B, vtmp1, vsrc); // vtmp2 = vtmp1[4:7] - ins(vtmp2, S, vtmp1, 0, 1); + ext(vtmp2, T8B, vtmp1, vtmp1, 4); // vtmp1[n] = vtmp1[n] * vtmp1[n + 4], where n=[0, 3] mulv(vtmp1, T8B, vtmp2, vtmp1); } else { - ins(vtmp1, S, vsrc, 0, 1); + ext(vtmp1, T8B, vsrc, vsrc, 4); mulv(vtmp1, T8B, vtmp1, vsrc); } // vtmp2 = vtmp1[2:3] - ins(vtmp2, H, vtmp1, 0, 1); + ext(vtmp2, T8B, vtmp1, vtmp1, 2); // vtmp2[n] = vtmp1[n] * vtmp1[n + 2], where n=[0, 1] mulv(vtmp2, T8B, vtmp2, vtmp1); // dst = vtmp2[0] * isrc * vtmp2[1] @@ -1831,12 +1831,12 @@ void C2_MacroAssembler::neon_reduce_mul_integral(Register dst, BasicType bt, break; case T_SHORT: if (isQ) { - ins(vtmp2, D, vsrc, 0, 1); + ext(vtmp2, T16B, vsrc, vsrc, 8); mulv(vtmp2, T4H, vtmp2, vsrc); - ins(vtmp1, S, vtmp2, 0, 1); + ext(vtmp1, T8B, vtmp2, vtmp2, 4); mulv(vtmp1, T4H, vtmp1, vtmp2); } else { - ins(vtmp1, S, vsrc, 0, 1); + ext(vtmp1, T8B, vsrc, vsrc, 4); mulv(vtmp1, T4H, vtmp1, vsrc); } umov(rscratch1, vtmp1, H, 0); @@ -1848,7 +1848,7 @@ void C2_MacroAssembler::neon_reduce_mul_integral(Register dst, BasicType bt, break; case T_INT: if (isQ) { - ins(vtmp1, D, vsrc, 0, 1); + ext(vtmp1, T16B, vsrc, vsrc, 8); mulv(vtmp1, T2S, vtmp1, vsrc); } else { vtmp1 = vsrc; @@ -1904,19 +1904,19 @@ void C2_MacroAssembler::neon_reduce_mul_fp(FloatRegister dst, BasicType bt, break; case T_FLOAT: fmuls(dst, fsrc, vsrc); - ins(vtmp, S, vsrc, 0, 1); + ext(vtmp, T8B, vsrc, vsrc, 4); fmuls(dst, dst, vtmp); if (isQ) { - ins(vtmp, S, vsrc, 0, 2); + ext(vtmp, T16B, vsrc, vsrc, 8); fmuls(dst, dst, vtmp); - ins(vtmp, S, vsrc, 0, 3); + ext(vtmp, T16B, vsrc, vsrc, 12); fmuls(dst, dst, vtmp); } break; case T_DOUBLE: assert(isQ, "unsupported"); fmuld(dst, fsrc, vsrc); - ins(vtmp, D, vsrc, 0, 1); + ext(vtmp, T16B, vsrc, vsrc, 8); fmuld(dst, dst, vtmp); break; default: From b3100b4173184a8c9d9c9ef0975c795bd4d64b7f Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Mon, 6 Jul 2026 07:58:46 +0000 Subject: [PATCH 155/707] 8387400: Force-inline Devirtualizer methods Reviewed-by: kvn, aboldtch --- src/hotspot/share/utilities/devirtualizer.hpp | 25 +++++++++++++------ .../share/utilities/devirtualizer.inline.hpp | 12 ++++++++- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/hotspot/share/utilities/devirtualizer.hpp b/src/hotspot/share/utilities/devirtualizer.hpp index b4d444dc5a8..39e1ba89239 100644 --- a/src/hotspot/share/utilities/devirtualizer.hpp +++ b/src/hotspot/share/utilities/devirtualizer.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -34,12 +34,23 @@ class ClassLoaderData; // a concrete implementation, otherwise a virtual call is taken. class Devirtualizer { public: - template static void do_oop(OopClosureType* closure, T* p); - template static void do_klass(OopClosureType* closure, Klass* k); - template static void do_cld(OopClosureType* closure, ClassLoaderData* cld); - template static bool do_metadata(OopClosureType* closure); - template static void do_derived_oop(DerivedOopClosureType* closure, derived_base* base, derived_pointer* derived); - template static bool do_bit(BitMapClosureType* closure, BitMap::idx_t index); + template + static ALWAYSINLINE void do_oop(OopClosureType* closure, T* p); + + template + static ALWAYSINLINE void do_klass(OopClosureType* closure, Klass* k); + + template + static ALWAYSINLINE void do_cld(OopClosureType* closure, ClassLoaderData* cld); + + template + static ALWAYSINLINE bool do_metadata(OopClosureType* closure); + + template + static ALWAYSINLINE void do_derived_oop(DerivedOopClosureType* closure, derived_base* base, derived_pointer* derived); + + template + static ALWAYSINLINE bool do_bit(BitMapClosureType* closure, BitMap::idx_t index); }; #endif // SHARE_UTILITIES_DEVIRTUALIZER_HPP diff --git a/src/hotspot/share/utilities/devirtualizer.inline.hpp b/src/hotspot/share/utilities/devirtualizer.inline.hpp index 7f49524e0fb..8cc6f931908 100644 --- a/src/hotspot/share/utilities/devirtualizer.inline.hpp +++ b/src/hotspot/share/utilities/devirtualizer.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -74,12 +74,14 @@ // p - The oop (or narrowOop) field to pass to the closure template +ALWAYSINLINE static typename EnableIf::value, void>::type call_do_oop(void (Receiver::*)(T*), void (Base::*)(T*), OopClosureType* closure, T* p) { closure->do_oop(p); } template +ALWAYSINLINE static typename EnableIf::value, void>::type call_do_oop(void (Receiver::*)(T*), void (Base::*)(T*), OopClosureType* closure, T* p) { // Sanity check @@ -95,12 +97,14 @@ inline void Devirtualizer::do_oop(OopClosureType* closure, T* p) { // Implementation of the non-virtual do_metadata dispatch. template +ALWAYSINLINE static typename EnableIf::value, bool>::type call_do_metadata(bool (Receiver::*)(), bool (Base::*)(), OopClosureType* closure) { return closure->do_metadata(); } template +ALWAYSINLINE static typename EnableIf::value, bool>::type call_do_metadata(bool (Receiver::*)(), bool (Base::*)(), OopClosureType* closure) { return closure->OopClosureType::do_metadata(); @@ -114,12 +118,14 @@ inline bool Devirtualizer::do_metadata(OopClosureType* closure) { // Implementation of the non-virtual do_klass dispatch. template +ALWAYSINLINE static typename EnableIf::value, void>::type call_do_klass(void (Receiver::*)(Klass*), void (Base::*)(Klass*), OopClosureType* closure, Klass* k) { closure->do_klass(k); } template +ALWAYSINLINE static typename EnableIf::value, void>::type call_do_klass(void (Receiver::*)(Klass*), void (Base::*)(Klass*), OopClosureType* closure, Klass* k) { closure->OopClosureType::do_klass(k); @@ -133,12 +139,14 @@ inline void Devirtualizer::do_klass(OopClosureType* closure, Klass* k) { // Implementation of the non-virtual do_cld dispatch. template +ALWAYSINLINE static typename EnableIf::value, void>::type call_do_cld(void (Receiver::*)(ClassLoaderData*), void (Base::*)(ClassLoaderData*), OopClosureType* closure, ClassLoaderData* cld) { closure->do_cld(cld); } template +ALWAYSINLINE static typename EnableIf::value, void>::type call_do_cld(void (Receiver::*)(ClassLoaderData*), void (Base::*)(ClassLoaderData*), OopClosureType* closure, ClassLoaderData* cld) { closure->OopClosureType::do_cld(cld); @@ -152,12 +160,14 @@ void Devirtualizer::do_cld(OopClosureType* closure, ClassLoaderData* cld) { // Implementation of the non-virtual do_derived_oop dispatch. template +ALWAYSINLINE static typename EnableIf::value, void>::type call_do_derived_oop(void (Receiver::*)(derived_base*, derived_pointer*), void (Base::*)(derived_base*, derived_pointer*), DerivedOopClosureType* closure, derived_base* base, derived_pointer* derived) { closure->do_derived_oop(base, derived); } template +ALWAYSINLINE static typename EnableIf::value, void>::type call_do_derived_oop(void (Receiver::*)(derived_base*, derived_pointer*), void (Base::*)(derived_base*, derived_pointer*), DerivedOopClosureType* closure, derived_base* base, derived_pointer* derived) { closure->DerivedOopClosureType::do_derived_oop(base, derived); From a96895c580c790e5ab0d4e88365d0e4f2ee9c568 Mon Sep 17 00:00:00 2001 From: David Briemann Date: Mon, 6 Jul 2026 08:15:19 +0000 Subject: [PATCH 156/707] 8387019: PPC64: Remove postalloc_expand from cmovI/cmovL bso_reg_conLvalue0 nodes Reviewed-by: mdoerr, rrich --- src/hotspot/cpu/ppc/ppc.ad | 181 ++++++++----------------------------- 1 file changed, 38 insertions(+), 143 deletions(-) diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 896128f99cc..d3e08a21640 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -3078,13 +3078,6 @@ encode %{ __ bind(done); %} - enc_class enc_cmove_bso_reg(iRegLdst dst, flagsRegSrc crx, regD src) %{ - Label done; - __ bso($crx$$CondRegister, done); - __ mffprd($dst$$Register, $src$$FloatRegister); - __ bind(done); - %} - enc_class enc_bc(flagsRegSrc crx, cmpOp cmp, Label lbl) %{ Label d; // dummy __ bind(d); @@ -9945,19 +9938,6 @@ instruct convL2I_reg(iRegIdst dst, iRegLsrc src) %{ ins_pipe(pipe_class_default); %} -instruct convD2IRaw_regD(regD dst, regD src) %{ - // no match-rule, false predicate - effect(DEF dst, USE src); - predicate(false); - - format %{ "FCTIWZ $dst, $src \t// convD2I, $src != NaN" %} - size(4); - ins_encode %{ - __ fctiwz($dst$$FloatRegister, $src$$FloatRegister); - %} - ins_pipe(pipe_class_default); -%} - instruct cmovI_bso_stackSlotL(iRegIdst dst, flagsRegSrc crx, stackSlotL src) %{ // no match-rule, false predicate effect(DEF dst, USE crx, USE src); @@ -9969,73 +9949,36 @@ instruct cmovI_bso_stackSlotL(iRegIdst dst, flagsRegSrc crx, stackSlotL src) %{ ins_pipe(pipe_class_default); %} -instruct cmovI_bso_reg(iRegIdst dst, flagsRegSrc crx, regD src) %{ +instruct cmovI_bso_reg_con0(iRegIdst dst, flagsRegSrc crx, regD src) %{ // no match-rule, false predicate effect(DEF dst, USE crx, USE src); predicate(false); - format %{ "CMOVI $crx, $dst, $src" %} - size(8); - ins_encode( enc_cmove_bso_reg(dst, crx, src) ); + format %{ "CMOVI $dst, $crx, $src, 0 \t// set to 0 if unordered" %} + size(12); + ins_encode %{ + Label done; + __ li($dst$$Register, 0); + __ bso($crx$$CondRegister, done); + __ mffprd($dst$$Register, $src$$FloatRegister); + __ bind(done); + %} ins_pipe(pipe_class_default); %} - -instruct cmovI_bso_reg_conLvalue0_Ex(iRegIdst dst, flagsRegSrc crx, regD src) %{ +instruct convD2IRaw_regD(regD dst, regD src) %{ // no match-rule, false predicate - effect(DEF dst, USE crx, USE src); + effect(DEF dst, USE src); predicate(false); - format %{ "CMOVI $dst, $crx, $src \t// postalloc expanded" %} - postalloc_expand %{ - // - // replaces - // - // region dst crx src - // \ | | / - // dst=cmovI_bso_reg_conLvalue0 - // - // with - // - // region dst - // \ / - // dst=loadConI16(0) - // | - // ^ region dst crx src - // | \ | | / - // dst=cmovI_bso_reg - // - - // Create new nodes. - MachNode *m1 = new loadConI16Node(); - MachNode *m2 = new cmovI_bso_regNode(); - - // inputs for new nodes - m1->add_req(n_region); - m2->add_req(n_region, n_crx, n_src); - - // precedences for new nodes - m2->add_prec(m1); - - // operands for new nodes - m1->_opnds[0] = op_dst; - m1->_opnds[1] = new immI16Oper(0); - - m2->_opnds[0] = op_dst; - m2->_opnds[1] = op_crx; - m2->_opnds[2] = op_src; - - // registers for new nodes - ra_->set_pair(m1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // dst - ra_->set_pair(m2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // dst - - // Insert new nodes. - nodes->push(m1); - nodes->push(m2); + format %{ "FCTIWZ $dst, $src \t// convD2I, $src != NaN" %} + size(4); + ins_encode %{ + __ fctiwz($dst$$FloatRegister, $src$$FloatRegister); %} + ins_pipe(pipe_class_default); %} - // Double to Int conversion, NaN is mapped to 0. Special version for Power8. instruct convD2I_reg_mffprd_ExEx(iRegIdst dst, regD src) %{ match(Set dst (ConvD2I src)); @@ -10046,7 +9989,7 @@ instruct convD2I_reg_mffprd_ExEx(iRegIdst dst, regD src) %{ flagsReg crx; cmpDUnordered_reg_reg(crx, src, src); // Check whether src is NaN. convD2IRaw_regD(tmpD, src); // Convert float to int (speculated). - cmovI_bso_reg_conLvalue0_Ex(dst, crx, tmpD); // Cmove based on NaN check. + cmovI_bso_reg_con0(dst, crx, tmpD); // Cmove based on NaN check. %} %} @@ -10074,7 +10017,7 @@ instruct convF2I_regF_mffprd_ExEx(iRegIdst dst, regF src) %{ flagsReg crx; cmpFUnordered_reg_reg(crx, src, src); // Check whether src is NaN. convF2IRaw_regF(tmpF, src); // Convert float to int (speculated). - cmovI_bso_reg_conLvalue0_Ex(dst, crx, tmpF); // Cmove based on NaN check. + cmovI_bso_reg_con0(dst, crx, tmpF); // Cmove based on NaN check. %} %} @@ -10116,19 +10059,6 @@ instruct zeroExtendL_regL(iRegLdst dst, iRegLsrc src, immL_32bits mask) %{ ins_pipe(pipe_class_default); %} -instruct convF2LRaw_regF(regF dst, regF src) %{ - // no match-rule, false predicate - effect(DEF dst, USE src); - predicate(false); - - format %{ "FCTIDZ $dst, $src \t// convF2L, $src != NaN" %} - size(4); - ins_encode %{ - __ fctidz($dst$$FloatRegister, $src$$FloatRegister); - %} - ins_pipe(pipe_class_default); -%} - instruct cmovL_bso_stackSlotL(iRegLdst dst, flagsRegSrc crx, stackSlotL src) %{ // no match-rule, false predicate effect(DEF dst, USE crx, USE src); @@ -10140,70 +10070,36 @@ instruct cmovL_bso_stackSlotL(iRegLdst dst, flagsRegSrc crx, stackSlotL src) %{ ins_pipe(pipe_class_default); %} -instruct cmovL_bso_reg(iRegLdst dst, flagsRegSrc crx, regD src) %{ +instruct cmovL_bso_reg_con0(iRegLdst dst, flagsRegSrc crx, regD src) %{ // no match-rule, false predicate effect(DEF dst, USE crx, USE src); predicate(false); - format %{ "CMOVL $crx, $dst, $src" %} - size(8); - ins_encode( enc_cmove_bso_reg(dst, crx, src) ); + format %{ "CMOVL $dst, $crx, $src, 0 \t// set to 0 if unordered" %} + size(12); + ins_encode %{ + Label done; + __ li($dst$$Register, 0); + __ bso($crx$$CondRegister, done); + __ mffprd($dst$$Register, $src$$FloatRegister); + __ bind(done); + %} ins_pipe(pipe_class_default); %} - -instruct cmovL_bso_reg_conLvalue0_Ex(iRegLdst dst, flagsRegSrc crx, regD src) %{ +instruct convF2LRaw_regF(regF dst, regF src) %{ // no match-rule, false predicate - effect(DEF dst, USE crx, USE src); + effect(DEF dst, USE src); predicate(false); - format %{ "CMOVL $dst, $crx, $src \t// postalloc expanded" %} - postalloc_expand %{ - // - // replaces - // - // region dst crx src - // \ | | / - // dst=cmovL_bso_reg_conLvalue0 - // - // with - // - // region dst - // \ / - // dst=loadConL16(0) - // | - // ^ region dst crx src - // | \ | | / - // dst=cmovL_bso_reg - // - - // Create new nodes. - MachNode *m1 = new loadConL16Node(); - MachNode *m2 = new cmovL_bso_regNode(); - - // inputs for new nodes - m1->add_req(n_region); - m2->add_req(n_region, n_crx, n_src); - m2->add_prec(m1); - - // operands for new nodes - m1->_opnds[0] = op_dst; - m1->_opnds[1] = new immL16Oper(0); - m2->_opnds[0] = op_dst; - m2->_opnds[1] = op_crx; - m2->_opnds[2] = op_src; - - // registers for new nodes - ra_->set_pair(m1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // dst - ra_->set_pair(m2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // dst - - // Insert new nodes. - nodes->push(m1); - nodes->push(m2); + format %{ "FCTIDZ $dst, $src \t// convF2L, $src != NaN" %} + size(4); + ins_encode %{ + __ fctidz($dst$$FloatRegister, $src$$FloatRegister); %} + ins_pipe(pipe_class_default); %} - // Float to Long conversion, NaN is mapped to 0. Special version for Power8. instruct convF2L_reg_mffprd_ExEx(iRegLdst dst, regF src) %{ match(Set dst (ConvF2L src)); @@ -10214,7 +10110,7 @@ instruct convF2L_reg_mffprd_ExEx(iRegLdst dst, regF src) %{ flagsReg crx; cmpFUnordered_reg_reg(crx, src, src); // Check whether src is NaN. convF2LRaw_regF(tmpF, src); // Convert float to long (speculated). - cmovL_bso_reg_conLvalue0_Ex(dst, crx, tmpF); // Cmove based on NaN check. + cmovL_bso_reg_con0(dst, crx, tmpF); // Cmove based on NaN check. %} %} @@ -10231,7 +10127,6 @@ instruct convD2LRaw_regD(regD dst, regD src) %{ ins_pipe(pipe_class_default); %} - // Double to Long conversion, NaN is mapped to 0. Special version for Power8. instruct convD2L_reg_mffprd_ExEx(iRegLdst dst, regD src) %{ match(Set dst (ConvD2L src)); @@ -10242,7 +10137,7 @@ instruct convD2L_reg_mffprd_ExEx(iRegLdst dst, regD src) %{ flagsReg crx; cmpDUnordered_reg_reg(crx, src, src); // Check whether src is NaN. convD2LRaw_regD(tmpD, src); // Convert float to long (speculated). - cmovL_bso_reg_conLvalue0_Ex(dst, crx, tmpD); // Cmove based on NaN check. + cmovL_bso_reg_con0(dst, crx, tmpD); // Cmove based on NaN check. %} %} From 6383ad150cf024ed0492526ed5dd042856b9cfee Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Mon, 6 Jul 2026 08:31:54 +0000 Subject: [PATCH 157/707] 8387581: Serial: Clean up startup allocation locking Reviewed-by: tschatzl, aboldtch --- src/hotspot/share/gc/serial/serialHeap.cpp | 27 +++++++++++----------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/hotspot/share/gc/serial/serialHeap.cpp b/src/hotspot/share/gc/serial/serialHeap.cpp index 5d068ff67e0..3de562e886d 100644 --- a/src/hotspot/share/gc/serial/serialHeap.cpp +++ b/src/hotspot/share/gc/serial/serialHeap.cpp @@ -306,11 +306,25 @@ HeapWord* SerialHeap::mem_allocate_work(size_t size, bool is_tlab) { for (uint try_count = 1; /* break */; try_count++) { { + // This lock is needed to sync with the VM-init expansion below. ConditionalMutexLocker locker(Heap_lock, !is_init_completed()); result = mem_allocate_cas_noexpand(size, is_tlab); if (result != nullptr) { break; } + + // Ensure that is_init_completed() does not transition while expanding the heap. + ConditionalMutexLocker ml_init(InitCompleted_lock, !is_init_completed(), Mutex::_no_safepoint_check_flag); + if (!is_init_completed()) { + // Rechecked !is_init_completed() implies we have mutual exclusion via + // `Heap_lock` and `InitCompleted_lock` + result = expand_heap_and_allocate(size, is_tlab); + // Return the result if it's tlab-allocation. If the result is null, + // callers will retry non-tlab allocation. + if (result != nullptr || is_tlab) { + return result; + } + } } uint gc_count_before; // Read inside the Heap_lock locked region. { @@ -323,19 +337,6 @@ HeapWord* SerialHeap::mem_allocate_work(size_t size, bool is_tlab) { break; } - if (!is_init_completed()) { - // Double checked locking, this ensure that is_init_completed() does not - // transition while expanding the heap. - MonitorLocker ml(InitCompleted_lock, Monitor::_no_safepoint_check_flag); - if (!is_init_completed()) { - // Can't do GC; try heap expansion to satisfy the request. - result = expand_heap_and_allocate(size, is_tlab); - if (result != nullptr) { - return result; - } - } - } - gc_count_before = total_collections(); } From 7ac72d18c2ad9839c4ebcda346cb604135aece49 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 6 Jul 2026 08:58:39 +0000 Subject: [PATCH 158/707] 8225186: G1: Compiler code cache requested GC deadlocks while WhiteBox has control Reviewed-by: ayang, iwalulya --- src/hotspot/share/code/codeCache.cpp | 27 ++- src/hotspot/share/code/codeCache.hpp | 10 +- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 24 +- src/hotspot/share/gc/g1/g1Policy.cpp | 6 +- src/hotspot/share/gc/g1/g1VMOperations.cpp | 30 ++- src/hotspot/share/gc/g1/g1VMOperations.hpp | 4 +- .../gc/shared/concurrentGCBreakpoints.cpp | 4 +- .../jtreg/gc/g1/TestCodeCacheWhiteBox.java | 216 ++++++++++++++++++ 8 files changed, 292 insertions(+), 29 deletions(-) create mode 100644 test/hotspot/jtreg/gc/g1/TestCodeCacheWhiteBox.java diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index f1ca317d36f..94cf8ebdec1 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -38,6 +38,7 @@ #include "gc/shared/barrierSetNMethod.hpp" #include "gc/shared/classUnloadingContext.hpp" #include "gc/shared/collectedHeap.hpp" +#include "gc/shared/gcCause.hpp" #include "jfr/jfrEvents.hpp" #include "jvm_io.h" #include "logging/log.hpp" @@ -814,7 +815,8 @@ void CodeCache::update_cold_gc_count() { size_t used = max - free; double gc_interval = time - last_time; - _unloading_threshold_gc_requested = false; + AtomicAccess::store(&_unloading_threshold_gc_state, UnloadingRequestState::Idle); + _last_unloading_time = time; _last_unloading_used = used; @@ -889,7 +891,7 @@ void CodeCache::gc_on_allocation() { double free_ratio = double(free) / double(max); if (free_ratio <= StartAggressiveSweepingAt / 100.0) { // In case the GC is concurrent, we make sure only one thread requests the GC. - if (AtomicAccess::cmpxchg(&_unloading_threshold_gc_requested, false, true) == false) { + if (AtomicAccess::cmpxchg(&_unloading_threshold_gc_state, UnloadingRequestState::Idle, UnloadingRequestState::Active) == UnloadingRequestState::Idle) { log_info(codecache)("Triggering aggressive GC due to having only %.3f%% free memory", free_ratio * 100.0); Universe::heap()->collect(GCCause::_codecache_GC_aggressive); } @@ -915,7 +917,7 @@ void CodeCache::gc_on_allocation() { // it is eventually invoked to avoid trouble. if (allocated_since_last_ratio > threshold) { // In case the GC is concurrent, we make sure only one thread requests the GC. - if (AtomicAccess::cmpxchg(&_unloading_threshold_gc_requested, false, true) == false) { + if (AtomicAccess::cmpxchg(&_unloading_threshold_gc_state, UnloadingRequestState::Idle, UnloadingRequestState::Active) == UnloadingRequestState::Idle) { log_info(codecache)("Triggering threshold (%.3f%%) GC due to allocating %.3f%% since last unloading (%.3f%% used -> %.3f%% used)", threshold * 100.0, allocated_since_last_ratio * 100.0, last_used_ratio * 100.0, used_ratio * 100.0); Universe::heap()->collect(GCCause::_codecache_GC_threshold); @@ -935,7 +937,7 @@ uint64_t CodeCache::_cold_gc_count = INT_MAX; double CodeCache::_last_unloading_time = 0.0; size_t CodeCache::_last_unloading_used = 0; -volatile bool CodeCache::_unloading_threshold_gc_requested = false; +volatile CodeCache::UnloadingRequestState CodeCache::_unloading_threshold_gc_state = UnloadingRequestState::Idle; TruncatedSeq CodeCache::_unloading_gc_intervals(10 /* samples */); TruncatedSeq CodeCache::_unloading_allocation_rates(10 /* samples */); @@ -970,6 +972,23 @@ void CodeCache::on_gc_marking_cycle_finish() { update_cold_gc_count(); } +void CodeCache::defer_unloading_gc_request() { + assert_at_safepoint(); + assert(_unloading_threshold_gc_state == UnloadingRequestState::Active, "only defer active requests"); + AtomicAccess::store(&_unloading_threshold_gc_state, UnloadingRequestState::Deferred); +} + +void CodeCache::clear_deferred_unloading_gc_request() { + // Codecache marking may still be active after aborting gc marking, so we can not + // use is_marking_active() to check whether we are in the correct state to clear + // the deferred state. + // Requests are only deferred outside GC marking, and only cleared after + // at the end of whitebox, we can just clear it if it was Deferred. + AtomicAccess::cmpxchg(&_unloading_threshold_gc_state, + UnloadingRequestState::Deferred, + UnloadingRequestState::Idle); +} + void CodeCache::arm_all_nmethods() { BarrierSet::barrier_set()->barrier_set_nmethod()->arm_all_nmethods(); } diff --git a/src/hotspot/share/code/codeCache.hpp b/src/hotspot/share/code/codeCache.hpp index 3b8aa5b2e58..bef114e5e19 100644 --- a/src/hotspot/share/code/codeCache.hpp +++ b/src/hotspot/share/code/codeCache.hpp @@ -107,7 +107,12 @@ class CodeCache : AllStatic { static double _last_unloading_time; static TruncatedSeq _unloading_gc_intervals; static TruncatedSeq _unloading_allocation_rates; - static volatile bool _unloading_threshold_gc_requested; + enum UnloadingRequestState : uint { + Idle, + Active, + Deferred + }; + static volatile UnloadingRequestState _unloading_threshold_gc_state; static ExceptionCache* volatile _exception_cache_purge_list; @@ -198,6 +203,9 @@ class CodeCache : AllStatic { static uint64_t previous_completed_gc_marking_cycle(); static void on_gc_marking_cycle_start(); static void on_gc_marking_cycle_finish(); + + static void defer_unloading_gc_request(); + static void clear_deferred_unloading_gc_request(); // Arm nmethods so that special actions are taken (nmethod_entry_barrier) for // on-stack nmethods. It's used in two places: // 1. Used before the start of concurrent marking so that oops inside diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index eaa6afb5efa..9dfdb376905 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -1932,7 +1932,7 @@ static bool should_retry_vm_op(GCCause::Cause cause, // GC, so try again. LOG_COLLECT_CONCURRENTLY(cause, "retry after in-progress"); return true; - } else if (op->whitebox_attached()) { + } else if (op->whitebox_controlled()) { // If WhiteBox wants control, wait for notification of a state // change in the controller, then try again. Don't wait for // release of control, since collections may complete while in @@ -2000,7 +2000,7 @@ bool G1CollectedHeap::try_collect_concurrently(size_t allocation_word_size, } // When _wb_breakpoint there can't be another cycle or deferred. assert(!op.cycle_already_in_progress(), "invariant"); - assert(!op.whitebox_attached(), "invariant"); + assert(!op.whitebox_controlled(), "invariant"); // Concurrent cycle attempt might have been cancelled by some other // collection, so retry. Unlike other cases below, we want to retry // even if cancelled by a STW full collection, because we really want @@ -2025,15 +2025,21 @@ bool G1CollectedHeap::try_collect_concurrently(size_t allocation_word_size, // Cases (2) and (3) are detected together by a change to // _old_marking_cycles_started. // - // Compared to other "automatic" GCs (see below), we do not consider being - // in whitebox as sufficient too because we might be anywhere within that - // cycle and we need to make progress. + // Compared to other "automatic" GCs (see below), being in WhiteBox is not + // addressed here because we need to handle it specially. if (op.mark_in_progress() || (old_marking_started_before != old_marking_started_after)) { LOG_COLLECT_CONCURRENTLY_COMPLETE(cause, true); return true; } + if (op.whitebox_controlled()) { + LOG_COLLECT_CONCURRENTLY(cause, "Suppressed CodeCache GC because of WhiteBox in control."); + // The caller in this case does not check the return value, so it does not + // really matter what we return. However we did not finish the request. + return false; + } + if (wait_full_mark_finished(cause, old_marking_started_before, old_marking_started_after, @@ -2041,7 +2047,11 @@ bool G1CollectedHeap::try_collect_concurrently(size_t allocation_word_size, return true; } - if (should_retry_vm_op(cause, &op)) { + if (op.cycle_already_in_progress()) { + // If VMOp failed because a cycle was already in progress, it + // is now complete (we just waited). But it didn't finish this + // request, so try again. + LOG_COLLECT_CONCURRENTLY(cause, "retry after in-progress"); continue; } } else if (!GCCause::is_user_requested_gc(cause)) { @@ -2062,7 +2072,7 @@ bool G1CollectedHeap::try_collect_concurrently(size_t allocation_word_size, // _old_marking_cycles_started. if (op.gc_succeeded() || op.cycle_already_in_progress() || - op.whitebox_attached() || + op.whitebox_controlled() || (old_marking_started_before != old_marking_started_after)) { LOG_COLLECT_CONCURRENTLY_COMPLETE(cause, true); return true; diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index e2c01f9a13e..2414fdd7840 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -1253,9 +1253,9 @@ void G1Policy::update_survivors_policy() { } bool G1Policy::force_concurrent_start_if_outside_cycle(GCCause::Cause gc_cause) { - // We actually check whether we are marking here and not if we are in a - // reclamation phase. This means that we will schedule a concurrent mark - // even while we are still in the process of reclaiming memory. + // Check whether a concurrent cycle is active, do not include the + // reclamation/mixed phase. This means that we can schedule a concurrent cycle + // even while in the mixed phase. bool during_cycle = collector_state()->is_in_concurrent_cycle(); if (!during_cycle) { log_debug(gc, ergo)("Request concurrent cycle initiation (requested by GC cause). " diff --git a/src/hotspot/share/gc/g1/g1VMOperations.cpp b/src/hotspot/share/gc/g1/g1VMOperations.cpp index 373ec9660da..577f3b5491d 100644 --- a/src/hotspot/share/gc/g1/g1VMOperations.cpp +++ b/src/hotspot/share/gc/g1/g1VMOperations.cpp @@ -22,6 +22,7 @@ * */ +#include "code/codeCache.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1ConcurrentMarkThread.inline.hpp" @@ -66,7 +67,7 @@ VM_G1TryInitiateConcMark::VM_G1TryInitiateConcMark(size_t allocation_word_size, _transient_failure(false), _mark_in_progress(false), _cycle_already_in_progress(false), - _whitebox_attached(false), + _whitebox_controlled(false), _gc_succeeded(false) {} @@ -88,19 +89,26 @@ void VM_G1TryInitiateConcMark::doit() { G1CollectorState* state = g1h->collector_state(); _mark_in_progress = state->is_in_marking(); _cycle_already_in_progress = state->is_in_concurrent_cycle(); - - if (!g1h->policy()->force_concurrent_start_if_outside_cycle(_gc_cause)) { + _whitebox_controlled = (_gc_cause != GCCause::_wb_breakpoint) && ConcurrentGCBreakpoints::is_controlled(); + + // Notify the code cache that we deferred clearing the unloading GC request if we are WhiteBox controlled + // and we are going to suppress it. If marking is active, we do not need to suppress because that will satisfy the + // request already. + // This needs to be atomic wrt. to all code-cache allocation threads to allow setting the request + // after WhiteBox releases control again. + bool defer_codecache_request = whitebox_controlled() && + GCCause::is_codecache_requested_gc(_gc_cause) && + !mark_in_progress(); + if (defer_codecache_request) { + CodeCache::defer_unloading_gc_request(); + return; + } else if (!g1h->policy()->force_concurrent_start_if_outside_cycle(_gc_cause)) { // Failure to force the next GC pause to be a concurrent start indicates // there is already a concurrent marking cycle in progress. Flags to indicate // that were already set, so return immediately. - } else if ((_gc_cause != GCCause::_wb_breakpoint) && - ConcurrentGCBreakpoints::is_controlled()) { - // WhiteBox wants to be in control of concurrent cycles, so don't try to - // start one. This check is after the force_concurrent_start_xxx so that a - // request will be remembered for a later partial collection, even though - // we've rejected this request. - _whitebox_attached = true; - } else { + return; + } else if (!whitebox_controlled()) { + // Only run a concurrent marking if not controlled by WhiteBox. g1h->do_collection_pause_at_safepoint(_word_size); _gc_succeeded = true; } diff --git a/src/hotspot/share/gc/g1/g1VMOperations.hpp b/src/hotspot/share/gc/g1/g1VMOperations.hpp index 7d56ea1916f..0c12e75eef0 100644 --- a/src/hotspot/share/gc/g1/g1VMOperations.hpp +++ b/src/hotspot/share/gc/g1/g1VMOperations.hpp @@ -48,7 +48,7 @@ class VM_G1TryInitiateConcMark : public VM_GC_Collect_Operation { bool _transient_failure; bool _mark_in_progress; bool _cycle_already_in_progress; - bool _whitebox_attached; + bool _whitebox_controlled; // The concurrent start pause may be cancelled for some reasons. Keep track of // this. bool _gc_succeeded; @@ -63,7 +63,7 @@ class VM_G1TryInitiateConcMark : public VM_GC_Collect_Operation { bool transient_failure() const { return _transient_failure; } bool mark_in_progress() const { return _mark_in_progress; } bool cycle_already_in_progress() const { return _cycle_already_in_progress; } - bool whitebox_attached() const { return _whitebox_attached; } + bool whitebox_controlled() const { return _whitebox_controlled; } bool gc_succeeded() const { return _gc_succeeded && VM_GC_Operation::gc_succeeded(); } }; diff --git a/src/hotspot/share/gc/shared/concurrentGCBreakpoints.cpp b/src/hotspot/share/gc/shared/concurrentGCBreakpoints.cpp index 3a974952fea..b0a784e9282 100644 --- a/src/hotspot/share/gc/shared/concurrentGCBreakpoints.cpp +++ b/src/hotspot/share/gc/shared/concurrentGCBreakpoints.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 @@ -22,6 +22,7 @@ * */ +#include "code/codeCache.hpp" #include "gc/shared/collectedHeap.hpp" #include "gc/shared/concurrentGCBreakpoints.hpp" #include "logging/log.hpp" @@ -89,6 +90,7 @@ void ConcurrentGCBreakpoints::release_control() { MonitorLocker ml(monitor()); log_trace(gc, breakpoint)("release_control"); reset_request_state(); + CodeCache::clear_deferred_unloading_gc_request(); ml.notify_all(); } diff --git a/test/hotspot/jtreg/gc/g1/TestCodeCacheWhiteBox.java b/test/hotspot/jtreg/gc/g1/TestCodeCacheWhiteBox.java new file mode 100644 index 00000000000..c47dafc3203 --- /dev/null +++ b/test/hotspot/jtreg/gc/g1/TestCodeCacheWhiteBox.java @@ -0,0 +1,216 @@ +/* + * 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 gc.g1; + +/* + * @test TestCodeCacheWhiteBox.java + * @bug 8225186 + * @summary Test to make sure that code cache unloading does not make the VM hang when receiving + * a request while WhiteBox is holding control. + * We do that by triggering a code cache gc request (by triggering compilations) during a + * synchronous compilation while whitebox is holding control, and additionally verify that + * after the concurrent cycle additional code cache gc requests start more concurrent cycles. + * @requires vm.gc.G1 + * @requires vm.flagless + * @library /test/lib /testlibrary / + * @modules java.base/jdk.internal.misc + * java.management + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xmx20M -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. gc.g1.TestCodeCacheWhiteBox + */ + + +import java.lang.reflect.Field; + +import java.net.URL; +import java.net.URLClassLoader; + +import jdk.test.lib.Asserts; +import jdk.test.lib.Platform; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.whitebox.WhiteBox; + +public class TestCodeCacheWhiteBox { + public static final String AFTER_FIRST_CYCLE_MARKER = "Marker for this test"; + + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + private static OutputAnalyzer runTest(String concPhase) throws Exception { + OutputAnalyzer output = ProcessTools.executeLimitedTestJava("-XX:+UseG1GC", + "-Xmx20M", + "-XX:+UnlockDiagnosticVMOptions", + "-Xbootclasspath/a:.", + "-Xbatch", // Needed to make compilation synchronous + "-Xlog:gc=trace,codecache", + "-XX:+WhiteBoxAPI", + "-XX:ReservedCodeCacheSize=" + (Platform.is32bit() ? "4M" : "8M"), + "-XX:StartAggressiveSweepingAt=50", + "-XX:CompileCommand=compileonly,gc.g1.SomeClass::*", + "-XX:CompileCommand=compileonly,gc.g1.Foo*::*", + TestCodeCacheWhiteBoxRunner.class.getName(), + concPhase); + return output; + } + + private static void runAndCheckTest(String test) throws Exception { + OutputAnalyzer output; + + output = runTest(test); + output.shouldHaveExitValue(0); + output.shouldNotContain("ERROR"); + System.out.println(output.getStdout()); + + String[] parts = output.getStdout().split(AFTER_FIRST_CYCLE_MARKER); + + // Either "Threshold" or "Aggressive" CodeCache GC are fine for the test. + final String codecacheGCStart = "Pause Young (Concurrent Start) (CodeCache GC "; + + boolean success = parts.length == 2 && parts[1].indexOf(codecacheGCStart) != -1; + Asserts.assertTrue(success, "Could not find a CodeCache GC Threshold GC after finishing the concurrent cycle"); + } + + public static void main(String[] args) throws Exception { + runAndCheckTest(WB.BEFORE_MARKING_COMPLETED); // This one should always complete. Just for sanity checking. + runAndCheckTest(WB.G1_BEFORE_REBUILD_COMPLETED); + runAndCheckTest(WB.G1_BEFORE_CLEANUP_COMPLETED); + } +} + +class TestCodeCacheWhiteBoxRunner { + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + private static void refClass(Class clazz) throws Exception { + Field name = clazz.getDeclaredField("NAME"); + name.setAccessible(true); + name.get(null); + } + + private static class MyClassLoader extends URLClassLoader { + public MyClassLoader(URL url) { + super(new URL[]{url}, null); + } + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + try { + return super.loadClass(name, resolve); + } catch (ClassNotFoundException e) { + return Class.forName(name, resolve, TestCodeCacheWhiteBoxRunner.class.getClassLoader()); + } + } + } + + private static void triggerCodeCacheGC() { + URL url = TestCodeCacheWhiteBoxRunner.class.getProtectionDomain().getCodeSource().getLocation(); + try { + int i = 0; + do { + ClassLoader cl = new MyClassLoader(url); + refClass(cl.loadClass("gc.g1.SomeClass")); + + if (i % 20 == 0) { + System.out.println("Compiled " + i + " classes"); + } + i++; + } while (i < 200); + System.out.println("Compilation done, compiled " + i + " classes"); + } catch (Throwable t) { + System.out.println("ERROR: threw exception " + t); + } + } + + public static void main(String[] args) throws Exception { + System.out.println("Running to breakpoint: " + args[0]); + try { + WB.concurrentGCAcquireControl(); + WB.concurrentGCRunTo(args[0]); + + System.out.println("Try to trigger code cache GC"); + + Thread toRun = new Thread(() -> + { + System.out.println("Thread is running"); + triggerCodeCacheGC(); + System.out.println("Thread completed"); + }); + toRun.setDaemon(true); // non-daemon thread could prevent VM shutdown after the main thread times out + toRun.start(); + toRun.join(60_000); + + if (toRun.isAlive()) { + toRun.interrupt(); + throw new RuntimeException("ERROR: thread took too long, deadlocked?"); + } + + WB.concurrentGCRunToIdle(); + } catch (InterruptedException e) { + System.out.println("ERROR: starting helper thread"); + throw e; + } finally { + // Make sure that the marker we use to find the expected log message is printed + // before we release whitebox control, i.e. before the expected garbage collection + // can start. + System.out.println(TestCodeCacheWhiteBox.AFTER_FIRST_CYCLE_MARKER); + WB.concurrentGCReleaseControl(); + } + Thread.sleep(1000); + triggerCodeCacheGC(); + } +} + +abstract class Foo { + public abstract int foo(); +} + +class Foo1 extends Foo { + private int a; + public int foo() { return a; } +} + +class Foo2 extends Foo { + private int a; + public int foo() { return a; } +} + +class Foo3 extends Foo { + private int a; + public int foo() { return a; } +} + +class Foo4 extends Foo { + private int a; + public int foo() { return a; } +} + +class SomeClass { + static final String NAME = "name"; + + static { + int res =0; + Foo[] foos = new Foo[] { new Foo1(), new Foo2(), new Foo3(), new Foo4() }; + for (int i = 0; i < 100000; i++) { + res = foos[i % foos.length].foo(); + } + } +} From b9f36a121a16ccea1877d0db8b49998d2df0cb17 Mon Sep 17 00:00:00 2001 From: Tobias Hartmann Date: Mon, 6 Jul 2026 14:09:26 +0000 Subject: [PATCH 159/707] 8387638: Some compiler/vectorization/runner/* tests timed out in Driver mode Reviewed-by: chagedorn, mhaessig --- .../runner/VectorizationTestRunner.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/VectorizationTestRunner.java b/test/hotspot/jtreg/compiler/vectorization/runner/VectorizationTestRunner.java index 9adebf30d31..7e836d78849 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/VectorizationTestRunner.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/VectorizationTestRunner.java @@ -141,17 +141,20 @@ private void runTestOnMethod(Method method) throws InterruptedException { Object expected = null; Object actual = null; - // Temporarily disable the compiler and invoke the method to get reference - // result from the interpreter - Flags.WHITEBOX.setBooleanVMFlag("UseCompiler", false); + // Temporarily make the test method not compilable and invoke it to get the + // reference result from the interpreter. + Flags.WHITEBOX.makeMethodNotCompilable(method, CompLevel.ANY.getValue(), true); + Flags.WHITEBOX.makeMethodNotCompilable(method, CompLevel.ANY.getValue(), false); try { expected = method.invoke(this); + assert(Flags.WHITEBOX.getMethodCompilationLevel(method) == COMP_LEVEL_INTP); } catch (Exception e) { e.printStackTrace(); fail("Exception is thrown in test method invocation (interpreter)."); + } finally { + // Make the test method compilable again + Flags.WHITEBOX.clearMethodState(method); } - assert(Flags.WHITEBOX.getMethodCompilationLevel(method) == COMP_LEVEL_INTP); - Flags.WHITEBOX.setBooleanVMFlag("UseCompiler", true); // Compile the method and invoke it again long enqueueTime = System.currentTimeMillis(); From 01a9a4021848aabc4bb68600a89a380a76a33ff1 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Mon, 6 Jul 2026 15:44:00 +0000 Subject: [PATCH 160/707] 8387721: C2: Print Node barrier data Reviewed-by: amitkumar, chagedorn --- src/hotspot/share/opto/memnode.cpp | 30 ++++++++++++++++++++++++++---- src/hotspot/share/opto/memnode.hpp | 4 ++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 4f68ff281a0..00ccb3e3dbc 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -86,12 +86,13 @@ bool MemNode::check_if_adr_maybe_raw(Node* adr) { #ifndef PRODUCT void MemNode::dump_spec(outputStream *st) const { - if (in(Address) == nullptr) return; // node is dead + if (in(Address) == nullptr) { + // node is dead + return; + } #ifndef ASSERT // fake the missing field - const TypePtr* _adr_type = nullptr; - if (in(Address) != nullptr) - _adr_type = in(Address)->bottom_type()->isa_ptr(); + const TypePtr* _adr_type = in(Address)->bottom_type()->isa_ptr(); #endif dump_adr_type(_adr_type, st); @@ -108,6 +109,7 @@ void MemNode::dump_spec(outputStream *st) const { if (_unsafe_access) { st->print(" unsafe"); } + st->print(" barrier(0x%x)", _barrier_data); } void MemNode::dump_adr_type(const TypePtr* adr_type, outputStream* st) { @@ -4145,6 +4147,26 @@ MemBarNode* LoadStoreNode::trailing_membar() const { uint LoadStoreNode::size_of() const { return sizeof(*this); } +#ifndef PRODUCT +void LoadStoreNode::dump_spec(outputStream* st) const { + if (in(MemNode::Address) == nullptr) { + // node is dead + return; + } +#ifndef ASSERT + // fake the missing field + const TypePtr* _adr_type = in(MemNode::Address)->bottom_type()->isa_ptr(); +#endif + MemNode::dump_adr_type(_adr_type, st); + + Compile* C = Compile::current(); + if (C->alias_type(_adr_type)->is_volatile()) { + st->print(" Volatile!"); + } + st->print(" barrier(0x%x)", _barrier_data); +} +#endif + //============================================================================= //----------------------------------LoadStoreConditionalNode-------------------- LoadStoreConditionalNode::LoadStoreConditionalNode( Node *c, Node *mem, Node *adr, Node *val, Node *ex ) : LoadStoreNode(c, mem, adr, val, nullptr, TypeInt::BOOL, 5) { diff --git a/src/hotspot/share/opto/memnode.hpp b/src/hotspot/share/opto/memnode.hpp index f3f65608972..77252520324 100644 --- a/src/hotspot/share/opto/memnode.hpp +++ b/src/hotspot/share/opto/memnode.hpp @@ -880,6 +880,10 @@ class LoadStoreNode : public Node { uint8_t barrier_data() { return _barrier_data; } void set_barrier_data(uint8_t barrier_data) { _barrier_data = barrier_data; } +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif + private: virtual bool depends_only_on_test_impl() const { return false; } }; From 223b80b6d6566c9a543c8cb1752393addb589923 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Mon, 6 Jul 2026 20:38:46 +0000 Subject: [PATCH 161/707] 8387713: Shenandoah: Rework native card table barrier Reviewed-by: wkemper, kdnilsen --- .../gc/shenandoah/shenandoahBarrierSet.hpp | 2 +- .../shenandoahBarrierSet.inline.hpp | 31 +++++++++++-------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp index 8989c5f2028..51b355e7042 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp @@ -119,7 +119,7 @@ class ShenandoahBarrierSet: public BarrierSet { inline oop oop_xchg(DecoratorSet decorators, T* addr, oop new_value); template - void write_ref_field_post(T* field); + void write_ref_field_post(T* field, oop new_value); void write_ref_array(HeapWord* start, size_t count); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp index e8eb4ee4180..f4b859afc44 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp @@ -208,23 +208,28 @@ inline void ShenandoahBarrierSet::keep_alive_if_weak(DecoratorSet decorators, oo } template -inline void ShenandoahBarrierSet::write_ref_field_post(T* field) { +inline void ShenandoahBarrierSet::write_ref_field_post(T* field, oop new_value) { assert(ShenandoahCardBarrier, "Should have been checked by caller"); - if (_heap->is_in_young(field)) { - // Young field stores do not require card mark. + + if (new_value == nullptr) { + // Null reference stores do not require card mark. return; } - T heap_oop = RawAccess<>::oop_load(field); - if (CompressedOops::is_null(heap_oop)) { - // Null reference store do not require card mark. + + if (_heap->is_in_young(field)) { + // Young field stores do not require card mark. return; } - oop obj = CompressedOops::decode_not_null(heap_oop); - if (!_heap->is_in_young(obj)) { + + if (!_heap->is_in_young(new_value)) { // Not an old->young reference store. return; } + volatile CardTable::CardValue* byte = card_table()->byte_for(field); + if (UseCondCardMark && (*byte == CardTable::dirty_card_val())) { + return; + } *byte = CardTable::dirty_card_val(); } @@ -321,7 +326,7 @@ inline void ShenandoahBarrierSet::AccessBarrier::oop_st oop_store_common(addr, value); if (ShenandoahCardBarrier) { ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set(); - bs->write_ref_field_post(addr); + bs->write_ref_field_post(addr, value); } } @@ -347,7 +352,7 @@ inline oop ShenandoahBarrierSet::AccessBarrier::oop_ato ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set(); oop result = bs->oop_cmpxchg(decorators, addr, compare_value, new_value); if (ShenandoahCardBarrier) { - bs->write_ref_field_post(addr); + bs->write_ref_field_post(addr, new_value); } return result; } @@ -371,7 +376,7 @@ inline oop ShenandoahBarrierSet::AccessBarrier::oop_ato auto addr = AccessInternal::oop_field_addr(base, offset); oop result = bs->oop_cmpxchg(resolved_decorators, addr, compare_value, new_value); if (ShenandoahCardBarrier) { - bs->write_ref_field_post(addr); + bs->write_ref_field_post(addr, new_value); } return result; } @@ -393,7 +398,7 @@ inline oop ShenandoahBarrierSet::AccessBarrier::oop_ato ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set(); oop result = bs->oop_xchg(decorators, addr, new_value); if (ShenandoahCardBarrier) { - bs->write_ref_field_post(addr); + bs->write_ref_field_post(addr, new_value); } return result; } @@ -417,7 +422,7 @@ inline oop ShenandoahBarrierSet::AccessBarrier::oop_ato auto addr = AccessInternal::oop_field_addr(base, offset); oop result = bs->oop_xchg(resolved_decorators, addr, new_value); if (ShenandoahCardBarrier) { - bs->write_ref_field_post(addr); + bs->write_ref_field_post(addr, new_value); } return result; } From 7be9cd96741084c983eacaff555bc554affb913a Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Tue, 7 Jul 2026 07:22:41 +0000 Subject: [PATCH 162/707] 8387403: BUILD_LIBJAVA remove special warning settings Reviewed-by: djelinski, naoto, jlu, lucy --- make/modules/java.base/lib/CoreLibraries.gmk | 2 -- .../unix/native/libjava/TimeZone_md.c | 19 ++++--------------- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/make/modules/java.base/lib/CoreLibraries.gmk b/make/modules/java.base/lib/CoreLibraries.gmk index 8e3891a344c..87a4460a972 100644 --- a/make/modules/java.base/lib/CoreLibraries.gmk +++ b/make/modules/java.base/lib/CoreLibraries.gmk @@ -57,8 +57,6 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJAVA, \ ProcessImpl_md.c_CFLAGS := $(VERSION_CFLAGS), \ java_props_md.c_CFLAGS := \ -DARCHPROPNAME='"$(OPENJDK_TARGET_CPU_OSARCH)"', \ - DISABLED_WARNINGS_gcc_ProcessImpl_md.c := unused-result, \ - DISABLED_WARNINGS_clang_TimeZone_md.c := unused-variable, \ JDK_LIBS := libjvm, \ LIBS_linux := $(LIBDL), \ LIBS_aix := $(LIBDL) $(LIBM), \ diff --git a/src/java.base/unix/native/libjava/TimeZone_md.c b/src/java.base/unix/native/libjava/TimeZone_md.c index bc2ed500d60..709617333d9 100644 --- a/src/java.base/unix/native/libjava/TimeZone_md.c +++ b/src/java.base/unix/native/libjava/TimeZone_md.c @@ -41,22 +41,11 @@ #include "TimeZone_md.h" #include "path_util.h" -#define fileopen fopen -#define filegets fgets -#define fileclose fclose - -#if defined(__linux__) || defined(_ALLBSD_SOURCE) +#if defined(__linux__) || defined(MACOSX) static const char *ZONEINFO_DIR = "/usr/share/zoneinfo"; static const char *DEFAULT_ZONEINFO_FILE = "/etc/localtime"; -#else -static const char *SYS_INIT_FILE = "/etc/default/init"; -static const char *ZONEINFO_DIR = "/usr/share/lib/zoneinfo"; -static const char *DEFAULT_ZONEINFO_FILE = "/usr/share/lib/zoneinfo/localtime"; -#endif /* defined(__linux__) || defined(_ALLBSD_SOURCE) */ - static const char popularZones[][4] = {"UTC", "GMT"}; -#if defined(__linux__) || defined(MACOSX) static char *isFileIdentical(char* buf, size_t size, char *pathname); /* @@ -121,7 +110,7 @@ getPathName(const char *dir, const char *name) { /* * Scans the specified directory and its subdirectories to find a * zoneinfo file which has the same content as /etc/localtime on Linux - * or /usr/share/lib/zoneinfo/localtime on Solaris given in 'buf'. + * given in 'buf'. * If file is symbolic link, then the contents it points to are in buf. * Returns a zone ID if found, otherwise, NULL is returned. */ @@ -475,7 +464,7 @@ mapPlatformToJavaTimezone(const char *java_home_dir, const char *tz) { return javatz; } -#endif /* defined(_AIX) */ +#endif /* defined(__linux__) || defined(MACOSX) || defined(_AIX) */ /* * findJavaTZ_md() maps platform time zone ID to Java time zone ID @@ -542,7 +531,6 @@ char * getGMTOffsetID() { char buf[32]; - char offset[6]; struct tm localtm; time_t clock = time(NULL); if (localtime_r(&clock, &localtm) == NULL) { @@ -576,6 +564,7 @@ getGMTOffsetID() snprintf(buf, sizeof(buf), (const char *)"GMT%c%02.2d:%02.2d", gmt_off < 0 ? '-' : '+' , abs(gmt_off / 60), gmt_off % 60); #else + char offset[6]; if (strftime(offset, 6, "%z", &localtm) != 5) { return strdup("GMT"); } From 5a7905643b7e61bcc4c99341a31a141638160117 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Tue, 7 Jul 2026 07:27:18 +0000 Subject: [PATCH 163/707] 8386592: Gtest os.trim_native_heap_vm sometimes fails in subtest os.trim_native_heap_vm Reviewed-by: clanger, stuefe --- test/hotspot/gtest/runtime/test_os.cpp | 31 +++++++++++++++++--------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/test/hotspot/gtest/runtime/test_os.cpp b/test/hotspot/gtest/runtime/test_os.cpp index cd0f0b9aa53..72e1080f099 100644 --- a/test/hotspot/gtest/runtime/test_os.cpp +++ b/test/hotspot/gtest/runtime/test_os.cpp @@ -1062,17 +1062,26 @@ TEST_VM(os, is_first_C_frame) { TEST_VM(os, trim_native_heap) { EXPECT_TRUE(os::can_trim_native_heap()); os::size_change_t sc; - sc.before = sc.after = (size_t)-1; - EXPECT_TRUE(os::trim_native_heap(&sc)); - tty->print_cr("%zu->%zu", sc.before, sc.after); - // Regardless of whether we freed memory, both before and after - // should be somewhat believable numbers (RSS). - const size_t min = 5 * M; - const size_t max = LP64_ONLY(20 * G) NOT_LP64(3 * G); - ASSERT_LE(min, sc.before); - ASSERT_GT(max, sc.before); - ASSERT_LE(min, sc.after); - ASSERT_GT(max, sc.after); + os::Linux::accurate_meminfo_t info1; + os::Linux::accurate_meminfo_t info2; + bool have_info1 = os::Linux::query_accurate_process_memory_info(&info1); + EXPECT_TRUE(os::trim_native_heap(nullptr)); + bool have_info2 = os::Linux::query_accurate_process_memory_info(&info2); + + if (have_info1 && have_info2) { + sc.before = (info1.rss + info1.swap) * K; + sc.after = (info2.rss + info2.swap) * K; + tty->print_cr("%zu->%zu", sc.before, sc.after); + + // Regardless of whether we freed memory, both before and after + // should be somewhat believable numbers (RSS). + const size_t min = 5 * M; + const size_t max = LP64_ONLY(20 * G) NOT_LP64(3 * G); + ASSERT_LE(min, sc.before); + ASSERT_GT(max, sc.before); + ASSERT_LE(min, sc.after); + ASSERT_GT(max, sc.after); + } // Should also work EXPECT_TRUE(os::trim_native_heap()); } From cb45fb887af0e6116db91bd2a5725efb02ae4780 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Tue, 7 Jul 2026 07:29:39 +0000 Subject: [PATCH 164/707] 8387697: Avoid using os::Linux::query_accurate_process_memory_info in JFR Reviewed-by: mgronlun, stuefe --- src/hotspot/os/linux/os_linux.cpp | 35 +++++-------------------------- 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index aa5a9b9d937..ad1f384fa32 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -2860,39 +2860,14 @@ void os::pd_print_cpu_info(outputStream* st, char* buf, size_t buflen) { #if INCLUDE_JFR -// hwm (high water mark) in K for the VM RSS -static long jfr_rss_hwm_k = -1; - -static void send_resident_set_size_event(ssize_t size, ssize_t peak) { - EventResidentSetSize event; - event.set_size(size * K); - event.set_peak(peak * K); - event.commit(); -} - void os::jfr_report_memory_info() { - os::Linux::accurate_meminfo_t accurate_info; - if (os::Linux::query_accurate_process_memory_info(&accurate_info) && accurate_info.rss != -1) { - // unfortunately the smaps_rollup/accurate_info contains no hwm (high water mark) for RSS - struct rusage ru; - if (getrusage(RUSAGE_SELF, &ru) == 0) { - if (ru.ru_maxrss > jfr_rss_hwm_k) { - jfr_rss_hwm_k = ru.ru_maxrss; - } - } - - // do not allow larger current RSS than hwm - if (accurate_info.rss > jfr_rss_hwm_k) { - jfr_rss_hwm_k = accurate_info.rss; - } - - send_resident_set_size_event(accurate_info.rss, jfr_rss_hwm_k); - return; - } - os::Linux::meminfo_t info; if (os::Linux::query_process_memory_info(&info)) { - send_resident_set_size_event(info.vmrss, info.vmhwm); + // Send the RSS JFR event + EventResidentSetSize event; + event.set_size(info.vmrss * K); + event.set_peak(info.vmhwm * K); + event.commit(); } else { // Log a warning static bool first_warning = true; From 63294ee8ba61fb58e8bf4be1eb0d46631d9ff270 Mon Sep 17 00:00:00 2001 From: Thomas Stuefe Date: Tue, 7 Jul 2026 09:39:32 +0000 Subject: [PATCH 165/707] 8387758: Oop verification wrong in StubGenerator::generate_generic_copy Reviewed-by: shade, galder --- src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp | 3 ++- src/hotspot/cpu/riscv/stubGenerator_riscv.cpp | 3 ++- src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp | 5 +++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index f6ed5c2862a..5dfd41293fd 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -2568,7 +2568,7 @@ class StubGenerator: public StubCodeGenerator { __ movw(scratch_length, length); // length (elements count, 32-bits value) __ tbnz(scratch_length, 31, L_failed); // i.e. sign bit set - __ load_klass(scratch_src_klass, src); + __ load_narrow_klass(scratch_src_klass, src); #ifdef ASSERT // assert(src->klass() != nullptr); { @@ -2583,6 +2583,7 @@ class StubGenerator: public StubCodeGenerator { BLOCK_COMMENT("} assert klasses not null done"); } #endif + __ decode_klass_not_null(scratch_src_klass, scratch_src_klass); // Load layout helper (32-bits) // diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp index 82e5a49faf0..06cf67e2486 100644 --- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp @@ -1889,7 +1889,7 @@ class StubGenerator: public StubCodeGenerator { __ sext(scratch_length, length, 32); // length (elements count, 32-bits value) __ bltz(scratch_length, L_failed); - __ load_klass(scratch_src_klass, src); + __ load_narrow_klass(scratch_src_klass, src); #ifdef ASSERT { BLOCK_COMMENT("assert klasses not null {"); @@ -1903,6 +1903,7 @@ class StubGenerator: public StubCodeGenerator { BLOCK_COMMENT("} assert klasses not null done"); } #endif + __ decode_klass_not_null(scratch_src_klass, t0); // Load layout helper (32-bits) // diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp index a45340b8800..cececa7b3ad 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp @@ -3560,13 +3560,13 @@ address StubGenerator::generate_generic_copy(address byte_copy_entry, address sh __ testl(r11_length, r11_length); __ jccb(Assembler::negative, L_failed_0); - __ load_klass(r10_src_klass, src, rklass_tmp); + __ load_narrow_klass(r10_src_klass, src); #ifdef ASSERT // assert(src->klass() != nullptr); { BLOCK_COMMENT("assert klasses not null {"); Label L1, L2; - __ testptr(r10_src_klass, r10_src_klass); + __ testl(r10_src_klass, r10_src_klass); __ jcc(Assembler::notZero, L2); // it is broken if klass is null __ bind(L1); __ stop("broken null klass"); @@ -3577,6 +3577,7 @@ address StubGenerator::generate_generic_copy(address byte_copy_entry, address sh BLOCK_COMMENT("} assert klasses not null done"); } #endif + __ decode_klass_not_null(r10_src_klass, rklass_tmp); // Load layout helper (32-bits) // From ed81db1fe22c8eed5c46cd03dec4aa802f880fcf Mon Sep 17 00:00:00 2001 From: Dingli Zhang Date: Tue, 7 Jul 2026 09:42:08 +0000 Subject: [PATCH 166/707] 8387747: Enable long vector multiply IR tests for RISC-V Reviewed-by: fyang, gcao --- .../TestVectorMulLongToSignedUnsignedInt.java | 22 ++++++++--------- .../compiler/vectorapi/VectorMultiplyOpt.java | 24 ++++++++++++------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestVectorMulLongToSignedUnsignedInt.java b/test/hotspot/jtreg/compiler/vectorapi/TestVectorMulLongToSignedUnsignedInt.java index e7745b5e88c..cb58cfca652 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/TestVectorMulLongToSignedUnsignedInt.java +++ b/test/hotspot/jtreg/compiler/vectorapi/TestVectorMulLongToSignedUnsignedInt.java @@ -89,7 +89,7 @@ public static void main(String[] args) { @Test @IR(counts = {IRNode.AND_VL, " >0 ", IRNode.MUL_VL, " >0 "}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @IR(failOn = {IRNode.X86_VMULUDQ_REG}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx", "true"}) @@ -119,7 +119,7 @@ public void runNegativeMask() { @Test @IR(counts = {IRNode.AND_VL, " >0 ", IRNode.MUL_VL, " >0 "}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @IR(failOn = {IRNode.X86_VMULUDQ_REG}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx", "true"}) @@ -147,7 +147,7 @@ public void runBit32SetMask() { @Test @IR(counts = {IRNode.AND_VL, " >0 ", IRNode.MUL_VL, " >0 "}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @IR(failOn = {IRNode.X86_VMULUDQ_REG}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx", "true"}) @@ -176,7 +176,7 @@ public void runMinValueMask() { // Case 5: Mask = 0xFFFF_FFFFL (exactly uint max, boundary valid case). @Test @IR(counts = {IRNode.MUL_VL, " >0 "}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @IR(counts = {IRNode.X86_VMULUDQ_REG, " >0 "}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx", "true"}) @@ -205,7 +205,7 @@ public void runUintMaxMask() { // Case 6: Small mask (0xFFFFL), clearly fits in uint. @Test @IR(counts = {IRNode.MUL_VL, " >0 "}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @IR(counts = {IRNode.X86_VMULUDQ_REG, " >0 "}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx", "true"}) @@ -235,7 +235,7 @@ public void runSmallMask() { @Test @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.URSHIFT_VL, " >0 "}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @IR(counts = {IRNode.X86_VMULUDQ_REG, " >0 "}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx", "true"}) @@ -265,7 +265,7 @@ public void runURShift32() { @Test @IR(counts = {IRNode.AND_VL, " >0 ", IRNode.MUL_VL, " >0 "}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @IR(failOn = {IRNode.X86_VMULUDQ_REG}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx", "true"}) @@ -297,7 +297,7 @@ public void runAsymmetricMask() { @IR(counts = {IRNode.URSHIFT_VL, " >0 ", IRNode.AND_VL, " >0 ", IRNode.MUL_VL, " >0 "}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @IR(failOn = {IRNode.X86_VMULUDQ_REG}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx", "true"}) @@ -327,7 +327,7 @@ public void runMixedURShiftAndNegMask() { @Test @IR(counts = {IRNode.AND_VL, " >0 ", IRNode.MUL_VL, " >0 "}, - applyIfCPUFeatureOr = {"avx512f", "true", "sve", "true"}) + applyIfCPUFeatureOr = {"avx512f", "true", "sve", "true", "rvv", "true"}) @IR(failOn = {IRNode.X86_VMULUDQ_REG}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx512f", "true"}) @@ -359,7 +359,7 @@ public void runPredicatedAndMask() { @Test @IR(counts = {IRNode.URSHIFT_VL, " >0 ", IRNode.MUL_VL, " >0 "}, - applyIfCPUFeatureOr = {"avx512f", "true", "sve", "true"}) + applyIfCPUFeatureOr = {"avx512f", "true", "sve", "true", "rvv", "true"}) @IR(failOn = {IRNode.X86_VMULUDQ_REG}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx512f", "true"}) @@ -391,7 +391,7 @@ public void runPredicatedURShift32() { @Test @IR(counts = {IRNode.RSHIFT_VL, " >0 ", IRNode.MUL_VL, " >0 "}, - applyIfCPUFeatureOr = {"avx512f", "true", "sve", "true"}) + applyIfCPUFeatureOr = {"avx512f", "true", "sve", "true", "rvv", "true"}) @IR(failOn = {IRNode.X86_VMULDQ_REG}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx512f", "true"}) diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorMultiplyOpt.java b/test/hotspot/jtreg/compiler/vectorapi/VectorMultiplyOpt.java index 68ac9249ebf..4d8344e729e 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/VectorMultiplyOpt.java +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorMultiplyOpt.java @@ -107,7 +107,8 @@ public static void validate(String msg, long[] actual, Object src1, Object src2, } @Test - @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.AND_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.AND_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) @IR(counts = {"vmuludq", " >0 "}, phase = CompilePhase.FINAL_CODE, applyIfCPUFeature = {"avx", "true"}) @IR(counts = {"vmulL_uint_sve2", " >0 "}, phase = CompilePhase.FINAL_CODE, applyIfCPUFeature = {"sve2", "true"}) @@ -135,7 +136,8 @@ public void test_pattern1_validate() { } @Test - @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.AND_VL, " >0 ", IRNode.URSHIFT_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.AND_VL, " >0 ", IRNode.URSHIFT_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) @IR(counts = {"vmuludq", " >0 "}, phase = CompilePhase.FINAL_CODE, applyIfCPUFeature = {"avx", "true"}) @IR(counts = {"vmulL_uint_sve2", " >0 "}, phase = CompilePhase.FINAL_CODE, applyIfCPUFeature = {"sve2", "true"}) @@ -163,7 +165,8 @@ public void test_pattern2_validate() { } @Test - @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.URSHIFT_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.URSHIFT_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) @IR(counts = {"vmuludq", " >0 "}, phase = CompilePhase.FINAL_CODE, applyIfCPUFeature = {"avx", "true"}) @IR(counts = {"vmulL_uint_sve2", " >0 "}, phase = CompilePhase.FINAL_CODE, applyIfCPUFeature = {"sve2", "true"}) @@ -191,7 +194,8 @@ public void test_pattern3_validate() { } @Test - @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.URSHIFT_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.URSHIFT_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) @IR(counts = {"vmuludq", " >0 "}, applyIfCPUFeature = {"avx", "true"}, phase = CompilePhase.FINAL_CODE) @IR(counts = {"vmulL_uint_sve2", " >0 "}, phase = CompilePhase.FINAL_CODE, applyIfCPUFeature = {"sve2", "true"}) @@ -219,7 +223,8 @@ public void test_pattern4_validate() { } @Test - @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.VECTOR_CAST_I2L, " >0 "}, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.VECTOR_CAST_I2L, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) @IR(counts = {"vmuldq", " >0 "}, applyIfCPUFeature = {"avx", "true"}, phase = CompilePhase.FINAL_CODE) @IR(counts = {"vmulL_int_sve2", " >0 "}, phase = CompilePhase.FINAL_CODE, applyIfCPUFeature = {"sve2", "true"}) @@ -250,7 +255,8 @@ public void test_pattern5_validate() { @Test - @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.RSHIFT_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.RSHIFT_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) @IR(counts = {"vmuldq", " >0 "}, applyIfCPUFeature = {"avx", "true"}, phase = CompilePhase.FINAL_CODE) @IR(counts = {"vmulL_int_sve2", " >0 "}, phase = CompilePhase.FINAL_CODE, applyIfCPUFeature = {"sve2", "true"}) @@ -280,7 +286,8 @@ public void test_pattern6_validate() { // Same-operand multiplication (v * v) where v has zero-extended high bits. // On NEON this should map to the dedicated rule that emits a single xtn. @Test - @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.AND_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.AND_VL, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) @IR(counts = {"vmuludq", " >0 "}, phase = CompilePhase.FINAL_CODE, applyIfCPUFeature = {"avx", "true"}) @IR(counts = {"vmulL_uint_sve2", " >0 "}, phase = CompilePhase.FINAL_CODE, applyIfCPUFeature = {"sve2", "true"}) @@ -309,7 +316,8 @@ public void test_pattern7_validate() { // Same-operand multiplication (v * v) where v has sign-extended high bits. // On NEON this should map to the dedicated rule that emits a single xtn. @Test - @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.VECTOR_CAST_I2L, " >0 "}, applyIfCPUFeature = {"avx", "true"}) + @IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.VECTOR_CAST_I2L, " >0 "}, + applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) @IR(counts = {"vmuldq", " >0 "}, applyIfCPUFeature = {"avx", "true"}, phase = CompilePhase.FINAL_CODE) @IR(counts = {"vmulL_int_sve2", " >0 "}, phase = CompilePhase.FINAL_CODE, applyIfCPUFeature = {"sve2", "true"}) From 432f005b87211b402bb9299fb123379543988168 Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Tue, 7 Jul 2026 12:22:01 +0000 Subject: [PATCH 167/707] 8387757: Man pages still say CompactObjectHeaders is not default Reviewed-by: stuefe, lfoltan, shade, rkennke, dholmes --- src/java.base/share/man/java.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/java.base/share/man/java.md b/src/java.base/share/man/java.md index 30f018314e5..89166ae39e1 100644 --- a/src/java.base/share/man/java.md +++ b/src/java.base/share/man/java.md @@ -1568,14 +1568,14 @@ These `java` options control the runtime behavior of the Java HotSpot VM. This option is similar to `-Xss`. -[`-XX:+UseCompactObjectHeaders`]{#-XX__UseCompactObjectHeaders} -: Enables compact object headers. By default, this option is disabled. - Enabling this option reduces memory footprint in the Java heap by - 4 bytes per object (on average) and often improves performance. - - The feature remains disabled by default while it continues to be evaluated. - In a future release it is expected to be enabled by default, and - eventually will be the only mode of operation. +[`-XX:-UseCompactObjectHeaders`]{#-XX__UseCompactObjectHeaders} +: Disables compact object headers. By default, this option is enabled and + compact object headers are used. Using compact object headers reduces + memory footprint in the Java heap by 4 bytes per object (on average) and + often improves performance. + + This option can be used if performance regressions are suspected. In a future + release compact object headers is expected to become the only mode of operation. [`-XX:-UseCompressedOops`]{#-XX__UseCompressedOops} : Disables the use of compressed pointers. By default, this option is From c8d4c7d1815fe44cecaceeec497ecb758aabebf7 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 7 Jul 2026 12:53:43 +0000 Subject: [PATCH 168/707] 8387760: G1: Let G1ConcurrentMark::_chunks_in_chunk_list use the Atomic API Reviewed-by: shade, aboldtch --- src/hotspot/share/gc/g1/g1ConcurrentMark.cpp | 6 +++--- src/hotspot/share/gc/g1/g1ConcurrentMark.hpp | 6 +++--- src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 6f9e4e2e9cf..233901c30f8 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -299,7 +299,7 @@ void G1CMMarkStack::add_chunk_to_list(Atomic* list, TaskQu void G1CMMarkStack::add_chunk_to_chunk_list(TaskQueueEntryChunk* elem) { MutexLocker x(G1MarkStackChunkList_lock, Mutex::_no_safepoint_check_flag); add_chunk_to_list(&_chunk_list, elem); - _chunks_in_chunk_list++; + _chunks_in_chunk_list.add_then_fetch(1u, memory_order_relaxed); } void G1CMMarkStack::add_chunk_to_free_list(TaskQueueEntryChunk* elem) { @@ -319,7 +319,7 @@ G1CMMarkStack::TaskQueueEntryChunk* G1CMMarkStack::remove_chunk_from_chunk_list( MutexLocker x(G1MarkStackChunkList_lock, Mutex::_no_safepoint_check_flag); TaskQueueEntryChunk* result = remove_chunk_from_list(&_chunk_list); if (result != nullptr) { - _chunks_in_chunk_list--; + _chunks_in_chunk_list.sub_then_fetch(1u, memory_order_relaxed); } return result; } @@ -363,7 +363,7 @@ bool G1CMMarkStack::par_pop_chunk(G1TaskQueueEntry* ptr_arr) { } void G1CMMarkStack::set_empty() { - _chunks_in_chunk_list = 0; + _chunks_in_chunk_list.store_relaxed(0); _chunk_list.store_relaxed(nullptr); _free_list.store_relaxed(nullptr); _chunk_allocator.reset(); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index 73dabc12863..f1f84bf246e 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -213,8 +213,8 @@ class G1CMMarkStack { Atomic _free_list; // Linked list of free chunks that can be allocated by users. char _pad1[DEFAULT_PADDING_SIZE - sizeof(TaskQueueEntryChunk*)]; Atomic _chunk_list; // List of chunks currently containing data. - volatile size_t _chunks_in_chunk_list; - char _pad2[DEFAULT_PADDING_SIZE - sizeof(TaskQueueEntryChunk*) - sizeof(size_t)]; + Atomic _chunks_in_chunk_list; + char _pad2[DEFAULT_PADDING_SIZE - sizeof(TaskQueueEntryChunk*) - sizeof(_chunks_in_chunk_list)]; // Atomically add the given chunk to the list. void add_chunk_to_list(Atomic* list, TaskQueueEntryChunk* elem); @@ -265,7 +265,7 @@ class G1CMMarkStack { // Return the approximate number of oops on this mark stack. Racy due to // unsynchronized access to _chunks_in_chunk_list. - size_t size() const { return _chunks_in_chunk_list * EntriesPerChunk; } + size_t size() const { return _chunks_in_chunk_list.load_relaxed() * EntriesPerChunk; } void set_empty(); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp index ec6a486dc02..76fdcd218ae 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp @@ -93,7 +93,7 @@ inline void G1CMMarkStack::iterate(Fn fn) const { TaskQueueEntryChunk* cur = _chunk_list.load_relaxed(); while (cur != nullptr) { - guarantee(num_chunks <= _chunks_in_chunk_list, "Found %zu oop chunks which is more than there should be", num_chunks); + guarantee(num_chunks <= _chunks_in_chunk_list.load_relaxed(), "Found %zu oop chunks which is more than there should be", num_chunks); for (size_t i = 0; i < EntriesPerChunk; ++i) { if (cur->data[i].is_null()) { From fc73ca7f38be7e82902f162bccc6009779223950 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 7 Jul 2026 12:54:01 +0000 Subject: [PATCH 169/707] 8387751: G1: Remove volatile from G1CollectorState::_initiate_conc_mark_if_possible Reviewed-by: aboldtch, ayang --- src/hotspot/share/gc/g1/g1CollectorState.hpp | 2 +- src/hotspot/share/gc/g1/g1CollectorState.inline.hpp | 2 +- src/hotspot/share/gc/g1/g1Policy.cpp | 1 + src/hotspot/share/gc/g1/g1Policy.hpp | 5 ++--- src/hotspot/share/gc/g1/g1YoungCollector.hpp | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectorState.hpp b/src/hotspot/share/gc/g1/g1CollectorState.hpp index 762ddb1fc8f..002b7894030 100644 --- a/src/hotspot/share/gc/g1/g1CollectorState.hpp +++ b/src/hotspot/share/gc/g1/g1CollectorState.hpp @@ -59,7 +59,7 @@ class G1CollectorState { // has been in progress when the request came in. // // This flag remembers that there is an unfullfilled request. - volatile bool _initiate_conc_mark_if_possible; + bool _initiate_conc_mark_if_possible; public: G1CollectorState() : diff --git a/src/hotspot/share/gc/g1/g1CollectorState.inline.hpp b/src/hotspot/share/gc/g1/g1CollectorState.inline.hpp index b63d683bb63..1a0e91f1adb 100644 --- a/src/hotspot/share/gc/g1/g1CollectorState.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectorState.inline.hpp @@ -42,7 +42,7 @@ inline void G1CollectorState::set_in_full_gc() { inline void G1CollectorState::set_in_concurrent_start_gc() { _phase = Phase::YoungConcurrentStart; - _initiate_conc_mark_if_possible = false; + set_initiate_conc_mark_if_possible(false); } inline void G1CollectorState::set_in_prepare_mixed_gc() { _phase = Phase::YoungPrepareMixed; diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 2414fdd7840..d271a8a610a 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -1253,6 +1253,7 @@ void G1Policy::update_survivors_policy() { } bool G1Policy::force_concurrent_start_if_outside_cycle(GCCause::Cause gc_cause) { + assert_at_safepoint_on_vm_thread(); // Check whether a concurrent cycle is active, do not include the // reclamation/mixed phase. This means that we can schedule a concurrent cycle // even while in the mixed phase. diff --git a/src/hotspot/share/gc/g1/g1Policy.hpp b/src/hotspot/share/gc/g1/g1Policy.hpp index 1fa81fe60b6..e09a76397cc 100644 --- a/src/hotspot/share/gc/g1/g1Policy.hpp +++ b/src/hotspot/share/gc/g1/g1Policy.hpp @@ -335,9 +335,8 @@ class G1Policy: public CHeapObj { public: // This sets the initiate_conc_mark_if_possible() flag to start a - // new cycle, as long as we are not already in one. It's best if it - // is called during a safepoint when the test whether a cycle is in - // progress or not is stable. + // new cycle, as long as we are not already in one. It is called + // at a safepoint. bool force_concurrent_start_if_outside_cycle(GCCause::Cause gc_cause); // Decide whether this garbage collection pause should be a concurrent start diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.hpp b/src/hotspot/share/gc/g1/g1YoungCollector.hpp index 7415bc83827..e9f2477ea76 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.hpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.hpp @@ -144,7 +144,7 @@ class G1YoungCollector { size_t allocation_word_size); void collect(); - G1CollectorState next_state() const { return _next_state; } + const G1CollectorState next_state() const { return _next_state; } bool concurrent_operation_is_full_mark() const { return _concurrent_operation_is_full_mark; } }; From f264341bacd7f8aae41772cc993f9741d68d59a1 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 7 Jul 2026 12:54:23 +0000 Subject: [PATCH 170/707] 8387490: G1: Dynamically creating worker threads exposes memory visibility race Reviewed-by: aboldtch, ayang --- src/hotspot/share/gc/shared/workerThread.cpp | 20 ++++++++++++-------- src/hotspot/share/gc/shared/workerThread.hpp | 8 ++++++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/hotspot/share/gc/shared/workerThread.cpp b/src/hotspot/share/gc/shared/workerThread.cpp index 35749452c85..94d0d904340 100644 --- a/src/hotspot/share/gc/shared/workerThread.cpp +++ b/src/hotspot/share/gc/shared/workerThread.cpp @@ -103,7 +103,7 @@ bool WorkerThreads::allow_inject_creation_failure() const { return false; } - if (_created_workers == 0) { + if (_created_workers.load_relaxed() == 0) { // Never allow creation failures of the first worker, it will cause the VM to exit return false; } @@ -135,18 +135,20 @@ uint WorkerThreads::set_active_workers(uint num_workers) { "Invalid number of active workers %u (should be 1-%u)", num_workers, _max_workers); - while (_created_workers < num_workers) { - WorkerThread* const worker = create_worker(_created_workers); + uint local_created_workers = created_workers(); + while (local_created_workers < num_workers) { + WorkerThread* const worker = create_worker(local_created_workers); if (worker == nullptr) { log_error(gc, task)("Failed to create worker thread"); break; } - _workers[_created_workers] = worker; - _created_workers++; + _workers[local_created_workers] = worker; + local_created_workers++; + _created_workers.release_store(local_created_workers); } - _active_workers = MIN2(_created_workers, num_workers); + _active_workers = MIN2(local_created_workers, num_workers); log_trace(gc, task)("%s: using %d out of %d workers", _name, _active_workers, _max_workers); @@ -154,14 +156,16 @@ uint WorkerThreads::set_active_workers(uint num_workers) { } void WorkerThreads::threads_do(ThreadClosure* tc) const { - for (uint i = 0; i < _created_workers; i++) { + uint local_created_workers = created_workers(); + for (uint i = 0; i < local_created_workers; i++) { tc->do_thread(_workers[i]); } } template void WorkerThreads::threads_do_f(Function function) const { - for (uint i = 0; i < _created_workers; i++) { + uint local_created_workers = created_workers(); + for (uint i = 0; i < local_created_workers; i++) { function(_workers[i]); } } diff --git a/src/hotspot/share/gc/shared/workerThread.hpp b/src/hotspot/share/gc/shared/workerThread.hpp index 003ce8a2959..6ed28e5b9b7 100644 --- a/src/hotspot/share/gc/shared/workerThread.hpp +++ b/src/hotspot/share/gc/shared/workerThread.hpp @@ -88,7 +88,11 @@ class WorkerThreads : public CHeapObj { const char* const _name; WorkerThread** _workers; const uint _max_workers; - uint _created_workers; + // _created_workers publishes the initialized prefix of _workers. + // Writers release-store to it after initializing an entry. Readers + // load-acquire before accessing _workers to not access uninitalized + // data. + Atomic _created_workers; uint _active_workers; WorkerTaskDispatcher _dispatcher; @@ -107,7 +111,7 @@ class WorkerThreads : public CHeapObj { bool allow_inject_creation_failure() const; uint max_workers() const { return _max_workers; } - uint created_workers() const { return _created_workers; } + uint created_workers() const { return _created_workers.load_acquire(); } uint active_workers() const { return _active_workers; } uint set_active_workers(uint num_workers); From 6e5bfcc6450512b4b974ed4afa067547552a0847 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 7 Jul 2026 12:54:47 +0000 Subject: [PATCH 171/707] 8387764: G1: Let G1CollectedHeap::_summary_bytes_used use the Atomic API Reviewed-by: aboldtch, shade --- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 15 +++++++-------- src/hotspot/share/gc/g1/g1CollectedHeap.hpp | 2 +- src/hotspot/share/gc/g1/vmStructs_g1.hpp | 4 ++-- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 9dfdb376905..3c41133e572 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -1761,12 +1761,11 @@ size_t G1CollectedHeap::unused_committed_regions_in_bytes() const { // Computes the sum of the storage used by the various regions. size_t G1CollectedHeap::used() const { - size_t result = _summary_bytes_used + _allocator->used_in_alloc_regions(); - return result; + return used_unlocked() + _allocator->used_in_alloc_regions(); } size_t G1CollectedHeap::used_unlocked() const { - return _summary_bytes_used; + return _summary_bytes_used.load_relaxed(); } class SumUsedClosure: public G1HeapRegionClosure { @@ -3034,18 +3033,18 @@ void G1CollectedHeap::prepare_region_for_full_compaction(G1HeapRegion* hr) { } void G1CollectedHeap::increase_used(size_t bytes) { - _summary_bytes_used += bytes; + _summary_bytes_used.add_then_fetch(bytes, memory_order_relaxed); } void G1CollectedHeap::decrease_used(size_t bytes) { - assert(_summary_bytes_used >= bytes, + assert(used_unlocked() >= bytes, "invariant: _summary_bytes_used: %zu should be >= bytes: %zu", - _summary_bytes_used, bytes); - _summary_bytes_used -= bytes; + used_unlocked(), bytes); + _summary_bytes_used.sub_then_fetch(bytes, memory_order_relaxed); } void G1CollectedHeap::set_used(size_t bytes) { - _summary_bytes_used = bytes; + _summary_bytes_used.store_relaxed(bytes); } class RebuildRegionSetsClosure : public G1HeapRegionClosure { diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 718c230851f..cb466a5e120 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -243,7 +243,7 @@ class G1CollectedHeap : public CollectedHeap { // Outside of GC pauses, the number of bytes used in all regions other // than the current allocation region(s). - volatile size_t _summary_bytes_used; + Atomic _summary_bytes_used; void increase_used(size_t bytes); void decrease_used(size_t bytes); diff --git a/src/hotspot/share/gc/g1/vmStructs_g1.hpp b/src/hotspot/share/gc/g1/vmStructs_g1.hpp index af236ec8581..e0179b69646 100644 --- a/src/hotspot/share/gc/g1/vmStructs_g1.hpp +++ b/src/hotspot/share/gc/g1/vmStructs_g1.hpp @@ -42,7 +42,7 @@ nonstatic_field(G1HeapRegion, _bottom, HeapWord* const) \ nonstatic_field(G1HeapRegion, _top, Atomic) \ nonstatic_field(G1HeapRegion, _end, HeapWord* const) \ - volatile_nonstatic_field(G1HeapRegion, _pinned_object_count, Atomic)\ + nonstatic_field(G1HeapRegion, _pinned_object_count, Atomic) \ \ nonstatic_field(G1HeapRegionType, _tag, G1HeapRegionType::Tag volatile) \ \ @@ -55,7 +55,7 @@ \ nonstatic_field(G1HeapRegionManager, _regions, G1HeapRegionTable) \ \ - volatile_nonstatic_field(G1CollectedHeap, _summary_bytes_used, size_t) \ + nonstatic_field(G1CollectedHeap, _summary_bytes_used, Atomic) \ nonstatic_field(G1CollectedHeap, _hrm, G1HeapRegionManager) \ nonstatic_field(G1CollectedHeap, _monitoring_support, G1MonitoringSupport*) \ nonstatic_field(G1CollectedHeap, _old_set, G1HeapRegionSetBase) \ From 74f9b51f3436018f5f0987cee253d01f2eb27541 Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Tue, 7 Jul 2026 14:52:07 +0000 Subject: [PATCH 172/707] 8381809: Template Framework Library: add Float16Vector type Reviewed-by: epeter, galder --- .../library/CodeGenerationDataNameType.java | 101 +++++++- .../library/Operations.java | 224 +++++++++++------- .../library/PrimitiveType.java | 52 +++- .../library/ShortCarriesFloat16Type.java | 112 +++++++++ .../library/VectorElementType.java | 103 ++++++++ .../library/VectorType.java | 23 +- .../jtreg/compiler/lib/verify/Verify.java | 54 +++++ .../vectorapi/VectorExpressionFuzzer.java | 67 ++++-- .../verify/tests/TestVerifyFloat16.java | 84 ++++++- 9 files changed, 695 insertions(+), 125 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/library/ShortCarriesFloat16Type.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/library/VectorElementType.java diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/CodeGenerationDataNameType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/CodeGenerationDataNameType.java index 33eba66cd8c..5bfa217a1bb 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/CodeGenerationDataNameType.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/CodeGenerationDataNameType.java @@ -33,6 +33,27 @@ * additional functionality for code generation. These types with their extended * functionality can be used with many other code generation facilities in the * library, such as generating random {@code Expression}s. + * + *

    This module distinguishes scalar Java types and + * Vector API lane-element types: + *

      + *
    • Scalar {@code PRIMITIVE_TYPES}/{@code FLOATING_TYPES}/etc. enumerate + * only Java primitive types ({@code byte}, {@code short}, ...). + * These lists are typed as {@code List} and are consumed + * by scalar fuzzers / scalar code generation. {@link Float16Type} (the + * scalar {@code Float16} logical type) is included in + * {@link #SCALAR_NUMERIC_TYPES}.
    • + *
    • Vector-lane lists ({@code VECTOR_ELEMENT_TYPES}, + * {@code FLOATING_VECTOR_ELEMENT_TYPES}, ...) enumerate the lane types + * valid for {@code VectorType.Vector}. These are typed as + * {@code List} and additionally include + * {@link ShortCarriesFloat16Type#SHORT_CARRIES_FLOAT16} since {@code Float16Vector} is a real + * Vector API type whose lanes happen to have no Java primitive + * keyword.
    • + *
    + * Vector generators (e.g. {@code Operations.VECTOR_OPERATIONS}) consume the + * vector-lane lists; scalar generators (e.g. + * {@code Operations.PRIMITIVE_OPERATIONS}) consume the scalar lists. */ public interface CodeGenerationDataNameType extends DataName.Type { @@ -101,9 +122,22 @@ public interface CodeGenerationDataNameType extends DataName.Type { static PrimitiveType booleans() { return PrimitiveType.BOOLEANS; } /** - * The Float16 type. + * The {@code short}-carried {@code Float16} lane-element type used by + * {@code Float16Vector}. This is a {@link VectorElementType}, + * not a Java {@link PrimitiveType}; it appears in + * vector-lane lists but never in the scalar + * {@code PRIMITIVE_TYPES}/{@code FLOATING_TYPES} lists. Its lanes carry the + * raw bits of the {@code Float16} value in a {@code short}, hence the + * explicit {@code shortCarriesFloat16} naming. + * + * @return The {@code Float16Vector} {@link VectorElementType}. + */ + static ShortCarriesFloat16Type shortCarriesFloat16() { return ShortCarriesFloat16Type.SHORT_CARRIES_FLOAT16; } + + /** + * The {@code Float16} scalar (boxed) type. * - * @return The Float16 type. + * @return The scalar {@code Float16} type. */ static CodeGenerationDataNameType float16() { return Float16Type.FLOAT16; } @@ -185,6 +219,61 @@ public interface CodeGenerationDataNameType extends DataName.Type { float16() ); + // -------------------------------------------------------------------- + // Vector API lane-element type lists. + // + // These are typed as List and may include + // ShortCarriesFloat16Type.SHORT_CARRIES_FLOAT16 in addition to the Java + // primitive lane carriers. Vector generators (e.g. Operations.VECTOR_OPS) + // iterate over these lists to enumerate the lane types they support. + // -------------------------------------------------------------------- + + /** + * All Vector API lane-element types: every Java numeric primitive lane + * carrier plus {@link ShortCarriesFloat16Type#SHORT_CARRIES_FLOAT16}. + */ + List VECTOR_ELEMENT_TYPES = List.of( + bytes(), + shorts(), + shortCarriesFloat16(), + ints(), + longs(), + floats(), + doubles() + ); + + /** + * Integral Vector API lane-element types (byte, short, int, long). + */ + List INTEGRAL_VECTOR_ELEMENT_TYPES = List.of( + bytes(), + shorts(), + ints(), + longs() + ); + + /** + * Floating Vector API lane-element types (float16, float, double). + */ + List FLOATING_VECTOR_ELEMENT_TYPES = List.of( + shortCarriesFloat16(), + floats(), + doubles() + ); + + /** + * Vector API lane-element types whose lanes are 32/64 bits and integral + * (int, long). + */ + List INT_LONG_VECTOR_ELEMENT_TYPES = List.of( + ints(), + longs() + ); + + // -------------------------------------------------------------------- + // Concrete VectorType lists (typed as the concrete Vector subclasses). + // -------------------------------------------------------------------- + List VECTOR_BYTE_VECTOR_TYPES = List.of( VectorType.BYTE_64, VectorType.BYTE_128, @@ -199,6 +288,13 @@ public interface CodeGenerationDataNameType extends DataName.Type { VectorType.SHORT_512 ); + List VECTOR_FLOAT16_VECTOR_TYPES = List.of( + VectorType.FLOAT16_64, + VectorType.FLOAT16_128, + VectorType.FLOAT16_256, + VectorType.FLOAT16_512 + ); + List VECTOR_INT_VECTOR_TYPES = List.of( VectorType.INT_64, VectorType.INT_128, @@ -230,6 +326,7 @@ public interface CodeGenerationDataNameType extends DataName.Type { List VECTOR_VECTOR_TYPES = Utils.concat( VECTOR_BYTE_VECTOR_TYPES, VECTOR_SHORT_VECTOR_TYPES, + VECTOR_FLOAT16_VECTOR_TYPES, VECTOR_INT_VECTOR_TYPES, VECTOR_LONG_VECTOR_TYPES, VECTOR_FLOAT_VECTOR_TYPES, diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java index 3dffa096525..e9218101081 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java @@ -36,10 +36,15 @@ import static compiler.lib.template_framework.library.PrimitiveType.DOUBLES; import static compiler.lib.template_framework.library.PrimitiveType.BOOLEANS; import static compiler.lib.template_framework.library.Float16Type.FLOAT16; +import static compiler.lib.template_framework.library.ShortCarriesFloat16Type.SHORT_CARRIES_FLOAT16; import static compiler.lib.template_framework.library.CodeGenerationDataNameType.PRIMITIVE_TYPES; import static compiler.lib.template_framework.library.CodeGenerationDataNameType.INTEGRAL_TYPES; import static compiler.lib.template_framework.library.CodeGenerationDataNameType.FLOATING_TYPES; import static compiler.lib.template_framework.library.CodeGenerationDataNameType.INT_LONG_TYPES; +import static compiler.lib.template_framework.library.CodeGenerationDataNameType.VECTOR_ELEMENT_TYPES; +import static compiler.lib.template_framework.library.CodeGenerationDataNameType.INTEGRAL_VECTOR_ELEMENT_TYPES; +import static compiler.lib.template_framework.library.CodeGenerationDataNameType.FLOATING_VECTOR_ELEMENT_TYPES; +import static compiler.lib.template_framework.library.CodeGenerationDataNameType.INT_LONG_VECTOR_ELEMENT_TYPES; /** * This class provides various lists of {@link Expression}s, that represent Java operators or library @@ -326,8 +331,11 @@ private enum VOPType { INTEGRAL_ASSOCIATIVE, // Binary - but only safe for integral reductions TERNARY } - private record VOP(String name, VOPType type, List elementTypes, boolean isDeterministic) { - VOP(String name, VOPType type, List elementTypes) { + // VOP element type pools are typed as VectorElementType so they can include + // ShortCarriesFloat16Type.SHORT_CARRIES_FLOAT16 (the Float16Vector lane type) alongside the + // primitive lane types. + private record VOP(String name, VOPType type, List elementTypes, boolean isDeterministic) { + VOP(String name, VOPType type, List elementTypes) { this(name, type, elementTypes, true); } } @@ -337,81 +345,81 @@ private record VOP(String name, VOPType type, List elementTypes, // But if a test is just interested in determinism, they are still // non-deterministic. private static final List VECTOR_OPS = List.of( - new VOP("ABS", VOPType.UNARY, PRIMITIVE_TYPES), - new VOP("ACOS", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("ADD", VOPType.INTEGRAL_ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("AND", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), - new VOP("AND_NOT", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("ASHR", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("ASIN", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("ATAN", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("ATAN2", VOPType.BINARY, FLOATING_TYPES, false), // 2 ulp - new VOP("BIT_COUNT", VOPType.UNARY, INTEGRAL_TYPES), - new VOP("BITWISE_BLEND", VOPType.TERNARY, INTEGRAL_TYPES), - new VOP("CBRT", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("COMPRESS_BITS", VOPType.BINARY, INT_LONG_TYPES), - new VOP("COS", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("COSH", VOPType.UNARY, FLOATING_TYPES, false), // 2.5 ulp - new VOP("DIV", VOPType.BINARY, FLOATING_TYPES), - new VOP("EXP", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("EXPAND_BITS", VOPType.BINARY, INT_LONG_TYPES), - new VOP("EXPM1", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("FIRST_NONZERO", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("FMA", VOPType.TERNARY, FLOATING_TYPES), - new VOP("HYPOT", VOPType.BINARY, FLOATING_TYPES, false), // 1.5 ulp - new VOP("LEADING_ZEROS_COUNT", VOPType.UNARY, INTEGRAL_TYPES), - new VOP("LOG", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("LOG10", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("LOG1P", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("LSHL", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("LSHR", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("MIN", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("MAX", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("MUL", VOPType.INTEGRAL_ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("NEG", VOPType.UNARY, PRIMITIVE_TYPES), - new VOP("NOT", VOPType.UNARY, INTEGRAL_TYPES), - new VOP("OR", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), - new VOP("POW", VOPType.BINARY, FLOATING_TYPES, false), // 1 ulp - new VOP("REVERSE", VOPType.UNARY, INTEGRAL_TYPES), - new VOP("REVERSE_BYTES", VOPType.UNARY, INTEGRAL_TYPES), - new VOP("ROL", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("ROR", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("SADD", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("SIN", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp - new VOP("SINH", VOPType.UNARY, FLOATING_TYPES, false), // 2.5 ulp - new VOP("SQRT", VOPType.UNARY, FLOATING_TYPES), - new VOP("SSUB", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("SUADD", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("SUB", VOPType.BINARY, PRIMITIVE_TYPES), - new VOP("SUSUB", VOPType.BINARY, INTEGRAL_TYPES), - new VOP("TAN", VOPType.UNARY, FLOATING_TYPES, false), // 1.25 ulp - new VOP("TANH", VOPType.UNARY, FLOATING_TYPES, false), // 2.5 ulp - new VOP("TRAILING_ZEROS_COUNT", VOPType.UNARY, INTEGRAL_TYPES), - new VOP("UMAX", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), - new VOP("UMIN", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), - new VOP("XOR", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), - new VOP("ZOMO", VOPType.UNARY, INTEGRAL_TYPES) + new VOP("ABS", VOPType.UNARY, VECTOR_ELEMENT_TYPES), + new VOP("ACOS", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("ADD", VOPType.INTEGRAL_ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("AND", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("AND_NOT", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("ASHR", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("ASIN", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("ATAN", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("ATAN2", VOPType.BINARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 2 ulp + new VOP("BIT_COUNT", VOPType.UNARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("BITWISE_BLEND", VOPType.TERNARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("CBRT", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("COMPRESS_BITS", VOPType.BINARY, INT_LONG_VECTOR_ELEMENT_TYPES), + new VOP("COS", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("COSH", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 2.5 ulp + new VOP("DIV", VOPType.BINARY, FLOATING_VECTOR_ELEMENT_TYPES), + new VOP("EXP", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("EXPAND_BITS", VOPType.BINARY, INT_LONG_VECTOR_ELEMENT_TYPES), + new VOP("EXPM1", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("FIRST_NONZERO", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("FMA", VOPType.TERNARY, FLOATING_VECTOR_ELEMENT_TYPES), + new VOP("HYPOT", VOPType.BINARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1.5 ulp + new VOP("LEADING_ZEROS_COUNT", VOPType.UNARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("LOG", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("LOG10", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("LOG1P", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("LSHL", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("LSHR", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("MIN", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("MAX", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("MUL", VOPType.INTEGRAL_ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("NEG", VOPType.UNARY, VECTOR_ELEMENT_TYPES), + new VOP("NOT", VOPType.UNARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("OR", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("POW", VOPType.BINARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("REVERSE", VOPType.UNARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("REVERSE_BYTES", VOPType.UNARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("ROL", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("ROR", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("SADD", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("SIN", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1 ulp + new VOP("SINH", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 2.5 ulp + new VOP("SQRT", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES), + new VOP("SSUB", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("SUADD", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("SUB", VOPType.BINARY, VECTOR_ELEMENT_TYPES), + new VOP("SUSUB", VOPType.BINARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("TAN", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 1.25 ulp + new VOP("TANH", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES, false), // 2.5 ulp + new VOP("TRAILING_ZEROS_COUNT", VOPType.UNARY, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("UMAX", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("UMIN", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("XOR", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("ZOMO", VOPType.UNARY, INTEGRAL_VECTOR_ELEMENT_TYPES) ); private static final List VECTOR_CMP = List.of( - new VOP("EQ", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("GE", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("GT", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("LE", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("LT", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("NE", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), - new VOP("UGE", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), - new VOP("UGT", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), - new VOP("ULE", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), - new VOP("ULT", VOPType.ASSOCIATIVE, INTEGRAL_TYPES) + new VOP("EQ", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("GE", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("GT", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("LE", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("LT", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("NE", VOPType.ASSOCIATIVE, VECTOR_ELEMENT_TYPES), + new VOP("UGE", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("UGT", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("ULE", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES), + new VOP("ULT", VOPType.ASSOCIATIVE, INTEGRAL_VECTOR_ELEMENT_TYPES) ); private static final List VECTOR_TEST = List.of( - new VOP("IS_DEFAULT", VOPType.UNARY, PRIMITIVE_TYPES), - new VOP("IS_NEGATIVE", VOPType.UNARY, PRIMITIVE_TYPES), - new VOP("IS_FINITE", VOPType.UNARY, FLOATING_TYPES), - new VOP("IS_NAN", VOPType.UNARY, FLOATING_TYPES), - new VOP("IS_INFINITE", VOPType.UNARY, FLOATING_TYPES) + new VOP("IS_DEFAULT", VOPType.UNARY, VECTOR_ELEMENT_TYPES), + new VOP("IS_NEGATIVE", VOPType.UNARY, VECTOR_ELEMENT_TYPES), + new VOP("IS_FINITE", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES), + new VOP("IS_NAN", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES), + new VOP("IS_INFINITE", VOPType.UNARY, FLOATING_VECTOR_ELEMENT_TYPES) ); // TODO: Conversion VectorOperators -> convertShape @@ -476,14 +484,14 @@ private static List generateVectorOperations() { "((" + type.name() + ")", type2, ".convert(VectorOperators.Conversion.ofCast(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), 0))")); + + type2.elementType.vectorElementClass() + ".class, " + + type.elementType.vectorElementClass() + ".class), 0))")); ops.add(Expression.make(type, "((" + type.name() + ")", type2, ".convert(VectorOperators.Conversion.ofCast(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class),", + + type2.elementType.vectorElementClass() + ".class, " + + type.elementType.vectorElementClass() + ".class),", INTS, // part "))", WITH_OUT_OF_BOUNDS_EXCEPTION)); } @@ -498,14 +506,14 @@ private static List generateVectorOperations() { "((" + type.name() + ")", type2, ".convert(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), 0))", reinterpretInfo)); + + type2.elementType.vectorElementClass() + ".class, " + + type.elementType.vectorElementClass() + ".class), 0))", reinterpretInfo)); ops.add(Expression.make(type, "((" + type.name() + ")", type2, ".convert(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class),", + + type2.elementType.vectorElementClass() + ".class, " + + type.elementType.vectorElementClass() + ".class),", INTS, // part "))", reinterpretInfo.combineWith(WITH_OUT_OF_BOUNDS_EXCEPTION))); if (type.elementType == BYTES) { @@ -523,6 +531,9 @@ private static List generateVectorOperations() { if (type.elementType == FLOATS) { ops.add(Expression.make(type, "", type2, ".reinterpretAsFloats()", reinterpretInfo)); } + if (type.elementType == SHORT_CARRIES_FLOAT16) { + ops.add(Expression.make(type, "", type2, ".reinterpretAsFloat16s()", reinterpretInfo)); + } if (type.elementType == DOUBLES) { ops.add(Expression.make(type, "", type2, ".reinterpretAsDoubles()", reinterpretInfo)); } @@ -558,8 +569,8 @@ private static List generateVectorOperations() { "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofCast(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), " + + type2.elementType.vectorElementClass() + ".class, " + + type.elementType.vectorElementClass() + ".class), " + type.speciesName + ", ", INTS, // part "))", WITH_OUT_OF_BOUNDS_EXCEPTION)); @@ -567,8 +578,8 @@ private static List generateVectorOperations() { "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), " + + type2.elementType.vectorElementClass() + ".class, " + + type.elementType.vectorElementClass() + ".class), " + type.speciesName + ", ", INTS, // part "))", reinterpretInfo.combineWith(WITH_OUT_OF_BOUNDS_EXCEPTION))); @@ -585,16 +596,16 @@ private static List generateVectorOperations() { "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofCast(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), " + + type2.elementType.vectorElementClass() + ".class, " + + type.elementType.vectorElementClass() + ".class), " + type.speciesName + ", ", INTS, " & " + partMask + "))")); ops.add(Expression.make(type, "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), " + + type2.elementType.vectorElementClass() + ".class, " + + type.elementType.vectorElementClass() + ".class), " + type.speciesName + ", ", INTS, " & " + partMask + "))", reinterpretInfo)); } else { @@ -604,16 +615,16 @@ private static List generateVectorOperations() { "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofCast(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), " + + type2.elementType.vectorElementClass() + ".class, " + + type.elementType.vectorElementClass() + ".class), " + type.speciesName + ", " + "-(", INTS, " & " + partMask + ")))")); ops.add(Expression.make(type, "((" + type.name() + ")", type2, ".convertShape(VectorOperators.Conversion.ofReinterpret(" - + type2.elementType.name() + ".class, " - + type.elementType.name() + ".class), " + + type2.elementType.vectorElementClass() + ".class, " + + type.elementType.vectorElementClass() + ".class), " + type.speciesName + ", " + "-(", INTS, " & " + partMask + ")))", reinterpretInfo)); } @@ -795,6 +806,27 @@ private static List generateVectorOperations() { // skip hashCode } + // ----------------- ShortCarriesFloat16Type lane bridges -------------------- + // ShortCarriesFloat16Type is the Float16Vector lane type; its lanes carry the raw + // bits of a Float16 in a short. We bridge it both to the boxed Float16 (rich + // float16 arithmetic) and to a plain short (raw-bit fiddling), so expression + // nesting can transition in and out of the lane type and so any IGVN + // optimizations on those transitions are exercised. + var float16Lane = ShortCarriesFloat16Type.SHORT_CARRIES_FLOAT16; + // Lane carrier -> boxed Float16: lifts a lane()/reduceLanes() result into the rich + // scalar Float16 world. The raw bits are not exposed (NaN-awareness is handled by + // Float16 verification), so deterministic. + ops.add(Expression.make(FLOAT16, "Float16.shortBitsToFloat16(", float16Lane, ")")); + // Boxed Float16 -> lane carrier: produces a ShortCarriesFloat16Type scalar to feed + // Float16Vector.broadcast/add(scalar)/withLane(...). + ops.add(Expression.make(float16Lane, "Float16.float16ToShortBits(", FLOAT16, ")")); + ops.add(Expression.make(float16Lane, "Float16.float16ToRawShortBits(", FLOAT16, ")")); + // Raw short <-> lane carrier: a Java-level no-op (both are carried in a short), but + // a type-level transition. short -> lane is deterministic; lane -> short exposes the + // raw bits, so distinct NaN encodings make it non-deterministic (preventing result verification). + ops.add(Expression.make(SHORT_CARRIES_FLOAT16, "/*cast to ShortCarriesFloat16Type*/(", SHORTS, ")")); + ops.add(Expression.make(SHORTS, "/*cast to short*/(", SHORT_CARRIES_FLOAT16, ")", WITH_NONDETERMINISTIC_RESULT)); + // TODO: VectorSpecies API methods // Make sure the list is not modifiable. @@ -838,8 +870,18 @@ private static List ternaryOps(Operations.VOP vop, VectorType.Vector FLOAT16_OPERATIONS ); + /** + * Provides a list of Vector API operations. Iterates over all + * {@link CodeGenerationDataNameType#VECTOR_VECTOR_TYPES}, including + * {@code Float16Vector_*}, whose lanes are described by + * {@link ShortCarriesFloat16Type#SHORT_CARRIES_FLOAT16}. + */ public static final List VECTOR_OPERATIONS = generateVectorOperations(); + /** + * Provides a list of all operations: every scalar operation and every + * Vector API operation. + */ public static final List ALL_OPERATIONS = Utils.concat( SCALAR_NUMERIC_OPERATIONS, VECTOR_OPERATIONS diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java index cd796fd0d31..31e0eecbac3 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java @@ -40,8 +40,19 @@ * The {@link PrimitiveType} models Java's primitive types, and provides a set * of useful methods for code generation, such as the {@link #byteSize} and * {@link #boxedTypeName}. + * + *

    {@link PrimitiveType} is a Java scalar type and additionally + * doubles as a {@link VectorElementType} for those Vector API lane types whose + * lane carrier is itself a Java primitive (e.g. {@code IntVector}'s lane + * carrier is {@code int}). For these primitive lane types + * {@link #carrierTypeName} coincides with {@link #name}. + * + *

    Non-primitive lane types, such as the {@code Float16Vector} lane, are + * modeled by separate {@link VectorElementType} implementations (see + * {@link ShortCarriesFloat16Type}). They do not appear in any of + * the scalar {@code PRIMITIVE_TYPES}/{@code FLOATING_TYPES} lists. */ -public final class PrimitiveType implements CodeGenerationDataNameType { +public final class PrimitiveType implements VectorElementType { private static final Random RANDOM = Utils.getRandomInstance(); private static final RestrictableGenerator GEN_BYTE = Generators.G.safeRestrict(Generators.G.ints(), Byte.MIN_VALUE, Byte.MAX_VALUE); private static final RestrictableGenerator GEN_CHAR = Generators.G.safeRestrict(Generators.G.ints(), Character.MIN_VALUE, Character.MAX_VALUE); @@ -107,6 +118,23 @@ public String name() { }; } + @Override + public String carrierTypeName() { + return name(); + } + + @Override + public String vectorElementClass() { + // For primitive lanes the code-usable name and the lane element class + // token coincide (e.g. "int" -> int.class). boolean/char are not real + // Vector API lane element types, so we fail fast during code generation + // rather than emitting code that would only break at compile/runtime. + if (kind == Kind.BOOLEAN || kind == Kind.CHAR) { + throw new UnsupportedOperationException(name() + " is not a Vector API lane element type"); + } + return name(); + } + @Override public String toString() { return name(); @@ -132,6 +160,7 @@ public Object con() { * @return Size of the type in bytes. * @throws UnsupportedOperationException for boolean which has no defined size. */ + @Override public int byteSize() { return switch (kind) { case BYTE -> 1; @@ -147,6 +176,7 @@ public int byteSize() { * * @return the name of the boxed type. */ + @Override public String boxedTypeName() { return switch (kind) { case BYTE -> "Byte"; @@ -194,6 +224,7 @@ public String abbrev() { * * @return true iff the type is a floating point type. */ + @Override public boolean isFloating() { return switch (kind) { case BYTE, SHORT, CHAR, INT, LONG, BOOLEAN -> false; @@ -213,6 +244,7 @@ public boolean isFloating() { * @return the token representing the method call to obtain a * random value for the given type at runtime. */ + @Override public Object callLibraryRNG() { return switch (kind) { case BYTE -> "LibraryRNG.nextByte()"; @@ -231,6 +263,12 @@ public Object callLibraryRNG() { * random number generators available, wrapping {@link Generators}. This * is supposed to be used in tandem with {@link #callLibraryRNG}. * + *

    In addition to the Java primitive generators, this also emits + * helpers for {@code Float16Vector}'s {@code short} carrier + * ({@code nextFloat16()} / {@code fill_float16(short[])}) so that + * {@link ShortCarriesFloat16Type#callLibraryRNG()} can be used with vector + * fuzzers without depending on this class importing Float16Vector itself. + * * Note: you must ensure that all required imports are performed: * {@code java.util.Random} * {@code jdk.test.lib.Utils} @@ -250,6 +288,7 @@ public static class LibraryRNG { private static final RestrictableGenerator GEN_LONG = Generators.G.longs(); private static final Generator GEN_DOUBLE = Generators.G.doubles(); private static final Generator GEN_FLOAT = Generators.G.floats(); + private static final Generator GEN_FLOAT16 = Generators.G.float16s(); public static byte nextByte() { return GEN_BYTE.next().byteValue(); @@ -283,6 +322,17 @@ public static boolean nextBoolean() { return RANDOM.nextBoolean(); } + // Float16Vector lane helpers. Float16 lanes are carried in short[]. + public static short nextFloat16() { + return GEN_FLOAT16.next(); + } + + public static void fill_float16(short[] a) { + for (int i = 0; i < a.length; i++) { + a[i] = nextFloat16(); + } + } + """, CodeGenerationDataNameType.PRIMITIVE_TYPES.stream().map(type -> scope( let("type", type), diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/ShortCarriesFloat16Type.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/ShortCarriesFloat16Type.java new file mode 100644 index 00000000000..33c5535b99b --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/ShortCarriesFloat16Type.java @@ -0,0 +1,112 @@ +/* + * 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.lib.template_framework.library; + +import compiler.lib.generators.Generators; +import compiler.lib.generators.Generator; + +import compiler.lib.template_framework.DataName; + +/** + * The {@link ShortCarriesFloat16Type} is the {@link VectorElementType} that describes + * the lane type of a {@code Float16Vector}. Its name makes the semantics + * explicit: a {@code Float16} value carried in a {@code short}. + * + *

    Float16 is not a Java primitive type and therefore does + * not appear in any of the scalar {@link PrimitiveType} lists. As a + * {@link VectorElementType} it appears in vector-lane-typed lists such as + * {@link CodeGenerationDataNameType#VECTOR_ELEMENT_TYPES} and + * {@link CodeGenerationDataNameType#FLOATING_VECTOR_ELEMENT_TYPES}, which are + * consumed by vector-only generators (e.g. {@code Operations.VECTOR_OPERATIONS}). + * + *

    The carrier type for a {@code Float16Vector} lane is {@code short}, so + * {@link #name()} (the code-usable type, per the {@code name()} contract) + * returns {@code "short"}. The logical lane element type token used in + * {@code VectorOperators.Conversion.of*} expressions and + * {@code Float16Vector.SPECIES_*} is {@code Float16}, returned by + * {@link #vectorElementClass()}. + * + *

    NaN handling note: there are multiple bit representations for NaN within + * {@code short}/{@code Float16}. Consumers comparing {@code short[]} carrier + * arrays should canonicalize via {@code Float.float16ToFloat} (which returns a + * canonical NaN) before structural comparison. + */ +public final class ShortCarriesFloat16Type implements VectorElementType { + private static final Generator GEN_FLOAT16 = Generators.G.float16s(); + + /** The singleton instance. */ + public static final ShortCarriesFloat16Type SHORT_CARRIES_FLOAT16 = new ShortCarriesFloat16Type(); + + private ShortCarriesFloat16Type() {} + + @Override + public boolean isSubtypeOf(DataName.Type other) { + return other instanceof ShortCarriesFloat16Type; + } + + @Override + public String name() { + return "short"; + } + + @Override + public String carrierTypeName() { + return "short"; + } + + @Override + public String vectorElementClass() { + return "Float16"; + } + + @Override + public String boxedTypeName() { + return "Float16"; + } + + @Override + public int byteSize() { + return 2; + } + + @Override + public boolean isFloating() { + return true; + } + + @Override + public String toString() { + return name(); + } + + @Override + public Object con() { + return "(short)" + GEN_FLOAT16.next(); + } + + @Override + public Object callLibraryRNG() { + return "LibraryRNG.nextFloat16()"; + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorElementType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorElementType.java new file mode 100644 index 00000000000..657dc80fd5e --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorElementType.java @@ -0,0 +1,103 @@ +/* + * 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.lib.template_framework.library; + +/** + * A {@link VectorElementType} describes a single lane-element of a Vector API + * vector ({@link VectorType.Vector}). It abstracts over: + *

      + *
    • {@link PrimitiveType} - the standard Java primitive lane types + * (byte, short, int, long, float, double). For these {@link #name()} is + * the primitive keyword + * (e.g. {@code "int"}) and {@link #vectorElementClass()} is the same + * token, so {@code vectorElementClass() + ".class"} yields the primitive + * {@code Class} literal ({@code int.class}).
    • + *
    • {@link ShortCarriesFloat16Type} - the {@code Float16Vector} lane type. Float16 + * has no Java primitive keyword; its lanes are stored in a {@code short[]} + * carrier, so {@link #name()} returns the code-usable carrier keyword + * {@code "short"} (consistent with {@link #toString()}), while + * {@link #vectorElementClass()} returns {@code "Float16"} so that + * {@code vectorElementClass() + ".class"} ({@code Float16.class}) is the + * token expected by + * {@code VectorOperators.Conversion.ofCast}/{@code ofReinterpret}.
    • + *
    + * + *

    This interface lives outside the scalar + * {@link PrimitiveType} type lists (e.g. {@code PRIMITIVE_TYPES}, + * {@code FLOATING_TYPES}). Those lists model Java scalar types and are consumed + * by scalar fuzzers. Vector-lane lists (e.g. {@code VECTOR_ELEMENT_TYPES}, + * {@code FLOATING_VECTOR_ELEMENT_TYPES}) are typed as {@code List} + * and may include {@link ShortCarriesFloat16Type#SHORT_CARRIES_FLOAT16}. + */ +public interface VectorElementType extends CodeGenerationDataNameType { + + /** + * The string whose {@code + ".class"} form is the lane element + * {@code Class} literal expected by the Vector API conversion factories + * ({@code VectorOperators.Conversion.ofCast}/{@code ofReinterpret}) and by + * {@code Float16Vector.SPECIES_*}/{@code IntVector.SPECIES_*} lookups. + * + *

    Unlike {@link #name()} (which must always be a Java type usable + * directly in code, e.g. for variable declarations and casts), this token + * is the logical lane element type. For Java primitive lanes the + * two coincide ({@code "int"} -> {@code int.class}); for {@code Float16} + * lanes {@link #name()} is the carrier {@code "short"} while this returns + * {@code "Float16"} ({@code Float16.class}). + * + * @return The logical lane element type token (e.g. {@code "int"}, + * {@code "float"}, {@code "Float16"}). + */ + String vectorElementClass(); + + /** + * @return The element type of the Java carrier array used to hold these + * lanes when calling {@code fromArray}/{@code intoArray}. For most + * lane types this is the same as {@link #name()}; for + * {@code Float16} it is {@code "short"}. + */ + String carrierTypeName(); + + /** + * @return The boxed type name used to parameterize generic types such as + * {@code VectorMask} and {@code VectorShuffle} + * (e.g. {@code "Integer"}, {@code "Float16"}). + */ + String boxedTypeName(); + + /** + * @return Size of the lane type in bytes. + */ + int byteSize(); + + /** + * @return {@code true} iff the lane type is a floating point type. + */ + boolean isFloating(); + + /** + * @return A token representing a call to the corresponding pseudo random + * number generator from {@link PrimitiveType#generateLibraryRNG()}. + */ + Object callLibraryRNG(); +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorType.java index 7eabd42a723..df1365a3566 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorType.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorType.java @@ -39,6 +39,11 @@ /** * The {@link VectorType} models the Vector API types. + * + *

    A {@code VectorType.Vector} is parameterized by a {@link VectorElementType} + * (its lane element type) and a lane count. The lane element type may be a + * Java primitive lane ({@link PrimitiveType}) or {@link ShortCarriesFloat16Type} for + * {@code Float16Vector}. */ public abstract class VectorType implements CodeGenerationDataNameType { private static final Random RANDOM = Utils.getRandomInstance(); @@ -73,6 +78,11 @@ public abstract class VectorType implements CodeGenerationDataNameType { public static final VectorType.Vector DOUBLE_256 = new VectorType.Vector(DOUBLES, 4); public static final VectorType.Vector DOUBLE_512 = new VectorType.Vector(DOUBLES, 8); + public static final VectorType.Vector FLOAT16_64 = new VectorType.Vector(ShortCarriesFloat16Type.SHORT_CARRIES_FLOAT16, 4); + public static final VectorType.Vector FLOAT16_128 = new VectorType.Vector(ShortCarriesFloat16Type.SHORT_CARRIES_FLOAT16, 8); + public static final VectorType.Vector FLOAT16_256 = new VectorType.Vector(ShortCarriesFloat16Type.SHORT_CARRIES_FLOAT16, 16); + public static final VectorType.Vector FLOAT16_512 = new VectorType.Vector(ShortCarriesFloat16Type.SHORT_CARRIES_FLOAT16, 32); + private final String vectorTypeName; private VectorType(String vectorTypeName) { @@ -95,8 +105,8 @@ public boolean isSubtypeOf(DataName.Type other) { return this == other; } - private static final String vectorTypeName(PrimitiveType elementType) { - return switch(elementType.name()) { + private static final String vectorTypeName(VectorElementType elementType) { + return switch(elementType.vectorElementClass()) { case "byte" -> "ByteVector"; case "short" -> "ShortVector"; case "char" -> throw new UnsupportedOperationException("VectorAPI has no char vector type"); @@ -104,19 +114,20 @@ private static final String vectorTypeName(PrimitiveType elementType) { case "long" -> "LongVector"; case "float" -> "FloatVector"; case "double" -> "DoubleVector"; - default -> throw new UnsupportedOperationException("Not supported: " + elementType.name()); + case "Float16" -> "Float16Vector"; + default -> throw new UnsupportedOperationException("Not supported: " + elementType.vectorElementClass()); }; } public static final class Vector extends VectorType { - public final PrimitiveType elementType; + public final VectorElementType elementType; public final int length; // lane count public final String speciesName; public final Mask maskType; public final Shuffle shuffleType; - private Vector(PrimitiveType elementType, int length) { + private Vector(VectorElementType elementType, int length) { super(vectorTypeName(elementType)); this.elementType = elementType; this.length = length; @@ -132,7 +143,7 @@ public final Object con() { return List.of(name(), ".zero(", speciesName, ")"); } else if (r <= 8) { return List.of( - name(), ".fromArray(", speciesName, ", new ", elementType.name(), "[] {", + name(), ".fromArray(", speciesName, ", new ", elementType.carrierTypeName(), "[] {", elementType.con(), Stream.generate(() -> List.of(", ", elementType.con()) diff --git a/test/hotspot/jtreg/compiler/lib/verify/Verify.java b/test/hotspot/jtreg/compiler/lib/verify/Verify.java index c79ad2c55a0..32463bf3454 100644 --- a/test/hotspot/jtreg/compiler/lib/verify/Verify.java +++ b/test/hotspot/jtreg/compiler/lib/verify/Verify.java @@ -52,6 +52,11 @@ * This applies to the boxed floating types, as well as arrays of floating arrays. With * {@link Verify#checkEQWithRawBits} we compare the raw bits, and so different NaN encodings are not equal. * Note: {@link MemorySegment} data is always compared with raw bits. + * + *

    + * The same NaN handling applies to {@code Float16}: both the scalar {@code Float16} box and the + * {@code Float16Vector} lanes (whose {@code short} carrier bits encode Float16 values) are compared + * with the selected NaN mode rather than as raw {@code short}s. */ public final class Verify { private final boolean isFloatCheckWithRawBits; @@ -455,9 +460,58 @@ private void checkEQForVectorAPIClass(Object a, Object b, String field, Object a } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { throw new RuntimeException("Could not invoke toArray on " + ca.getName(), e); } + // A Float16Vector carries its lanes in a short[], but those short bits encode Float16 + // values rather than plain shorts. Comparing them as a raw short[] would treat distinct + // NaN encodings as unequal, even in the non-raw mode. Compare them with Float16 NaN + // semantics instead. + if (va instanceof short[] sa && vb instanceof short[] sb && isFloat16VectorClass(ca)) { + checkEQForFloat16Carrier(sa, sb, field + ".toArray", aParent, bParent); + return; + } checkEQdispatch(va, vb, field + ".toArray", aParent, bParent); } + private static boolean isFloat16VectorClass(Class c) { + // The concrete classes (Float16Vector64/128/256/512/Max) all extend Float16Vector. + for (Class k = c; k != null; k = k.getSuperclass()) { + if (k.getName().equals("jdk.incubator.vector.Float16Vector")) { + return true; + } + } + return false; + } + + /** + * Compare the {@code short[]} carriers of two {@code Float16Vector}s. The short bits encode + * Float16 values, so in the non-raw mode we canonicalize NaN by widening each lane to float + * via {@link Float#float16ToFloat}, and then reuse the float canonicalization. In the raw mode we + * compare the carrier bits directly, so distinct NaN encodings are not equal. See {@link #isFloatEQ}. + */ + private void checkEQForFloat16Carrier(short[] a, short[] b, String field, Object aParent, Object bParent) { + if (a.length != b.length) { + System.err.println("ERROR: Equality matching failed: length mismatch: " + a.length + " vs " + b.length); + print(a, b, field, aParent, bParent); + throw new VerifyException("Float16 array length mismatch."); + } + + for (int i = 0; i < a.length; i++) { + if (!isFloat16EQ(a[i], b[i])) { + System.err.println("ERROR: Equality matching failed: value mismatch at " + i + ": " + a[i] + " vs " + b[i] + ". check raw: " + isFloatCheckWithRawBits); + print(a, b, field, aParent, bParent); + throw new VerifyException("Float16 array value mismatch " + a[i] + " vs " + b[i]); + } + } + } + + /** + * For Float16 we widen each lane to float, which is exact and lossless and maps every NaN encoding + * to the canonical float NaN, and then reuse the float canonicalization. + */ + private boolean isFloat16EQ(short a, short b) { + return isFloatCheckWithRawBits ? a == b + : Float.floatToIntBits(Float.float16ToFloat(a)) == Float.floatToIntBits(Float.float16ToFloat(b)); + } + private static boolean isFloat16Class(Class c) { return c.getName().equals("jdk.incubator.vector.Float16"); } diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorExpressionFuzzer.java b/test/hotspot/jtreg/compiler/vectorapi/VectorExpressionFuzzer.java index cb5b95109f5..3413ece592f 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/VectorExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorExpressionFuzzer.java @@ -66,6 +66,8 @@ import compiler.lib.template_framework.library.Operations; import compiler.lib.template_framework.library.TestFrameworkClass; import compiler.lib.template_framework.library.PrimitiveType; +import compiler.lib.template_framework.library.ShortCarriesFloat16Type; +import compiler.lib.template_framework.library.VectorElementType; import compiler.lib.template_framework.library.VectorType; /** @@ -162,23 +164,39 @@ public static String generate(CompileFramework comp) { // - We check correctness with a reference method that does the same but runs in the interpreter. // - Input values are delivered via fields or array loads. // - The final vector is written into an array, and that array is returned. - var template2Body = Template.make("expression", "arguments", (Expression expression, List arguments) -> scope( - let("elementType", ((VectorType.Vector)expression.returnType).elementType), - """ - try { - #elementType[] out = new #elementType[1000]; - """, - expression.asToken(arguments), ".intoArray(out, 0);\n", - "return out;\n", - expression.info.exceptions.stream().map(exception -> - "} catch (" + exception + " e) { return e;\n" - ).toList(), - """ - } finally { - // Just javac is happy if there are no exceptions to catch. - } - """ - )); + // + // NaN canonicalization (Float16Vector only): the {@code short} carrier of Float16Vector lanes + // distinguishes multiple NaN bit patterns, so a structural comparison between two distinct NaN + // bit patterns would spuriously fail. We widen the {@code short[]} carrier to {@code float[]} + // via {@link Float#float16ToFloat}, which returns a canonical NaN for any NaN input. + var template2Body = Template.make("expression", "arguments", (Expression expression, List arguments) -> { + VectorType.Vector retType = (VectorType.Vector) expression.returnType; + boolean float16Result = retType.elementType instanceof ShortCarriesFloat16Type; + return scope( + let("carrierType", retType.elementType.carrierTypeName()), + """ + try { + #carrierType[] out = new #carrierType[1000]; + """, + expression.asToken(arguments), ".intoArray(out, 0);\n", + float16Result + ? """ + // Float16Vector NaN canonicalization: widen short carrier to float for compare. + float[] outF = new float[out.length]; + for (int i = 0; i < out.length; i++) { outF[i] = Float.float16ToFloat(out[i]); } + return outF; + """ + : "return out;\n", + expression.info.exceptions.stream().map(exception -> + "} catch (" + exception + " e) { return e;\n" + ).toList(), + """ + } finally { + // Just javac is happy if there are no exceptions to catch. + } + """ + ); + }); var template2 = Template.make("type", (VectorType.Vector type) -> { // The depth determines roughly how many operations are going to be used in the expression. @@ -210,24 +228,25 @@ public static String generate(CompileFramework comp) { )); } default -> { - if (argumentType instanceof PrimitiveType t) { + if (argumentType instanceof VectorElementType vet) { // We can use the LibraryRGN to create a new value for the primitive in each // invocation. We have to make sure to call the LibraryRNG in the "defineAndFill", // so we get the same value for both test and reference. If we called LibraryRNG // for "use", we would get separate values, which is not helpful. arguments.add(new TestArgument( - List.of(t.name(), " ", name, " = ", t.callLibraryRNG(), ";\n"), + List.of(vet.carrierTypeName(), " ", name, " = ", vet.callLibraryRNG(), ";\n"), name, - List.of(t.name(), " ", name), + List.of(vet.carrierTypeName(), " ", name), name )); } else if (argumentType instanceof VectorType.Vector t) { - PrimitiveType et = t.elementType; + VectorElementType et = t.elementType; + String fillMethod = (et instanceof ShortCarriesFloat16Type) ? "fill_float16" : "fill"; arguments.add(new TestArgument( - List.of(et.name(), "[] ", name, " = new ", et.name(), "[1000];\n", - "LibraryRNG.fill(", name,");\n"), + List.of(et.carrierTypeName(), "[] ", name, " = new ", et.carrierTypeName(), "[1000];\n", + "LibraryRNG.", fillMethod, "(", name,");\n"), name, - List.of(et.name(), "[] ", name), + List.of(et.carrierTypeName(), "[] ", name), List.of(t.name(), ".fromArray(", t.speciesName, ", ", name, ", 0)") )); } else if (argumentType instanceof VectorType.Mask t) { diff --git a/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerifyFloat16.java b/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerifyFloat16.java index 8d1d763a250..7fc3bc11dee 100644 --- a/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerifyFloat16.java +++ b/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerifyFloat16.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 @@ -33,10 +33,12 @@ package verify.tests; import java.lang.foreign.*; +import java.util.Arrays; import java.util.Random; import jdk.test.lib.Utils; import jdk.incubator.vector.Float16; +import jdk.incubator.vector.Float16Vector; import compiler.lib.verify.*; @@ -47,6 +49,8 @@ public static void main(String[] args) { testArrayFloat16(); testRawFloat16(); testFloat16Random(); + testFloat16VectorCarrier(); + testFloat16VectorCarrierRandom(); } public static void testArrayFloat16() { @@ -113,6 +117,84 @@ public static void testFloat16Random() { } } + /** + * Exercises the {@code Float16Vector} short[]-carrier path in Verify + * (checkEQForFloat16Carrier). The short carrier bits encode Float16 values, so the + * non-raw mode must canonicalize NaN (distinct NaN encodings are equal) while the raw + * mode must compare the carrier bits directly (distinct NaN encodings are not equal). + */ + public static void testFloat16VectorCarrier() { + var species = Float16Vector.SPECIES_128; + int len = species.length(); + + // Two different NaN encodings of Float16. + short nan1 = (short)0xFFFF; + short nan2 = (short)0x7FFF; + + short[] aBits = new short[len]; + short[] bBits = new short[len]; + Arrays.fill(aBits, nan1); + Arrays.fill(bBits, nan2); + Float16Vector va = Float16Vector.fromArray(species, aBits, 0); + Float16Vector vb = Float16Vector.fromArray(species, bBits, 0); + + // Same vector: equal in both modes. + Verify.checkEQ(va, va); + Verify.checkEQWithRawBits(va, va); + + // Distinct NaN encodings: equal in non-raw mode (canonicalized) ... + Verify.checkEQ(va, vb); + // ... but not equal in raw mode. + checkNEWithRawBits(va, vb); + + // A real value mismatch must fail in both modes. + short[] oneBits = new short[len]; + short[] twoBits = new short[len]; + Arrays.fill(oneBits, Float16.float16ToShortBits(Float16.valueOf(1f))); + Arrays.fill(twoBits, Float16.float16ToShortBits(Float16.valueOf(2f))); + Float16Vector vOne = Float16Vector.fromArray(species, oneBits, 0); + Float16Vector vTwo = Float16Vector.fromArray(species, twoBits, 0); + Verify.checkEQ(vOne, vOne); + Verify.checkEQWithRawBits(vOne, vOne); + checkNE(vOne, vTwo); + checkNEWithRawBits(vOne, vTwo); + + // NaN vs a real number: not equal in either mode. + checkNE(va, vOne); + checkNEWithRawBits(va, vOne); + } + + public static void testFloat16VectorCarrierRandom() { + var species = Float16Vector.SPECIES_128; + int len = species.length(); + // Testing all 2^16 * 2^16 = 2^32 would take a bit long, so we randomly sample instead. + for (int i = 0; i < 10_000; i++) { + short bitsA = (short)RANDOM.nextInt(); + short bitsB = (short)RANDOM.nextInt(); + short[] aBits = new short[len]; + short[] bBits = new short[len]; + Arrays.fill(aBits, bitsA); + Arrays.fill(bBits, bitsB); + Float16Vector va = Float16Vector.fromArray(species, aBits, 0); + Float16Vector vb = Float16Vector.fromArray(species, bBits, 0); + + // Raw mode: equal iff identical carrier bits. + if (bitsA == bitsB) { + Verify.checkEQWithRawBits(va, vb); + } else { + checkNEWithRawBits(va, vb); + } + + // Non-raw mode: equal iff the canonicalized Float16 values match. + if (Float.floatToIntBits(Float.float16ToFloat(bitsA)) == + Float.floatToIntBits(Float.float16ToFloat(bitsB))) { + Verify.checkEQ(va, vb); + } else { + checkNE(va, vb); + } + } + } + public static void checkNE(Object a, Object b) { try { Verify.checkEQ(a, b); From 5926bbfa0a479a84ccfaad8f4f11ee081fe9adaf Mon Sep 17 00:00:00 2001 From: Patrick Fontanilla Date: Tue, 7 Jul 2026 20:23:42 +0000 Subject: [PATCH 173/707] 8386512: Shenandoah: Add a diagnostic option to facilitate testing pinned regions Reviewed-by: kdnilsen, wkemper --- .../gc/shenandoah/shenandoahConcurrentGC.cpp | 18 ++++++++++ .../gc/shenandoah/shenandoahDegeneratedGC.cpp | 1 + .../share/gc/shenandoah/shenandoahFullGC.cpp | 1 + .../share/gc/shenandoah/shenandoahHeap.cpp | 36 ++++++++++++++++++- .../share/gc/shenandoah/shenandoahHeap.hpp | 13 +++++++ .../share/gc/shenandoah/shenandoahOldGC.cpp | 1 + .../gc/shenandoah/shenandoah_globals.hpp | 6 ++++ .../jtreg/gc/TestAllocHumongousFragment.java | 10 ++++++ .../gc/shenandoah/TestAllocIntArrays.java | 10 ++++++ .../gc/shenandoah/TestAllocObjectArrays.java | 16 +++++++++ .../jtreg/gc/shenandoah/TestAllocObjects.java | 10 ++++++ .../jtreg/gc/shenandoah/TestJcmdHeapDump.java | 5 +++ .../jtreg/gc/shenandoah/TestLotsOfCycles.java | 6 ++++ .../gc/shenandoah/TestRetainObjects.java | 10 ++++++ .../jtreg/gc/shenandoah/TestSieveObjects.java | 10 ++++++ .../gcbasher/TestGCBasherWithShenandoah.java | 11 ++++++ .../stress/gcold/TestGCOldWithShenandoah.java | 5 +++ 17 files changed, 168 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index 28f04de2f86..18f8f5a4142 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -109,6 +109,7 @@ void ShenandoahConcurrentGC::entry_concurrent_update_refs_prepare(ShenandoahHeap ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_update_refs_prepare); EventMark em("%s", msg); + heap->try_inject_pin(); // Evacuation is complete, retire gc labs and change gc state heap->concurrent_prepare_for_update_refs(); } @@ -125,6 +126,7 @@ void ShenandoahConcurrentGC::entry_update_card_table() { ShenandoahWorkerPolicy::calc_workers_for_conc_evac(), "concurrent update cards"); + heap->try_inject_pin(); // Heap needs to be parsable here. // Also, parallel heap region iterate must have a phase set. assert(ShenandoahTimingsTracker::is_current_phase_valid(), "Current phase must be set"); @@ -301,6 +303,7 @@ void ShenandoahConcurrentGC::entry_complete_abbreviated_cycle() { ShenandoahWorkerPolicy::calc_workers_for_conc_evac(), msg); + heap->try_inject_pin(); // We chose not to evacuate because we found sufficient immediate garbage. // However, there may still be regions to promote in place, so do that now. if (heap->old_generation()->has_in_place_promotions()) { @@ -335,6 +338,7 @@ void ShenandoahConcurrentGC::vmop_entry_final_mark() { heap->try_inject_alloc_failure(); VM_ShenandoahFinalMarkStartEvac op(this); VMThread::execute(&op); // jump to entry_final_mark under safepoint + heap->try_inject_pin(); } void ShenandoahConcurrentGC::vmop_entry_init_update_refs() { @@ -343,6 +347,7 @@ void ShenandoahConcurrentGC::vmop_entry_init_update_refs() { ShenandoahTimingsTracker timing(ShenandoahPhaseTimings::init_update_refs_gross); heap->try_inject_alloc_failure(); + heap->try_inject_pin(); VM_ShenandoahInitUpdateRefs op(this); VMThread::execute(&op); } @@ -353,6 +358,7 @@ void ShenandoahConcurrentGC::vmop_entry_final_update_refs() { ShenandoahTimingsTracker timing(ShenandoahPhaseTimings::final_update_refs_gross); heap->try_inject_alloc_failure(); + heap->try_inject_pin(); VM_ShenandoahFinalUpdateRefs op(this); VMThread::execute(&op); } @@ -364,6 +370,7 @@ void ShenandoahConcurrentGC::vmop_entry_final_verify() { // This phase does not use workers, no need for setup heap->try_inject_alloc_failure(); + heap->try_inject_pin(); VM_ShenandoahFinalVerify op(this); VMThread::execute(&op); } @@ -423,6 +430,7 @@ void ShenandoahConcurrentGC::entry_final_verify() { void ShenandoahConcurrentGC::entry_reset() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); + heap->release_injected_pins(); heap->try_inject_alloc_failure(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); @@ -483,6 +491,7 @@ void ShenandoahConcurrentGC::entry_mark() { heap->try_inject_alloc_failure(); op_mark(); + heap->try_inject_pin(); } void ShenandoahConcurrentGC::entry_thread_roots() { @@ -496,6 +505,7 @@ void ShenandoahConcurrentGC::entry_thread_roots() { msg); heap->try_inject_alloc_failure(); + heap->try_inject_pin(); op_thread_roots(); } @@ -510,6 +520,7 @@ void ShenandoahConcurrentGC::entry_weak_refs() { "concurrent weak references"); heap->try_inject_alloc_failure(); + heap->try_inject_pin(); op_weak_refs(); } @@ -525,6 +536,7 @@ void ShenandoahConcurrentGC::entry_weak_roots() { "concurrent weak root"); heap->try_inject_alloc_failure(); + heap->try_inject_pin(); op_weak_roots(); } @@ -540,6 +552,7 @@ void ShenandoahConcurrentGC::entry_class_unloading() { "concurrent class unloading"); heap->try_inject_alloc_failure(); + heap->try_inject_pin(); op_class_unloading(); } @@ -557,6 +570,7 @@ void ShenandoahConcurrentGC::entry_strong_roots() { "concurrent strong root"); heap->try_inject_alloc_failure(); + heap->try_inject_pin(); op_strong_roots(); } @@ -569,6 +583,7 @@ void ShenandoahConcurrentGC::entry_cleanup_early() { // This phase does not use workers, no need for setup heap->try_inject_alloc_failure(); + heap->try_inject_pin(); op_cleanup_early(); if (!heap->is_evacuation_in_progress()) { // This is an abbreviated cycle. Rebuild the freeset in order to establish reserves for the next GC cycle. Doing @@ -591,6 +606,7 @@ void ShenandoahConcurrentGC::entry_evacuate() { "concurrent evacuation"); heap->try_inject_alloc_failure(); + heap->try_inject_pin(); op_evacuate(); } @@ -604,6 +620,7 @@ void ShenandoahConcurrentGC::entry_update_thread_roots() { // No workers used in this phase, no setup required heap->try_inject_alloc_failure(); + heap->try_inject_pin(); op_update_thread_roots(); } @@ -619,6 +636,7 @@ void ShenandoahConcurrentGC::entry_update_refs() { "concurrent reference update"); heap->try_inject_alloc_failure(); + heap->try_inject_pin(); op_update_refs(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp index cfa56c3ec20..3c3cdc4a90a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp @@ -91,6 +91,7 @@ void ShenandoahDegenGC::entry_degenerated() { void ShenandoahDegenGC::op_degenerated() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); + heap->release_injected_pins(); // Degenerated GC is STW, but it can also fail. Current mechanics communicates // GC failure via cancelled_concgc() flag. So, if we detect the failure after // some phase, we have to upgrade the Degenerate GC to Full GC. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp index cd04db383ed..cab0db7e78a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp @@ -136,6 +136,7 @@ void ShenandoahFullGC::op_full(GCCause::Cause cause) { void ShenandoahFullGC::do_it(GCCause::Cause gc_cause) { ShenandoahHeap* heap = ShenandoahHeap::heap(); + heap->release_injected_pins(); // A full GC may be entered directly, or as an upgrade from a failed // degenerated GC. In the latter case, self-forwarded objects may be diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index ae0c873fa58..7731ad911c5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -595,7 +595,8 @@ ShenandoahHeap::ShenandoahHeap(ShenandoahCollectorPolicy* policy) : _aux_bitmap_region_special(false), _liveness_cache(nullptr), _collection_set(nullptr), - _evac_tracker(new ShenandoahEvacuationTracker()) + _evac_tracker(new ShenandoahEvacuationTracker()), + _injected_pin_count(0) { // Initialize GC mode early, many subsequent initialization procedures depend on it initialize_mode(); @@ -2752,6 +2753,39 @@ bool ShenandoahHeap::should_inject_alloc_failure() { return _inject_alloc_failure.is_set() && _inject_alloc_failure.try_unset(); } +void ShenandoahHeap::try_inject_pin() { + assert(!ShenandoahSafepoint::is_at_shenandoah_safepoint(), "try_inject_pin() must be called outside a safepoint."); + assert(active_generation() != nullptr, "Active generation must be set before we inject pins."); + assert(is_concurrent_mark_in_progress() || active_generation()->is_mark_complete(), + "try_inject_pin() requires marking is in progress or has completed."); + if (ShenandoahPinRegionRate && !cancelled_gc() && ((uintx)(os::random() % 1000) < ShenandoahPinRegionRate) && + _injected_pin_count < MAX_INJECTED_PINS) { + const size_t idx = os::random() % num_regions(); + ShenandoahHeapRegion* r = get_region(idx); + if ((r->is_regular() || r->is_humongous_start()) && r->has_live()) { + r->record_pin(); + _injected_pin_indices[_injected_pin_count] = idx; + _injected_pin_count++; + } + } +} + +void ShenandoahHeap::release_injected_pins() { + if (_injected_pin_count == 0) { + return; + } + + assert(_injected_pin_count <= MAX_INJECTED_PINS, + "Injected pin count: %u exceeds max: %u.", _injected_pin_count, MAX_INJECTED_PINS); + for (uint i = 0; i < _injected_pin_count; i++) { + const size_t idx = _injected_pin_indices[i]; + ShenandoahHeapRegion* r = get_region(idx); + assert(r->pin_count() > 0, "Region %zu in tracker must contain a pin.", idx); + r->record_unpin(); + } + _injected_pin_count = 0; +} + void ShenandoahHeap::initialize_serviceability() { _memory_pool = new ShenandoahMemoryPool(this); _cycle_memory_manager.add_pool(_memory_pool); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index 9810b316c21..171f473d06a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -868,6 +868,19 @@ class ShenandoahHeap : public CollectedHeap { void try_inject_alloc_failure(); bool should_inject_alloc_failure(); + + // Randomly pin a region when ShenandoahPinRegionRate > 0. Pin injection is only called after + // the cycle has populated _live_data and runs concurrently on the control thread. Releasing + // injected pins is done at the start of every cycle preventing stale pinned region states. + void try_inject_pin(); + void release_injected_pins(); + + // Maximum number of regions that can be injected with pins. + static const uint MAX_INJECTED_PINS = 32; + + // Tracker for injected pins added by try_inject_pin(). + size_t _injected_pin_indices[MAX_INJECTED_PINS]; + uint _injected_pin_count; }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHHEAP_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp index df41069d922..c98b96c689b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp @@ -84,6 +84,7 @@ bool ShenandoahOldGC::collect(GCCause::Cause cause) { auto heap = ShenandoahGenerationalHeap::heap(); assert(!_old_generation->is_doing_mixed_evacuations(), "Should not start an old gc with pending mixed evacuations"); assert(!_old_generation->is_preparing_for_mark(), "Old regions need to be parsable during concurrent mark."); + heap->release_injected_pins(); // Enable preemption of old generation mark. _allow_preemption.set(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp index 3647a818490..793b2f3b6d1 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp @@ -461,6 +461,12 @@ product(bool, ShenandoahAllocFailureALot, false, DIAGNOSTIC, \ "Testing: make lots of artificial allocation failures.") \ \ + product(uintx, ShenandoahPinRegionRate, 0, DIAGNOSTIC, \ + "Testing: rate at which to artificially pin regions. Expressed " \ + "as N in 1000 chances for a region to be randomly pinned per " \ + "injection attempt.") \ + range(0, 1000) \ + \ product(uintx, ShenandoahCoalesceChance, 0, DIAGNOSTIC, \ "Testing: Abandon remaining mixed collections with this " \ "likelihood. Following each mixed collection, abandon all " \ diff --git a/test/hotspot/jtreg/gc/TestAllocHumongousFragment.java b/test/hotspot/jtreg/gc/TestAllocHumongousFragment.java index 446cf3c27bb..bcd6e33c81e 100644 --- a/test/hotspot/jtreg/gc/TestAllocHumongousFragment.java +++ b/test/hotspot/jtreg/gc/TestAllocHumongousFragment.java @@ -71,6 +71,11 @@ * * @run main/othervm -Xmx1g -Xms1g -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:ShenandoahTargetNumRegions=2048 * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 -XX:+ShenandoahVerify + * TestAllocHumongousFragment + * + * @run main/othervm -Xmx1g -Xms1g -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:ShenandoahTargetNumRegions=2048 + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * -XX:+ShenandoahOOMDuringEvacALot * TestAllocHumongousFragment * @@ -78,6 +83,11 @@ * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * -XX:+ShenandoahAllocFailureALot * TestAllocHumongousFragment + * + * @run main/othervm -Xmx1g -Xms1g -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:ShenandoahTargetNumRegions=2048 + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 + * TestAllocHumongousFragment */ /* diff --git a/test/hotspot/jtreg/gc/shenandoah/TestAllocIntArrays.java b/test/hotspot/jtreg/gc/shenandoah/TestAllocIntArrays.java index 457af294f6f..8488b8f4a8d 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestAllocIntArrays.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestAllocIntArrays.java @@ -70,6 +70,11 @@ * * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 -XX:+ShenandoahVerify + * TestAllocIntArrays + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * -XX:+ShenandoahOOMDuringEvacALot * TestAllocIntArrays * @@ -80,6 +85,11 @@ * * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 + * TestAllocIntArrays + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * TestAllocIntArrays */ diff --git a/test/hotspot/jtreg/gc/shenandoah/TestAllocObjectArrays.java b/test/hotspot/jtreg/gc/shenandoah/TestAllocObjectArrays.java index 1df8f7453f7..bc8c451450c 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestAllocObjectArrays.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestAllocObjectArrays.java @@ -70,6 +70,11 @@ * * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 -XX:+ShenandoahVerify + * TestAllocObjectArrays + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * -XX:+ShenandoahOOMDuringEvacALot * TestAllocObjectArrays * @@ -80,6 +85,11 @@ * * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 + * TestAllocObjectArrays + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * TestAllocObjectArrays */ @@ -126,6 +136,12 @@ * * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -XX:ShenandoahPinRegionRate=1000 + * -XX:+ShenandoahVerify + * TestAllocObjectArrays + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational * TestAllocObjectArrays */ diff --git a/test/hotspot/jtreg/gc/shenandoah/TestAllocObjects.java b/test/hotspot/jtreg/gc/shenandoah/TestAllocObjects.java index fa6f3ab9b04..a1d06945b79 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestAllocObjects.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestAllocObjects.java @@ -64,6 +64,11 @@ * -XX:+ShenandoahAllocFailureALot -XX:+ShenandoahVerify * TestAllocObjects * + * @run main/othervm/timeout=480 -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 -XX:+ShenandoahVerify + * TestAllocObjects + * * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * -XX:+ShenandoahOOMDuringEvacALot @@ -76,6 +81,11 @@ * * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 + * TestAllocObjects + * + * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * TestAllocObjects */ diff --git a/test/hotspot/jtreg/gc/shenandoah/TestJcmdHeapDump.java b/test/hotspot/jtreg/gc/shenandoah/TestJcmdHeapDump.java index 1b607bf96ca..e790851e2e8 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestJcmdHeapDump.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestJcmdHeapDump.java @@ -58,6 +58,11 @@ * * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 + * TestJcmdHeapDump + * + * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * TestJcmdHeapDump */ diff --git a/test/hotspot/jtreg/gc/shenandoah/TestLotsOfCycles.java b/test/hotspot/jtreg/gc/shenandoah/TestLotsOfCycles.java index 569406fa95c..fbf3cd5c34b 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestLotsOfCycles.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestLotsOfCycles.java @@ -58,6 +58,12 @@ * * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 + * -Dtarget=1000 + * TestLotsOfCycles + * + * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * -Dtarget=1000 * TestLotsOfCycles */ diff --git a/test/hotspot/jtreg/gc/shenandoah/TestRetainObjects.java b/test/hotspot/jtreg/gc/shenandoah/TestRetainObjects.java index d25c8dd0f5e..010bdb5e4f1 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestRetainObjects.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestRetainObjects.java @@ -66,6 +66,11 @@ * * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 + * TestRetainObjects + * + * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * TestRetainObjects */ @@ -106,6 +111,11 @@ * * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -XX:ShenandoahPinRegionRate=1000 -XX:+ShenandoahVerify + * TestRetainObjects + * + * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational * TestRetainObjects */ diff --git a/test/hotspot/jtreg/gc/shenandoah/TestSieveObjects.java b/test/hotspot/jtreg/gc/shenandoah/TestSieveObjects.java index 79259168bf3..fa140d62a66 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestSieveObjects.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestSieveObjects.java @@ -68,6 +68,11 @@ * -XX:+ShenandoahAllocFailureALot * TestSieveObjects * + * @run main/othervm/timeout=240 -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 + * TestSieveObjects + * * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * TestSieveObjects @@ -108,6 +113,11 @@ * -XX:+ShenandoahAllocFailureALot -XX:+ShenandoahVerify * TestSieveObjects * + * @run main/othervm/timeout=480 -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -XX:ShenandoahPinRegionRate=1000 -XX:+ShenandoahVerify + * TestSieveObjects + * * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational * TestSieveObjects diff --git a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java index 3bf0e59dce3..532bf6c07de 100644 --- a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java +++ b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java @@ -66,6 +66,11 @@ * * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 + * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 + * + * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 */ @@ -144,6 +149,12 @@ * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * -XX:+DeoptimizeNMethodBarriersALot -XX:-Inline + * -XX:ShenandoahPinRegionRate=1000 + * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 + * + * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:+DeoptimizeNMethodBarriersALot -XX:-Inline * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 */ diff --git a/test/hotspot/jtreg/gc/stress/gcold/TestGCOldWithShenandoah.java b/test/hotspot/jtreg/gc/stress/gcold/TestGCOldWithShenandoah.java index 5418bb12492..9b2eb530b2a 100644 --- a/test/hotspot/jtreg/gc/stress/gcold/TestGCOldWithShenandoah.java +++ b/test/hotspot/jtreg/gc/stress/gcold/TestGCOldWithShenandoah.java @@ -71,6 +71,11 @@ * * @run main/othervm -Xmx384M -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive + * -XX:ShenandoahPinRegionRate=1000 + * gc.stress.gcold.TestGCOld 50 1 20 10 10000 + * + * @run main/othervm -Xmx384M -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * gc.stress.gcold.TestGCOld 50 1 20 10 10000 */ From ff32e76f4892c0b4c0fbe1efa1ebf7b94023870b Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Wed, 8 Jul 2026 05:44:30 +0000 Subject: [PATCH 174/707] 8387865: ThisEscapeAnalyzer crashes for erroneous source code Reviewed-by: asotona, vromero --- .../tools/javac/comp/ThisEscapeAnalyzer.java | 3 +- .../tools/javac/recovery/AttrRecovery.java | 29 ++++++++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java index 6fb1feed08d..e24b331dd63 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java @@ -873,11 +873,10 @@ public void visitLambda(JCLambda lambda) { @Override public void visitAssign(JCAssign tree) { - VarSymbol sym = (VarSymbol)TreeInfo.symbolFor(tree.lhs); scan(tree.lhs); refs.discardExprs(depth); scan(tree.rhs); - if (isParamOrVar(sym)) + if (TreeInfo.symbolFor(tree.lhs) instanceof VarSymbol sym && isParamOrVar(sym)) refs.replaceExprs(depth, ref -> new VarRef(sym, ref)); else refs.discardExprs(depth); // we don't track fields yet diff --git a/test/langtools/tools/javac/recovery/AttrRecovery.java b/test/langtools/tools/javac/recovery/AttrRecovery.java index 64aaad2a184..fb852b5b3f2 100644 --- a/test/langtools/tools/javac/recovery/AttrRecovery.java +++ b/test/langtools/tools/javac/recovery/AttrRecovery.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8301580 8322159 8333107 8332230 8338678 8351260 8366196 8372336 8373094 8384229 + * @bug 8301580 8322159 8333107 8332230 8338678 8351260 8366196 8372336 8373094 8384229 8387865 * @summary Verify error recovery w.r.t. Attr * @library /tools/lib * @modules jdk.compiler/com.sun.tools.javac.api @@ -860,6 +860,33 @@ class Test { .writeAll(); } + @Test //JDK-8387865 + public void testThisEscapeUnknownField() throws Exception { + String code = """ + public class C { + public C() { + this.unknown = unknown; + } + } + """; + List actual = new JavacTask(tb) + .options("-XDrawDiagnostics", "-XDdev", + "-XDshould-stop.at=WARN", "-Xlint:this-escape") + .sources(code) + .outdir(base) + .run(Expect.FAIL) + .writeAll() + .getOutputLines(OutputKind.DIRECT); + + List expected = List.of( + "C.java:3:13: compiler.err.cant.resolve: kindname.variable, unknown, , ", + "C.java:3:24: compiler.err.cant.resolve.location: kindname.variable, unknown, , , (compiler.misc.location: kindname.class, C, null)", + "2 errors" + ); + + assertEquals(expected, actual); + } + @BeforeEach public void setUp(TestInfo info) throws IOException { base = Path.of(info.getTestMethod().orElseThrow().getName()); From 597053969e4055227d1931d4bce8e96930bd997f Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Wed, 8 Jul 2026 06:01:28 +0000 Subject: [PATCH 175/707] 8387754: G1: Let Eden/SurvivorRegions use Atomic instead of volatile Reviewed-by: aboldtch --- src/hotspot/share/gc/g1/g1EdenRegions.hpp | 14 +++++++------- src/hotspot/share/gc/g1/g1RegionsOnNodes.cpp | 12 +++++------- src/hotspot/share/gc/g1/g1RegionsOnNodes.hpp | 5 +++-- src/hotspot/share/gc/g1/g1SurvivorRegions.cpp | 6 +++--- src/hotspot/share/gc/g1/g1SurvivorRegions.hpp | 7 ++++--- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1EdenRegions.hpp b/src/hotspot/share/gc/g1/g1EdenRegions.hpp index 7cb4f93519e..14a2eb65329 100644 --- a/src/hotspot/share/gc/g1/g1EdenRegions.hpp +++ b/src/hotspot/share/gc/g1/g1EdenRegions.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 @@ -27,15 +27,15 @@ #include "gc/g1/g1HeapRegion.hpp" #include "gc/g1/g1RegionsOnNodes.hpp" +#include "runtime/atomic.hpp" #include "runtime/globals.hpp" #include "utilities/debug.hpp" class G1EdenRegions { -private: - uint _length; + uint _length; // Sum of used bytes from all retired eden regions. // I.e. updated when mutator regions are retired. - volatile size_t _used_bytes; + Atomic _used_bytes; G1RegionsOnNodes _regions_on_node; public: @@ -49,17 +49,17 @@ class G1EdenRegions { void clear() { _length = 0; - _used_bytes = 0; + _used_bytes.store_relaxed(0); _regions_on_node.clear(); } uint length() const { return _length; } uint regions_on_node(uint node_index) const { return _regions_on_node.count(node_index); } - size_t used_bytes() const { return _used_bytes; } + size_t used_bytes() const { return _used_bytes.load_relaxed(); } void add_used_bytes(size_t used_bytes) { - _used_bytes += used_bytes; + _used_bytes.add_then_fetch(used_bytes, memory_order_relaxed); } }; diff --git a/src/hotspot/share/gc/g1/g1RegionsOnNodes.cpp b/src/hotspot/share/gc/g1/g1RegionsOnNodes.cpp index 9550e57698e..2e509e79ec3 100644 --- a/src/hotspot/share/gc/g1/g1RegionsOnNodes.cpp +++ b/src/hotspot/share/gc/g1/g1RegionsOnNodes.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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 @@ -27,7 +27,7 @@ #include "gc/g1/g1RegionsOnNodes.hpp" G1RegionsOnNodes::G1RegionsOnNodes() : _count_per_node(nullptr), _numa(G1NUMA::numa()) { - _count_per_node = NEW_C_HEAP_ARRAY(uint, _numa->num_active_nodes(), mtGC); + _count_per_node = NEW_C_HEAP_ARRAY(Atomic, _numa->num_active_nodes(), mtGC); clear(); } @@ -40,16 +40,14 @@ void G1RegionsOnNodes::add(G1HeapRegion* hr) { // Update only if the node index is valid. if (node_index < _numa->num_active_nodes()) { - *(_count_per_node + node_index) += 1; + _count_per_node[node_index].add_then_fetch(1u, memory_order_relaxed); } } void G1RegionsOnNodes::clear() { - for (uint i = 0; i < _numa->num_active_nodes(); i++) { - _count_per_node[i] = 0; - } + ::new (_count_per_node) Atomic[_numa->num_active_nodes()]{}; } uint G1RegionsOnNodes::count(uint node_index) const { - return _count_per_node[node_index]; + return _count_per_node[node_index].load_relaxed(); } diff --git a/src/hotspot/share/gc/g1/g1RegionsOnNodes.hpp b/src/hotspot/share/gc/g1/g1RegionsOnNodes.hpp index fb1f2381dba..e528a147150 100644 --- a/src/hotspot/share/gc/g1/g1RegionsOnNodes.hpp +++ b/src/hotspot/share/gc/g1/g1RegionsOnNodes.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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,13 +26,14 @@ #define SHARE_VM_GC_G1_G1REGIONS_HPP #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" class G1NUMA; class G1HeapRegion; // Contains per node index region count class G1RegionsOnNodes : public StackObj { - volatile uint* _count_per_node; + Atomic* _count_per_node; G1NUMA* _numa; public: diff --git a/src/hotspot/share/gc/g1/g1SurvivorRegions.cpp b/src/hotspot/share/gc/g1/g1SurvivorRegions.cpp index 84609df4fc9..806df4abacb 100644 --- a/src/hotspot/share/gc/g1/g1SurvivorRegions.cpp +++ b/src/hotspot/share/gc/g1/g1SurvivorRegions.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 @@ -55,10 +55,10 @@ void G1SurvivorRegions::convert_to_eden() { void G1SurvivorRegions::clear() { _regions.clear(); - _used_bytes = 0; + _used_bytes.store_relaxed(0); _regions_on_node.clear(); } void G1SurvivorRegions::add_used_bytes(size_t used_bytes) { - _used_bytes += used_bytes; + _used_bytes.add_then_fetch(used_bytes, memory_order_relaxed); } diff --git a/src/hotspot/share/gc/g1/g1SurvivorRegions.hpp b/src/hotspot/share/gc/g1/g1SurvivorRegions.hpp index 4e4966f6797..5ced2fced3c 100644 --- a/src/hotspot/share/gc/g1/g1SurvivorRegions.hpp +++ b/src/hotspot/share/gc/g1/g1SurvivorRegions.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 @@ #define SHARE_GC_G1_G1SURVIVORREGIONS_HPP #include "gc/g1/g1RegionsOnNodes.hpp" +#include "runtime/atomic.hpp" #include "runtime/globals.hpp" #include "utilities/growableArray.hpp" @@ -36,7 +37,7 @@ class G1HeapRegion; // Set of current survivor regions. class G1SurvivorRegions { GrowableArray _regions; - volatile size_t _used_bytes; + Atomic _used_bytes; G1RegionsOnNodes _regions_on_node; public: @@ -56,7 +57,7 @@ class G1SurvivorRegions { } // Used bytes of all survivor regions. - size_t used_bytes() const { return _used_bytes; } + size_t used_bytes() const { return _used_bytes.load_relaxed(); } void add_used_bytes(size_t used_bytes); }; From cc2cd968c5bea6464dae87d2652446c4cb99bdf2 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Wed, 8 Jul 2026 06:01:46 +0000 Subject: [PATCH 176/707] 8387765: G1: Let G1HeapRegionType::_tag use the Atomic API Reviewed-by: aboldtch --- src/hotspot/share/gc/g1/g1HeapRegionType.cpp | 11 +++---- src/hotspot/share/gc/g1/g1HeapRegionType.hpp | 32 ++++++++++++-------- src/hotspot/share/gc/g1/vmStructs_g1.hpp | 6 ++-- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1HeapRegionType.cpp b/src/hotspot/share/gc/g1/g1HeapRegionType.cpp index ba6bf7e870d..c62bac8bcbc 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionType.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionType.cpp @@ -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 @@ -45,8 +45,7 @@ bool G1HeapRegionType::is_valid(Tag tag) { } const char* G1HeapRegionType::get_str() const { - hrt_assert_is_valid(_tag); - switch (_tag) { + switch (get()) { case FreeTag: return "FREE"; case EdenTag: return "EDEN"; case SurvTag: return "SURV"; @@ -60,8 +59,7 @@ const char* G1HeapRegionType::get_str() const { } const char* G1HeapRegionType::get_short_str() const { - hrt_assert_is_valid(_tag); - switch (_tag) { + switch (get()) { case FreeTag: return "F"; case EdenTag: return "E"; case SurvTag: return "S"; @@ -75,8 +73,7 @@ const char* G1HeapRegionType::get_short_str() const { } G1HeapRegionTraceType::Type G1HeapRegionType::get_trace_type() { - hrt_assert_is_valid(_tag); - switch (_tag) { + switch (get()) { case FreeTag: return G1HeapRegionTraceType::Free; case EdenTag: return G1HeapRegionTraceType::Eden; case SurvTag: return G1HeapRegionTraceType::Survivor; diff --git a/src/hotspot/share/gc/g1/g1HeapRegionType.hpp b/src/hotspot/share/gc/g1/g1HeapRegionType.hpp index 92d3efc2f87..3ffa7faecff 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionType.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionType.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, 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 @@ -26,6 +26,7 @@ #define SHARE_GC_G1_G1HEAPREGIONTYPE_HPP #include "gc/g1/g1HeapRegionTraceType.hpp" +#include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" #define hrt_assert_is_valid(tag) \ @@ -34,7 +35,6 @@ class G1HeapRegionType { friend class VMStructs; -private: // We encode the value of the heap region type so the generation can be // determined quickly. The tag is split into two parts: // @@ -73,20 +73,21 @@ class G1HeapRegionType { OldTag = OldMask } Tag; - volatile Tag _tag; + Atomic _tag; static bool is_valid(Tag tag); Tag get() const { - hrt_assert_is_valid(_tag); - return _tag; + Tag result = _tag.load_relaxed(); + hrt_assert_is_valid(result); + return result; } // Sets the type to 'tag'. void set(Tag tag) { hrt_assert_is_valid(tag); - hrt_assert_is_valid(_tag); - _tag = tag; + hrt_assert_is_valid(_tag.load_relaxed()); + _tag.store_relaxed(tag); } // Sets the type to 'tag', expecting the type to be 'before'. This @@ -95,13 +96,12 @@ class G1HeapRegionType { void set_from(Tag tag, Tag before) { hrt_assert_is_valid(tag); hrt_assert_is_valid(before); - hrt_assert_is_valid(_tag); - assert(_tag == before, "HR tag: %u, expected: %u new tag; %u", _tag, before, tag); - _tag = tag; + assert(get() == before, "HR tag: %u, expected: %u new tag; %u", get(), before, tag); + _tag.store_relaxed(tag); } // Private constructor used for static constants - G1HeapRegionType(Tag t) : _tag(t) { hrt_assert_is_valid(_tag); } + G1HeapRegionType(Tag t) : _tag(t) { hrt_assert_is_valid(t); } public: // Queries @@ -159,7 +159,15 @@ class G1HeapRegionType { const char* get_short_str() const; G1HeapRegionTraceType::Type get_trace_type(); - G1HeapRegionType() : _tag(FreeTag) { hrt_assert_is_valid(_tag); } + G1HeapRegionType() : G1HeapRegionType(FreeTag) { } + + G1HeapRegionType(const G1HeapRegionType& other) : G1HeapRegionType(other.get()) { } + G1HeapRegionType& operator=(const G1HeapRegionType& other) { + if (this != &other) { + set(other.get()); + } + return *this; + } static const G1HeapRegionType Eden; static const G1HeapRegionType Survivor; diff --git a/src/hotspot/share/gc/g1/vmStructs_g1.hpp b/src/hotspot/share/gc/g1/vmStructs_g1.hpp index e0179b69646..23beb75211b 100644 --- a/src/hotspot/share/gc/g1/vmStructs_g1.hpp +++ b/src/hotspot/share/gc/g1/vmStructs_g1.hpp @@ -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 @@ -44,7 +44,7 @@ nonstatic_field(G1HeapRegion, _end, HeapWord* const) \ nonstatic_field(G1HeapRegion, _pinned_object_count, Atomic) \ \ - nonstatic_field(G1HeapRegionType, _tag, G1HeapRegionType::Tag volatile) \ + nonstatic_field(G1HeapRegionType, _tag, Atomic) \ \ \ nonstatic_field(G1HeapRegionTable, _base, address) \ @@ -104,6 +104,6 @@ declare_toplevel_type(G1HeapRegion*) \ declare_toplevel_type(G1MonitoringSupport*) \ \ - declare_integer_type(G1HeapRegionType::Tag volatile) + declare_integer_type(Atomic) #endif // SHARE_GC_G1_VMSTRUCTS_G1_HPP From cef023b03b6075bf9c0855935fa78ac6e79add3d Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Wed, 8 Jul 2026 06:50:30 +0000 Subject: [PATCH 177/707] 8386705: Parallel: Allow NUMA with explicit huge pages and adaptive resizing Reviewed-by: tschatzl, mbaesken --- src/hotspot/os/linux/os_linux.cpp | 14 -------------- src/hotspot/share/gc/parallel/mutableNUMASpace.cpp | 2 +- src/hotspot/share/gc/parallel/mutableSpace.cpp | 2 +- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index ad1f384fa32..aad18edf2a6 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -4662,20 +4662,6 @@ void os::Linux::numa_init() { if (UseNUMA && !UseNUMAInterleaving) { FLAG_SET_ERGO_IF_DEFAULT(UseNUMAInterleaving, true); } - -#if INCLUDE_PARALLELGC - if (UseParallelGC && UseNUMA && UseLargePages && !can_commit_large_page_memory()) { - // With static large pages we cannot uncommit a page, so there's no way - // we can make the adaptive lgrp chunk resizing work. If the user specified both - // UseNUMA and UseLargePages on the command line - warn and disable adaptive resizing. - if (UseAdaptiveSizePolicy || UseAdaptiveNUMAChunkSizing) { - warning("UseNUMA is not fully compatible with +UseLargePages, " - "disabling adaptive resizing (-XX:-UseAdaptiveSizePolicy -XX:-UseAdaptiveNUMAChunkSizing)"); - UseAdaptiveSizePolicy = false; - UseAdaptiveNUMAChunkSizing = false; - } - } -#endif } void os::Linux::disable_numa(const char* reason, bool warning) { diff --git a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp index 8b514fe7199..ca4e77bab8e 100644 --- a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp +++ b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp @@ -154,7 +154,7 @@ void MutableNUMASpace::bias_region(MemRegion mr, uint lgrp_id) { // First we tell the OS which page size we want in the given range. The underlying // large page can be broken down if we require small pages. os::realign_memory((char*) mr.start(), mr.byte_size(), page_size()); - // Then we uncommit the pages in the range. + // Then we disclaim the pages in the range so they can be faulted in again. os::disclaim_memory((char*) mr.start(), mr.byte_size()); // And make them local/first-touch biased. os::numa_make_local((char*)mr.start(), mr.byte_size(), checked_cast(lgrp_id)); diff --git a/src/hotspot/share/gc/parallel/mutableSpace.cpp b/src/hotspot/share/gc/parallel/mutableSpace.cpp index d99db493989..9b10f9faee2 100644 --- a/src/hotspot/share/gc/parallel/mutableSpace.cpp +++ b/src/hotspot/share/gc/parallel/mutableSpace.cpp @@ -49,7 +49,7 @@ void MutableSpace::numa_setup_pages(MemRegion mr, bool clear_space) { } if (clear_space) { - // Prefer page reallocation to migration. + // Prefer page discard and refault under the requested NUMA policy to migration. os::disclaim_memory((char*) mr.start(), mr.byte_size()); } os::numa_make_global((char*) mr.start(), mr.byte_size()); From 2dc0c1c5a90610460a68803655be351c85bec9e3 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Wed, 8 Jul 2026 07:54:18 +0000 Subject: [PATCH 178/707] 8358342: G1: G1CodeRootSet performance breaks down on even moderate load Reviewed-by: aboldtch --- src/hotspot/share/gc/g1/g1CodeRootSet.cpp | 35 +++++++- src/hotspot/share/gc/g1/g1CodeRootSet.hpp | 6 +- src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp | 24 ++--- src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp | 23 ++--- .../share/gc/g1/g1HeapRegionRemSet.cpp | 4 + .../share/gc/g1/g1HeapRegionRemSet.hpp | 1 + src/hotspot/share/gc/g1/g1NMethodClosure.cpp | 46 ++++++++-- src/hotspot/share/gc/g1/g1NMethodClosure.hpp | 19 ++-- .../share/gc/g1/g1ParScanThreadState.cpp | 89 ++++++++++++++++--- .../share/gc/g1/g1ParScanThreadState.hpp | 36 +++++++- .../gc/g1/g1ParScanThreadState.inline.hpp | 33 ++++++- src/hotspot/share/gc/g1/g1Policy.cpp | 6 +- src/hotspot/share/gc/g1/g1RemSet.cpp | 2 + src/hotspot/share/gc/g1/g1RootClosures.hpp | 4 +- src/hotspot/share/gc/g1/g1SharedClosures.hpp | 4 +- .../gc/g1/g1YoungGCPostEvacuateTasks.cpp | 84 +++++++++++++---- .../gc/g1/g1YoungGCPostEvacuateTasks.hpp | 11 ++- .../jtreg/gc/g1/TestGCLogMessages.java | 5 +- .../gc/collection/TestG1ParallelPhases.java | 7 +- 19 files changed, 358 insertions(+), 81 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CodeRootSet.cpp b/src/hotspot/share/gc/g1/g1CodeRootSet.cpp index ca4487876b9..7f1dec462d4 100644 --- a/src/hotspot/share/gc/g1/g1CodeRootSet.cpp +++ b/src/hotspot/share/gc/g1/g1CodeRootSet.cpp @@ -196,12 +196,12 @@ class G1CodeRootSetHashTable : public CHeapObj { clean(delete_check); } - // Calculate the log2 of the table size we want to shrink to. - size_t log2_target_shrink_size(size_t current_size) const { + // Calculate the log2 of the table size we want to change to. + size_t log2_target_size(size_t new_size) const { // A table with the new size should be at most filled by this factor. Otherwise // we would grow again quickly. const float WantedLoadFactor = 0.5; - size_t min_expected_size = checked_cast(ceil(current_size / WantedLoadFactor)); + size_t min_expected_size = checked_cast(ceil(new_size / WantedLoadFactor)); size_t result = Log2DefaultNumBuckets; if (min_expected_size != 0) { @@ -214,12 +214,34 @@ class G1CodeRootSetHashTable : public CHeapObj { // Shrink to keep table size appropriate to the given number of entries. void shrink_to_match(size_t current_size) { size_t prev_log2size = _table.get_size_log2(Thread::current()); - size_t new_log2_table_size = log2_target_shrink_size(current_size); + size_t new_log2_table_size = log2_target_size(current_size); if (new_log2_table_size < prev_log2size) { _table.shrink(Thread::current(), new_log2_table_size); } } + void grow_to_match_unsafe(size_t new_size) { + assert_at_safepoint(); + + size_t prev_log2size = _table.get_size_log2(Thread::current()); + size_t new_log2_table_size = log2_target_size(new_size); + // If there is nothing in the table, we can reset directly. Otherwise double + // the table in size until the target is reached, which is the only grow + // operation CHT supports. + if ((prev_log2size != new_log2_table_size) && (number_of_entries() == 0)) { + _table.unsafe_reset(new_log2_table_size); + } else { + while (new_log2_table_size > prev_log2size) { + if (!_table.grow(Thread::current(), new_log2_table_size)) { + // Should always succeed during safepoint. + ShouldNotReachHere(); + break; + } + prev_log2size = _table.get_size_log2(Thread::current()); + } + } + } + void reset_table_scanner() { _table_scanner.set(&_table, BucketClaimSize); } @@ -269,6 +291,11 @@ void G1CodeRootSet::bulk_remove() { _table->bulk_remove(); } +void G1CodeRootSet::prepare_for_adding_code_roots(size_t num_new_code_roots) { + assert(!_is_iterating, "should not mutate while iterating the table"); + _table->grow_to_match_unsafe(_table->number_of_entries() + num_new_code_roots); +} + bool G1CodeRootSet::contains(nmethod* method) { return _table->contains(method); } diff --git a/src/hotspot/share/gc/g1/g1CodeRootSet.hpp b/src/hotspot/share/gc/g1/g1CodeRootSet.hpp index ffa1cddbe54..b298bbfb914 100644 --- a/src/hotspot/share/gc/g1/g1CodeRootSet.hpp +++ b/src/hotspot/share/gc/g1/g1CodeRootSet.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, 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 @@ -45,6 +45,10 @@ class G1CodeRootSet { void add(nmethod* method); bool remove(nmethod* method); void bulk_remove(); + // Notify the code root set that we are about to add the given + // number of code roots. Only to be used during safepoint, not + // in parallel to other modifications. + void prepare_for_adding_code_roots(size_t num_code_roots); bool contains(nmethod* method); void clear(); diff --git a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp index a04b50ec1e7..e5bf8137811 100644 --- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp +++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp @@ -94,7 +94,8 @@ G1GCPhaseTimes::G1GCPhaseTimes(STWGCTimer* gc_timer, uint max_gc_threads) : _gc_par_phases[GCWorkerTotal] = new WorkerDataArray("GCWorkerTotal", "GC Worker Total (ms):", max_gc_threads); _gc_par_phases[GCWorkerEnd] = new WorkerDataArray("GCWorkerEnd", "GC Worker End (ms):", max_gc_threads); _gc_par_phases[Other] = new WorkerDataArray("Other", "GC Worker Other (ms):", max_gc_threads); - _gc_par_phases[MergePSS] = new WorkerDataArray("MergePSS", "Merge Per-Thread State (ms):", max_gc_threads); + _gc_par_phases[FlushPSS] = new WorkerDataArray("FlushPSS", "Flush Per-Thread State (ms):", max_gc_threads); + _gc_par_phases[DestroyPSS] = new WorkerDataArray("DestroyPSS", "Destroy Per-Thread State (ms):", max_gc_threads); _gc_par_phases[RestoreEvacuationFailedRegions] = new WorkerDataArray("RestoreEvacuationFailedRegions", "Restore Evacuation Failed Regions (ms):", max_gc_threads); _gc_par_phases[RemoveSelfForwards] = new WorkerDataArray("RemoveSelfForwards", "Remove Self Forwards (ms):", max_gc_threads); _gc_par_phases[ClearCardTable] = new WorkerDataArray("ClearPendingCards", "Clear Pending Cards (ms):", max_gc_threads); @@ -103,7 +104,7 @@ G1GCPhaseTimes::G1GCPhaseTimes(STWGCTimer* gc_timer, uint max_gc_threads) : _gc_par_phases[UpdateDerivedPointers] = new WorkerDataArray("UpdateDerivedPointers", "Update Derived Pointers (ms):", max_gc_threads); #endif // COMPILER2 _gc_par_phases[EagerlyReclaimHumongousObjects] = new WorkerDataArray("EagerlyReclaimHumongousObjects", "Eagerly Reclaim Humongous Objects (ms):", max_gc_threads); - _gc_par_phases[ResetPartialArrayStateManager] = new WorkerDataArray("ResetPartialArrayStateManager", "Reset Partial Array State Manager (ms):", max_gc_threads); + _gc_par_phases[UpdateCodeRoots] = new WorkerDataArray("UpdateCodeRoots", "Update Code Roots (ms):", _max_gc_threads); _gc_par_phases[ProcessEvacuationFailedRegions] = new WorkerDataArray("ProcessEvacuationFailedRegions", "Process Evacuation Failed Regions (ms):", max_gc_threads); _gc_par_phases[ScanHR]->create_thread_work_items("Pending Cards:", ScanHRPendingCards); @@ -126,13 +127,13 @@ G1GCPhaseTimes::G1GCPhaseTimes(STWGCTimer* gc_timer, uint max_gc_threads) : _gc_par_phases[OptCodeRoots]->create_thread_work_items("Scanned Nmethods:", CodeRootsScannedNMethods); - _gc_par_phases[MergePSS]->create_thread_work_items("Copied Bytes:", MergePSSCopiedBytes); - _gc_par_phases[MergePSS]->create_thread_work_items("LAB Waste:", MergePSSLABWasteBytes); - _gc_par_phases[MergePSS]->create_thread_work_items("LAB Undo Waste:", MergePSSLABUndoWasteBytes); - _gc_par_phases[MergePSS]->create_thread_work_items("Pending Cards:", MergePSSPendingCards); - _gc_par_phases[MergePSS]->create_thread_work_items("To-Young-Gen Cards:", MergePSSToYoungGenCards); - _gc_par_phases[MergePSS]->create_thread_work_items("Evac-Fail Cards:", MergePSSEvacFail); - _gc_par_phases[MergePSS]->create_thread_work_items("Marked Cards:", MergePSSMarked); + _gc_par_phases[FlushPSS]->create_thread_work_items("Copied Bytes:", FlushPSSCopiedBytes); + _gc_par_phases[FlushPSS]->create_thread_work_items("LAB Waste:", FlushPSSLABWasteBytes); + _gc_par_phases[FlushPSS]->create_thread_work_items("LAB Undo Waste:", FlushPSSLABUndoWasteBytes); + _gc_par_phases[FlushPSS]->create_thread_work_items("Pending Cards:", FlushPSSPendingCards); + _gc_par_phases[FlushPSS]->create_thread_work_items("To-Young-Gen Cards:", FlushPSSToYoungGenCards); + _gc_par_phases[FlushPSS]->create_thread_work_items("Evac-Fail Cards:", FlushPSSEvacFail); + _gc_par_phases[FlushPSS]->create_thread_work_items("Marked Cards:", FlushPSSMarked); _gc_par_phases[RestoreEvacuationFailedRegions]->create_thread_work_items("Evacuation Failed Regions:", RestoreEvacFailureRegionsEvacFailedNum); _gc_par_phases[RestoreEvacuationFailedRegions]->create_thread_work_items("Pinned Regions:", RestoreEvacFailureRegionsPinnedNum); @@ -495,7 +496,8 @@ double G1GCPhaseTimes::print_post_evacuate_collection_set(bool evacuation_failed _weak_phase_times.log_subtotals(3); debug_time("Post Evacuate Cleanup 1", _cur_post_evacuate_cleanup_1_time_ms); - debug_phase(_gc_par_phases[MergePSS], 1); + debug_phase(_gc_par_phases[FlushPSS], 1); + debug_phase(_gc_par_phases[UpdateCodeRoots], 1); debug_phase(_gc_par_phases[ClearCardTable], 1); debug_phase(_gc_par_phases[RecalculateUsed], 1); if (evacuation_failed) { @@ -512,7 +514,7 @@ double G1GCPhaseTimes::print_post_evacuate_collection_set(bool evacuation_failed debug_phase(_gc_par_phases[UpdateDerivedPointers], 1); #endif // COMPILER2 debug_phase(_gc_par_phases[EagerlyReclaimHumongousObjects], 1); - trace_phase(_gc_par_phases[ResetPartialArrayStateManager]); + trace_phase(_gc_par_phases[DestroyPSS]); if (G1CollectedHeap::heap()->should_sample_collection_set_candidates()) { debug_phase(_gc_par_phases[SampleCollectionSetCandidates], 1); diff --git a/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp b/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp index 31bfd38ddb9..078a819986c 100644 --- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp +++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp @@ -76,7 +76,7 @@ class G1GCPhaseTimes : public CHeapObj { ResizeThreadLABs, RebuildFreeList, SampleCollectionSetCandidates, - MergePSS, + FlushPSS, RestoreEvacuationFailedRegions, RemoveSelfForwards, ClearCardTable, @@ -85,7 +85,8 @@ class G1GCPhaseTimes : public CHeapObj { UpdateDerivedPointers, #endif // COMPILER2 EagerlyReclaimHumongousObjects, - ResetPartialArrayStateManager, + UpdateCodeRoots, + DestroyPSS, ProcessEvacuationFailedRegions, ResetMarkingState, NoteStartOfMark, @@ -134,15 +135,15 @@ class G1GCPhaseTimes : public CHeapObj { CodeRootsScannedNMethods }; - enum GCMergePSSWorkItems { - MergePSSCopiedBytes, - MergePSSLABSize, - MergePSSLABWasteBytes, - MergePSSLABUndoWasteBytes, - MergePSSPendingCards, // To be scanned cards generated by GC (from cross-references and evacuation failure). - MergePSSToYoungGenCards, // To-young-gen cards generated by GC. - MergePSSEvacFail, // Evacuation failure generated dirty cards by GC. - MergePSSMarked, // Total newly marked cards. + enum GCFlushPSSWorkItems { + FlushPSSCopiedBytes, + FlushPSSLABSize, + FlushPSSLABWasteBytes, + FlushPSSLABUndoWasteBytes, + FlushPSSPendingCards, // To be scanned cards generated by GC (from cross-references and evacuation failure). + FlushPSSToYoungGenCards, // To-young-gen cards generated by GC. + FlushPSSEvacFail, // Evacuation failure generated dirty cards by GC. + FlushPSSMarked, // Total newly marked cards. }; enum RestoreEvacFailureRegionsWorkItems { diff --git a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp index ef42538d4d6..e2009b0e77d 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp @@ -132,6 +132,10 @@ void G1HeapRegionRemSet::bulk_remove_code_roots() { _code_roots.bulk_remove(); } +void G1HeapRegionRemSet::prepare_for_adding_code_roots(size_t num_code_roots) { + _code_roots.prepare_for_adding_code_roots(num_code_roots); +} + void G1HeapRegionRemSet::code_roots_do(NMethodClosure* blk) const { _code_roots.nmethods_do(blk); } diff --git a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp index 2e97d6a7597..20f7b785f45 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp @@ -182,6 +182,7 @@ class G1HeapRegionRemSet : public CHeapObj { void add_code_root(nmethod* nm); void remove_code_root(nmethod* nm); void bulk_remove_code_roots(); + void prepare_for_adding_code_roots(size_t num_code_roots); // Applies blk->do_nmethod() to each of the entries in _code_roots void code_roots_do(NMethodClosure* blk) const; diff --git a/src/hotspot/share/gc/g1/g1NMethodClosure.cpp b/src/hotspot/share/gc/g1/g1NMethodClosure.cpp index d74aa5eae1d..d7dcbeb87fb 100644 --- a/src/hotspot/share/gc/g1/g1NMethodClosure.cpp +++ b/src/hotspot/share/gc/g1/g1NMethodClosure.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 @@ -35,13 +35,47 @@ template void G1NMethodClosure::HeapRegionGatheringOopClosure::do_oop_work(T* p) { + T old_oop_or_narrowoop = RawAccess<>::oop_load(p); + _work->do_oop(p); T oop_or_narrowoop = RawAccess<>::oop_load(p); - if (!CompressedOops::is_null(oop_or_narrowoop)) { + // If the oop moved, we need to update the code root set at the new location. If it did not + // change, it is either in the existing code root set, or an earlier evacuation round already + // enqueued it for deferred update. + // + // We defer actual update to the code roots to later. This can, in presence of optional + // collections, ultimately result in duplicates in the per-thread code root set update list. + // We consider this negligible, given that optional collection is rare and typically does + // not cover many regions/nmethods. + if (oop_or_narrowoop != old_oop_or_narrowoop) { + // If the oop moved, it must not have been null. + assert(!CompressedOops::is_null(oop_or_narrowoop), "must be"); oop o = CompressedOops::decode_not_null(oop_or_narrowoop); + assert(!_g1h->is_in_cset(o), "must be"); + G1HeapRegion* hr = _g1h->heap_region_containing(o); - assert(!_g1h->is_in_cset(o) || hr->rem_set()->code_roots_list_contains(_nm), "if o still in collection set then evacuation failed and nm must already be in the remset"); - hr->add_code_root(_nm); + _affected_regions.append_if_missing(hr); + } else { + // We could be tempted to verify that for a non-null oop, the _nm is already in the target code root + // set or in one of the deferred code root set update lists. It would not be sufficient to verify the + // current thread's list, because across evacuation rounds (i.e. initial/multiple optional) different + // threads may have worked on a given oop from an nmethod. + // This is rather expensive, not only requiring looking at all threads' lists, but also making sure + // that there are no memory ordering issues when doing that. So we skip it. + } +} + +G1NMethodClosure::HeapRegionGatheringOopClosure::HeapRegionGatheringOopClosure(OopClosure* oc, G1ParScanThreadState* pss) : + _g1h(G1CollectedHeap::heap()), + _work(oc), + _pss(pss), + _nm(nullptr), + _affected_regions(5) { +} + +void G1NMethodClosure::HeapRegionGatheringOopClosure::add_to_remsets() { + while (!_affected_regions.is_empty()) { + _pss->remember_nmethod_into_region(_affected_regions.pop(), _nm); } } @@ -74,11 +108,13 @@ void G1NMethodClosure::MarkingOopClosure::do_oop(narrowOop* o) { } void G1NMethodClosure::do_evacuation_and_fixup(nmethod* nm) { - _oc.set_nm(nm); + _oc.set_nmethod(nm); // Evacuate objects pointed to by the nmethod nm->oops_do(&_oc); + _oc.add_to_remsets(); + if (_strong) { // CodeCache unloading support nm->mark_as_maybe_on_stack(); diff --git a/src/hotspot/share/gc/g1/g1NMethodClosure.hpp b/src/hotspot/share/gc/g1/g1NMethodClosure.hpp index 91906932d4f..95d0ee1942d 100644 --- a/src/hotspot/share/gc/g1/g1NMethodClosure.hpp +++ b/src/hotspot/share/gc/g1/g1NMethodClosure.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, 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 @@ -27,8 +27,10 @@ #include "gc/g1/g1CollectedHeap.hpp" #include "memory/iterator.hpp" +#include "utilities/growableArray.hpp" class G1ConcurrentMark; +class G1ParScanThreadState; class nmethod; class G1NMethodClosure : public NMethodClosure { @@ -36,20 +38,27 @@ class G1NMethodClosure : public NMethodClosure { class HeapRegionGatheringOopClosure : public OopClosure { G1CollectedHeap* _g1h; OopClosure* _work; + G1ParScanThreadState* _pss; + nmethod* _nm; + GrowableArrayCHeap _affected_regions; template void do_oop_work(T* p); public: - HeapRegionGatheringOopClosure(OopClosure* oc) : _g1h(G1CollectedHeap::heap()), _work(oc), _nm(nullptr) {} + HeapRegionGatheringOopClosure(OopClosure* oc, G1ParScanThreadState* pss); + ~HeapRegionGatheringOopClosure() = default; void do_oop(oop* o); void do_oop(narrowOop* o); - void set_nm(nmethod* nm) { + void set_nmethod(nmethod* nm) { + assert(_affected_regions.is_empty(), "must be"); _nm = nm; } + + void add_to_remsets(); }; // Mark all oops below TAMS. @@ -72,8 +81,8 @@ class G1NMethodClosure : public NMethodClosure { bool _strong; public: - G1NMethodClosure(uint worker_id, OopClosure* oc, bool strong) : - _oc(oc), _marking_oc(worker_id), _strong(strong) { } + G1NMethodClosure(uint worker_id, OopClosure* oc, bool strong, G1ParScanThreadState* pss) : + _oc(oc, pss), _marking_oc(worker_id), _strong(strong) { } void do_evacuation_and_fixup(nmethod* nm); void do_marking(nmethod* nm); diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp index 5a66f64090a..3e6f8758744 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp @@ -55,12 +55,20 @@ // Explicit NOINLINE to block ATTRIBUTE_FLATTENing. #define MAYBE_INLINE_EVACUATION NOT_DEBUG(inline) DEBUG_ONLY(NOINLINE) +// Good estimate for the initial table size. +static uint initial_nmethod_table_size(G1CollectedHeap* g1h) { + // The +1 is both to consider the retained old region likely to be added, and avoid zero-sized initial tables. + return MIN3(g1h->collection_set()->num_regions(), g1h->max_num_regions() / 2, g1h->num_available_regions()) + 1; +} + G1ParScanThreadState::G1ParScanThreadState(G1CollectedHeap* g1h, + G1ParScanThreadStateSet* per_thread_states, uint worker_id, uint num_workers, G1CollectionSet* collection_set, G1EvacFailureRegions* evac_failure_regions) : _g1h(g1h), + _per_thread_states(per_thread_states), _task_queue(g1h->task_queue(worker_id)), _ct(g1h->refinement_table()), _closures(nullptr), @@ -83,6 +91,10 @@ G1ParScanThreadState::G1ParScanThreadState(G1CollectedHeap* g1h, _max_num_optional_regions(collection_set->num_optional_regions()), _numa(g1h->numa()), _obj_alloc_stat(nullptr), + // The initial size estimate is relatively conservative, assuming that all regions + // in the collection set get evacuated into the same amount of new regions. + _nmethods_to_add(initial_nmethod_table_size(g1h), + MAX2(initial_nmethod_table_size(g1h), _g1h->max_num_regions() / 2)), ALLOCATION_FAILURE_INJECTOR_ONLY(_allocation_failure_inject_counter(0) COMMA) _evacuation_failed_info(), _evac_failure_regions(evac_failure_regions), @@ -129,6 +141,12 @@ size_t G1ParScanThreadState::flush_stats(size_t* surviving_young_words, uint num } G1ParScanThreadState::~G1ParScanThreadState() { + auto delete_all = [&] (uint region, G1NmethodSet* nmethods) -> bool { + delete nmethods; + return true; + }; + _nmethods_to_add.iterate(delete_all); + delete _plab_allocator; delete _closures; FREE_C_HEAP_ARRAY(_surviving_young_words_base); @@ -575,6 +593,7 @@ G1ParScanThreadState* G1ParScanThreadStateSet::state_for_worker(uint worker_id) if (_states[worker_id] == nullptr) { _states[worker_id] = new G1ParScanThreadState(_g1h, + this, worker_id, _num_workers, _collection_set, @@ -606,19 +625,57 @@ void G1ParScanThreadStateSet::flush_stats() { size_t evac_failure_cards = pss->num_cards_from_evac_failure(); size_t marked_cards = pss->num_cards_marked(); - p->record_or_add_thread_work_item(G1GCPhaseTimes::MergePSS, worker_id, copied_bytes, G1GCPhaseTimes::MergePSSCopiedBytes); - p->record_or_add_thread_work_item(G1GCPhaseTimes::MergePSS, worker_id, lab_waste_bytes, G1GCPhaseTimes::MergePSSLABWasteBytes); - p->record_or_add_thread_work_item(G1GCPhaseTimes::MergePSS, worker_id, lab_undo_waste_bytes, G1GCPhaseTimes::MergePSSLABUndoWasteBytes); - p->record_or_add_thread_work_item(G1GCPhaseTimes::MergePSS, worker_id, pending_cards, G1GCPhaseTimes::MergePSSPendingCards); - p->record_or_add_thread_work_item(G1GCPhaseTimes::MergePSS, worker_id, to_young_gen_cards, G1GCPhaseTimes::MergePSSToYoungGenCards); - p->record_or_add_thread_work_item(G1GCPhaseTimes::MergePSS, worker_id, evac_failure_cards, G1GCPhaseTimes::MergePSSEvacFail); - p->record_or_add_thread_work_item(G1GCPhaseTimes::MergePSS, worker_id, marked_cards, G1GCPhaseTimes::MergePSSMarked); + p->record_or_add_thread_work_item(G1GCPhaseTimes::FlushPSS, worker_id, copied_bytes, G1GCPhaseTimes::FlushPSSCopiedBytes); + p->record_or_add_thread_work_item(G1GCPhaseTimes::FlushPSS, worker_id, lab_waste_bytes, G1GCPhaseTimes::FlushPSSLABWasteBytes); + p->record_or_add_thread_work_item(G1GCPhaseTimes::FlushPSS, worker_id, lab_undo_waste_bytes, G1GCPhaseTimes::FlushPSSLABUndoWasteBytes); + p->record_or_add_thread_work_item(G1GCPhaseTimes::FlushPSS, worker_id, pending_cards, G1GCPhaseTimes::FlushPSSPendingCards); + p->record_or_add_thread_work_item(G1GCPhaseTimes::FlushPSS, worker_id, to_young_gen_cards, G1GCPhaseTimes::FlushPSSToYoungGenCards); + p->record_or_add_thread_work_item(G1GCPhaseTimes::FlushPSS, worker_id, evac_failure_cards, G1GCPhaseTimes::FlushPSSEvacFail); + p->record_or_add_thread_work_item(G1GCPhaseTimes::FlushPSS, worker_id, marked_cards, G1GCPhaseTimes::FlushPSSMarked); + } + + _flushed = true; +} - delete pss; +void G1ParScanThreadStateSet::destroy_worker_states() { + assert(_flushed, "statistics must already be flushed"); + for (uint worker_id = 0; worker_id < _num_workers; ++worker_id) { + delete _states[worker_id]; _states[worker_id] = nullptr; } +} - _flushed = true; +void G1ParScanThreadStateSet::update_nmethod_regions_to_add(G1NmethodsToAdd* nmethods) { + if (nmethods->number_of_entries() == 0) { + return; + } + + // Take the key set, look which are not yet in the global set, and update the necessary ones. + ResourceMark rm; + GrowableArray regions_to_add = GrowableArray(nmethods->table_size()); + + nmethods->iterate_all([&] (uint& region, void*) { + if (_has_nmethods_to_add.par_set_bit(region, memory_order_relaxed)) { + regions_to_add.push(region); + } + }); + + uint num_regions_to_add = (uint)regions_to_add.length(); + + if (num_regions_to_add == 0) { + return; + } + + uint first_index = _num_nmethod_regions_to_add.fetch_then_add(num_regions_to_add, memory_order_relaxed); + guarantee(first_index + num_regions_to_add <= _g1h->max_num_regions(), "must be"); + + memcpy(&_nmethod_regions_to_add[first_index], regions_to_add.adr_at(0), num_regions_to_add * sizeof(uint)); +} + +void G1ParScanThreadStateSet::par_iterate_nmethod_regions_to_add(G1HeapRegionClosure* cl, + G1HeapRegionClaimer* claimer, + uint worker_id) { + _g1h->par_iterate_regions_array(cl, claimer, _nmethod_regions_to_add, num_nmethod_regions_to_add(), worker_id); } void G1ParScanThreadStateSet::record_unused_optional_region(G1HeapRegion* hr) { @@ -676,6 +733,10 @@ oop G1ParScanThreadState::handle_evacuation_failure_par(oop old, markWord m, Kla } } +void G1ParScanThreadState::update_nmethod_regions_to_add() { + _per_thread_states->update_nmethod_regions_to_add(&_nmethods_to_add); +} + void G1ParScanThreadState::initialize_numa_stats() { if (_numa->is_enabled()) { LogTarget(Info, gc, heap, numa) lt; @@ -720,7 +781,10 @@ G1ParScanThreadStateSet::G1ParScanThreadStateSet(G1CollectedHeap* g1h, _surviving_young_words_total(NEW_C_HEAP_ARRAY(size_t, collection_set->num_young_regions() + 1, mtGC)), _num_workers(num_workers), _flushed(false), - _evac_failure_regions(evac_failure_regions) + _evac_failure_regions(evac_failure_regions), + _has_nmethods_to_add(g1h->max_num_regions(), mtGC), + _num_nmethod_regions_to_add(0), + _nmethod_regions_to_add(NEW_C_HEAP_ARRAY(uint, g1h->max_num_regions(), mtGC)) // Conservative length estimation. { for (uint i = 0; i < num_workers; ++i) { _states[i] = nullptr; @@ -729,7 +793,10 @@ G1ParScanThreadStateSet::G1ParScanThreadStateSet(G1CollectedHeap* g1h, } G1ParScanThreadStateSet::~G1ParScanThreadStateSet() { - assert(_flushed, "thread local state from the per thread states should have been flushed"); + for (uint i = 0; i < _num_workers; i++) { + assert(_states[i] == nullptr, "must be"); + } + FREE_C_HEAP_ARRAY(_nmethod_regions_to_add); FREE_C_HEAP_ARRAY(_states); FREE_C_HEAP_ARRAY(_surviving_young_words_total); } diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp index 3fb080d40be..efecbe1f786 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp @@ -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 @@ -37,6 +37,9 @@ #include "gc/shared/taskqueue.hpp" #include "memory/allocation.hpp" #include "oops/oop.hpp" +#include "runtime/atomic.hpp" +#include "utilities/growableArray.hpp" +#include "utilities/resizableHashTable.hpp" #include "utilities/ticks.hpp" class G1CardTable; @@ -44,12 +47,16 @@ class G1CollectionSet; class G1EvacFailureRegions; class G1EvacuationRootClosures; class G1OopStarChunkedList; +class G1ParScanThreadStateSet; class G1PLABAllocator; class G1HeapRegion; class outputStream; +typedef GrowableArrayCHeap G1NmethodSet; +typedef ResizeableHashTable G1NmethodsToAdd; class G1ParScanThreadState : public CHeapObj { G1CollectedHeap* _g1h; + G1ParScanThreadStateSet* _per_thread_states; G1ScannerTasksQueue* _task_queue; G1CardTable* _ct; G1EvacuationRootClosures* _closures; @@ -96,6 +103,9 @@ class G1ParScanThreadState : public CHeapObj { // transferred when flushed. size_t* _obj_alloc_stat; + // The nmethods that were found during code root scan that need to be redistributed. + G1NmethodsToAdd _nmethods_to_add; + // Per-thread evacuation failure data structures. ALLOCATION_FAILURE_INJECTOR_ONLY(size_t _allocation_failure_inject_counter;) @@ -114,6 +124,7 @@ class G1ParScanThreadState : public CHeapObj { public: G1ParScanThreadState(G1CollectedHeap* g1h, + G1ParScanThreadStateSet* per_thread_states, uint worker_id, uint num_workers, G1CollectionSet* collection_set, @@ -243,6 +254,16 @@ class G1ParScanThreadState : public CHeapObj { // An attempt to evacuate "obj" has failed; take necessary steps. oop handle_evacuation_failure_par(oop obj, markWord m, Klass* klass, G1HeapRegionAttr attr, size_t word_sz, bool cause_pinned); + inline void remember_nmethod_into_region(G1HeapRegion* r, nmethod* nm); + // Updates the global set of regions that need updates to the code root set + // later with the ones gathered so far. + void update_nmethod_regions_to_add(); + + inline size_t num_nmethods(uint index) const; + // Iterate nmethods stored for the given region index. + template + inline void iterate_nmethods(uint index, Function fn); + template inline void remember_root_into_optional_region(T* p); template @@ -260,6 +281,10 @@ class G1ParScanThreadStateSet : public StackObj { bool _flushed; G1EvacFailureRegions* _evac_failure_regions; + CHeapBitMap _has_nmethods_to_add; + Atomic _num_nmethod_regions_to_add; + uint* _nmethod_regions_to_add; + public: G1ParScanThreadStateSet(G1CollectedHeap* g1h, uint num_workers, @@ -268,6 +293,15 @@ class G1ParScanThreadStateSet : public StackObj { ~G1ParScanThreadStateSet(); void flush_stats(); + void destroy_worker_states(); + + // Updates the region set that has code root updates with the regions in the given set. + void update_nmethod_regions_to_add(G1NmethodsToAdd* nmethods); + void par_iterate_nmethod_regions_to_add(G1HeapRegionClosure* cl, + G1HeapRegionClaimer* claimer, + uint worker_id); + uint num_nmethod_regions_to_add() const { return _num_nmethod_regions_to_add.load_relaxed(); } + void record_unused_optional_region(G1HeapRegion* hr); #if TASKQUEUE_STATS void print_partial_array_task_stats(); diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp index 854c341f720..c42f5f4c4f6 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp @@ -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 @@ -70,6 +70,37 @@ inline void G1ParScanThreadState::reset_trim_ticks() { _trim_ticks = Tickspan(); } +inline void G1ParScanThreadState::remember_nmethod_into_region(G1HeapRegion* r, nmethod* nm) { + uint index = r->hrm_index(); + + G1NmethodSet** nmethods = _nmethods_to_add.get(index); + if (nmethods != nullptr) { + (*nmethods)->push(nm); + } else { + G1NmethodSet* new_set = new G1NmethodSet(3); + new_set->push(nm); + bool put_result = _nmethods_to_add.put(index, new_set); + assert(put_result, "must be"); + _nmethods_to_add.maybe_grow(3 /* load_factor */); + } +} + +inline size_t G1ParScanThreadState::num_nmethods(uint region) const { + G1NmethodSet** nmethods = _nmethods_to_add.get(region); + return nmethods != nullptr ? (size_t)(*nmethods)->length() : 0; +} + +template +inline void G1ParScanThreadState::iterate_nmethods(uint index, Function fn) { + G1NmethodSet** nmethods = _nmethods_to_add.get(index); + if (nmethods == nullptr) { + return; + } + for (nmethod* nm : **nmethods) { + fn(nm); + } +} + template inline void G1ParScanThreadState::remember_root_into_optional_region(T* p) { oop o = RawAccess::oop_load(p); diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index d271a8a610a..f9b1c182a38 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -913,7 +913,7 @@ G1CollectorState G1Policy::record_young_collection_end(bool concurrent_operation } // Update prediction for copy cost per byte - size_t copied_bytes = p->sum_thread_work_items(G1GCPhaseTimes::MergePSS, G1GCPhaseTimes::MergePSSCopiedBytes); + size_t copied_bytes = p->sum_thread_work_items(G1GCPhaseTimes::FlushPSS, G1GCPhaseTimes::FlushPSSCopiedBytes); if (copied_bytes > 0) { double avg_copy_time = average_time_ms(G1GCPhaseTimes::ObjCopy) + average_time_ms(G1GCPhaseTimes::OptObjCopy); @@ -950,8 +950,8 @@ G1CollectorState G1Policy::record_young_collection_end(bool concurrent_operation mutator_end_time, pending_cards_from_refinement_table, yield_duration_ms, - phase_times()->sum_thread_work_items(G1GCPhaseTimes::MergePSS, G1GCPhaseTimes::MergePSSPendingCards), - phase_times()->sum_thread_work_items(G1GCPhaseTimes::MergePSS, G1GCPhaseTimes::MergePSSToYoungGenCards)); + phase_times()->sum_thread_work_items(G1GCPhaseTimes::FlushPSS, G1GCPhaseTimes::FlushPSSPendingCards), + phase_times()->sum_thread_work_items(G1GCPhaseTimes::FlushPSS, G1GCPhaseTimes::FlushPSSToYoungGenCards)); } if (collector_state()->is_in_prepare_mixed_gc()) { diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp index bcb50dcc98f..5f58ca2e053 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.cpp +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp @@ -630,6 +630,8 @@ void G1RemSet::scan_collection_set_code_roots(G1ParScanThreadState* pss, // set regions for all threads. _g1h->collection_set_iterate_increment_from(&cl, worker_id); + pss->update_nmethod_regions_to_add(); + p->record_or_add_thread_work_item(coderoots_phase, worker_id, cl.code_roots_scanned(), G1GCPhaseTimes::CodeRootsScannedNMethods); } diff --git a/src/hotspot/share/gc/g1/g1RootClosures.hpp b/src/hotspot/share/gc/g1/g1RootClosures.hpp index 35ce038e1f8..c1c80655c91 100644 --- a/src/hotspot/share/gc/g1/g1RootClosures.hpp +++ b/src/hotspot/share/gc/g1/g1RootClosures.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, 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,6 +42,8 @@ class G1RootClosures : public CHeapObj { // Applied to nmethods reachable as strong roots. virtual NMethodClosure* strong_nmethods() = 0; + + virtual ~G1RootClosures() = default; }; class G1EvacuationRootClosures : public G1RootClosures { diff --git a/src/hotspot/share/gc/g1/g1SharedClosures.hpp b/src/hotspot/share/gc/g1/g1SharedClosures.hpp index a81f62ff308..dc6ff646271 100644 --- a/src/hotspot/share/gc/g1/g1SharedClosures.hpp +++ b/src/hotspot/share/gc/g1/g1SharedClosures.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 @@ -55,7 +55,7 @@ class G1SharedClosures { _oops_in_cld(g1h, pss), _oops_in_nmethod(g1h, pss), _clds(&_oops_in_cld, process_only_dirty), - _nmethods(pss->worker_id(), &_oops_in_nmethod, should_mark) {} + _nmethods(pss->worker_id(), &_oops_in_nmethod, should_mark, pss) {} }; #endif // SHARE_GC_G1_G1SHAREDCLOSURES_HPP diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp index 97378d0542e..e561252ab25 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp @@ -54,12 +54,12 @@ #include "utilities/bitMap.inline.hpp" #include "utilities/ticks.hpp" -class G1PostEvacuateCollectionSetCleanupTask1::MergePssTask : public G1AbstractSubTask { +class G1PostEvacuateCollectionSetCleanupTask1::FlushPssTask : public G1AbstractSubTask { G1ParScanThreadStateSet* _per_thread_states; public: - MergePssTask(G1ParScanThreadStateSet* per_thread_states) : - G1AbstractSubTask(G1GCPhaseTimes::MergePSS), + FlushPssTask(G1ParScanThreadStateSet* per_thread_states) : + G1AbstractSubTask(G1GCPhaseTimes::FlushPSS), _per_thread_states(per_thread_states) { } double worker_cost() const override { return 1.0; } @@ -119,6 +119,58 @@ class G1PostEvacuateCollectionSetCleanupTask1::SampleCollectionSetCandidatesTask } }; +class G1PostEvacuateCollectionSetCleanupTask1::UpdateCodeRootsTask + : public G1AbstractSubTask +{ + class ProcessRegionClosure : public G1HeapRegionClosure { + G1ParScanThreadStateSet* _psss; + + public: + ProcessRegionClosure(G1ParScanThreadStateSet* psss) : _psss(psss) { } + + bool do_heap_region(G1HeapRegion* r) override { + uint index = r->hrm_index(); + + size_t num_nmethods = 0; + for (uint i = 0; i < _psss->num_workers(); i++) { + G1ParScanThreadState* pss = _psss->state_for_worker(i); + num_nmethods += pss->num_nmethods(index); + } + if (num_nmethods != 0) { + // Notify the code root sets that we are going to add code roots. + r->rem_set()->prepare_for_adding_code_roots(num_nmethods); + + // Add roots. + for (uint i = 0; i < _psss->num_workers(); i++) { + G1ParScanThreadState* pss = _psss->state_for_worker(i); + pss->iterate_nmethods(index, [&] (nmethod* nm) { r->add_code_root(nm); }); + } + } + return false; + } + }; + + G1ParScanThreadStateSet* _psss; + G1HeapRegionClaimer _claimer; + +public: + UpdateCodeRootsTask(G1ParScanThreadStateSet* per_thread_states) + : G1AbstractSubTask(G1GCPhaseTimes::UpdateCodeRoots), _psss(per_thread_states), _claimer(0) { } + + double worker_cost() const override { + return _psss->num_nmethod_regions_to_add(); + } + + void set_max_workers(uint max_workers) override { + _claimer.set_n_workers(max_workers); + } + + void do_work(uint worker_id) override { + ProcessRegionClosure cl(_psss); + _psss->par_iterate_nmethod_regions_to_add(&cl, &_claimer, worker_id); + } +}; + class G1PostEvacuateCollectionSetCleanupTask1::RestoreEvacFailureRegionsTask : public G1AbstractSubTask { G1CollectedHeap* _g1h; G1ConcurrentMark* _cm; @@ -327,11 +379,13 @@ G1PostEvacuateCollectionSetCleanupTask1::G1PostEvacuateCollectionSetCleanupTask1 bool evac_failed = evac_failure_regions->has_regions_evac_failed(); bool alloc_failed = evac_failure_regions->has_regions_alloc_failed(); - add_serial_task(new MergePssTask(per_thread_states)); + add_serial_task(new FlushPssTask(per_thread_states)); add_serial_task(new RecalculateUsedTask(evac_failed, alloc_failed)); if (SampleCollectionSetCandidatesTask::should_execute()) { add_serial_task(new SampleCollectionSetCandidatesTask()); } + add_parallel_task(new UpdateCodeRootsTask(per_thread_states)); + add_parallel_task(G1CollectedHeap::heap()->rem_set()->create_cleanup_after_scan_heap_roots_task()); if (evac_failed) { add_parallel_task(new RestoreEvacFailureRegionsTask(evac_failure_regions)); @@ -871,21 +925,19 @@ class G1PostEvacuateCollectionSetCleanupTask2::ResizeTLABsAndSwapCardTableTask : } }; -class G1PostEvacuateCollectionSetCleanupTask2::ResetPartialArrayStateManagerTask - : public G1AbstractSubTask -{ +class G1PostEvacuateCollectionSetCleanupTask2::DestroyPssTask : public G1AbstractSubTask { + G1ParScanThreadStateSet* _per_thread_states; + public: - ResetPartialArrayStateManagerTask() - : G1AbstractSubTask(G1GCPhaseTimes::ResetPartialArrayStateManager) - {} + DestroyPssTask(G1ParScanThreadStateSet* per_thread_states) : + G1AbstractSubTask(G1GCPhaseTimes::DestroyPSS), + _per_thread_states(per_thread_states) { } - double worker_cost() const override { - return AlmostNoWork; - } + double worker_cost() const override { return 1.0; } void do_work(uint worker_id) override { - // This must be in phase2 cleanup, after phase1 has destroyed all of the - // associated allocators. + _per_thread_states->destroy_worker_states(); + // This must be here after above destroyed the per-thread allocators. G1CollectedHeap::heap()->partial_array_state_manager()->reset(); } }; @@ -901,7 +953,7 @@ G1PostEvacuateCollectionSetCleanupTask2::G1PostEvacuateCollectionSetCleanupTask2 if (G1CollectedHeap::heap()->has_humongous_reclaim_candidates()) { add_serial_task(new EagerlyReclaimHumongousObjectsTask()); } - add_serial_task(new ResetPartialArrayStateManagerTask()); + add_serial_task(new DestroyPssTask(per_thread_states)); if (evac_failure_regions->has_regions_evac_failed()) { add_parallel_task(new ProcessEvacuationFailedRegionsTask(evac_failure_regions)); diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.hpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.hpp index 557ce454c78..95d0fee6ad7 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.hpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.hpp @@ -35,16 +35,18 @@ class G1EvacInfo; class G1ParScanThreadStateSet; // First set of post evacuate collection set tasks containing ("s" means serial): -// - Merge PSS (s) +// - Flush PSS (s) // - Recalculate Used (s) // - Sample Collection Set Candidates (s) // - Clear Card Table // - Restore evac failure regions (on evacuation failure) +// - Update code roots (for regions that need code roots to be added) class G1PostEvacuateCollectionSetCleanupTask1 : public G1BatchedTask { - class MergePssTask; + class FlushPssTask; class RecalculateUsedTask; class SampleCollectionSetCandidatesTask; class RestoreEvacFailureRegionsTask; + class UpdateCodeRootsTask; public: G1PostEvacuateCollectionSetCleanupTask1(G1ParScanThreadStateSet* per_thread_states, @@ -54,10 +56,10 @@ class G1PostEvacuateCollectionSetCleanupTask1 : public G1BatchedTask { // Second set of post evacuate collection set tasks containing (s means serial): // - Eagerly Reclaim Humongous Objects (s) // - Update Derived Pointers (s) +// - Destroy PSS (s) + Reset the reusable PartialArrayStateManager // - Clear Retained Region Data (on evacuation failure) // - Free Collection Set // - Resize TLABs and Swap Card Table -// - Reset the reusable PartialArrayStateManager. class G1PostEvacuateCollectionSetCleanupTask2 : public G1BatchedTask { class EagerlyReclaimHumongousObjectsTask; #ifdef COMPILER2 @@ -67,7 +69,8 @@ class G1PostEvacuateCollectionSetCleanupTask2 : public G1BatchedTask { class ProcessEvacuationFailedRegionsTask; class FreeCollectionSetTask; class ResizeTLABsAndSwapCardTableTask; - class ResetPartialArrayStateManagerTask; + + class DestroyPssTask; public: G1PostEvacuateCollectionSetCleanupTask2(G1ParScanThreadStateSet* per_thread_states, diff --git a/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java b/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java index 68391893a32..16b73d4c354 100644 --- a/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java +++ b/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java @@ -170,7 +170,8 @@ public boolean isAvailable() { // Post Evacuate Cleanup 1 new LogMessageWithLevel("Post Evacuate Cleanup 1:", Level.DEBUG), - new LogMessageWithLevel("Merge Per-Thread State \\(ms\\):", Level.DEBUG), + new LogMessageWithLevel("Flush Per-Thread State \\(ms\\):", Level.DEBUG), + new LogMessageWithLevel("Update Code Roots \\(ms\\):", Level.DEBUG), new LogMessageWithLevel("LAB Waste:", Level.DEBUG), new LogMessageWithLevel("LAB Undo Waste:", Level.DEBUG), new LogMessageWithLevel("Pending Cards:", Level.DEBUG), @@ -188,7 +189,7 @@ public boolean isAvailable() { new LogMessageWithLevel("Serial Free Collection Set:", Level.TRACE), new LogMessageWithLevel("Young Free Collection Set \\(ms\\):", Level.TRACE), new LogMessageWithLevel("Non-Young Free Collection Set \\(ms\\):", Level.TRACE), - new LogMessageWithLevel("Reset Partial Array State Manager \\(ms\\)", Level.TRACE), + new LogMessageWithLevel("Destroy Per-Thread State \\(ms\\):", Level.TRACE), // Misc Top-level new LogMessageWithLevel("Rebuild Free List:", Level.DEBUG), diff --git a/test/jdk/jdk/jfr/event/gc/collection/TestG1ParallelPhases.java b/test/jdk/jdk/jfr/event/gc/collection/TestG1ParallelPhases.java index d69d47f1911..cde561a68e7 100644 --- a/test/jdk/jdk/jfr/event/gc/collection/TestG1ParallelPhases.java +++ b/test/jdk/jdk/jfr/event/gc/collection/TestG1ParallelPhases.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -107,9 +107,10 @@ public static void main(String[] args) throws IOException { "FreeCSet", "UpdateDerivedPointers", "EagerlyReclaimHumongousObjects", - "ResetPartialArrayStateManager", "ClearPendingCards", - "MergePSS", + "FlushPSS", + "DestroyPSS", + "UpdateCodeRoots", "NonYoungFreeCSet", "YoungFreeCSet", "RebuildFreeList", From dca8681fb737a9a71ddb06169f6202ac1cf64be3 Mon Sep 17 00:00:00 2001 From: Jorn Vernee Date: Wed, 8 Jul 2026 15:24:43 +0000 Subject: [PATCH 179/707] 8386848: testBool in java/foreign/normalize/TestNormalize.java fails on Zero VM with expected [true] but found [false] Reviewed-by: mcimadamore, vlivanov --- .../modules/GensrcStreamPreProcessing.gmk | 2 +- .../java.base/gensrc/GensrcVarHandles.gmk | 2 +- .../X-VarHandleSegmentView.java.template | 162 ++++++++++-------- test/jdk/ProblemList.txt | 2 - .../TestNormalizeBooleanVarHandle.java | 93 ++++++++++ 5 files changed, 182 insertions(+), 79 deletions(-) create mode 100644 test/jdk/java/foreign/normalize/TestNormalizeBooleanVarHandle.java diff --git a/make/common/modules/GensrcStreamPreProcessing.gmk b/make/common/modules/GensrcStreamPreProcessing.gmk index a48e3c98d4b..eb92ed99ed4 100644 --- a/make/common/modules/GensrcStreamPreProcessing.gmk +++ b/make/common/modules/GensrcStreamPreProcessing.gmk @@ -116,7 +116,7 @@ Conv_A = \ # Return integer type with same size as the type Conv_memtype = \ - $(if $(filter float, $1), int, $(if $(filter double, $1), long, $1)) + $(if $(filter float, $1), int, $(if $(filter double, $1), long, $(if $(filter boolean, $1), byte, $1))) # Return capitalized integer type with same size as the type Conv_Memtype = \ diff --git a/make/modules/java.base/gensrc/GensrcVarHandles.gmk b/make/modules/java.base/gensrc/GensrcVarHandles.gmk index 341a8c9dc2c..4b1697fd354 100644 --- a/make/modules/java.base/gensrc/GensrcVarHandles.gmk +++ b/make/modules/java.base/gensrc/GensrcVarHandles.gmk @@ -111,7 +111,7 @@ define GenerateVarHandleMemorySegment $1_KEYS += CAS endif ifneq ($$(filter boolean byte, $1),) - $1_KEYS += byte + $1_KEYS += ByteOrBoolean endif ifneq ($$(filter float double, $1),) $1_KEYS += floatingPoint diff --git a/src/java.base/share/classes/java/lang/invoke/X-VarHandleSegmentView.java.template b/src/java.base/share/classes/java/lang/invoke/X-VarHandleSegmentView.java.template index aa8c7b28617..0147810cb4e 100644 --- a/src/java.base/share/classes/java/lang/invoke/X-VarHandleSegmentView.java.template +++ b/src/java.base/share/classes/java/lang/invoke/X-VarHandleSegmentView.java.template @@ -33,20 +33,20 @@ import static java.lang.invoke.SegmentVarHandle.*; #warn -{#if[byte]?final:sealed} class VarHandleSegmentAs$Type$s { +{#if[ByteOrBoolean]?final:sealed} class VarHandleSegmentAs$Type$s { -#if[!byte] +#if[!ByteOrBoolean] static final int NON_PLAIN_ACCESS_MIN_ALIGN_MASK = $BoxType$.BYTES - 1; -#end[byte] +#end[ByteOrBoolean] static VarForm selectForm(long alignmentMask, boolean constantOffset) { -#if[byte] +#if[ByteOrBoolean] return constantOffset ? CONSTANT_OFFSET_FORM : VARIABLE_OFFSET_FORM; -#else[byte] +#else[ByteOrBoolean] return (alignmentMask & NON_PLAIN_ACCESS_MIN_ALIGN_MASK) != NON_PLAIN_ACCESS_MIN_ALIGN_MASK ? (constantOffset ? CONSTANT_OFFSET_FORM : VARIABLE_OFFSET_FORM) : (constantOffset ? VarHandleSegmentAs$Type$sAligned.CONSTANT_OFFSET_FORM : VarHandleSegmentAs$Type$sAligned.VARIABLE_OFFSET_FORM); -#end[byte] +#end[ByteOrBoolean] } static final VarForm CONSTANT_OFFSET_FORM = new VarForm(VarHandleSegmentAs$Type$s.class, MemorySegment.class, $type$.class, long.class); @@ -70,16 +70,16 @@ import static java.lang.invoke.SegmentVarHandle.*; handle.be); return $Type$.$rawType$BitsTo$Type$(rawValue); #else[floatingPoint] -#if[byte] - return SCOPED_MEMORY_ACCESS.get$Type$(bb.sessionImpl(), +#if[ByteOrBoolean] + return SCOPED_MEMORY_ACCESS.get$RawType$(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, offset)); -#else[byte] + offset(bb, base, offset)){#if[boolean]? != 0}; +#else[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.get$Type$Unaligned(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), handle.be); -#end[byte] +#end[ByteOrBoolean] #end[floatingPoint] } @@ -99,21 +99,21 @@ import static java.lang.invoke.SegmentVarHandle.*; $Type$.$type$ToRaw$RawType$Bits(value), handle.be); #else[floatingPoint] -#if[byte] +#if[ByteOrBoolean] SCOPED_MEMORY_ACCESS.put$Type$(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), value); -#else[byte] +#else[ByteOrBoolean] SCOPED_MEMORY_ACCESS.put$Type$Unaligned(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), value, handle.be); -#end[byte] +#end[ByteOrBoolean] #end[floatingPoint] } -#if[!byte] +#if[!ByteOrBoolean] } // This class must be accessed through non-aligned VarHandleSegmentAs$Type$s @@ -123,7 +123,7 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static final VarForm VARIABLE_OFFSET_FORM = new VarForm(VarHandleSegmentAs$Type$sAligned.class, VarHandleSegmentAs$Type$s.VARIABLE_OFFSET_FORM); VarHandleSegmentAs$Type$sAligned() { throw new AssertionError(); } -#end[byte] +#end[ByteOrBoolean] #if[floatingPoint] @ForceInline @@ -138,17 +138,29 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { return $Type$.$rawType$BitsTo$Type$(rv); } #else[floatingPoint] -#if[byte] +#if[ByteOrBoolean] +#if[boolean] + @ForceInline + static $rawType$ convEndian(boolean big, $type$ v) { + return (byte) (v ? 1 : 0); + } + + @ForceInline + static $type$ convEndian(boolean big, $rawType$ n) { + return n != 0; + } +#else[boolean] @ForceInline static $type$ convEndian(boolean big, $type$ n) { return n; } -#else[byte] +#end[boolean] +#else[ByteOrBoolean] @ForceInline static $type$ convEndian(boolean big, $type$ n) { return big == BE ? n : $BoxType$.reverseBytes(n); } -#end[byte] +#end[ByteOrBoolean] #end[floatingPoint] @ForceInline @@ -424,18 +436,18 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static $type$ getAndAdd(VarHandle ob, Object obb, long base, long offset, $type$ delta) { SegmentVarHandle handle = (SegmentVarHandle)ob; AbstractMemorySegmentImpl bb = handle.checkSegment(obb, base, false); -#if[!byte] +#if[!ByteOrBoolean] if (handle.be == BE) { -#end[byte] +#end[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.getAndAdd$RawType$(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), delta); -#if[!byte] +#if[!ByteOrBoolean] } else { return getAndAddConvEndianWithCAS(bb, offset(bb, base, offset), delta); } -#end[byte] +#end[ByteOrBoolean] } @ForceInline @@ -447,18 +459,18 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static $type$ getAndAddAcquire(VarHandle ob, Object obb, long base, long offset, $type$ delta) { SegmentVarHandle handle = (SegmentVarHandle)ob; AbstractMemorySegmentImpl bb = handle.checkSegment(obb, base, false); -#if[!byte] +#if[!ByteOrBoolean] if (handle.be == BE) { -#end[byte] +#end[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.getAndAdd$RawType$Acquire(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), delta); -#if[!byte] +#if[!ByteOrBoolean] } else { return getAndAddConvEndianWithCAS(bb, offset(bb, base, offset), delta); } -#end[byte] +#end[ByteOrBoolean] } @ForceInline @@ -470,20 +482,20 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static $type$ getAndAddRelease(VarHandle ob, Object obb, long base, long offset, $type$ delta) { SegmentVarHandle handle = (SegmentVarHandle)ob; AbstractMemorySegmentImpl bb = handle.checkSegment(obb, base, false); -#if[!byte] +#if[!ByteOrBoolean] if (handle.be == BE) { -#end[byte] +#end[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.getAndAdd$RawType$Release(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), delta); -#if[!byte] +#if[!ByteOrBoolean] } else { return getAndAddConvEndianWithCAS(bb, offset(bb, base, offset), delta); } -#end[byte] +#end[ByteOrBoolean] } -#if[!byte] +#if[!ByteOrBoolean] @ForceInline static $type$ getAndAddConvEndianWithCAS(AbstractMemorySegmentImpl bb, long offset, $type$ delta) { @@ -496,7 +508,7 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { nativeExpectedValue, $RawBoxType$.reverseBytes({#if[ShorterThanInt]?($type$) }(expectedValue + delta)))); return expectedValue; } -#end[byte] +#end[ByteOrBoolean] #end[AtomicAdd] #if[Bitwise] @@ -509,18 +521,18 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static $type$ getAndBitwiseOr(VarHandle ob, Object obb, long base, long offset, $type$ value) { SegmentVarHandle handle = (SegmentVarHandle)ob; AbstractMemorySegmentImpl bb = handle.checkSegment(obb, base, false); -#if[!byte] +#if[!ByteOrBoolean] if (handle.be == BE) { -#end[byte] +#end[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.getAndBitwiseOr$RawType$(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), value); -#if[!byte] +#if[!ByteOrBoolean] } else { return getAndBitwiseOrConvEndianWithCAS(bb, offset(bb, base, offset), value); } -#end[byte] +#end[ByteOrBoolean] } @ForceInline @@ -532,18 +544,18 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static $type$ getAndBitwiseOrRelease(VarHandle ob, Object obb, long base, long offset, $type$ value) { SegmentVarHandle handle = (SegmentVarHandle)ob; AbstractMemorySegmentImpl bb = handle.checkSegment(obb, base, false); -#if[!byte] +#if[!ByteOrBoolean] if (handle.be == BE) { -#end[byte] +#end[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.getAndBitwiseOr$RawType$Release(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), value); -#if[!byte] +#if[!ByteOrBoolean] } else { return getAndBitwiseOrConvEndianWithCAS(bb, offset(bb, base, offset), value); } -#end[byte] +#end[ByteOrBoolean] } @ForceInline @@ -555,20 +567,20 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static $type$ getAndBitwiseOrAcquire(VarHandle ob, Object obb, long base, long offset, $type$ value) { SegmentVarHandle handle = (SegmentVarHandle)ob; AbstractMemorySegmentImpl bb = handle.checkSegment(obb, base, false); -#if[!byte] +#if[!ByteOrBoolean] if (handle.be == BE) { -#end[byte] +#end[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.getAndBitwiseOr$RawType$Acquire(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), value); -#if[!byte] +#if[!ByteOrBoolean] } else { return getAndBitwiseOrConvEndianWithCAS(bb, offset(bb, base, offset), value); } -#end[byte] +#end[ByteOrBoolean] } -#if[!byte] +#if[!ByteOrBoolean] @ForceInline static $type$ getAndBitwiseOrConvEndianWithCAS(AbstractMemorySegmentImpl bb, long offset, $type$ value) { @@ -581,7 +593,7 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { nativeExpectedValue, $RawBoxType$.reverseBytes({#if[ShorterThanInt]?($type$) }(expectedValue | value)))); return expectedValue; } -#end[byte] +#end[ByteOrBoolean] @ForceInline static $type$ getAndBitwiseAnd(VarHandle ob, Object obb, long base, $type$ value) { @@ -592,18 +604,18 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static $type$ getAndBitwiseAnd(VarHandle ob, Object obb, long base, long offset, $type$ value) { SegmentVarHandle handle = (SegmentVarHandle)ob; AbstractMemorySegmentImpl bb = handle.checkSegment(obb, base, false); -#if[!byte] +#if[!ByteOrBoolean] if (handle.be == BE) { -#end[byte] +#end[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.getAndBitwiseAnd$RawType$(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), value); -#if[!byte] +#if[!ByteOrBoolean] } else { return getAndBitwiseAndConvEndianWithCAS(bb, offset(bb, base, offset), value); } -#end[byte] +#end[ByteOrBoolean] } @ForceInline @@ -615,18 +627,18 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static $type$ getAndBitwiseAndRelease(VarHandle ob, Object obb, long base, long offset, $type$ value) { SegmentVarHandle handle = (SegmentVarHandle)ob; AbstractMemorySegmentImpl bb = handle.checkSegment(obb, base, false); -#if[!byte] +#if[!ByteOrBoolean] if (handle.be == BE) { -#end[byte] +#end[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.getAndBitwiseAnd$RawType$Release(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), value); -#if[!byte] +#if[!ByteOrBoolean] } else { return getAndBitwiseAndConvEndianWithCAS(bb, offset(bb, base, offset), value); } -#end[byte] +#end[ByteOrBoolean] } @ForceInline @@ -639,20 +651,20 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static $type$ getAndBitwiseAndAcquire(VarHandle ob, Object obb, long base, long offset, $type$ value) { SegmentVarHandle handle = (SegmentVarHandle)ob; AbstractMemorySegmentImpl bb = handle.checkSegment(obb, base, false); -#if[!byte] +#if[!ByteOrBoolean] if (handle.be == BE) { -#end[byte] +#end[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.getAndBitwiseAnd$RawType$Acquire(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), value); -#if[!byte] +#if[!ByteOrBoolean] } else { return getAndBitwiseAndConvEndianWithCAS(bb, offset(bb, base, offset), value); } -#end[byte] +#end[ByteOrBoolean] } -#if[!byte] +#if[!ByteOrBoolean] @ForceInline static $type$ getAndBitwiseAndConvEndianWithCAS(AbstractMemorySegmentImpl bb, long offset, $type$ value) { @@ -665,7 +677,7 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { nativeExpectedValue, $RawBoxType$.reverseBytes({#if[ShorterThanInt]?($type$) }(expectedValue & value)))); return expectedValue; } -#end[byte] +#end[ByteOrBoolean] @ForceInline static $type$ getAndBitwiseXor(VarHandle ob, Object obb, long base, $type$ value) { @@ -676,18 +688,18 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static $type$ getAndBitwiseXor(VarHandle ob, Object obb, long base, long offset, $type$ value) { SegmentVarHandle handle = (SegmentVarHandle)ob; AbstractMemorySegmentImpl bb = handle.checkSegment(obb, base, false); -#if[!byte] +#if[!ByteOrBoolean] if (handle.be == BE) { -#end[byte] +#end[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.getAndBitwiseXor$RawType$(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), value); -#if[!byte] +#if[!ByteOrBoolean] } else { return getAndBitwiseXorConvEndianWithCAS(bb, offset(bb, base, offset), value); } -#end[byte] +#end[ByteOrBoolean] } @ForceInline @@ -699,18 +711,18 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static $type$ getAndBitwiseXorRelease(VarHandle ob, Object obb, long base, long offset, $type$ value) { SegmentVarHandle handle = (SegmentVarHandle)ob; AbstractMemorySegmentImpl bb = handle.checkSegment(obb, base, false); -#if[!byte] +#if[!ByteOrBoolean] if (handle.be == BE) { -#end[byte] +#end[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.getAndBitwiseXor$RawType$Release(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), value); -#if[!byte] +#if[!ByteOrBoolean] } else { return getAndBitwiseXorConvEndianWithCAS(bb, offset(bb, base, offset), value); } -#end[byte] +#end[ByteOrBoolean] } @ForceInline @@ -722,20 +734,20 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { static $type$ getAndBitwiseXorAcquire(VarHandle ob, Object obb, long base, long offset, $type$ value) { SegmentVarHandle handle = (SegmentVarHandle)ob; AbstractMemorySegmentImpl bb = handle.checkSegment(obb, base, false); -#if[!byte] +#if[!ByteOrBoolean] if (handle.be == BE) { -#end[byte] +#end[ByteOrBoolean] return SCOPED_MEMORY_ACCESS.getAndBitwiseXor$RawType$Acquire(bb.sessionImpl(), bb.unsafeGetBase(), offset(bb, base, offset), value); -#if[!byte] +#if[!ByteOrBoolean] } else { return getAndBitwiseXorConvEndianWithCAS(bb, offset(bb, base, offset), value); } -#end[byte] +#end[ByteOrBoolean] } -#if[!byte] +#if[!ByteOrBoolean] @ForceInline static $type$ getAndBitwiseXorConvEndianWithCAS(AbstractMemorySegmentImpl bb, long offset, $type$ value) { @@ -748,6 +760,6 @@ final class VarHandleSegmentAs$Type$sAligned extends VarHandleSegmentAs$Type$s { nativeExpectedValue, $RawBoxType$.reverseBytes({#if[ShorterThanInt]?($type$) }(expectedValue ^ value)))); return expectedValue; } -#end[byte] +#end[ByteOrBoolean] #end[Bitwise] } diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 5e730af92b0..fcde1d9c01d 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -648,8 +648,6 @@ jdk/jfr/event/oldobject/TestZ.java 8375615 generic- # jdk_foreign -java/foreign/normalize/TestNormalize.java 8386848 generic-all - ############################################################################ # Client manual tests diff --git a/test/jdk/java/foreign/normalize/TestNormalizeBooleanVarHandle.java b/test/jdk/java/foreign/normalize/TestNormalizeBooleanVarHandle.java new file mode 100644 index 00000000000..acca0d095c3 --- /dev/null +++ b/test/jdk/java/foreign/normalize/TestNormalizeBooleanVarHandle.java @@ -0,0 +1,93 @@ +/* + * 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 + * @run testng TestNormalizeBooleanVarHandle + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.lang.invoke.VarHandle; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; + +import static java.lang.foreign.ValueLayout.*; +import static org.testng.Assert.*; + +// test normalization of smaller than int primitive types +public class TestNormalizeBooleanVarHandle { + + static final VarHandle VH = JAVA_BOOLEAN.varHandle(); + + @Test(dataProvider = "bools") + public void testBool(Function segmentFactory, Predicate accessor, + byte testValue, boolean expected) { + try (Arena arena = Arena.ofConfined()) { + MemorySegment ms = segmentFactory.apply(arena); + ms.set(JAVA_BYTE, 0L, testValue); + + boolean b = accessor.test(ms); + assertEquals(b, expected); + } + } + + @DataProvider + public static Object[][] bools() { + List cases = new ArrayList<>(); + for (Function segmentFactory : factories()) { + for (Predicate accessor : accessors()) { + cases.add(new Object[]{ segmentFactory, accessor, + (byte) 0b0 , false }); // canonical false + cases.add(new Object[]{ segmentFactory, accessor, + (byte) 0b01, true }); // canonical true + cases.add(new Object[]{ segmentFactory, accessor, + (byte) 0b10, true }); // zero least significant bit, but non-zero first byte + } + } + + return cases.toArray(Object[][]::new); + } + + private static List> factories() { + return List.of( + a -> a.allocate(JAVA_BYTE), + _ -> MemorySegment.ofArray(new byte[1]) + ); + } + + private static List> accessors() { + return List.of( + ms -> ms.get(JAVA_BOOLEAN, 0L), + ms -> (boolean) VH.get(ms, 0L), + ms -> (boolean) VH.getVolatile(ms, 0L), + ms -> (boolean) VH.getAcquire(ms, 0L), + ms -> (boolean) VH.getOpaque(ms, 0L) + ); + } +} From 2130e2555b45f3d53602ab19433f4562458bbd43 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Wed, 8 Jul 2026 16:14:12 +0000 Subject: [PATCH 180/707] 8387707: Shenandoah: Simplify reserved queue handling in mark loop Reviewed-by: kdnilsen, xpeng --- .../share/gc/shenandoah/shenandoahMark.cpp | 65 ++++++++++--------- .../share/gc/shenandoah/shenandoahMark.hpp | 4 ++ 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp index a72c557a5fe..fc508dddd84 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp @@ -57,12 +57,17 @@ ShenandoahMark::ShenandoahMark(ShenandoahGeneration* generation) : template void ShenandoahMark::mark_loop_prework(uint w, TaskTerminator *t, StringDedup::Requests* const req, bool update_refs) { + ShenandoahObjToScanQueueSet* queues = task_queues(); ShenandoahObjToScanQueue* q = get_queue(w); ShenandoahObjToScanQueue* old_q = get_old_queue(w); ShenandoahReferenceProcessor *rp = _generation->ref_processor(); ShenandoahHeap* const heap = ShenandoahHeap::heap(); ShenandoahLiveData* ld = heap->get_liveness_cache(w); + // Take outstanding work from queues not covered by current workers. + // We expect there is little work in those queues. + mark_drain_extra_queues(queues, q); + // TODO: We can clean up this if we figure out how to do templated oop closures that // play nice with specialized_oop_iterators. if (update_refs) { @@ -120,53 +125,51 @@ void ShenandoahMark::mark_loop(uint worker_id, TaskTerminator* terminator, Shena } } -template -void ShenandoahMark::mark_loop_work(T* cl, ShenandoahLiveData* live_data, uint worker_id, TaskTerminator *terminator, StringDedup::Requests* const req) { +template +void ShenandoahMark::mark_drain_extra_queues(ShenandoahObjToScanQueueSet* queues, ShenandoahObjToScanQueue* local_q) { uintx stride = ShenandoahMarkLoopStride; ShenandoahHeap* heap = ShenandoahHeap::heap(); - ShenandoahObjToScanQueueSet* queues = task_queues(); - ShenandoahObjToScanQueue* q; ShenandoahMarkTask t; - assert(_generation->type() == GENERATION, "Sanity: %d != %d", _generation->type(), GENERATION); - _generation->ref_processor()->set_mark_closure(worker_id, cl); - - /* - * Process outstanding queues, if any. - * - * There can be more queues than workers. To deal with the imbalance, we claim - * extra queues first. Since marking can push new tasks into the queue associated - * with this worker id, we come back to process this queue in the normal loop. - */ assert(queues->get_reserved() == heap->workers()->active_workers(), - "Need to reserve proper number of queues: reserved: %u, active: %u", queues->get_reserved(), heap->workers()->active_workers()); + "Safety: claimable queues do not intersect with worker queues: %u == %u", + queues->get_reserved(), heap->workers()->active_workers()); - q = queues->claim_next(); + ShenandoahObjToScanQueue* q = queues->claim_next(); while (q != nullptr) { - if (CANCELLABLE && heap->check_cancelled_gc_and_yield()) { - return; - } - - for (uint i = 0; i < stride; i++) { - if (q->pop(t)) { - do_task(q, cl, live_data, req, &t, worker_id); - } else { - assert(q->is_empty(), "Must be empty"); - q = queues->claim_next(); - break; + while (!q->is_empty()) { + if (CANCELLABLE && heap->check_cancelled_gc_and_yield()) { + return; + } + for (uint i = 0; i < stride; i++) { + if (q->pop(t)) { + local_q->push(t); + } else { + break; + } } } + q = queues->claim_next(); } - q = get_queue(worker_id); +} + +template +void ShenandoahMark::mark_loop_work(T* cl, ShenandoahLiveData* live_data, uint worker_id, TaskTerminator *terminator, StringDedup::Requests* const req) { + uintx stride = ShenandoahMarkLoopStride; + + ShenandoahHeap* heap = ShenandoahHeap::heap(); + ShenandoahObjToScanQueueSet* queues = task_queues(); + ShenandoahObjToScanQueue* q = get_queue(worker_id); ShenandoahObjToScanQueue* old_q = get_old_queue(worker_id); + ShenandoahMarkTask t; + + assert(_generation->type() == GENERATION, "Sanity: %d != %d", _generation->type(), GENERATION); + _generation->ref_processor()->set_mark_closure(worker_id, cl); ShenandoahSATBBufferClosure drain_satb(q, old_q); SATBMarkQueueSet& satb_mq_set = ShenandoahBarrierSet::satb_mark_queue_set(); - /* - * Normal marking loop: - */ while (true) { if (CANCELLABLE && heap->check_cancelled_gc_and_yield()) { return; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp index 1ba2cd067b6..69d792d0277 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp @@ -109,6 +109,10 @@ class ShenandoahMark: public StackObj { NOINLINE // Main hot loop, start inlining from here void mark_loop_work(T* cl, ShenandoahLiveData* live_data, uint worker_id, TaskTerminator *t, StringDedup::Requests* const req); + template + NOINLINE // Utility loop, maybe hot, start inlining from here + void mark_drain_extra_queues(ShenandoahObjToScanQueueSet* queues, ShenandoahObjToScanQueue* local_q); + protected: template void mark_loop(uint worker_id, TaskTerminator* terminator, ShenandoahGenerationType generation_type, From 1911bd7782e075f01eca7578c07d3f805c41c21d Mon Sep 17 00:00:00 2001 From: Boris Ulasevich Date: Wed, 8 Jul 2026 18:05:14 +0000 Subject: [PATCH 181/707] 8378719: CompiledDirectCall::set_to_interpreted() fails with guarantee(chk == -1 || chk == 0) failed: Field too big for insn Reviewed-by: eastigeevich, dlong --- src/hotspot/cpu/aarch64/aarch64.ad | 8 +- .../cpu/aarch64/macroAssembler_aarch64.cpp | 14 +- .../cpu/aarch64/macroAssembler_aarch64.hpp | 19 +-- .../codecache/TestNonNMethodHeapOverflow.java | 138 ++++++++++++++++++ 4 files changed, 156 insertions(+), 23 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/codecache/TestNonNMethodHeapOverflow.java diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 05e4321b663..be9d79d03c7 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -1198,8 +1198,12 @@ class HandlerImpl { static int emit_deopt_handler(C2_MacroAssembler* masm); static uint size_deopt_handler() { - // count one branch instruction and one far call instruction sequence - return NativeInstruction::instruction_size + MacroAssembler::far_codestub_branch_size(); + bool use_far_branch = MacroAssembler::target_needs_far_branch(SharedRuntime::deopt_blob()->unpack()); + // far: adrp, add, blr; near: bl + uint target_branch_instructions = use_far_branch ? 3 : 1; + // target branch + one branch instruction + uint deopt_handler_instructions = target_branch_instructions + 1; + return deopt_handler_instructions * NativeInstruction::instruction_size; } }; diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 62a6f61599c..f2208aa0ad6 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -622,20 +622,18 @@ void MacroAssembler::set_last_Java_frame(Register last_java_sp, } } -static inline bool target_needs_far_branch(address addr) { +bool MacroAssembler::target_needs_far_branch(address addr) { if (AOTCodeCache::is_on_for_dump()) { return true; } - // codecache size <= 128M - if (!MacroAssembler::far_branches()) { + if (!far_branches()) { return false; } - // codecache size > 240M - if (MacroAssembler::codestub_branch_needs_far_jump()) { - return true; + if (CodeCache::is_non_nmethod(addr) && + CodeCache::max_distance_to_non_nmethod() <= branch_range) { + return false; } - // codecache size: 128M..240M - return !CodeCache::is_non_nmethod(addr); + return true; } void MacroAssembler::far_call(Address entry, Register tmp) { diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index 740b783cbd4..b39596aab53 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -1354,15 +1354,16 @@ class MacroAssembler: public Assembler { static bool far_branches() { return ReservedCodeCacheSize > branch_range; } - - // Check if branches to the non nmethod section require a far jump + // Check if the static call stub branch needs a far jump. static bool codestub_branch_needs_far_jump() { if (AOTCodeCache::is_on_for_dump()) { - // To calculate far_codestub_branch_size correctly. + // To calculate static_call_stub_size correctly. return true; } - return CodeCache::max_distance_to_non_nmethod() > branch_range; + return far_branches(); } + // Check if a branch to the given address needs a far jump. + static bool target_needs_far_branch(address addr); // Emit a direct call/jump if the entry address will always be in range, // otherwise a far call/jump. @@ -1374,18 +1375,10 @@ class MacroAssembler: public Assembler { // In the case of a far call/jump, the entry address is put in the tmp register. // The tmp register is invalidated. // - // Far_jump returns the amount of the emitted code. void far_call(Address entry, Register tmp = rscratch1); + // Far_jump returns the amount of the emitted code. int far_jump(Address entry, Register tmp = rscratch1); - static int far_codestub_branch_size() { - if (codestub_branch_needs_far_jump()) { - return 3 * 4; // adrp, add, br - } else { - return 4; - } - } - // Emit the CompiledIC call idiom address ic_call(address entry, jint method_index = 0); static int ic_check_size(); diff --git a/test/hotspot/jtreg/compiler/codecache/TestNonNMethodHeapOverflow.java b/test/hotspot/jtreg/compiler/codecache/TestNonNMethodHeapOverflow.java new file mode 100644 index 00000000000..27562575459 --- /dev/null +++ b/test/hotspot/jtreg/compiler/codecache/TestNonNMethodHeapOverflow.java @@ -0,0 +1,138 @@ +/* + * 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 8378719 + * @summary Reproduces a RuntimeStub::resolve_static_call_blob pd_patch_instruction_size guarantee + * - forces adapters to be allocated outside the NonNMethod heap + * - puts c2i adapter and compiled method at 128+ MB distance + * @requires vm.flagless + * @requires os.arch == "aarch64" + * @requires vm.debug == false + * @library /test/lib + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+UnlockExperimentalVMOptions + * -XX:+WhiteBoxAPI + * -XX:ReservedCodeCacheSize=240M + * -XX:NonNMethodCodeHeapSize=8M + * -XX:ProfiledCodeHeapSize=116M + * -XX:NonProfiledCodeHeapSize=116M + * -XX:CodeCacheMinBlockLength=1 + * -XX:CodeCacheSegmentSize=128 + * -XX:-UseCodeCacheFlushing + * -XX:CompileCommand=dontinline,compiler.codecache.TestNonNMethodHeapOverflowTarget::a + * -XX:CompileCommand=exclude,compiler.codecache.TestNonNMethodHeapOverflowTarget::a + * -XX:CompileCommand=compileonly,compiler.codecache.TestNonNMethodHeapOverflowTarget::b + * compiler.codecache.TestNonNMethodHeapOverflow + */ + +package compiler.codecache; + +import jdk.test.whitebox.WhiteBox; +import jdk.test.whitebox.code.BlobType; +import jdk.test.whitebox.code.CodeBlob; +import jdk.test.whitebox.code.NMethod; + +import java.lang.reflect.Method; + +public class TestNonNMethodHeapOverflow { + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + private static final int HEAP_BLOCK_HEADER_SIZE = 8; + + public static void main(String[] args) throws Exception { + WB.lockCompilation(); + + BlobType blobType; + int blobSize = 1024; + int allocSize = blobSize - HEAP_BLOCK_HEADER_SIZE; + // fill the NonNMethod heap + do { + long addr = WB.allocateCodeBlob(allocSize, BlobType.NonNMethod.id); + if (addr == 0) { + throw new RuntimeException("Failed to allocate in BlobType.NonNMethod"); + } + blobType = CodeBlob.getCodeBlob(addr).code_blob_type; + } while (blobType == BlobType.NonNMethod); + + if (blobType != BlobType.MethodNonProfiled) { + throw new RuntimeException("NonNMethod->NonProfiled fallback mechanism was changed? Need to update the test"); + } + + long heapSize = BlobType.MethodNonProfiled.getSize(); + int allocated = 0; + // fill the first half of NonProfiled heap + while (allocated < heapSize / 2) { + long addr = WB.allocateCodeBlob(allocSize, BlobType.MethodNonProfiled.id); + if (addr == 0) { + throw new RuntimeException("Failed to allocate in MethodNonProfiled"); + } + allocated += blobSize; + } + + WB.unlockCompilation(); + + // loading triggers i2c/c2i adapter generation; NonNMethod heap is full, adapters go into a middle of NonProfiled heap + Class c = Class.forName("compiler.codecache.TestNonNMethodHeapOverflowTarget"); + Method methodB = c.getDeclaredMethod("b"); + methodB.invoke(null); + + // compile b() at level 2 so the nmethod goes into the beginning of Profiled heap + int compLevel = 2; + WB.enqueueMethodForCompilation(methodB, compLevel); + while (WB.isMethodQueuedForCompilation(methodB)) { + Thread.sleep(100); + } + if (WB.getMethodCompilationLevel(methodB) != compLevel) { + throw new IllegalStateException("b() is not compiled at the compilation level " + compLevel + + ". Got: " + WB.getMethodCompilationLevel(methodB)); + } + + // The distance from the static call stub in nmethod to the c2i adapter exceeds 128MB (AArch64 near-branch range): + // + // | Profiled | NonNMethod | NonProfiled | + // -------------------------------- ------------ -------------------------------- + // |[nmethod] |############|################[c2i] | + + NMethod nm = NMethod.get(methodB, false); + System.out.println("b() at 0x" + Long.toHexString(nm.address) + " heap=" + nm.code_blob_type); + if (nm.code_blob_type != BlobType.MethodProfiled) { + throw new RuntimeException("b() is expected to be in MethodProfiled heap, got: " + nm.code_blob_type); + } + + // invoke compiled b(): triggers resolve_static_call_blob to patch the static call stub + // in nmethod to point to the c2i adapter for a() + methodB.invoke(null); + } +} + +class TestNonNMethodHeapOverflowTarget { + static float a(float f1, double d1, long l1, int i1, float f2, double d2) { + return f1; + } + static float b() { + return a(1.0f, 2.0, 3L, 4, 5.0f, 6.0); + } +} From d5dcbe860da9adf41f2ed0ecbb6f5aa17468d0ee Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Wed, 8 Jul 2026 21:25:57 +0000 Subject: [PATCH 182/707] 8387907: Shenandoah: Marking loop prefetch Reviewed-by: wkemper, xpeng, stuefe --- .../gc/shenandoah/shenandoahMark.inline.hpp | 3 +- .../shenandoah/shenandoahPrefetch.inline.hpp | 77 +++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahPrefetch.inline.hpp diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp index 72129ff9e14..8a7ce7ea831 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp @@ -37,13 +37,13 @@ #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" #include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahPrefetch.inline.hpp" #include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" #include "gc/shenandoah/shenandoahTaskqueue.inline.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" #include "memory/iterator.inline.hpp" #include "oops/compressedOops.inline.hpp" #include "oops/oop.inline.hpp" -#include "runtime/prefetch.inline.hpp" #include "utilities/devirtualizer.inline.hpp" #include "utilities/powerOfTwo.hpp" @@ -365,6 +365,7 @@ inline void ShenandoahMark::mark_ref(ShenandoahObjToScanQueue* q, marked = mark_context->mark_strong(obj, /* was_upgraded = */ skip_live); } if (marked) { + ShenandoahPrefetch::prefetch(obj); bool pushed = q->push(ShenandoahMarkTask(obj, skip_live, weak)); assert(pushed, "overflow queue should always succeed pushing"); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPrefetch.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahPrefetch.inline.hpp new file mode 100644 index 00000000000..35aa297e629 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahPrefetch.inline.hpp @@ -0,0 +1,77 @@ +/* + * Copyright Amazon.com Inc. 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_GC_SHENANDOAH_SHENANDOAHPREFETCH_INLINE_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHPREFETCH_INLINE_HPP + +// No shenandoahPrefetch.hpp + +#include "memory/allStatic.hpp" +#include "runtime/prefetch.inline.hpp" + +// Utility to centralize prefetching decisions. +// +// Prefetching needs to strike the balance between the latency savings +// from upcoming accesses and the excess memory throughput for accesses +// that are prefetched but are never used. +// +// A common access pattern for the object in hot GC code is: +// [mark word] // sometimes, for forwarding pointer accesses +// [klass word] // very often, to discover object type +// ... +// [oop field N] // often, to traverse the heap or fix references +// +// Prefetches work on cache line granularity, so we can pick and choose +// good static offsets at which to prefetch. It also frees us from +// polling mark/klass word offsets at runtime. +// +// It stands to reason that prefetching at zero is most beneficial. +// Since it is almost guaranteed to be used by future accesses, there is +// little downside. For objects that are fully within the cache line, +// that zero-prefetch also picks up oop fields nicely. +// +// Experiments suggest it is also important to handle the case when +// object crosses the cache line. In this case, zero-prefetch is likely +// to miss the oop fields cache line. In extreme case, it can prefetch only +// the mark word, leaving klass word unprefetched. We can prefetch +// the full next cache line to deal with this case, but it is wasteful, +// especially on platforms with very large cache lines. +// +// Therefore, the second prefetch is done at some small offset to balance +// the crossing case. If second prefetch hits the same cache line as the +// first one, there is little downside. This also works automagically with +// platforms with larger cache line sizes, as both prefetches would converge. +// If prefetch hits another cache line, it likely means the object crosses +// the cache line, and that the second prefetch is profitable. +// +class ShenandoahPrefetch : AllStatic { +public: + static void prefetch(oop obj) { + void* addr = obj->base_addr(); + Prefetch::read(addr, 0); + Prefetch::read(addr, 32); + } +}; + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHPREFETCH_INLINE_HPP From 182b0cb2063012c6f5cb40617f33165fc8a13249 Mon Sep 17 00:00:00 2001 From: April Ivy Date: Thu, 9 Jul 2026 00:17:45 +0000 Subject: [PATCH 183/707] 8387937: C1: aarch64: two-arg add_debug_info_for_branch looks obsolete Reviewed-by: dlong --- src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp | 10 ---------- src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp | 3 +-- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index 202f3227e2d..0290a200366 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -452,16 +452,6 @@ int LIR_Assembler::emit_deopt_handler() { return entry_offset; } -void LIR_Assembler::add_debug_info_for_branch(address adr, CodeEmitInfo* info) { - _masm->code_section()->relocate(adr, relocInfo::poll_type); - int pc_offset = code_offset(); - flush_debug_info(pc_offset); - info->record_debug_info(compilation()->debug_info_recorder(), pc_offset); - if (info->exception_handlers() != nullptr) { - compilation()->add_exception_handlers_for_pco(pc_offset, info->exception_handlers()); - } -} - void LIR_Assembler::return_op(LIR_Opr result, C1SafepointPollStub* code_stub) { assert(result->is_illegal() || !result->is_single_cpu() || result->as_register() == r0, "word returns are in r0,"); diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp index 367256d2f69..bebc9543b40 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -52,7 +52,6 @@ friend class ArrayCopyStub; // Record the type of the receiver in ReceiverTypeData void type_profile_helper(Register mdo, ciMethodData *md, ciProfileData *data, Register recv); - void add_debug_info_for_branch(address adr, CodeEmitInfo* info); void casw(Register addr, Register newval, Register cmpval); void casl(Register addr, Register newval, Register cmpval); From 05c93a1dbb0d6f9c4da10d5a7d924a64408d40d2 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 9 Jul 2026 04:49:49 +0000 Subject: [PATCH 184/707] 8387411: C2: assert((in_vt->isa_pvectmask() == nullptr) == (vt->isa_pvectmask() == nullptr)) failed: Both BVectMask, or both NVectMask, or both PVectMask Reviewed-by: chagedorn, thartmann, vlivanov --- src/hotspot/share/opto/vectornode.cpp | 8 +- .../TestMaskUnboxingTypeMismatch.java | 80 +++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 test/hotspot/jtreg/compiler/vectorapi/TestMaskUnboxingTypeMismatch.java diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index a0454a41044..20857eed35c 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -2309,7 +2309,13 @@ Node* VectorUnboxNode::Ideal(PhaseGVN* phase, bool can_reshape) { if (is_vector_mask) { // VectorUnbox (VectorBox vmask) ==> VectorMaskCast vmask const TypeVect* vmask_type = TypeVect::makemask(out_vt->element_basic_type(), out_vt->length()); - return new VectorMaskCastNode(value, vmask_type); + const TypeVect* value_type = value->bottom_type()->is_vect(); + // Very rarely, profiling can give us output types that are not + // compatible with the input type, where one is PVectMask and + // the other not. Such a path should be unreachable anyway. + if ((value_type->isa_pvectmask() == nullptr) == (vmask_type->isa_pvectmask() == nullptr)) { + return new VectorMaskCastNode(value, vmask_type); + } } else { // Vector type mismatch is only supported for masks, but sometimes it happens in pathological cases. } diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestMaskUnboxingTypeMismatch.java b/test/hotspot/jtreg/compiler/vectorapi/TestMaskUnboxingTypeMismatch.java new file mode 100644 index 00000000000..f24d2d9d087 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestMaskUnboxingTypeMismatch.java @@ -0,0 +1,80 @@ +/* + * 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.vectorapi; + +import jdk.incubator.vector.*; + +/* + * @test id=vanilla + * @bug 8387411 + * @modules jdk.incubator.vector + * + * @run driver ${test.main.class} + */ + +/* + * @test id=KNL + * @bug 8387411 + * @modules jdk.incubator.vector + * + * @run main/othervm -Xbatch + * -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+UseKNLSetting + * -XX:CompileCommand=compileonly,${test.main.class}::test + * ${test.main.class} + */ + +public class TestMaskUnboxingTypeMismatch { + + public static Object pollute() { + VectorMask intMask = VectorMask.fromLong(IntVector.SPECIES_512, 1L); + // Profile "andNot" with I512. + return intMask.andNot(intMask); + } + + public static Object test() { + var v0 = ByteVector.broadcast(ByteVector.SPECIES_128, (byte)7); + var v1 = VectorMask.fromLong(ByteVector.SPECIES_128, 1L); + var v2 = VectorMask.fromLong(ByteVector.SPECIES_128, 2L); + // Use "andNot" with B128. + // We can get some boxing of B128 mask, which is later unboxed + // as profiled I512, which is impossible. When trying to insert + // an VectorMaskCast in VectorUnboxNode::Ideal, we hit an assert, + // because with UseKNLSetting, B128 mask is a NVectMask, and I512 + // a PVectMask. + var v3 = v1.andNot(v2); + var v4 = v0.lanewise(VectorOperators.UMAX, (byte)42, v3); + return v4; + } + + public static void main(String[] args) { + // Sufficient repetitions to get some profiling. + for (int i = 0; i < 10_000; i++) { + pollute(); + } + // Sufficient repetitions to get compilation. + for (int i = 0; i < 50_000; i++) { + test(); + } + } +} From 333deb2cc63d8a16600a09e5f933c2a5091fda3d Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 9 Jul 2026 06:57:14 +0000 Subject: [PATCH 185/707] 8380967: Canceled HttpClient.sendAsync futures throw inconsistent exceptions Reviewed-by: dfuchs --- .../net/http/common/MinimalFuture.java | 13 +++++--- .../net/httpclient/CancelRequestTest.java | 29 +++++------------ .../net/http/common/MinimalFutureTest.java | 32 +++++++++++++++++++ 3 files changed, 48 insertions(+), 26 deletions(-) diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/common/MinimalFuture.java b/src/java.net.http/share/classes/jdk/internal/net/http/common/MinimalFuture.java index ddbcce661aa..268705f6c1f 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/common/MinimalFuture.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/common/MinimalFuture.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -102,11 +102,14 @@ public String toString() { @Override public boolean cancel(boolean mayInterruptIfRunning) { - boolean result = false; - if (cancelable != null && !isDone()) { - result = cancelable.cancel(mayInterruptIfRunning); + if (!super.cancel(mayInterruptIfRunning)) { + assert isDone(); + return false; } - return super.cancel(mayInterruptIfRunning) || result; + if (cancelable != null) { + cancelable.cancel(mayInterruptIfRunning); + } + return true; } private Cancelable cancelable() { diff --git a/test/jdk/java/net/httpclient/CancelRequestTest.java b/test/jdk/java/net/httpclient/CancelRequestTest.java index f21d13d5e98..418127c7735 100644 --- a/test/jdk/java/net/httpclient/CancelRequestTest.java +++ b/test/jdk/java/net/httpclient/CancelRequestTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8245462 8229822 8254786 8297075 8297149 8298340 8302635 8377181 + * @bug 8245462 8229822 8254786 8297075 8297149 8298340 8302635 8377181 8380967 * @summary Tests cancelling the request. * @library /test/lib /test/jdk/java/net/httpclient/lib * @key randomness @@ -79,6 +79,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Assumptions; @@ -394,15 +395,9 @@ public void testGetSendAsync(String uri, boolean sameClient, boolean mayInterrup requestLatch.countDown(); } - // Cancelling the request may cause an IOException instead... - boolean hasCancellationException = false; - try { - cf1.get(); - } catch (CancellationException | ExecutionException x) { - out.println(now() + "Got expected exception: " + x); - assertTrue(isCancelled(x)); - hasCancellationException = x instanceof CancellationException; - } + var cancelX = assertThrows(CancellationException.class, cf1::get); + out.println(now() + "Got expected exception: " + cancelX); + assertTrue(cf1.isCancelled()); // because it's cf1 that was cancelled then response might not have // completed yet - so wait for it here... @@ -447,7 +442,6 @@ public void testGetSendAsync(String uri, boolean sameClient, boolean mayInterrup assertTrue(response.isDone()); assertFalse(response.isCancelled()); - assertEquals(hasCancellationException, cf1.isCancelled()); assertTrue(cf2.isDone()); assertFalse(cf2.isCancelled()); assertEquals(0, latch.getCount()); @@ -529,15 +523,9 @@ public Iterator iterator() { requestLatch.countDown(); } - // Cancelling the request may cause an IOException instead... - boolean hasCancellationException = false; - try { - cf1.get(); - } catch (CancellationException | ExecutionException x) { - out.println(now() + "Got expected exception: " + x); - assertTrue(isCancelled(x)); - hasCancellationException = x instanceof CancellationException; - } + var cancelX = assertThrows(CancellationException.class, cf1::get); + out.println(now() + "Got expected exception: " + cancelX); + assertTrue(cf1.isCancelled()); // because it's cf1 that was cancelled then response might not have // completed yet - so wait for it here... @@ -576,7 +564,6 @@ public Iterator iterator() { assertTrue(response.isDone()); assertFalse(response.isCancelled()); - assertEquals(hasCancellationException, cf1.isCancelled()); assertTrue(cf2.isDone()); assertFalse(cf2.isCancelled()); assertEquals(0, latch.getCount()); diff --git a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/common/MinimalFutureTest.java b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/common/MinimalFutureTest.java index 2c33f6f0018..ccac8fcf439 100644 --- a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/common/MinimalFutureTest.java +++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/common/MinimalFutureTest.java @@ -27,10 +27,16 @@ import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; public class MinimalFutureTest { @@ -101,6 +107,32 @@ public void test(CompletableFuture mf) { } } + @Test + public void testCancel() { + AtomicInteger cancelCount = new AtomicInteger(); + AtomicBoolean cancelled = new AtomicBoolean(); + Cancelable cancelable = mayInterruptIfRunning -> { + cancelCount.incrementAndGet(); + if (mayInterruptIfRunning) { + cancelled.set(true); + } + return cancelled.get(); + }; + MinimalFuture future = new MinimalFuture<>(cancelable); + CompletableFuture dependent = future.copy().whenComplete((x,t) -> + System.out.println("expected: " + t)); + assertTrue(dependent.cancel(false)); + assertTrue(dependent.isCancelled()); + assertFalse(future.isCancelled()); + assertFalse(cancelled.get()); + assertEquals(1, cancelCount.get()); + assertTrue(dependent.cancel(true)); + assertTrue(dependent.isCancelled()); + assertFalse(future.isCancelled()); + assertTrue(cancelled.get()); + assertEquals(2, cancelCount.get()); + } + private static CompletableFuture otherFuture() { return MinimalFuture.completedFuture(new Object()); } From 31dede3f96d78dd0b1c93d84adb1f97b34ff339f Mon Sep 17 00:00:00 2001 From: Dingli Zhang Date: Thu, 9 Jul 2026 07:47:14 +0000 Subject: [PATCH 186/707] 8368180: RISC-V: Remove redundant ext_Zicboz.enable_feature() Reviewed-by: fyang, gcao --- src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp | 1 - 1 file changed, 1 deletion(-) 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 648131b94a3..c9556d32cc5 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 @@ -307,7 +307,6 @@ void VM_Version::rivos_features() { ext_Zfh.enable_feature(); - ext_Zicboz.enable_feature(); ext_Zicsr.enable_feature(); ext_Zifencei.enable_feature(); ext_Zic64b.enable_feature(); From 7753c98686006bc5710169dd4d3ca312495a8ad1 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Thu, 9 Jul 2026 13:19:37 +0000 Subject: [PATCH 187/707] 8386475: C2 x64: -XX:-UseBMI2Instructions is broken for AVX-512 Reviewed-by: galder, epeter, kvn --- src/hotspot/cpu/x86/macroAssembler_x86.cpp | 6 +- src/hotspot/cpu/x86/stubGenerator_x86_64.cpp | 4 +- .../cpuflags/TestUseBMI2Instructions.java | 62 +++++++++++++++++++ 3 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/cpuflags/TestUseBMI2Instructions.java diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index d1250f0820f..6c0b1178b0e 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -5874,7 +5874,7 @@ void MacroAssembler::xmm_clear_mem(Register base, Register cnt, Register rtmp, X // cnt - number of qwords (8-byte words). // base - start address, qword aligned. Label L_zero_64_bytes, L_loop, L_sloop, L_tail, L_end; - bool use64byteVector = (MaxVectorSize == 64) && (CopyAVX3Threshold == 0); + bool use64byteVector = (MaxVectorSize == 64) && (CopyAVX3Threshold == 0) && VM_Version::supports_bmi2(); if (use64byteVector) { vpxor(xtmp, xtmp, xtmp, AVX_512bit); } else if (MaxVectorSize >= 32) { @@ -5921,7 +5921,7 @@ void MacroAssembler::xmm_clear_mem(Register base, Register cnt, Register rtmp, X BIND(L_tail); addptr(cnt, 4); jccb(Assembler::lessEqual, L_end); - if (UseAVX > 2 && MaxVectorSize >= 32 && VM_Version::supports_avx512vl()) { + if (UseAVX > 2 && MaxVectorSize >= 32 && VM_Version::supports_avx512vl() && VM_Version::supports_bmi2()) { fill32_masked(3, base, 0, xtmp, mask, cnt, rtmp); } else { decrement(cnt); @@ -6984,7 +6984,7 @@ void MacroAssembler::vectorized_mismatch(Register obja, Register objb, Register xorq(result, result); if ((AVX3Threshold == 0) && (UseAVX > 2) && - VM_Version::supports_avx512vlbw() && UseCountTrailingZerosInstruction) { + VM_Version::supports_avx512vlbw() && UseCountTrailingZerosInstruction && VM_Version::supports_bmi2()) { Label VECTOR64_LOOP, VECTOR64_NOT_EQUAL, VECTOR32_TAIL; cmpq(length, 64); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp index afd9c126a21..2b37e39ec86 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp @@ -3069,7 +3069,7 @@ address StubGenerator::generate_base64_decodeBlock() { // If AVX512 VBMI not supported, just compile non-AVX code if(VM_Version::supports_avx512_vbmi() && - VM_Version::supports_avx512bw()) { + VM_Version::supports_avx512bw() && VM_Version::supports_bmi2()) { __ cmpl(length, 31); // 32-bytes is break-even for AVX-512 __ jcc(Assembler::lessEqual, L_lastChunk); @@ -4887,7 +4887,7 @@ void StubGenerator::generate_compiler_stubs() { StubRoutines::_data_cache_writeback = generate_data_cache_writeback(); StubRoutines::_data_cache_writeback_sync = generate_data_cache_writeback_sync(); - if ((UseAVX == 2) && EnableX86ECoreOpts && UseCountTrailingZerosInstruction) { + if ((UseAVX == 2) && EnableX86ECoreOpts && UseCountTrailingZerosInstruction && VM_Version::supports_bmi2()) { generate_string_indexof(StubRoutines::_string_indexof_array); } diff --git a/test/hotspot/jtreg/compiler/cpuflags/TestUseBMI2Instructions.java b/test/hotspot/jtreg/compiler/cpuflags/TestUseBMI2Instructions.java new file mode 100644 index 00000000000..df595a5ff26 --- /dev/null +++ b/test/hotspot/jtreg/compiler/cpuflags/TestUseBMI2Instructions.java @@ -0,0 +1,62 @@ +/* + * 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 8386475 + * @summary Verify no assertions with -XX:+UseBMI2Instructions + * @requires os.simpleArch == "x64" + * @run main/othervm -XX:+UseBMI2Instructions ${test.main.class} + */ + +/* + * @test + * @bug 8386475 + * @summary Verify no assertions with -XX:-UseBMI2Instructions + * @requires os.simpleArch == "x64" + * @run main/othervm -Xcomp -XX:CompileCommand=compileonly,java.lang.CharacterDataLatin1:: -XX:+UnlockDiagnosticVMOptions -XX:CopyAVX3Threshold=0 -XX:-UseBMI2Instructions ${test.main.class} + */ + +/* + * @test + * @bug 8386475 + * @summary Verify no assertions when generating vectorizedMismatch stub with -XX:-UseBMI2Instructions + * @requires os.simpleArch == "x64" & vm.cpu.features ~= ".*avx2.*" + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:AVX3Threshold=0 -XX:-UseBMI2Instructions ${test.main.class} + */ + +/* + * @test + * @bug 8386475 + * @summary Verify no assertions when generating string_indexof stub with -XX:-UseBMI2Instructions + * @requires os.simpleArch == "x64" & vm.cpu.features ~= ".*avx2.*" + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseAVX=2 -XX:+EnableX86ECoreOpts -XX:-UseBMI2Instructions ${test.main.class} + */ + +package compiler.cpuflags; + +public class TestUseBMI2Instructions { + public static void main(String args[]) { + // intentionally empty + } +} From d425cbe2f0df08aacd9f5093a758455309ab0cf5 Mon Sep 17 00:00:00 2001 From: Amit Kumar Date: Thu, 9 Jul 2026 13:38:52 +0000 Subject: [PATCH 188/707] 8286300: Port JEP 425 to S390X 8377034: Enable full JSR166TestCase.java test for s390x 8380035: compiler/intrinsics/TestReturnOopSetForJFRWriteCheckpoint.java crashes on s390x 8335163: [s390x] test failure - PrintClasses.java Co-authored-by: Andrew Haley Co-authored-by: Richard Reingruber Reviewed-by: rrich, aph, pchilanomate --- .../cpu/s390/abstractInterpreter_s390.cpp | 8 +- src/hotspot/cpu/s390/assembler_s390.hpp | 5 +- src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp | 4 +- src/hotspot/cpu/s390/c1_Runtime1_s390.cpp | 49 +- .../cpu/s390/continuationEntry_s390.hpp | 6 +- .../s390/continuationEntry_s390.inline.hpp | 23 +- .../continuationFreezeThaw_s390.inline.hpp | 277 ++++++++- .../s390/continuationHelper_s390.inline.hpp | 116 ++-- src/hotspot/cpu/s390/frame_s390.cpp | 85 ++- src/hotspot/cpu/s390/frame_s390.hpp | 49 +- src/hotspot/cpu/s390/frame_s390.inline.hpp | 106 +++- src/hotspot/cpu/s390/globals_s390.hpp | 2 +- src/hotspot/cpu/s390/interp_masm_s390.cpp | 126 +++- src/hotspot/cpu/s390/interp_masm_s390.hpp | 8 +- src/hotspot/cpu/s390/macroAssembler_s390.cpp | 114 +++- src/hotspot/cpu/s390/macroAssembler_s390.hpp | 26 +- .../cpu/s390/macroAssembler_s390.inline.hpp | 14 +- src/hotspot/cpu/s390/nativeInst_s390.cpp | 31 +- src/hotspot/cpu/s390/nativeInst_s390.hpp | 44 +- src/hotspot/cpu/s390/s390.ad | 35 ++ src/hotspot/cpu/s390/sharedRuntime_s390.cpp | 582 +++++++++++++++++- .../cpu/s390/smallRegisterMap_s390.inline.hpp | 17 +- .../stackChunkFrameStream_s390.inline.hpp | 90 ++- .../cpu/s390/stackChunkOop_s390.inline.hpp | 11 +- .../cpu/s390/stubDeclarations_s390.hpp | 4 +- src/hotspot/cpu/s390/stubGenerator_s390.cpp | 184 +++++- .../templateInterpreterGenerator_s390.cpp | 71 ++- src/hotspot/cpu/s390/templateTable_s390.cpp | 8 +- src/hotspot/cpu/s390/upcallLinker_s390.cpp | 4 + .../share/oops/stackChunkOop.inline.hpp | 4 +- src/hotspot/share/runtime/continuation.cpp | 2 +- .../share/runtime/continuationFreezeThaw.cpp | 29 +- src/hotspot/share/runtime/frame.cpp | 6 +- src/hotspot/share/runtime/sharedRuntime.cpp | 10 +- test/hotspot/jtreg/ProblemList.txt | 2 - test/jdk/ProblemList.txt | 18 - .../util/concurrent/tck/JSR166TestCase.java | 14 - 37 files changed, 1840 insertions(+), 344 deletions(-) diff --git a/src/hotspot/cpu/s390/abstractInterpreter_s390.cpp b/src/hotspot/cpu/s390/abstractInterpreter_s390.cpp index 96990f0ce94..c54f1a4b010 100644 --- a/src/hotspot/cpu/s390/abstractInterpreter_s390.cpp +++ b/src/hotspot/cpu/s390/abstractInterpreter_s390.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -197,8 +197,10 @@ void AbstractInterpreter::layout_activation(Method* method, assert(is_bottom_frame && (sender_sp == caller->unextended_sp()), "must initialize sender_sp of bottom skeleton frame when pushing it"); } else { - assert(caller->is_entry_frame() || caller->is_upcall_stub_frame(), "is there a new frame type??"); - sender_sp = caller->sp(); // Call_stub only uses it's fp. + // For entry, upcall_stub, and native frames, sender_sp is simply the caller's sp. + // These frames use the standard C ABI and don't require adjustment. + assert(caller->is_entry_frame() || caller->is_upcall_stub_frame() || caller->is_native_frame(), "is there a new frame type??"); + sender_sp = caller->sp(); } interpreter_frame->interpreter_frame_set_method(method); diff --git a/src/hotspot/cpu/s390/assembler_s390.hpp b/src/hotspot/cpu/s390/assembler_s390.hpp index c0cee5bd555..95ae442bb49 100644 --- a/src/hotspot/cpu/s390/assembler_s390.hpp +++ b/src/hotspot/cpu/s390/assembler_s390.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -3279,6 +3279,9 @@ class Assembler : public AbstractAssembler { static bool is_z_nop(address x) { return is_z_nop(* (short *) x); } + static bool is_z_illtrap(address x) { + return *(uint16_t*)x == 0u; + } static bool is_z_br(long x) { return is_z_bcr(x) && ((x & 0x00f0) == 0x00f0); } diff --git a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp index 08f922a0b9a..db3f2f6218f 100644 --- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp @@ -524,6 +524,7 @@ void LIR_Assembler::call(LIR_OpJavaCall* op, relocInfo::relocType rtype) { __ z_nop(); __ z_brasl(Z_R14, op->addr()); add_call_info(code_offset(), op->info()); + __ post_call_nop(); } void LIR_Assembler::ic_call(LIR_OpJavaCall* op) { @@ -539,7 +540,7 @@ void LIR_Assembler::ic_call(LIR_OpJavaCall* op) { // CALL to fixup routine. Fixup routine uses ScopeDesc info // to determine who we intended to call. __ relocate(virtual_call_Relocation::spec(virtual_call_oop_addr)); - call(op, relocInfo::none); + call(op, relocInfo::none); // call will emit a post call nop, see above method. } void LIR_Assembler::move_regs(Register from_reg, Register to_reg) { @@ -2792,6 +2793,7 @@ void LIR_Assembler::rt_call(LIR_Opr result, address dest, if (info != nullptr) { add_call_info_here(info); } + __ post_call_nop(); } void LIR_Assembler::volatile_move_op(LIR_Opr src, LIR_Opr dest, BasicType type, CodeEmitInfo* info) { diff --git a/src/hotspot/cpu/s390/c1_Runtime1_s390.cpp b/src/hotspot/cpu/s390/c1_Runtime1_s390.cpp index e78b04fe911..d26db67d078 100644 --- a/src/hotspot/cpu/s390/c1_Runtime1_s390.cpp +++ b/src/hotspot/cpu/s390/c1_Runtime1_s390.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2023 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -52,13 +52,8 @@ int StubAssembler::call_RT(Register oop_result1, Register metadata_result, addre set_num_rt_args(0); // Nothing on stack. assert(!(oop_result1->is_valid() || metadata_result->is_valid()) || oop_result1 != metadata_result, "registers must be different"); - // We cannot trust that code generated by the C++ compiler saves R14 - // to z_abi_160.return_pc, because sometimes it spills R14 using stmg at - // z_abi_160.gpr14 (e.g. InterpreterRuntime::_new()). - // Therefore we load the PC into Z_R1_scratch and let set_last_Java_frame() save - // it into the frame anchor. - address pc = get_PC(Z_R1_scratch); - int call_offset = (int)(pc - addr_at(0)); + Label resume; + z_larl(Z_R1_scratch, resume); set_last_Java_frame(Z_SP, Z_R1_scratch); // ARG1 must hold thread address. @@ -67,9 +62,12 @@ int StubAssembler::call_RT(Register oop_result1, Register metadata_result, addre address return_pc = nullptr; align_call_far_patchable(this->pc()); return_pc = call_c_opt(entry_point); + + bind(resume); + int call_offset = offset(); assert(return_pc != nullptr, "const section overflow"); - reset_last_Java_frame(); + reset_last_Java_frame(/* check_last_java_sp= */ false); // Check for pending exceptions. { @@ -208,8 +206,37 @@ void Runtime1::initialize_pd() { } uint Runtime1::runtime_blob_current_thread_offset(frame f) { - Unimplemented(); - return 0; + CodeBlob* cb = f.cb(); + assert(cb == Runtime1::blob_for(StubId::c1_monitorenter_id) || + cb == Runtime1::blob_for(StubId::c1_monitorenter_nofpu_id), "must be"); + assert(cb != nullptr && cb->is_runtime_stub(), "invalid frame"); + + // Calculate the offset of Z_thread (Z_R8) in the saved register area. + // Both c1_monitorenter_id and c1_monitorenter_nofpu_id have the same frame layout: + // - c1_monitorenter_id uses RegisterSaver::all_registers (saves FPU regs) + // - c1_monitorenter_nofpu_id uses RegisterSaver::all_integer_registers (excludes FPU regs but reserves space) + // + // From RegisterSaver_LiveRegs and RegisterSaver_LiveIntRegs: + // Both have 15 float register slots (F0, F2-F15, F1 is excluded as scratch) + // Then integer registers: R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13 + // Z_thread is Z_R8, which is the 7th integer register (index 6 from R2) + // + // Stack layout from SP: + // [0..159] : z_abi_160 + // [160..279] : 15 float register slots (15 * 8 = 120 bytes) + // [280..327] : R2-R7 (6 * 8 = 48 bytes) + // [328..335] : R8 (Z_thread) <- this is what we need + // + // Offset = 160 + 120 + 48 = 328 bytes from SP + // Return value is in 64-bit words: 328 / 8 = 41 + + const int float_reg_slots = 15; // F0, F2-F15 (F1 is scratch, excluded) + const int int_regs_before_r8 = 6; // R2, R3, R4, R5, R6, R7 + const int z_thread_offset = frame::z_abi_160_size + + (float_reg_slots * 8) + + (int_regs_before_r8 * 8); + + return z_thread_offset / wordSize; } OopMapSet* Runtime1::generate_exception_throw(StubAssembler* sasm, address target, bool has_argument) { diff --git a/src/hotspot/cpu/s390/continuationEntry_s390.hpp b/src/hotspot/cpu/s390/continuationEntry_s390.hpp index e4e611d2b15..15b1347ce0a 100644 --- a/src/hotspot/cpu/s390/continuationEntry_s390.hpp +++ b/src/hotspot/cpu/s390/continuationEntry_s390.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2022 SAP SE. All rights reserved. + * Copyright (c) 2026 IBM Corporation. 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,8 +26,11 @@ #ifndef CPU_S390_CONTINUATIONENTRY_S390_HPP #define CPU_S390_CONTINUATIONENTRY_S390_HPP +#include "runtime/frame.hpp" + class ContinuationEntryPD { - // empty + // This is needed to position the ContinuationEntry at the unextended sp of the entry frame + frame::z_abi_160_base _abi; }; #endif // CPU_S390_CONTINUATIONENTRY_S390_HPP diff --git a/src/hotspot/cpu/s390/continuationEntry_s390.inline.hpp b/src/hotspot/cpu/s390/continuationEntry_s390.inline.hpp index 1d4e3c2439d..58ff8f0d194 100644 --- a/src/hotspot/cpu/s390/continuationEntry_s390.inline.hpp +++ b/src/hotspot/cpu/s390/continuationEntry_s390.inline.hpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 IBM Corporation. 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,22 +26,28 @@ #ifndef CPU_S390_CONTINUATIONENTRY_S390_INLINE_HPP #define CPU_S390_CONTINUATIONENTRY_S390_INLINE_HPP +#include "oops/method.inline.hpp" +#include "runtime/frame.inline.hpp" +#include "runtime/registerMap.hpp" +#include "utilities/macros.hpp" #include "runtime/continuationEntry.hpp" -// TODO: Implement - inline frame ContinuationEntry::to_frame() const { - Unimplemented(); - return frame(); + static CodeBlob* cb = CodeCache::find_blob_fast(entry_pc()); + assert(cb != nullptr, ""); + assert(cb->as_nmethod()->method()->is_continuation_enter_intrinsic(), ""); + return frame(entry_sp(), entry_pc(), entry_sp(), entry_fp(), cb); } inline intptr_t* ContinuationEntry::entry_fp() const { - Unimplemented(); - return nullptr; + return (intptr_t*)((address)this + size()); } inline void ContinuationEntry::update_register_map(RegisterMap* map) const { - Unimplemented(); + // No register map update needed for s390. + // In the Java calling convention on s390, all registers are volatile (caller-saved), + // so there are no non-volatile (callee-saved) registers that need to be tracked + // in the register map for continuation entry frames. } #endif // CPU_S390_CONTINUATIONENTRY_S390_INLINE_HPP diff --git a/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp b/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp index 1102a745ac0..2f7660052c0 100644 --- a/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp +++ b/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 IBM Corporation. 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,98 +30,316 @@ #include "runtime/frame.hpp" #include "runtime/frame.inline.hpp" +inline void patch_callee_link(const frame& f, intptr_t* fp) { + *ContinuationHelper::Frame::callee_link_address(f) = fp; +} + +inline void patch_callee_link_relative(const frame& f, intptr_t* fp) { + intptr_t* la = (intptr_t*)ContinuationHelper::Frame::callee_link_address(f); + intptr_t new_value = fp - la; + *la = new_value; +} + inline void FreezeBase::set_top_frame_metadata_pd(const frame& hf) { - Unimplemented(); + stackChunkOop chunk = _cont.tail(); + assert(chunk->is_in_chunk(hf.sp()), "hf.sp()=" PTR_FORMAT, p2i(hf.sp())); + + hf.own_abi()->return_pc = (uint64_t)hf.pc(); + if (hf.is_interpreted_frame()) { + patch_callee_link_relative(hf, hf.fp()); + } else { +#ifdef ASSERT + // See also FreezeBase::patch_pd() + patch_callee_link(hf, (intptr_t*)badAddress); +#endif + } } template inline frame FreezeBase::sender(const frame& f) { - Unimplemented(); - return frame(); + assert(FKind::is_instance(f), ""); + + if (FKind::interpreted) { + return frame(f.sender_sp(), f.sender_pc(), f.interpreter_frame_sender_sp()); + } + + intptr_t* sender_sp = f.sender_sp(); + address sender_pc = f.sender_pc(); + assert(sender_sp != f.sp(), "must have changed"); + int slot = 0; + CodeBlob* sender_cb = CodeCache::find_blob_and_oopmap(sender_pc, slot); + return sender_cb != nullptr + ? frame(sender_sp, sender_sp, nullptr, sender_pc, sender_cb, slot == -1 ? nullptr : sender_cb->oop_map_for_slot(slot, sender_pc)) + : frame(sender_sp, sender_pc, sender_sp); } template frame FreezeBase::new_heap_frame(frame& f, frame& caller) { - Unimplemented(); - return frame(); + assert(FKind::is_instance(f), ""); + intptr_t *sp, *fp; + if (FKind::interpreted) { + intptr_t locals_offset = *f.addr_at(_z_ijava_idx(locals)); + + // If the caller.is_empty(), i.e. we're freezing into an empty chunk, then we set + // the chunk's argsize in finalize_freeze and make room for it above the unextended_sp + // See also comment on StackChunkFrameStream::interpreter_frame_size() + + int overlap = + (caller.is_interpreted_frame() || caller.is_empty()) + ? ContinuationHelper::InterpretedFrame::stack_argsize(f) + frame::metadata_words_at_top + : 0; + + // Calculate the new frame's FP in the heap chunk. + // Starting from caller's unextended_sp, we: + // - subtract 1 for the z_parent_ijava_frame_abi (which sits just below the locals) + // - subtract locals_offset (distance from FP to locals in the original frame) + // - add overlap (to account for shared stack args when caller is interpreted or empty) + // This positions FP such that locals are correctly placed relative to the caller's frame. + fp = caller.unextended_sp() - 1 - locals_offset + overlap; + + // esp points one slot below the last argument + intptr_t* x86_64_like_unextended_sp = f.interpreter_frame_esp() + 1 - frame::metadata_words_at_top; + + sp = fp - (f.fp() - x86_64_like_unextended_sp); + assert (sp <= fp && (fp <= caller.unextended_sp() || caller.is_interpreted_frame()), + "sp=" PTR_FORMAT " fp=" PTR_FORMAT " caller.unextended_sp()=" PTR_FORMAT " caller.is_interpreted_frame()=%d", + p2i(sp), p2i(fp), p2i(caller.unextended_sp()), caller.is_interpreted_frame()); + caller.set_sp(fp); + + assert(_cont.tail()->is_in_chunk(sp), ""); + + frame hf(sp, sp, fp, f.pc(), nullptr, nullptr, true /* on_heap */); + // frame_top() and frame_bottom() read these before relativize_interpreted_frame_metadata() is called + *hf.addr_at(_z_ijava_idx(locals)) = locals_offset; + *hf.addr_at(_z_ijava_idx(esp)) = f.interpreter_frame_esp() - f.fp(); + return hf; + } else { + int fsize = FKind::size(f); + sp = caller.unextended_sp() - fsize; + if (caller.is_interpreted_frame()) { + // If the caller is interpreted, our stackargs are not supposed to overlap with it + // so we make more room by moving sp down by argsize + int argsize = FKind::stack_argsize(f); + sp -= argsize + frame::metadata_words_at_top; + } + fp = sp + fsize; + caller.set_sp(fp); + + assert(_cont.tail()->is_in_chunk(sp), ""); + + return frame(sp, sp, fp, f.pc(), nullptr, nullptr, true /* on_heap */); + } } void FreezeBase::adjust_interpreted_frame_unextended_sp(frame& f) { - Unimplemented(); + // Nothing to do on s390 and ppc. On x86/aarch64/riscv, the unextended_sp is stored + // in interpreter_frame_last_sp and needs to be restored from there. On s390/ppc, + // the frame structure doesn't have interpreter_frame_last_sp; instead, the unextended_sp + // is directly maintained in the frame and doesn't need adjustment. } inline void FreezeBase::prepare_freeze_interpreted_top_frame(frame& f) { - Unimplemented(); + // Nothing to do. We don't save a last sp because we cannot use sp as esp. + // Instead the top frame is trimmed when making an i2i call. The original + // top_frame_sp is set when the frame is pushed (see generate_fixed_frame()). + // An interpreter top frame that was just thawed is resized to top_frame_sp by the + // resume adapter (see generate_cont_resume_interpreter_adapter()). So the assertion is + // false, if we freeze again right after thawing as we do when redoing a vm call wasn't + // successful. + assert(_thread->interp_redoing_vm_call() || + ((intptr_t*)f.at_relative(_z_ijava_idx(top_frame_sp)) == f.unextended_sp()), + "top_frame_sp:" PTR_FORMAT " usp:" PTR_FORMAT, f.at_relative(_z_ijava_idx(top_frame_sp)), p2i(f.unextended_sp())); } inline void FreezeBase::relativize_interpreted_frame_metadata(const frame& f, const frame& hf) { - Unimplemented(); + intptr_t* vfp = f.fp(); + intptr_t* hfp = hf.fp(); + assert(f.fp() > (intptr_t*)f.interpreter_frame_esp(), ""); + + // There is alignment padding between vfp and f's locals array in the original + // frame, because we freeze the padding (see recurse_freeze_interpreted_frame) + // in order to keep the same relativized locals pointer, we don't need to change it here. + + // Make sure that monitors is already relativized. + assert(hf.at_absolute(_z_ijava_idx(monitors)) <= -(frame::z_ijava_state_size / wordSize), ""); + // Make sure that esp is already relativized. + assert(hf.at_absolute(_z_ijava_idx(esp)) <= hf.at_absolute(_z_ijava_idx(monitors)), ""); + // top_frame_sp is already relativized + + // hfp == hf.sp() + (f.fp() - f.sp()) is not true on ppc because the stack frame has room for + // the maximal expression stack and the expression stack in the heap frame is trimmed. + assert(hf.fp() == hf.interpreter_frame_esp() + (f.fp() - f.interpreter_frame_esp()), ""); + assert(hf.fp() <= (intptr_t*)hf.at(_z_ijava_idx(locals)), ""); } inline void FreezeBase::patch_pd(frame& hf, const frame& caller) { - Unimplemented(); + if (caller.is_interpreted_frame()) { + assert(!caller.is_empty(), ""); + patch_callee_link_relative(caller, caller.fp()); + } +#ifdef ASSERT + else { + // For compiled frames the back link is actually redundant. It gets computed + // as unextended_sp + frame_size. + + // Note a difference from x86_64: the link is not made relative if the caller + // is a compiled frame because there rbp is used as a non-volatile register by + // c1/c2 so it could be a computed value local to the caller. + + // See also: + // - FreezeBase::set_top_frame_metadata_pd + // - StackChunkFrameStream::fp() + // - UseContinuationFastPath: compiled frames are copied in a batch w/o patching the back link. + // The backlinks are restored when thawing (see Thaw::patch_caller_links()) + patch_callee_link(hf, (intptr_t*)badAddress); + } +#endif } inline void FreezeBase::patch_pd_unused(intptr_t* sp) { - Unimplemented(); } inline void FreezeBase::patch_stack_pd(intptr_t* frame_sp, intptr_t* heap_sp) { - Unimplemented(); + // Nothing to do. The backchain is reconstructed when thawing (see Thaw::patch_caller_links()) } inline intptr_t* AnchorMark::anchor_mark_set_pd() { - Unimplemented(); - return nullptr; + // Nothing to do on s390 because the interpreter does not use SP as expression stack pointer. + // Instead there is a dedicated register Z_esp which is not affected by VM calls. + return _top_frame.sp(); } inline void AnchorMark::anchor_mark_clear_pd() { - Unimplemented(); + // Nothing to do. See anchor_mark_set_pd(). } inline frame ThawBase::new_entry_frame() { - Unimplemented(); - return frame(); + intptr_t* sp = _cont.entrySP(); + return frame(sp, _cont.entryPC(), sp, _cont.entryFP()); } template frame ThawBase::new_stack_frame(const frame& hf, frame& caller, bool bottom) { - Unimplemented(); - return frame(); + assert(FKind::is_instance(hf), ""); + + assert(is_aligned(caller.fp(), frame::frame_alignment), PTR_FORMAT, p2i(caller.fp())); + // caller.sp() can be unaligned. This is fixed below. + if (FKind::interpreted) { + // Note: we have to overlap with the caller, at least if it is interpreted, to match the + // max_thawing_size calculation during freeze. See also comment above. + intptr_t* heap_sp = hf.unextended_sp(); + const int fsize = ContinuationHelper::InterpretedFrame::frame_bottom(hf) - hf.unextended_sp(); + const int overlap = !caller.is_interpreted_frame() ? 0 + : ContinuationHelper::InterpretedFrame::stack_argsize(hf) + frame::metadata_words_at_top; + intptr_t* frame_sp = caller.unextended_sp() + overlap - fsize; + intptr_t* fp = frame_sp + (hf.fp() - heap_sp); + // align fp + int padding = fp - align_down(fp, frame::frame_alignment); + fp -= padding; + // alignment of sp is done by callee or in finish_thaw() + frame_sp -= padding; + + // On s390 esp points to the first free slot on the expression stack (see frame_s390.hpp). + // The assertion verifies that frame_sp + metadata_words_at_top points to the slot above esp, + // which corresponds to the last parameter position. + DEBUG_ONLY(intptr_t* esp = fp + *hf.addr_at(_z_ijava_idx(esp));) + assert(frame_sp + frame::metadata_words_at_top == esp+1, " frame_sp=" PTR_FORMAT " esp=" PTR_FORMAT, p2i(frame_sp), p2i(esp)); + caller.set_sp(fp); + frame f(frame_sp, hf.pc(), frame_sp, fp); + // we need to set the locals so that the caller of new_stack_frame() can call + // ContinuationHelper::InterpretedFrame::frame_bottom + // copy relativized locals from the heap frame + *f.addr_at(_z_ijava_idx(locals)) = *hf.addr_at(_z_ijava_idx(locals)); + + return f; + } else { + int fsize = FKind::size(hf); + int argsize = FKind::stack_argsize(hf); + intptr_t* frame_sp = caller.sp() - fsize; + + if ((bottom && argsize > 0) || caller.is_interpreted_frame()) { + assert(!_should_patch_caller_pc, "what??"); + _should_patch_caller_pc = caller.is_interpreted_frame(); + frame_sp -= argsize + frame::metadata_words_at_top; + frame_sp = align_down(frame_sp, frame::alignment_in_bytes); + caller.set_sp(frame_sp + fsize); + } + + assert(hf.cb() != nullptr, ""); + assert(hf.oop_map() != nullptr, ""); + intptr_t* fp = frame_sp + fsize; + return frame(frame_sp, frame_sp, fp, hf.pc(), hf.cb(), hf.oop_map(), false); + } } inline void ThawBase::derelativize_interpreted_frame_metadata(const frame& hf, const frame& f) { - Unimplemented(); + // Make sure that monitors is still relativized. + assert(f.at_absolute(_z_ijava_idx(monitors)) <= -(frame::z_ijava_state_size / wordSize), ""); + // Make sure that esp is still relativized. + assert(f.at_absolute(_z_ijava_idx(esp)) <= f.at_absolute(_z_ijava_idx(monitors)), ""); + // Keep top_frame_sp relativized. } inline intptr_t* ThawBase::align(const frame& hf, intptr_t* frame_sp, frame& caller, bool bottom) { - Unimplemented(); + // Unused. Alignment is done directly in new_stack_frame() / finish_thaw(). return nullptr; } inline void ThawBase::patch_pd(frame& f, const frame& caller) { - Unimplemented(); + patch_callee_link(caller, caller.fp()); + // Prevent assertion if f gets deoptimized right away before it's fully initialized + f.mark_not_fully_initialized(); } inline void ThawBase::patch_pd(frame& f, intptr_t* caller_sp) { - Unimplemented(); + assert(f.own_abi()->callers_sp == (uint64_t)caller_sp, "should have been fixed by patch_caller_links"); } inline intptr_t* ThawBase::push_cleanup_continuation() { - Unimplemented(); - return nullptr; + frame enterSpecial = new_entry_frame(); + frame::z_common_abi* enterSpecial_abi = (frame::z_common_abi*)enterSpecial.sp(); + + enterSpecial_abi->return_pc = (intptr_t)ContinuationEntry::cleanup_pc(); + + log_develop_trace(continuations, preempt)("push_cleanup_continuation enterSpecial sp: " INTPTR_FORMAT " cleanup pc: " INTPTR_FORMAT, + p2i(enterSpecial_abi), + p2i(ContinuationEntry::cleanup_pc())); + + return enterSpecial.sp(); } inline intptr_t* ThawBase::push_preempt_adapter() { - Unimplemented(); - return nullptr; + frame enterSpecial = new_entry_frame(); + frame::z_common_abi* enterSpecial_abi = (frame::z_common_abi*)enterSpecial.sp(); + + enterSpecial_abi->return_pc = (intptr_t)StubRoutines::cont_preempt_stub(); + + log_develop_trace(continuations, preempt)("push_preempt_adapter enterSpecial sp: " INTPTR_FORMAT " adapter pc: " INTPTR_FORMAT, + p2i(enterSpecial_abi), + p2i(StubRoutines::cont_preempt_stub())); + + return enterSpecial.sp(); } template inline void Thaw::patch_caller_links(intptr_t* sp, intptr_t* bottom) { - Unimplemented(); + for (intptr_t* callers_sp; sp < bottom; sp = callers_sp) { + address pc = (address)((frame::z_java_abi*) sp)->return_pc; + assert(pc != nullptr, ""); + // see ThawBase::patch_return() which gets called just before + bool is_entry_frame = pc == StubRoutines::cont_returnBarrier() || pc == _cont.entryPC(); + if (is_entry_frame) { + callers_sp = _cont.entryFP(); + } else { + assert(!Interpreter::contains(pc), "sp:" PTR_FORMAT " pc:" PTR_FORMAT, p2i(sp), p2i(pc)); + CodeBlob* cb = CodeCache::find_blob(pc); + callers_sp = sp + cb->frame_size(); + } + // set the back link + ((frame::z_java_abi*) sp)->callers_sp = (intptr_t) callers_sp; + } } inline void ThawBase::prefetch_chunk_pd(void* start, int size) { - Unimplemented(); + // TODO: implement in future; } #endif // CPU_S390_CONTINUATION_S390_INLINE_HPP diff --git a/src/hotspot/cpu/s390/continuationHelper_s390.inline.hpp b/src/hotspot/cpu/s390/continuationHelper_s390.inline.hpp index fb7d998c458..11944a8f040 100644 --- a/src/hotspot/cpu/s390/continuationHelper_s390.inline.hpp +++ b/src/hotspot/cpu/s390/continuationHelper_s390.inline.hpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 IBM Corporation. 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 @@ -27,108 +28,133 @@ #include "runtime/continuationHelper.hpp" -// TODO: Implement - -template -static inline intptr_t** link_address(const frame& f) { - Unimplemented(); - return nullptr; -} - static inline void patch_return_pc_with_preempt_stub(frame& f) { - Unimplemented(); + if (f.is_runtime_frame()) { + // Patch the pc of the now old last Java frame (we already set the anchor to enterSpecial) + // so that when target returns to Java it will actually return to the preempt cleanup stub. + // We step over the runtime stub frame and patch the return PC in the caller's frame. + intptr_t* caller_sp = f.sp() + f.cb()->frame_size(); + frame::z_common_abi* abi = (frame::z_common_abi*)caller_sp; + abi->return_pc = (uint64_t)StubRoutines::cont_preempt_stub(); + } else { + // The target will check for preemption once it returns to the interpreter + // or the native wrapper code and will manually jump to the preempt stub. + JavaThread *thread = JavaThread::current(); + DEBUG_ONLY(Method* m = f.is_interpreted_frame() ? f.interpreter_frame_method() : f.cb()->as_nmethod()->method();) + assert(m->is_object_wait0() || thread->interp_at_preemptable_vmcall_cnt() > 0, + "preemptable VM call not using call_VM_preemptable"); + thread->set_preempt_alternate_return(StubRoutines::cont_preempt_stub()); + } } inline int ContinuationHelper::frame_align_words(int size) { - Unimplemented(); + // S390 requires 8-byte (1-word) frame alignment, not 16-byte like other platforms. + // Because frames are already 8-byte aligned, no additional padding words are needed. + // Other platforms (x86, aarch64, ppc) return size & 1 to ensure 16-byte alignment, + // but s390's 8-byte alignment requirement is already satisfied. return 0; } -inline intptr_t* ContinuationHelper::frame_align_pointer(intptr_t* sp) { - Unimplemented(); - return nullptr; +inline intptr_t* ContinuationHelper::frame_align_pointer(intptr_t* p) { + return align_down(p, frame::frame_alignment); } template inline void ContinuationHelper::update_register_map(const frame& f, RegisterMap* map) { - Unimplemented(); + // All registers are considered volatile and saved in the caller (Java) frame if needed. + // No register map update required for s390. } inline void ContinuationHelper::update_register_map_with_callee(const frame& f, RegisterMap* map) { - Unimplemented(); + // All registers are considered volatile and saved in the caller (Java) frame if needed. + // No register map update required for s390. } inline void ContinuationHelper::push_pd(const frame& f) { - Unimplemented(); + f.own_abi()->callers_sp = (uint64_t)f.fp(); } inline void ContinuationHelper::set_anchor_to_entry_pd(JavaFrameAnchor* anchor, ContinuationEntry* cont) { - Unimplemented(); + // No frame pointer update needed for s390. + // Unlike x86/aarch64, s390 doesn't require setting last_Java_fp in the anchor. } inline void ContinuationHelper::set_anchor_pd(JavaFrameAnchor* anchor, intptr_t* sp) { - Unimplemented(); + // No frame pointer update needed for s390. + // Unlike x86/aarch64, s390 doesn't require setting last_Java_fp in the anchor. } #ifdef ASSERT inline bool ContinuationHelper::Frame::assert_frame_laid_out(frame f) { - Unimplemented(); - return false; + intptr_t* sp = f.sp(); + address pc = *(address*)(sp - frame::sender_sp_ret_address_offset()); + intptr_t* fp = (intptr_t*)f.own_abi()->callers_sp; + assert(f.raw_pc() == pc, "f.ra_pc: " INTPTR_FORMAT " actual: " INTPTR_FORMAT, p2i(f.raw_pc()), p2i(pc)); + assert(f.fp() == fp, "f.fp: " INTPTR_FORMAT " actual: " INTPTR_FORMAT, p2i(f.fp()), p2i(fp)); + return f.raw_pc() == pc && f.fp() == fp; } #endif inline intptr_t** ContinuationHelper::Frame::callee_link_address(const frame& f) { - Unimplemented(); - return nullptr; -} - -template -static inline intptr_t* real_fp(const frame& f) { - Unimplemented(); - return nullptr; + return (intptr_t**)&f.own_abi()->callers_sp; } inline address* ContinuationHelper::InterpretedFrame::return_pc_address(const frame& f) { - Unimplemented(); - return nullptr; + return (address*)&f.callers_abi()->return_pc; } inline void ContinuationHelper::InterpretedFrame::patch_sender_sp(frame& f, const frame& caller) { - Unimplemented(); + intptr_t* sp = caller.unextended_sp(); + if (!f.is_heap_frame() && caller.is_interpreted_frame()) { + // When the caller is an interpreted frame, we need to use the caller's top_frame_sp + // instead of unextended_sp. This is because the interpreter resizes the caller's + // frame before making a call + sp = (intptr_t*)caller.at_relative(_z_ijava_idx(top_frame_sp)); + } + assert(f.is_interpreted_frame(), ""); + assert(f.is_heap_frame() || is_aligned(sp, frame::alignment_in_bytes), ""); + intptr_t* la = f.addr_at(_z_ijava_idx(sender_sp)); + *la = f.is_heap_frame() ? (intptr_t)(sp - f.fp()) : (intptr_t)sp; } inline address* ContinuationHelper::Frame::return_pc_address(const frame& f) { - Unimplemented(); - return nullptr; + return (address*)&f.callers_abi()->return_pc; } inline address ContinuationHelper::Frame::real_pc(const frame& f) { - Unimplemented(); - return nullptr; + return (address)f.own_abi()->return_pc; } inline void ContinuationHelper::Frame::patch_pc(const frame& f, address pc) { - Unimplemented(); + f.own_abi()->return_pc = (uint64_t)pc; } inline intptr_t* ContinuationHelper::InterpretedFrame::frame_top(const frame& f, InterpreterOopMap* mask) { // inclusive; this will be copied with the frame - Unimplemented(); - return nullptr; + int expression_stack_sz = expression_stack_size(f, mask); + intptr_t* res = (intptr_t*)f.interpreter_frame_monitor_end() - expression_stack_sz; + assert(res <= (intptr_t*)f.ijava_state() - expression_stack_sz, + "res=" PTR_FORMAT " f.ijava_state()=" PTR_FORMAT " expression_stack_sz=%d", + p2i(res), p2i(f.ijava_state()), expression_stack_sz); + assert(res >= f.unextended_sp(), + "res: " INTPTR_FORMAT " ijava_state: " INTPTR_FORMAT " esp: " INTPTR_FORMAT " unextended_sp: " INTPTR_FORMAT " expression_stack_size: %d", + p2i(res), p2i(f.ijava_state()), f.ijava_state()->esp, p2i(f.unextended_sp()), expression_stack_sz); + return res; } inline intptr_t* ContinuationHelper::InterpretedFrame::frame_bottom(const frame& f) { // exclusive; this will not be copied with the frame - Unimplemented(); - return nullptr; + return (intptr_t*)f.at_relative(_z_ijava_idx(locals)) + 1; // exclusive; this will not be copied with the frame } inline intptr_t* ContinuationHelper::InterpretedFrame::frame_top(const frame& f, int callee_argsize, bool callee_interpreted) { - Unimplemented(); - return nullptr; + intptr_t* pseudo_unextended_sp = f.interpreter_frame_esp() + 1 - frame::metadata_words_at_top; + // callee_argsize includes metadata (frame::metadata_words_at_top). + // When the callee is interpreted, we add callee_argsize to account for the arguments + // that are part of the caller's frame but logically belong to the callee. + return pseudo_unextended_sp + (callee_interpreted ? callee_argsize : 0); } inline intptr_t* ContinuationHelper::InterpretedFrame::callers_sp(const frame& f) { - Unimplemented(); - return nullptr; + return f.fp(); } #endif // CPU_S390_CONTINUATIONHELPER_S390_INLINE_HPP diff --git a/src/hotspot/cpu/s390/frame_s390.cpp b/src/hotspot/cpu/s390/frame_s390.cpp index b602d0adce5..af4c670132a 100644 --- a/src/hotspot/cpu/s390/frame_s390.cpp +++ b/src/hotspot/cpu/s390/frame_s390.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2023 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -54,6 +54,10 @@ void RegisterMap::check_location_valid() { // Profiling/safepoint support bool frame::safe_for_sender(JavaThread *thread) { + if (is_heap_frame()) { + return true; + } + address sp = (address)_sp; address fp = (address)_fp; address unextended_sp = (address)_unextended_sp; @@ -120,6 +124,13 @@ bool frame::safe_for_sender(JavaThread *thread) { intptr_t* sender_sp = (intptr_t*) fp; address sender_pc = (address) sender_abi->return_pc; + if (Continuation::is_return_barrier_entry(sender_pc)) { + // If our sender_pc is the return barrier, then our "real" sender is the continuation entry + frame s = Continuation::continuation_bottom_sender(thread, *this, sender_sp); + sender_sp = s.sp(); + sender_pc = s.pc(); + } + // We must always be able to find a recognizable pc. CodeBlob* sender_blob = CodeCache::find_blob(sender_pc); if (sender_blob == nullptr) { @@ -192,7 +203,8 @@ void frame::interpreter_frame_set_locals(intptr_t* locs) { // sender_sp intptr_t* frame::interpreter_frame_sender_sp() const { - return sender_sp(); + assert(is_interpreted_frame(), "interpreted frame expected"); + return (intptr_t*)at(_z_ijava_idx(sender_sp)); } frame frame::sender_for_entry_frame(RegisterMap *map) const { @@ -244,16 +256,52 @@ frame frame::sender_for_upcall_stub_frame(RegisterMap* map) const { frame fr(jfa->last_Java_sp(), jfa->last_Java_pc()); return fr; + +} + +#if defined(ASSERT) +static address get_register_address_in_stub(const frame& stub_fr, VMReg reg) { + RegisterMap map(nullptr, + RegisterMap::UpdateMap::include, + RegisterMap::ProcessFrames::skip, + RegisterMap::WalkContinuation::skip); + stub_fr.oop_map()->update_register_map(&stub_fr, &map); + return map.location(reg, stub_fr.sp()); } +#endif JavaThread** frame::saved_thread_address(const frame& f) { - Unimplemented(); - return nullptr; + CodeBlob* cb = f.cb(); + assert(cb != nullptr && cb->is_runtime_stub(), "invalid frame"); + + JavaThread** thread_addr; +#ifdef COMPILER1 + if (cb == Runtime1::blob_for(StubId::c1_monitorenter_id) || + cb == Runtime1::blob_for(StubId::c1_monitorenter_nofpu_id)) { + thread_addr = (JavaThread**)(f.sp() + Runtime1::runtime_blob_current_thread_offset(f)); + } else +#endif + { + // c2 only saves Z_fp in the stub frame so nothing to do. + thread_addr = nullptr; + } + assert(get_register_address_in_stub(f, SharedRuntime::thread_register()) == (address)thread_addr, "wrong thread address"); + return thread_addr; } frame frame::sender_for_interpreter_frame(RegisterMap *map) const { - // Pass callers sender_sp as unextended_sp. - return frame(sender_sp(), sender_pc(), (intptr_t*)(ijava_state()->sender_sp)); + // This is the sp before any possible extension (adapter/locals). + intptr_t* unextended_sp = interpreter_frame_sender_sp(); + address sender_pc = this->sender_pc(); + if (Continuation::is_return_barrier_entry(sender_pc)) { + if (map->walk_cont()) { // about to walk into an h-stack + return Continuation::top_frame(*this, map); + } else { + return Continuation::continuation_bottom_sender(map->thread(), *this, sender_sp()); + } + } + + return frame(sender_sp(), sender_pc, unextended_sp); } void frame::patch_pc(Thread* thread, address pc) { @@ -284,7 +332,7 @@ void frame::patch_pc(Thread* thread, address pc) { #ifdef ASSERT { - frame f(this->sp(), pc, this->unextended_sp()); + frame f(sp(), unextended_sp(), fp(), pc, cb(), oop_map(), is_heap_frame()); assert(f.is_deoptimized_frame() == this->is_deoptimized_frame() && f.pc() == this->pc() && f.raw_pc() == this->raw_pc(), "must be (f.is_deoptimized_frame(): %d this->is_deoptimized_frame(): %d " "f.pc(): " INTPTR_FORMAT " this->pc(): " INTPTR_FORMAT " f.raw_pc(): " INTPTR_FORMAT " this->raw_pc(): " INTPTR_FORMAT ")", @@ -648,6 +696,8 @@ extern "C" void bt_max(intptr_t *start_sp, intptr_t *top_pc, int max_frames) { } #if !defined(PRODUCT) +#define DESCRIBE_ADDRESS_MAGIC(name) \ + values.describe(frame_no, (intptr_t*)&ijava_state()->name, #name "_number_debug"); #define DESCRIBE_ADDRESS(name) \ values.describe(frame_no, (intptr_t*)&ijava_state()->name, #name); @@ -656,25 +706,38 @@ void frame::describe_pd(FrameValues& values, int frame_no) { if (is_interpreted_frame()) { // Describe z_ijava_state elements. DESCRIBE_ADDRESS(method); + DESCRIBE_ADDRESS(mirror); DESCRIBE_ADDRESS(locals); DESCRIBE_ADDRESS(monitors); DESCRIBE_ADDRESS(cpoolCache); DESCRIBE_ADDRESS(bcp); - DESCRIBE_ADDRESS(mdx); DESCRIBE_ADDRESS(esp); - DESCRIBE_ADDRESS(sender_sp); + DESCRIBE_ADDRESS(mdx); DESCRIBE_ADDRESS(top_frame_sp); + DESCRIBE_ADDRESS(sender_sp); DESCRIBE_ADDRESS(oop_tmp); DESCRIBE_ADDRESS(lresult); DESCRIBE_ADDRESS(fresult); + DESCRIBE_ADDRESS_MAGIC(magic); + } + + if (is_java_frame() || Continuation::is_continuation_enterSpecial(*this)) { + intptr_t* ret_pc_loc = (intptr_t*)&own_abi()->return_pc; + address ret_pc = *(address*)ret_pc_loc; + values.describe(frame_no, ret_pc_loc, + Continuation::is_return_barrier_entry(ret_pc) ? "return address (return barrier)" : "return address"); } } #endif // !PRODUCT intptr_t *frame::initial_deoptimization_info() { - // Used to reset the saved FP. - return fp(); + // `this` is the caller of the deoptee. We want to trim it, if compiled, to + // unextended_sp. This is necessary if the deoptee frame is the bottom frame + // of a continuation on stack (more frames could be in a StackChunk) as it + // will pop its stack args. Otherwise the recursion in + // FreezeBase::recurse_freeze_java_frame() would not stop at the bottom frame. + return is_compiled_frame() ? unextended_sp() : sp(); } BasicObjectLock* frame::interpreter_frame_monitor_end() const { diff --git a/src/hotspot/cpu/s390/frame_s390.hpp b/src/hotspot/cpu/s390/frame_s390.hpp index 664a49fdd21..36fc5970cd8 100644 --- a/src/hotspot/cpu/s390/frame_s390.hpp +++ b/src/hotspot/cpu/s390/frame_s390.hpp @@ -130,6 +130,7 @@ enum { z_native_abi_size = sizeof(z_native_abi), + z_abi_160_base_size = sizeof(z_abi_160_base), z_abi_160_size = sizeof(z_abi_160_base) }; @@ -442,6 +443,14 @@ private: + + #ifdef ASSERT + enum special_backlink_values : uint64_t { + NOT_FULLY_INITIALIZED = 0xDEADBEEF8 + }; + bool is_fully_initialized() const { return (uint64_t)_fp != NOT_FULLY_INITIALIZED; } +#endif // ASSERT + // STACK: // ... // [THIS_FRAME] <-- this._sp (stack pointer for this frame) @@ -452,10 +461,16 @@ // NOTE: Stack pointer is now held in the base class, so remove it from here. // Needed by deoptimization. - intptr_t* _unextended_sp; + union { + intptr_t* _unextended_sp; + int _offset_unextended_sp; // for use in stack-chunk frames + }; // Frame pointer for this frame. - intptr_t* _fp; + union { + intptr_t* _fp; // frame pointer + int _offset_fp; // relative frame pointer for use in stack-chunk frames + }; public: @@ -464,17 +479,25 @@ // Accessors inline intptr_t* fp() const { assert_absolute(); return _fp; } + void set_fp(intptr_t* newfp) { _fp = newfp; } + int offset_fp() const { assert_offset(); return _offset_fp; } + void set_offset_fp(int value) { assert_on_heap(); _offset_fp = value; } + + // Mark a frame as not fully initialized. Must not be used for frames in the valid back chain. + void mark_not_fully_initialized() const { DEBUG_ONLY(own_abi()->callers_sp = NOT_FULLY_INITIALIZED;) } private: // Initialize frame members (_pc and _sp must be given) inline void setup(); - // Constructors - public: + + // Constructors + inline frame(intptr_t* sp, intptr_t* fp, address pc); // To be used, if sp was not extended to match callee's calling convention. inline frame(intptr_t* sp, address pc, intptr_t* unextended_sp = nullptr, intptr_t* fp = nullptr, CodeBlob* cb = nullptr); + inline frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc, CodeBlob* cb, const ImmutableOopMap* oop_map, bool on_heap); inline frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc, CodeBlob* cb, const ImmutableOopMap* oop_map = nullptr); // Access frame via stack pointer. @@ -495,11 +518,8 @@ // template interpreter state inline z_ijava_state* ijava_state_unchecked() const; - private: - - inline z_ijava_state* ijava_state() const; - public: + inline z_ijava_state* ijava_state() const; inline intptr_t* interpreter_frame_esp() const; // Where z_ijava_state.esp is saved. @@ -542,14 +562,19 @@ unsigned long flags, int max_frames = 0); enum { - metadata_words = 0, + // size, in words, of frame metadata (e.g. pc and link) + metadata_words = sizeof(z_java_abi) >> LogBytesPerWord, metadata_words_at_bottom = 0, - metadata_words_at_top = 0, - frame_alignment = 16, + metadata_words_at_top = sizeof(z_java_abi) >> LogBytesPerWord, + // in bytes + frame_alignment = 8, // size, in words, of maximum shift in frame position due to alignment - align_wiggle = 1 + align_wiggle = 0 }; static jint interpreter_frame_expression_stack_direction() { return -1; } + // returns the sending frame, without applying any barriers + inline frame sender_raw(RegisterMap* map) const; + #endif // CPU_S390_FRAME_S390_HPP diff --git a/src/hotspot/cpu/s390/frame_s390.inline.hpp b/src/hotspot/cpu/s390/frame_s390.inline.hpp index 6fcd36c57d1..e31b0d5a426 100644 --- a/src/hotspot/cpu/s390/frame_s390.inline.hpp +++ b/src/hotspot/cpu/s390/frame_s390.inline.hpp @@ -26,7 +26,8 @@ #ifndef CPU_S390_FRAME_S390_INLINE_HPP #define CPU_S390_FRAME_S390_INLINE_HPP -#include "code/codeCache.hpp" +#include "code/codeBlob.inline.hpp" +#include "code/codeCache.inline.hpp" #include "code/vmreg.inline.hpp" #include "runtime/sharedRuntime.hpp" #include "utilities/align.hpp" @@ -44,14 +45,25 @@ inline void frame::setup() { _cb = CodeCache::find_blob(_pc); } - if (_fp == nullptr) { - _fp = (intptr_t*)own_abi()->callers_sp; - } - if (_unextended_sp == nullptr) { _unextended_sp = _sp; } + if (_fp == nullptr) { + // The back link for compiled frames on the heap is not valid + if (is_heap_frame()) { + // fp for interpreted frames should have been derelativized and passed to the constructor + assert(is_compiled_frame() + || is_native_frame() // native wrapper (nmethod) for j.l.Object::wait0 + || is_runtime_frame(), // e.g. Runtime1::monitorenter, SharedRuntime::complete_monitor_locking_C + "sp:" PTR_FORMAT " fp:" PTR_FORMAT " name:%s", p2i(_sp), p2i(_unextended_sp + _cb->frame_size()), _cb->name()); + // The back link for compiled frames on the heap is invalid. + _fp = _unextended_sp + _cb->frame_size(); + } else { + _fp = (intptr_t *) own_abi()->callers_sp; + } + } + // When thawing continuation frames the _unextended_sp passed to the constructor is not aligend assert(_on_heap || (is_aligned(_sp, alignment_in_bytes) && is_aligned(_fp, alignment_in_bytes)), "invalid alignment sp:" PTR_FORMAT " unextended_sp:" PTR_FORMAT " fp:" PTR_FORMAT, p2i(_sp), p2i(_unextended_sp), p2i(_fp)); @@ -70,7 +82,12 @@ inline void frame::setup() { } } - // assert(_on_heap || is_aligned(_sp, frame::frame_alignment), "SP must be 8-byte aligned"); + // Continuation frames on the java heap are not aligned. + // When thawing interpreted frames the sp can be unaligned (see new_stack_frame()). + assert(_on_heap || + ((is_aligned(_sp, alignment_in_bytes) || is_interpreted_frame()) && + (is_aligned(_fp, alignment_in_bytes) || !is_fully_initialized())), + "invalid alignment sp:" PTR_FORMAT " unextended_sp:" PTR_FORMAT " fp:" PTR_FORMAT, p2i(_sp), p2i(_unextended_sp), p2i(_fp)); } // Constructors @@ -87,11 +104,26 @@ inline frame::frame(intptr_t* sp, address pc, intptr_t* unextended_sp, intptr_t* inline frame::frame(intptr_t* sp) : frame(sp, nullptr) {} +inline frame::frame(intptr_t* sp, intptr_t* fp, address pc) + : _sp(sp), _pc(pc), _cb(nullptr), _oop_map(nullptr), _deopt_state(unknown), + _on_heap(false), DEBUG_ONLY(_frame_index(-1) COMMA) _unextended_sp(nullptr), _fp(fp) { + setup(); +} + inline frame::frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc, CodeBlob* cb, const ImmutableOopMap* oop_map) :_sp(sp), _pc(pc), _cb(cb), _oop_map(oop_map), _on_heap(false), DEBUG_ONLY(_frame_index(-1) COMMA) _unextended_sp(unextended_sp), _fp(fp) { setup(); } +inline frame::frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc, CodeBlob* cb, const ImmutableOopMap* oop_map, bool on_heap) + :_sp(sp), _pc(pc), _cb(cb), _oop_map(oop_map), _on_heap(on_heap), DEBUG_ONLY(_frame_index(-1) COMMA) _unextended_sp(unextended_sp), _fp(fp) { + // In thaw, non-heap frames use this constructor to pass oop_map. I don't know why. + assert(_on_heap || _cb != nullptr, "these frames are always heap frames"); + if (cb != nullptr) { + setup(); + } +} + // Generic constructor. Used by pns() in debug.cpp only #ifndef PRODUCT inline frame::frame(void* sp, void* pc, void* unextended_sp) @@ -295,11 +327,11 @@ inline JavaCallWrapper** frame::entry_frame_call_wrapper_addr() const { } inline oop frame::saved_oop_result(RegisterMap* map) const { - return *((oop*) map->location(Z_R2->as_VMReg(), nullptr)); // R2 is return register. + return *((oop*) map->location(Z_R2->as_VMReg(), sp())); // R2 is return register. } inline void frame::set_saved_oop_result(RegisterMap* map, oop obj) { - *((oop*) map->location(Z_R2->as_VMReg(), nullptr)) = obj; // R2 is return register. + *((oop*) map->location(Z_R2->as_VMReg(), sp())) = obj; // R2 is return register. } inline intptr_t* frame::real_fp() const { @@ -307,40 +339,55 @@ inline intptr_t* frame::real_fp() const { } inline int frame::compiled_frame_stack_argsize() const { - Unimplemented(); - return 0; + assert(cb()->is_nmethod(), "what ?"); + return (cb()->as_nmethod()->num_stack_arg_slots() * VMRegImpl::stack_slot_size) >> LogBytesPerWord; } inline void frame::interpreted_frame_oop_map(InterpreterOopMap* mask) const { - Unimplemented(); + assert(mask != nullptr, ""); + Method* m = interpreter_frame_method(); + int bci = interpreter_frame_bci(); + m->mask_for(bci, mask); // OopMapCache::compute_one_oop_map(m, bci, mask); } inline int frame::sender_sp_ret_address_offset() { - Unimplemented(); - return 0; + return -(int)(_z_common_abi(return_pc) >> LogBytesPerWord); } inline void frame::set_unextended_sp(intptr_t* value) { - Unimplemented(); + _unextended_sp = value; } inline int frame::offset_unextended_sp() const { - Unimplemented(); - return 0; + assert_offset(); return _offset_unextended_sp; } inline void frame::set_offset_unextended_sp(int value) { - Unimplemented(); + assert_on_heap(); _offset_unextended_sp = value; } //------------------------------------------------------------------------------ // frame::sender inline frame frame::sender(RegisterMap* map) const { + frame result = sender_raw(map); + + if (map->process_frames() && !map->in_cont()) { + StackWatermarkSet::on_iteration(map->thread(), result); + } + + return result; +} + +inline frame frame::sender_raw(RegisterMap* map) const { // Default is we don't have to follow them. The sender_for_xxx will // update it accordingly. map->set_include_argument_oops(false); + if (map->in_cont()) { // already in an h-stack + return map->stack_chunk()->sender(*this, map); + } + if (is_entry_frame()) return sender_for_entry_frame(map); if (is_upcall_stub_frame()) return sender_for_upcall_stub_frame(map); if (is_interpreted_frame()) return sender_for_interpreter_frame(map); @@ -362,12 +409,31 @@ inline frame frame::sender_for_compiled_frame(RegisterMap *map) const { // Now adjust the map. if (map->update_map()) { // Tell GC to use argument oopmaps for some runtime stubs that need it. - map->set_include_argument_oops(_cb->caller_must_gc_arguments(map->thread())); - if (_cb->oop_maps() != nullptr) { - OopMapSet::update_register_map(this, map); + + // For C1, some runtime stubs don't have oop maps (e.g., slow_subtype_check, + // unwind_exception), so set this flag outside of update_register_map to ensure + // the GC can handle arguments correctly even when oop_map() is null. + if (!_cb->is_nmethod()) { // compiled frames do not use callee-saved registers + map->set_include_argument_oops(_cb->caller_must_gc_arguments(map->thread())); + if (oop_map() != nullptr) { + _oop_map->update_register_map(this, map); + } + } else { + assert(!_cb->caller_must_gc_arguments(map->thread()), ""); + assert(!map->include_argument_oops(), ""); + assert(oop_map() == nullptr || !oop_map()->has_any(OopMapValue::callee_saved_value), "callee-saved value in compiled frame"); } } + assert(sender_sp != sp(), "must have changed"); + + if (Continuation::is_return_barrier_entry(sender_pc)) { + if (map->walk_cont()) { // about to walk into an h-stack + return Continuation::top_frame(*this, map); + } else { + return Continuation::continuation_bottom_sender(map->thread(), *this, sender_sp); + } + } return frame(sender_sp, sender_pc); } diff --git a/src/hotspot/cpu/s390/globals_s390.hpp b/src/hotspot/cpu/s390/globals_s390.hpp index 80ed6d1acc8..745a6171ca2 100644 --- a/src/hotspot/cpu/s390/globals_s390.hpp +++ b/src/hotspot/cpu/s390/globals_s390.hpp @@ -64,7 +64,7 @@ define_pd_global(intx, StackRedPages, DEFAULT_STACK_RED_PAGES); define_pd_global(intx, StackShadowPages, DEFAULT_STACK_SHADOW_PAGES); define_pd_global(intx, StackReservedPages, DEFAULT_STACK_RESERVED_PAGES); -define_pd_global(bool, VMContinuations, false); +define_pd_global(bool, VMContinuations, true); define_pd_global(bool, RewriteBytecodes, true); define_pd_global(bool, RewriteFrequentPairs, true); diff --git a/src/hotspot/cpu/s390/interp_masm_s390.cpp b/src/hotspot/cpu/s390/interp_masm_s390.cpp index d50cb833e68..5d86a0c3182 100644 --- a/src/hotspot/cpu/s390/interp_masm_s390.cpp +++ b/src/hotspot/cpu/s390/interp_masm_s390.cpp @@ -165,6 +165,109 @@ void InterpreterMacroAssembler::dispatch_via(TosState state, address *table) { // to perform additional, template interpreter specific tasks before actually // calling their MacroAssembler counterparts. +void InterpreterMacroAssembler::call_VM_preemptable(Register oop_result, address entry_point, + Register arg_1, bool check_exceptions) { + if (!Continuations::enabled()) { + call_VM(oop_result, entry_point, arg_1, check_exceptions); + return; + } + call_VM_preemptable(oop_result, entry_point, arg_1, noreg /* arg_2 */, check_exceptions); +} + +void InterpreterMacroAssembler::call_VM_preemptable(Register oop_result, address entry_point, + Register arg_1, Register arg_2, bool check_exceptions) { + if (!Continuations::enabled()) { + call_VM(oop_result, entry_point, arg_1, arg_2, check_exceptions); + return; + } + + Label resume_pc, not_preempted; + Register tmp = Z_R1_scratch; + assert(InterpreterRuntime::is_preemptable_call(entry_point), "VM call not preemptable, should use call_VM()"); + assert_different_registers(arg_1, tmp); + assert_different_registers(arg_2, tmp); + +#ifdef ASSERT + { + NearLabel L1; + asm_assert_mem8_is_zero(in_bytes(JavaThread::preempt_alternate_return_offset()), Z_thread, + "Should not have alternate return address set", 100); + // We check this counter in patch_return_pc_with_preempt_stub() during freeze. + z_asi(Address(Z_thread, JavaThread::interp_at_preemptable_vmcall_cnt_offset()), 1); + z_lt(tmp, Address(Z_thread, JavaThread::interp_at_preemptable_vmcall_cnt_offset())); + z_brh(L1); + stop("call_VM_preemptable_helper: should be > 0"); + bind(L1); + } +#endif // ASSERT + + lgr_if_needed(Z_ARG2, arg_1); + assert(arg_2 != Z_ARG2, "smashed argument"); + + if (arg_2 != noreg) { + lgr_if_needed(Z_ARG3, arg_2); + } + + // Force freeze slow path. + push_cont_fastpath(); + // Make VM call. In case of preemption set last_pc to the one we want to resume to. + // Note: call_VM_base will use resume_pc label to set last_Java_pc. + call_VM(noreg, entry_point, false /*check_exceptions*/, &resume_pc /* last_java_pc */); + pop_cont_fastpath(); + + +#ifdef ASSERT + { + NearLabel L; + z_asi(Address(Z_thread, JavaThread::interp_at_preemptable_vmcall_cnt_offset()), -1); + z_lt(tmp, Address(Z_thread, JavaThread::interp_at_preemptable_vmcall_cnt_offset())); + z_brnl(L); + stop("call_VM_preemptable_helper: should be >= 0"); + bind(L); + } +#endif // ASSERT + + // Check if preempted. + z_ltg(tmp, Address(Z_thread, JavaThread::preempt_alternate_return_offset())); + z_brz(not_preempted); + + // Preempted. Frames are already frozen on heap. + z_mvghi(Address(Z_thread, JavaThread::preempt_alternate_return_offset()), 0); + z_br(tmp); // branch to handler in Z_R1_scratch + + bind(resume_pc); // Location to resume execution + restore_after_resume(); + + bind(not_preempted); + + if (check_exceptions) { + NearLabel ok; + load_and_test_long(tmp, Address(Z_thread, Thread::pending_exception_offset())); + z_bre(ok); + load_const_optimized(tmp, StubRoutines::forward_exception_entry()); + z_br(tmp); + bind(ok); + } + + // get oop result if there is one and reset the value in the thread + if (oop_result->is_valid()) { + get_vm_result_oop(oop_result); + } +} + +void InterpreterMacroAssembler::restore_after_resume() { + if (!Continuations::enabled()) return; + load_const_optimized(Z_R1, Interpreter::cont_resume_interpreter_adapter()); + call(Z_R1); +#ifdef ASSERT + NearLabel ok; + z_cg(Z_fp, _z_common_abi(callers_sp), Z_SP); + z_bre(ok); + stop(FILE_AND_LINE ": FP is expected in Z_fp"); + bind(ok); +#endif // ASSERT +} + void InterpreterMacroAssembler::call_VM_leaf_base(address entry_point) { bool allow_relocation = true; // Fenerally valid variant. Assume code is relocated. // interpreter specific @@ -193,20 +296,20 @@ void InterpreterMacroAssembler::call_VM_base(Register oop_result, Register last_ save_esp(); // super call MacroAssembler::call_VM_base(oop_result, last_java_sp, - entry_point, allow_relocation, check_exceptions); + entry_point, allow_relocation, check_exceptions, nullptr); restore_bcp(); } void InterpreterMacroAssembler::call_VM_base(Register oop_result, Register last_java_sp, address entry_point, bool allow_relocation, - bool check_exceptions) { + bool check_exceptions, Label* last_java_pc) { // interpreter specific save_bcp(); save_esp(); // super call MacroAssembler::call_VM_base(oop_result, last_java_sp, - entry_point, allow_relocation, check_exceptions); + entry_point, allow_relocation, check_exceptions, last_java_pc); restore_bcp(); } @@ -697,7 +800,7 @@ void InterpreterMacroAssembler::get_monitors(Register reg) { bind(ok); #endif // ASSERT mem2reg_opt(reg, Address(Z_fp, _z_ijava_state_neg(monitors))); - z_slag(reg, reg, Interpreter::logStackElementSize); + z_slag(reg, reg, Interpreter::logStackElementSize); // sign preserved z_agr(reg, Z_fp); } @@ -968,6 +1071,14 @@ void InterpreterMacroAssembler::remove_activation(TosState state, bool install_monitor_exception, bool notify_jvmti) { BLOCK_COMMENT("remove_activation {"); + +#ifdef ASSERT + { + asm_assert_mem8_is_zero(in_bytes(JavaThread::preempt_alternate_return_offset()), Z_thread, + "remove_activation: should not have alternate return address set", 101); + } +#endif // ASSERT + unlock_if_synchronized_method(state, throw_monitor_exception, install_monitor_exception); // Save result (push state before jvmti call and pop it afterwards) and notify jvmti. @@ -1003,6 +1114,7 @@ void InterpreterMacroAssembler::remove_activation(TosState state, verify_oop(Z_tos, state); pop_interpreter_frame(return_pc, Z_ARG2, Z_ARG3); + pop_cont_fastpath(); BLOCK_COMMENT("} remove_activation"); } @@ -1023,9 +1135,9 @@ void InterpreterMacroAssembler::lock_object(Register monitor, Register object) { z_bru(done); bind(slow_case); - call_VM(noreg, - CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), - monitor); + call_VM_preemptable(noreg, + CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), + monitor); bind(done); } diff --git a/src/hotspot/cpu/s390/interp_masm_s390.hpp b/src/hotspot/cpu/s390/interp_masm_s390.hpp index a210588d062..6921fd05ff0 100644 --- a/src/hotspot/cpu/s390/interp_masm_s390.hpp +++ b/src/hotspot/cpu/s390/interp_masm_s390.hpp @@ -46,7 +46,8 @@ class InterpreterMacroAssembler: public MacroAssembler { Register last_java_sp, address entry_point, bool allow_relocation, - bool check_exceptions); + bool check_exceptions, + Label *last_java_pc); // Base routine for all dispatches. void dispatch_base(TosState state, address* table, bool generate_poll = false); @@ -55,9 +56,14 @@ class InterpreterMacroAssembler: public MacroAssembler { InterpreterMacroAssembler(CodeBuffer* c) : MacroAssembler(c) {} + void restore_after_resume(); virtual void check_and_handle_popframe(Register java_thread); virtual void check_and_handle_earlyret(Register java_thread); + // Use for vthread preemption + void call_VM_preemptable(Register oop_result, address entry_point, Register arg_1, bool check_exceptions = true); + void call_VM_preemptable(Register oop_result, address entry_point, Register arg_1, Register arg_2, bool check_exceptions = true); + void jump_to_entry(address entry, Register Rscratch); virtual void load_earlyret_value(TosState state); diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp index ea75d483e5f..5d5c7570e27 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp @@ -32,6 +32,7 @@ #include "gc/shared/barrierSetAssembler.hpp" #include "gc/shared/collectedHeap.inline.hpp" #include "interpreter/interpreter.hpp" +#include "interpreter/interpreterRuntime.hpp" #include "gc/shared/cardTableBarrierSet.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" @@ -1932,6 +1933,12 @@ unsigned long MacroAssembler::patched_branch(address dest_pos, unsigned long ins // Only called when binding labels (share/vm/asm/assembler.cpp) // Pass arguments as intended. Do not pre-calculate distance. void MacroAssembler::pd_patch_instruction(address branch, address target, const char* file, int line) { + + if (is_load_const(branch)) { + patch_const(branch, (long)target); + return; + } + unsigned long stub_inst; int inst_len = get_instruction(branch, &stub_inst); @@ -2249,7 +2256,8 @@ void MacroAssembler::call_VM_base(Register oop_result, Register last_java_sp, address entry_point, bool allow_relocation, - bool check_exceptions) { // Defaults to true. + bool check_exceptions, // Defaults to true. + Label *last_java_pc) { // Allow_relocation indicates, if true, that the generated code shall // be fit for code relocation or referenced data relocation. In other // words: all addresses must be considered variable. PC-relative addressing @@ -2263,7 +2271,7 @@ void MacroAssembler::call_VM_base(Register oop_result, last_java_sp = Z_SP; // Load Z_SP as SP. } - set_top_ijava_frame_at_SP_as_last_Java_frame(last_java_sp, Z_R1, allow_relocation); + set_top_ijava_frame_at_SP_as_last_Java_frame(last_java_sp, Z_R1, allow_relocation, last_java_pc); // ARG1 must hold thread address. z_lgr(Z_ARG1, Z_thread); @@ -2309,14 +2317,14 @@ void MacroAssembler::call_VM_base(Register oop_result, address entry_point, bool check_exceptions) { // Defaults to true. bool allow_relocation = true; - call_VM_base(oop_result, last_java_sp, entry_point, allow_relocation, check_exceptions); + call_VM_base(oop_result, last_java_sp, entry_point, allow_relocation, check_exceptions, nullptr); } // VM calls without explicit last_java_sp. -void MacroAssembler::call_VM(Register oop_result, address entry_point, bool check_exceptions) { +void MacroAssembler::call_VM(Register oop_result, address entry_point, bool check_exceptions, Label* last_java_pc) { // Call takes possible detour via InterpreterMacroAssembler. - call_VM_base(oop_result, noreg, entry_point, true, check_exceptions); + call_VM_base(oop_result, noreg, entry_point, true, check_exceptions, last_java_pc); } void MacroAssembler::call_VM(Register oop_result, address entry_point, Register arg_1, bool check_exceptions) { @@ -2348,7 +2356,7 @@ void MacroAssembler::call_VM(Register oop_result, address entry_point, Register void MacroAssembler::call_VM_static(Register oop_result, address entry_point, bool check_exceptions) { // Call takes possible detour via InterpreterMacroAssembler. - call_VM_base(oop_result, noreg, entry_point, false, check_exceptions); + call_VM_base(oop_result, noreg, entry_point, false, check_exceptions, nullptr); } void MacroAssembler::call_VM_static(Register oop_result, address entry_point, Register arg_1, Register arg_2, @@ -2366,7 +2374,7 @@ void MacroAssembler::call_VM_static(Register oop_result, address entry_point, Re void MacroAssembler::call_VM(Register oop_result, Register last_java_sp, address entry_point, bool check_exceptions) { // Call takes possible detour via InterpreterMacroAssembler. - call_VM_base(oop_result, last_java_sp, entry_point, true, check_exceptions); + call_VM_base(oop_result, last_java_sp, entry_point, true, check_exceptions, nullptr); } void MacroAssembler::call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, bool check_exceptions) { @@ -3810,19 +3818,21 @@ void MacroAssembler::set_last_Java_frame(Register last_Java_sp, Register last_Ja BLOCK_COMMENT("} set_last_Java_frame"); } -void MacroAssembler::reset_last_Java_frame(bool allow_relocation) { +void MacroAssembler::reset_last_Java_frame(bool check_last_java_sp, bool allow_relocation) { BLOCK_COMMENT("reset_last_Java_frame {"); - if (allow_relocation) { - asm_assert_mem8_isnot_zero(in_bytes(JavaThread::last_Java_sp_offset()), - Z_thread, - "SP was not set, still zero", - 0x202); - } else { - asm_assert_mem8_isnot_zero_static(in_bytes(JavaThread::last_Java_sp_offset()), - Z_thread, - "SP was not set, still zero", - 0x202); + if (check_last_java_sp) { + if (allow_relocation) { + asm_assert_mem8_isnot_zero(in_bytes(JavaThread::last_Java_sp_offset()), + Z_thread, + "SP was not set, still zero", + 0x202); + } else { + asm_assert_mem8_isnot_zero_static(in_bytes(JavaThread::last_Java_sp_offset()), + Z_thread, + "SP was not set, still zero", + 0x202); + } } // _last_Java_sp = 0 @@ -3836,15 +3846,14 @@ void MacroAssembler::reset_last_Java_frame(bool allow_relocation) { return; } -void MacroAssembler::set_top_ijava_frame_at_SP_as_last_Java_frame(Register sp, Register tmp1, bool allow_relocation) { +void MacroAssembler::set_top_ijava_frame_at_SP_as_last_Java_frame(Register sp, Register tmp1, bool allow_relocation, Label* jpc) { assert_different_registers(sp, tmp1); - // We cannot trust that code generated by the C++ compiler saves R14 - // to z_abi_160.return_pc, because sometimes it spills R14 using stmg at - // z_abi_160.gpr14 (e.g. InterpreterRuntime::_new()). - // Therefore we load the PC into tmp1 and let set_last_Java_frame() save - // it into the frame anchor. - get_PC(tmp1); + if (jpc == nullptr || jpc->is_bound()) { + load_const_optimized(tmp1, jpc == nullptr ? pc() : target(*jpc)); + } else { + load_const(tmp1, *jpc); + } set_last_Java_frame(/*sp=*/sp, /*pc=*/tmp1, allow_relocation); } @@ -5890,7 +5899,7 @@ bool is_excluded(Register excluded_register[], Register reg, int n) { } void MacroAssembler::clobber_volatile_registers(Register excluded_register[], int n) { - const int magic_number = 0x82; + const int magic_number = 0xbadbad; for (int i = 0; i < 6 /* R0 to R5 */; i++) { Register reg = as_Register(i); @@ -5899,6 +5908,26 @@ void MacroAssembler::clobber_volatile_registers(Register excluded_register[], in } } } + +void MacroAssembler::clobber_nonvolatile_registers() { + BLOCK_COMMENT("clobber_nonvolatile_registers {"); + static const Register regs[] = { + Z_R6, + Z_R7, + // don't zap Z_thread (Z_R8) + Z_R9, + Z_R10, + Z_R11, + Z_R12, + Z_R13 + }; + Register bad = regs[0]; + load_const_optimized(bad, 0xbad0101babe11111); + for (uint32_t i = 1; i < (sizeof(regs) / sizeof(Register)); i++) { + z_lgr(regs[i], bad); + } + BLOCK_COMMENT("} clobber_nonvolatile_registers"); +} #endif // ASSERT // Save and restore functions: Exclude Z_R0. @@ -6742,6 +6771,39 @@ void MacroAssembler::pop_count_int_with_ext3(Register r_dst, Register r_src) { BLOCK_COMMENT("} pop_count_int_with_ext3"); } +void MacroAssembler::post_call_nop() { + // Make inline again when loom is always enabled. + if (!Continuations::enabled()) { + return; + } + nop(); + // TODO: + // 1. https://bugs.openjdk.org/browse/JDK-8300002 + // 2. https://bugs.openjdk.org/browse/JDK-8290965 +} + +void MacroAssembler::push_cont_fastpath() { + BLOCK_COMMENT("push_cont_fastpath {"); + if (!Continuations::enabled()) return; + NearLabel done; + z_clg(Z_SP, Address(Z_thread, JavaThread::cont_fastpath_offset())); + z_brnh(done); // bcondNotHigh -> less than equal + z_stg(Z_SP, Address(Z_thread, JavaThread::cont_fastpath_offset())); + bind(done); + BLOCK_COMMENT("} push_cont_fastpath"); +} + +void MacroAssembler::pop_cont_fastpath() { + BLOCK_COMMENT("pop_cont_fastpath {"); + if (!Continuations::enabled()) return; + NearLabel done; + z_clg(Z_SP, Address(Z_thread, JavaThread::cont_fastpath_offset())); + z_brl(done); + z_mvghi(Address(Z_thread, JavaThread::cont_fastpath_offset()), 0); + bind(done); + BLOCK_COMMENT("} pop_cont_fastpath"); +} + // LOAD HALFWORD IMMEDIATE ON CONDITION (32 <- 16) void MacroAssembler::load_on_condition_imm_32(Register dst, int64_t i2, branch_condition cc) { if (VM_Version::has_LoadStoreConditional2()) { // z_lochi works on z13 or above diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.hpp b/src/hotspot/cpu/s390/macroAssembler_s390.hpp index 8e2834ba9b7..9dd4054a36c 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.hpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.hpp @@ -1,7 +1,7 @@ /* * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. - * Copyright (c) 2024 IBM Corporation. All rights reserved. + * Copyright (c) 2024, 2026, IBM Corporation. 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 @@ -525,12 +525,13 @@ class MacroAssembler: public Assembler { Register last_java_sp, // To set up last_Java_frame in stubs; use noreg otherwise. address entry_point, // The entry point. bool allow_relocation, // Flag to request generation of relocatable code. - bool check_exception); // Flag which indicates if exception should be checked. + bool check_exception, // Flag which indicates if exception should be checked. + Label *last_java_pc); // Call into the VM. // Passes the thread pointer (in Z_ARG1) as a prepended argument. // Makes sure oop return values are visible to the GC. - void call_VM(Register oop_result, address entry_point, bool check_exceptions = true); + void call_VM(Register oop_result, address entry_point, bool check_exceptions = true, Label* last_java_pc = nullptr); void call_VM(Register oop_result, address entry_point, Register arg_1, bool check_exceptions = true); void call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, bool check_exceptions = true); void call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, @@ -575,6 +576,8 @@ class MacroAssembler: public Assembler { // Get the pc where the last call will return to. Returns _last_calls_return_pc. inline address last_calls_return_pc(); + void post_call_nop(); + static int ic_check_size(); int ic_check(int end_alignment); @@ -805,14 +808,14 @@ class MacroAssembler: public Assembler { // Support for last Java frame (but use call_VM instead where possible). private: void set_last_Java_frame(Register last_Java_sp, Register last_Java_pc, bool allow_relocation); - void reset_last_Java_frame(bool allow_relocation); - void set_top_ijava_frame_at_SP_as_last_Java_frame(Register sp, Register tmp1, bool allow_relocation); + void reset_last_Java_frame(bool check_last_java_sp, bool allow_relocation); + void set_top_ijava_frame_at_SP_as_last_Java_frame(Register sp, Register tmp1, bool allow_relocation, Label* last_java_pc = nullptr); public: inline void set_last_Java_frame(Register last_java_sp, Register last_Java_pc); inline void set_last_Java_frame_static(Register last_java_sp, Register last_Java_pc); - inline void reset_last_Java_frame(void); - inline void reset_last_Java_frame_static(void); - inline void set_top_ijava_frame_at_SP_as_last_Java_frame(Register sp, Register tmp1); + inline void reset_last_Java_frame(bool check_last_java_sp = true); + inline void reset_last_Java_frame_static(bool check_last_java_sp = true); + inline void set_top_ijava_frame_at_SP_as_last_Java_frame(Register sp, Register tmp1, Label* jpc = nullptr); inline void set_top_ijava_frame_at_SP_as_last_Java_frame_static(Register sp, Register tmp1); void set_thread_state(JavaThreadState new_state); @@ -979,6 +982,10 @@ class MacroAssembler: public Assembler { } void asm_assert_frame_size(Register expected_size, Register tmp, const char* msg, int id); + // Load bad values into registers that are nonvolatile according to the ABI except Z_thread. + // This is done after vthread preemption and before vthread resume. + void clobber_nonvolatile_registers() NOT_DEBUG_RETURN; + // Save and restore functions: Exclude Z_R0. void save_volatile_regs( Register dst, int offset, bool include_fp, bool include_flags); void restore_volatile_regs(Register src, int offset, bool include_fp, bool include_flags); @@ -1109,6 +1116,9 @@ class MacroAssembler: public Assembler { void pop_count_int_with_ext3(Register dst, Register src); void pop_count_long_with_ext3(Register dst, Register src); + void push_cont_fastpath(); + void pop_cont_fastpath(); + void load_on_condition_imm_32(Register dst, int64_t i2, branch_condition cc); void load_on_condition_imm_64(Register dst, int64_t i2, branch_condition cc); diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.inline.hpp b/src/hotspot/cpu/s390/macroAssembler_s390.inline.hpp index 72724fb66d1..24bec32f8b4 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.inline.hpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2023 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -295,16 +295,16 @@ inline void MacroAssembler::set_last_Java_frame_static(Register last_Java_sp, Re set_last_Java_frame(last_Java_sp, last_Java_pc, false); } -inline void MacroAssembler::reset_last_Java_frame(void) { - reset_last_Java_frame(true); +inline void MacroAssembler::reset_last_Java_frame(bool check_last_java_sp) { + reset_last_Java_frame(check_last_java_sp, true); } -inline void MacroAssembler::reset_last_Java_frame_static(void) { - reset_last_Java_frame(false); +inline void MacroAssembler::reset_last_Java_frame_static(bool check_last_java_sp) { + reset_last_Java_frame(check_last_java_sp, false); } -inline void MacroAssembler::set_top_ijava_frame_at_SP_as_last_Java_frame(Register sp, Register tmp1) { - set_top_ijava_frame_at_SP_as_last_Java_frame(sp, tmp1, true); +inline void MacroAssembler::set_top_ijava_frame_at_SP_as_last_Java_frame(Register sp, Register tmp1, Label *jpc) { + set_top_ijava_frame_at_SP_as_last_Java_frame(sp, tmp1, true, jpc); } inline void MacroAssembler::set_top_ijava_frame_at_SP_as_last_Java_frame_static(Register sp, Register tmp1) { diff --git a/src/hotspot/cpu/s390/nativeInst_s390.cpp b/src/hotspot/cpu/s390/nativeInst_s390.cpp index 546f8b13397..3520e9a3493 100644 --- a/src/hotspot/cpu/s390/nativeInst_s390.cpp +++ b/src/hotspot/cpu/s390/nativeInst_s390.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -630,3 +630,32 @@ void NativeGeneralJump::replace_mt_safe(address instr_addr, address code_buffer) *(intptr_t*)instr_addr = load_const_bytes | bytes_after_jump; ICache::invalidate_range(instr_addr, 6); } + +void NativeDeoptInstruction::verify() { +} + +void NativePostCallNop::make_deopt() { + NativeDeoptInstruction::insert(addr_at(0)); +} + +void NativeDeoptInstruction::insert(address code_pos) { + ResourceMark rm; + int code_size = 2; // z_illtrap is of 2 bytes + CodeBuffer cb(code_pos, code_size + 1); + MacroAssembler* a = new MacroAssembler(&cb); + a->z_illtrap(); + // forcing CPU to reload these 2 bytes of instruction by setting current range invalid + ICache::invalidate_range(code_pos, code_size); +} + +bool NativeDeoptInstruction::is_deopt_at(address instr){ + // Check if the instruction is an illtrap (illegal instruction used for deoptimization) + if (!Assembler::is_z_illtrap(instr)) return false; + + // Verify the instruction belongs to an nmethod + CodeBlob* cb = CodeCache::find_blob(instr); + if (cb == nullptr || !cb->is_nmethod()) { + return false; + } + return true; +} diff --git a/src/hotspot/cpu/s390/nativeInst_s390.hpp b/src/hotspot/cpu/s390/nativeInst_s390.hpp index 9852bc410b1..0ba97830bb7 100644 --- a/src/hotspot/cpu/s390/nativeInst_s390.hpp +++ b/src/hotspot/cpu/s390/nativeInst_s390.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -82,6 +82,11 @@ class NativeInstruction { bool is_illegal(); + bool is_nop() const { + // TODO update: https://bugs.openjdk.org/browse/JDK-8290965 + return Assembler::is_z_nop(addr_at(0)); + } + // Bcrl is currently the only accepted instruction here. bool is_jump(); @@ -650,39 +655,40 @@ class NativeGeneralJump: public NativeInstruction { class NativePostCallNop: public NativeInstruction { public: enum z_specific_constants { - // Once the check is implemented, this has to specify number of bytes checked on the first - // read. If the check would read beyond size of the instruction at the deopt handler stub - // code entry point, then it has to happen in two stages - to prevent out of bounds access - // in case the return address points to the entry point which could be at the end of page. - first_check_size = 0 // check is unimplemented + // The check reads a 2-byte nop instruction. Since s390 nop is 2 bytes (BCR instruction), + // we can safely read it in a single stage without risk of out-of-bounds access. + // The nop instruction is checked by is_nop() which reads a short (2 bytes). + first_check_size = 2 }; - bool check() const { Unimplemented(); return false; } + bool check() const { return is_nop(); } bool decode(int32_t& oopmap_slot, int32_t& cb_offset) const { return false; } bool patch(int32_t oopmap_slot, int32_t cb_offset) { Unimplemented(); return false; } - void make_deopt() { Unimplemented(); } + void make_deopt(); }; inline NativePostCallNop* nativePostCallNop_at(address address) { - // Unimplemented(); + NativePostCallNop* nop = (NativePostCallNop*) address; + if (nop->check()) { + return nop; + } return nullptr; } class NativeDeoptInstruction: public NativeInstruction { public: - address instruction_address() const { Unimplemented(); return nullptr; } - address next_instruction_address() const { Unimplemented(); return nullptr; } + enum { + instruction_offset = 0 + }; - void verify() { Unimplemented(); } + address instruction_address() const { return addr_at(instruction_offset); } + address next_instruction_address() const { return instruction_address() + Assembler::instr_len(addr_at(0)); } - static bool is_deopt_at(address instr) { - // Unimplemented(); - return false; - } + void verify(); + + static bool is_deopt_at(address instr); // MT-safe patching - static void insert(address code_pos) { - Unimplemented(); - } + static void insert(address code_pos); }; #endif // CPU_S390_NATIVEINST_S390_HPP diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index c0e51bd2bfd..6cdf40cda9c 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -2361,6 +2361,7 @@ encode %{ unsigned int actual_ret_off = __ offset(); assert(start_off + size_of_code == actual_ret_off, "wrong return_pc"); #endif + __ post_call_nop(); %} enc_class z_enc_java_static_call(method meth) %{ @@ -2393,6 +2394,7 @@ encode %{ } __ clear_inst_mark(); + __ post_call_nop(); %} // Java dynamic call @@ -2449,6 +2451,7 @@ encode %{ __ z_basr(Z_R14, Z_R1_scratch); unsigned int ret_off = __ offset(); } + __ post_call_nop(); %} enc_class z_enc_cmov_reg(cmpOp cmp, iRegI dst, iRegI src) %{ @@ -5557,6 +5560,38 @@ instruct compareAndSwapN_bool(iRegP mem_ptr, rarg5RegN oldval, iRegN_P2N newval, ins_pipe(pipe_class_dummy); %} +instruct compareAndExchangeN(iRegN res, iRegP mem_ptr, rarg5RegN oldval, iRegN_P2N newval, flagsReg cr) %{ + match(Set res (CompareAndExchangeN mem_ptr (Binary oldval newval))); + predicate(n->as_LoadStore()->barrier_data() == 0); + effect(TEMP_DEF res, USE mem_ptr, USE_KILL oldval, KILL cr); + format %{ "$res = CompareAndExchangeN $oldval,$newval,$mem_ptr" %} + ins_encode %{ + Register Rcomp = reg_to_register_object($oldval$$reg); + Register Rnew = reg_to_register_object($newval$$reg); + Register Raddr = reg_to_register_object($mem_ptr$$reg); + Register Rres = reg_to_register_object($res$$reg); + __ z_lr(Rres, Rcomp); + __ z_cs(Rres, Rnew, 0, Raddr); + %} + ins_pipe(pipe_class_dummy); +%} + +instruct compareAndExchangeP(iRegP res, iRegP mem_ptr, rarg5RegP oldval, iRegP_N2P newval, flagsReg cr) %{ + match(Set res (CompareAndExchangeP mem_ptr (Binary oldval newval))); + predicate(n->as_LoadStore()->barrier_data() == 0); + effect(TEMP_DEF res, USE mem_ptr, USE_KILL oldval, KILL cr); + format %{ "$res = CompareAndExchangeP $oldval,$newval,$mem_ptr" %} + ins_encode %{ + Register Rcomp = reg_to_register_object($oldval$$reg); + Register Rnew = reg_to_register_object($newval$$reg); + Register Raddr = reg_to_register_object($mem_ptr$$reg); + Register Rres = reg_to_register_object($res$$reg); + __ z_lgr(Rres, Rcomp); + __ z_csg(Rres, Rnew, 0, Raddr); + %} + ins_pipe(pipe_class_dummy); +%} + //----------Atomic operations on memory (GetAndSet*, GetAndAdd*)--------------- // Exploit: direct memory arithmetic diff --git a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp index e5a27e66968..1a13e76e930 100644 --- a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp +++ b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. + * Copyright (c) 2026 IBM Corporation. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,6 +38,8 @@ #include "oops/klass.inline.hpp" #include "prims/methodHandles.hpp" #include "registerSaver_s390.hpp" +#include "runtime/continuation.hpp" +#include "runtime/continuationEntry.inline.hpp" #include "runtime/jniHandles.hpp" #include "runtime/safepointMechanism.hpp" #include "runtime/sharedRuntime.hpp" @@ -1339,6 +1342,395 @@ static void move32_64(MacroAssembler *masm, // Wrap a JNI call. //---------------------------------------------------------------------- #undef USE_RESIZE_FRAME + +static void check_continuation_enter_argument(VMReg actual_vmreg, + Register expected_reg, + const char* name) { + assert(!actual_vmreg->is_stack(), "%s cannot be on stack", name); + assert(actual_vmreg->as_Register() == expected_reg, + "%s is in unexpected register: %s instead of %s", + name, actual_vmreg->as_Register()->name(), expected_reg->name()); +} + +//---------------------------- continuation_enter_setup --------------------------- +// +// Frame setup. +// +// Arguments: +// None. +// +// Results: +// Z_SP: pointer to blank ContinuationEntry in the pushed frame. +// +// Kills: +// Nothing +// +static OopMap* continuation_enter_setup(MacroAssembler* masm, int& framesize_words) { + + assert(ContinuationEntry::size() % VMRegImpl::stack_slot_size == 0, ""); + assert(in_bytes(ContinuationEntry::cont_offset()) % VMRegImpl::stack_slot_size == 0, ""); + assert(in_bytes(ContinuationEntry::chunk_offset()) % VMRegImpl::stack_slot_size == 0, ""); + + const int frame_size_in_bytes = (int)ContinuationEntry::size(); + assert(is_aligned(frame_size_in_bytes, frame::alignment_in_bytes), "alignment error"); + + framesize_words = frame_size_in_bytes / wordSize; + + DEBUG_ONLY(__ block_comment("continuation_enter_setup {")); + __ save_return_pc(); // preserve current Z_R14 + __ push_frame(frame_size_in_bytes); + + OopMap* map = new OopMap((int)frame_size_in_bytes / VMRegImpl::stack_slot_size, 0 /* arg_slots*/); + __ z_mvc(Address(Z_SP, ContinuationEntry::parent_offset()), /* move to */ + Address(Z_thread, JavaThread::cont_entry_offset()), /* move from */ + sizeof(ContinuationEntry*) /* size of data to be moved */ + ); + __ z_stg(Z_SP, Address(Z_thread, JavaThread::cont_entry_offset())); + DEBUG_ONLY(__ block_comment("} continuation_enter_setup")); + return map; +} + +//---------------------------- fill_continuation_entry --------------------------- +// +// Initialize the new ContinuationEntry. +// +// Arguments: +// Z_SP : pointer to blank Continuation entry +// reg_cont_obj : pointer to the continuation +// reg_flags : flags / isVirtualThread +// +// Results: +// Z_SP : pointer to filled out ContinuationEntry +// +// Kills: +// This is peace driven method, doesn't kill anyone. +// +static void fill_continuation_entry(MacroAssembler* masm, Register reg_cont_obj, Register reg_flags) { + assert_different_registers(reg_cont_obj, reg_flags); + DEBUG_ONLY(__ block_comment("fill_continuation_entry {")); +#ifdef ASSERT + assert(Immediate::is_simm16(ContinuationEntry::cookie_value()), "update below instruction"); + __ z_mvhi(Address(Z_SP, ContinuationEntry::cookie_offset()), ContinuationEntry::cookie_value()); +#endif //ASSERT + __ z_stg(reg_cont_obj, Address(Z_SP, ContinuationEntry::cont_offset())); + __ z_st(reg_flags, Address(Z_SP, ContinuationEntry::flags_offset())); + __ z_mvghi(Address(Z_SP, ContinuationEntry::chunk_offset()), 0); + __ z_mvhi( Address(Z_SP, ContinuationEntry::argsize_offset()), 0); + __ z_mvhi( Address(Z_SP, ContinuationEntry::pin_count_offset()), 0); + + __ z_mvc(Address(Z_SP, ContinuationEntry::parent_cont_fastpath_offset()), /* move to */ + Address(Z_thread, JavaThread::cont_fastpath_offset()), /* move from */ + sizeof(ContinuationEntry*) /* size of data to be moved */ + ); + + __ z_mvghi(Address(Z_thread, JavaThread::cont_fastpath_offset()), 0); + + DEBUG_ONLY(__ block_comment("} fill_continuation_entry")); +} + +//---------------------------- continuation_enter_cleanup --------------------------- +// +// Copy corresponding attributes from the top ContinuationEntry to the JavaThread +// before deleting it. +// +// Arguments: +// Z_SP: pointer to the ContinuationEntry +// +// Results: +// None. +// +// Kills: +// Z_R0_scratch (in debug builds) +// Z_R10 (when CheckJNICalls is enabled) +// +static void continuation_enter_cleanup(MacroAssembler* masm) { + __ block_comment("continuation_enter_cleanup {"); + +#ifdef ASSERT + __ z_cg(Z_SP, Address(Z_thread, JavaThread::cont_entry_offset())); + __ asm_assert(Assembler::bcondEqual, FILE_AND_LINE ": incorrect Z_SP", 0x1bb); + + __ z_lgf(Z_R0, Address(Z_SP, ContinuationEntry::cookie_offset())); + __ z_cfi(Z_R0, ContinuationEntry::cookie_value()); + __ asm_assert(Assembler::bcondEqual, FILE_AND_LINE ": incorrect cookie value", 0x1cc); +#endif // ASSERT + + __ z_mvc(Address(Z_thread, JavaThread::cont_fastpath_offset()), /* move to */ + Address(Z_SP, ContinuationEntry::parent_cont_fastpath_offset()), /* move from */ + sizeof(ContinuationEntry*) /* size of data to be moved */ + ); + + __ z_mvc(Address(Z_thread, JavaThread::cont_entry_offset()), /* move to */ + Address(Z_SP, ContinuationEntry::parent_offset()), /* move from */ + sizeof(ContinuationEntry*) /* size of data to be moved */ + ); + + __ block_comment("} continuation_enter_cleanup"); +} +static void gen_continuation_enter(MacroAssembler* masm, + const VMRegPair* regs, + int& exception_offset, + OopMapSet* oop_maps, + int& frame_complete, + int& framesize_words, + int& interpreted_entry_offset, + int& compiled_entry_offset) { + // enterSpecial(Continuation c, boolean isContinue, boolean isVirtualThread) + int pos_cont_obj = 0; + int pos_is_cont = 1; + int pos_is_virtual = 2; + + // The platform-specific calling convention may present the arguments in various registers. + // To simplify the rest of the code, we expect the arguments to reside at these known + // registers, and we additionally check the placement here in case calling convention ever + // changes. + Register reg_cont_obj = Z_ARG1; + Register reg_is_cont = Z_ARG2; + Register reg_is_virtual = Z_ARG3; + + check_continuation_enter_argument(regs[pos_cont_obj].first(), reg_cont_obj, "Continuation object"); + check_continuation_enter_argument(regs[pos_is_cont].first(), reg_is_cont, "isContinue"); + check_continuation_enter_argument(regs[pos_is_virtual].first(), reg_is_virtual, "isVirtualThread"); + + address resolve_static_call = SharedRuntime::get_resolve_static_call_stub(); + + address start = __ pc(); + + Label L_thaw, L_exit; + + // i2i entry used at interp_only_mode only + interpreted_entry_offset = __ pc() - start; + { +#ifdef ASSERT + NearLabel is_interp_only; + __ load_and_test_int(Z_R0_scratch, Address(Z_thread, JavaThread::interp_only_mode_offset())); + __ z_brnz(is_interp_only); + __ stop("enterSpecial interpreter entry called when not in interp_only_mode"); + __ bind(is_interp_only); +#endif + + // Read interpreter arguments into registers (this is an ad-hoc i2c adapter) + // s390x stores frame pointer in the slot 0, so argument will be loaded from slot 1 + __ z_lg(reg_cont_obj, Address(Z_esp, Interpreter::stackElementSize*3)); + __ z_llgf(reg_is_cont, Address(Z_esp, Interpreter::stackElementSize*2)); + __ z_llgf(reg_is_virtual, Address(Z_esp, Interpreter::stackElementSize*1)); + + __ push_cont_fastpath(); + + OopMap* map = continuation_enter_setup(masm, framesize_words); + + // The frame is complete here, but we only record it for the compiled entry, so the frame would appear unsafe, + // but that's okay because at the very worst we'll miss an async sample, but we're in interp_only_mode anyway. + + __ verify_oop(reg_cont_obj); + + fill_continuation_entry(masm, reg_cont_obj, reg_is_virtual); + + // If isContinue, call to thaw. Otherwise, call Continuation.enter(Continuation c, boolean isContinue) + __ compare32_and_branch(reg_is_cont, 0, Assembler::bcondNotZero, L_thaw); + + // --- call Continuation.enter(Continuation c, boolean isContinue) + + // Emit compiled static call. The call will be always resolved to the c2i + // entry of Continuation.enter(Continuation c, boolean isContinue). + // There are special cases in SharedRuntime::resolve_static_call_C() and + // SharedRuntime::resolve_sub_helper_internal() to achieve this + // See also corresponding call below. + // Make sure the call is patchable + + __ align(NativeCall::call_far_pcrelative_displacement_alignment, + __ offset() + NativeCall::call_far_pcrelative_displacement_offset); + + // Emit stub for static call + address stub = CompiledDirectCall::emit_to_interp_stub(masm, __ pc()); + if (stub == nullptr) { + fatal("CodeCache is full at gen_continuation_enter"); + } + __ relocate(relocInfo::static_call_type); + __ z_nop(); + __ z_brasl(Z_R14, resolve_static_call); + oop_maps->add_gc_map(__ pc() - start, map); + __ post_call_nop(); + __ branch_optimized(Assembler::bcondAlways, L_exit); + } + + // compiled entry + __ align(CodeEntryAlignment); + compiled_entry_offset = __ pc() - start; + + OopMap* map = continuation_enter_setup(masm, framesize_words); + + // Frame is now completed as far as size and linkage. + + frame_complete =__ pc() - start; + + __ verify_oop(reg_cont_obj); + + fill_continuation_entry(masm, reg_cont_obj, reg_is_virtual); + + // If isContinue, call to thaw. Otherwise, call Continuation.enter(Continuation c, boolean isContinue) + __ z_ltr(reg_is_cont, reg_is_cont); + __ branch_optimized(Assembler::bcondNotEqual, L_thaw); // was reg_is_cont equal to 0 ? + + // --- call Continuation.enter(Continuation c, boolean isContinue) + + // Make sure the call is patchable + __ align(NativeCall::call_far_pcrelative_displacement_alignment, + __ offset() + NativeCall::call_far_pcrelative_displacement_offset); + + // Emit stub for static call + address stub = CompiledDirectCall::emit_to_interp_stub(masm, __ pc()); + guarantee(stub != nullptr, "CodeCache is full at gen_continuation_enter"); + + assert((__ offset() + NativeCall::call_far_pcrelative_displacement_offset) % NativeCall::call_far_pcrelative_displacement_alignment == 0, + "must be aligned (offset=%d)", __ offset()); + + // The call needs to be resolved. There's a special case for this in + // SharedRuntime::find_callee_info_helper() which calls + // LinkResolver::resolve_continuation_enter() which resolves the call to + // Continuation.enter(Continuation c, boolean isContinue). + __ relocate(relocInfo::static_call_type); + __ z_nop(); + __ z_brasl(Z_R14, resolve_static_call); + oop_maps->add_gc_map(__ pc() - start, map); + __ post_call_nop(); + + __ branch_optimized(Assembler::bcondAlways, L_exit); + + // --- Thawing path + + __ bind(L_thaw); + ContinuationEntry::_thaw_call_pc_offset = __ pc() - start; + __ load_const_optimized(Z_R1_scratch, StubRoutines::cont_thaw()); + __ call(Z_R1_scratch); + oop_maps->add_gc_map(__ pc() - start, map->deep_copy()); + ContinuationEntry::_return_pc_offset = __ pc() - start; + __ post_call_nop(); + + // --- Normal exit (resolve/thawing) + __ bind(L_exit); + ContinuationEntry::_cleanup_offset = __ pc() - start; + continuation_enter_cleanup(masm); + + // Pop frame and return + DEBUG_ONLY(__ z_lg(Z_R0, Address(Z_SP, 0))); + __ add2reg(Z_SP, framesize_words * wordSize); + +#ifdef ASSERT + NearLabel ok; + __ z_cgr(Z_R0, Z_SP); + __ z_bre(ok); + __ stop("inconsistent frame size"); + __ bind(ok); +#endif // ASSERT + + __ restore_return_pc(); + __ z_br(Z_R14); + + // --- Exception handling path + exception_offset = __ pc() - start; + + continuation_enter_cleanup(masm); + + // Load caller's return pc + __ z_lg(Z_ARG2, _z_common_abi(callers_sp), Z_SP); + __ z_lg(Z_ARG2, _z_common_abi(return_pc), Z_ARG2); + + __ save_return_pc(); + __ push_frame_abi160(0 + 2 * BytesPerWord); + + __ z_stg(Z_ARG1, 0 * BytesPerWord + frame::z_abi_160_size, Z_SP); // save return value containing the exception oop + __ z_stg(Z_ARG2, 1 * BytesPerWord + frame::z_abi_160_size, Z_SP); // save exception_pc + + // Find exception handler. + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), + Z_thread, + Z_ARG2); + + // Copy handler's address. + __ z_lgr(Z_R1, Z_RET); + + // Set up the arguments for the exception handler: + // - Z_ARG1: exception oop + // - Z_ARG2: exception pc + __ z_lg(Z_ARG1, 0 * BytesPerWord + frame::z_abi_160_size, Z_SP); // load the exception oop + __ z_lg(Z_ARG2, 1 * BytesPerWord + frame::z_abi_160_size, Z_SP); // load the exception pc + + __ pop_frame(); // pop frame pushed before runtime call + // __ restore_return_pc(); // can be skipped + + __ pop_frame(); // pop enterSpecial frame + __ restore_return_pc(); + + // Jump to exception handler + __ z_br(Z_R1 /*handler address*/); +} + +static void gen_continuation_yield(MacroAssembler* masm, + const VMRegPair* regs, + OopMapSet* oop_maps, + int& frame_complete, + int& framesize_words, + int& compiled_entry_offset) { + const int framesize_bytes = (int)align_up((int)frame::z_abi_160_size, frame::alignment_in_bytes); + framesize_words = framesize_bytes / wordSize; + + Register Rtmp = Z_R1_scratch; + + address start = __ pc(); + compiled_entry_offset = __ pc() - start; + + // Save return pc and push entry frame + __ save_return_pc(); + __ push_frame(framesize_bytes); + + DEBUG_ONLY(__ block_comment("Frame Complete (gen_continuation_yield):")); + frame_complete = __ pc() - start; + address last_java_pc = __ pc(); + + + // This nop must be exactly at the PC we push into the frame info. + // We use this nop for fast CodeBlob lookup, associate the OopMap + // with it right away. + __ post_call_nop(); + OopMap* map = new OopMap(framesize_bytes / VMRegImpl::stack_slot_size, 1); + oop_maps->add_gc_map(last_java_pc - start, map); + + __ z_larl(Rtmp, last_java_pc); + __ set_last_Java_frame(Z_SP, Rtmp); + __ call_VM_leaf(Continuation::freeze_entry(), Z_thread, Z_SP); + __ reset_last_Java_frame(); + + NearLabel L_pinned; + __ z_cij(Z_RET, 0, Assembler::bcondNotEqual, L_pinned); + + // Pop frames of continuation including this stub's frame + __ z_lg(Z_SP, Address(Z_thread, JavaThread::cont_entry_offset())); + // The frame pushed by gen_continuation_enter() is on top now again + continuation_enter_cleanup(masm); + // Pop frame and return + Label L_return; + __ bind(L_return); + __ pop_frame(); + __ restore_return_pc(); + __ z_br(Z_R14); + + // yield failed - continuation is pinned + __ bind(L_pinned); + + // handle pending exception thrown by freeze + __ load_and_test_long(Rtmp, Address(Z_thread, Thread::pending_exception_offset())); + __ z_bre(L_return); // return if no exception is pending + __ pop_frame(); + __ restore_return_pc(); + __ load_const_optimized(Z_R1_scratch, StubRoutines::forward_exception_entry()); + __ z_br(Z_R1_scratch); +} + +void SharedRuntime::continuation_enter_cleanup(MacroAssembler* masm) { + ::continuation_enter_cleanup(masm); +} + nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, const methodHandle& method, int compile_id, @@ -1346,6 +1738,66 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, VMRegPair *in_regs, BasicType ret_type) { int total_in_args = method->size_of_parameters(); + if (method->is_continuation_native_intrinsic()) { + int exception_offset = -1; + OopMapSet* oop_maps = new OopMapSet(); + int frame_complete = -1; + int stack_slots = -1; + int interpreted_entry_offset = -1; + int vep_offset = -1; // verified entry point offset + if (method->is_continuation_enter_intrinsic()) { + gen_continuation_enter(masm, + in_regs, + exception_offset, + oop_maps, + frame_complete, + stack_slots, + interpreted_entry_offset, + vep_offset); + } else if(method->is_continuation_yield_intrinsic()) { + gen_continuation_yield(masm, + in_regs, + oop_maps, + frame_complete, + stack_slots, + vep_offset); + } else { + guarantee(false, "Unknown Continuation native intrinsic"); + } + +#ifdef ASSERT + if (method->is_continuation_enter_intrinsic()) { + assert(interpreted_entry_offset != -1, "Must be set"); + assert(exception_offset != -1, "Must be set"); + } else { + assert(interpreted_entry_offset == -1, "Must be unset"); + assert(exception_offset == -1, "Must be unset"); + } + assert(frame_complete != -1, "Must be set"); + assert(stack_slots != -1, "Must be set"); + assert(vep_offset != -1, "Must be set"); +#endif + + __ flush(); + nmethod* nm = nmethod::new_native_nmethod(method, + compile_id, + masm->code(), + vep_offset, + frame_complete, + stack_slots, + in_ByteSize(-1), + in_ByteSize(-1), + oop_maps, + exception_offset); + if (nm == nullptr) return nm; + if (method->is_continuation_enter_intrinsic()) { + ContinuationEntry::set_enter_code(nm, interpreted_entry_offset); + } else if (method->is_continuation_yield_intrinsic()) { + _cont_doYield_stub = nm; + } + return nm; + } + if (method->is_method_handle_intrinsic()) { vmIntrinsics::ID iid = method->intrinsic_id(); intptr_t start = (intptr_t) __ pc(); @@ -1545,6 +1997,7 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, unsigned int wrapper_FrameDone; unsigned int wrapper_CRegsSet; Label handle_pending_exception; + Label last_java_pc; //--------------------------------------------------------------------- // Unverified entry point (UEP) @@ -1726,16 +2179,9 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, // So if we must call out we must push a new frame. ////////////////////////////////////////////////////////////////////// - - // Calc the current pc into Z_R10 and into wrapper_CRegsSet. - // Both values represent the same position. - __ get_PC(Z_R10); // PC into register - wrapper_CRegsSet = __ offset(); // and into into variable. - - // Z_R10 now has the pc loaded that we will use when we finally call to native. - - // We use the same pc/oopMap repeatedly when we call out. - oop_maps->add_gc_map((int)(wrapper_CRegsSet-wrapper_CodeStart), map); + // The last java pc will also be used as resume pc if this is the wrapper for wait0. + // For this purpose the precise location matters but not for oopmap lookup. + __ z_larl(Z_R10, last_java_pc); // Lock a synchronized method. @@ -1780,10 +2226,13 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, __ z_lgr(Z_ARG3, Z_thread); __ set_last_Java_frame(oldSP, Z_R10 /* gc map pc */); + assert(Z_R10->is_nonvolatile(), "Z_R10 needs to be preserved accross complete_monitor_locking_C call"); // Do the call. + __ push_cont_fastpath(); __ load_const_optimized(Z_R1_scratch, CAST_FROM_FN_PTR(address, SharedRuntime::complete_monitor_locking_C)); __ call(Z_R1_scratch); + __ pop_cont_fastpath(); __ reset_last_Java_frame(); @@ -1910,6 +2359,23 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, // Transition from _thread_in_native_trans to _thread_in_Java. __ set_thread_state(_thread_in_Java); + // Check preemption for Object.wait() + if (method->is_object_wait0()) { + NearLabel not_preempted; + __ z_ltg(Z_R1_scratch, Address(Z_thread, JavaThread::preempt_alternate_return_offset())); + __ z_brz(not_preempted); // if 0, jump to not_preempted + __ z_mvghi(Address(Z_thread, JavaThread::preempt_alternate_return_offset()), 0); + __ z_br(Z_R1_scratch); + __ bind(not_preempted); + } + __ bind(last_java_pc); + + // Calc the current pc into wrapper_CRegsSet. + wrapper_CRegsSet = __ offset(); // and into into variable. + + // We use the same pc/oopMap repeatedly when we call out. + oop_maps->add_gc_map((int)(wrapper_CRegsSet-wrapper_CodeStart), map); + //-------------------------------------------------------------------- // Reguard any pages if necessary. // Protect native result from being destroyed. @@ -2012,7 +2478,10 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, // Clear "last Java frame" SP and PC. //-------------------------------------------------------------------- - __ reset_last_Java_frame(); + + // Last java frame won't be set if we're resuming after preemption + bool maybe_preempted = method->is_object_wait0(); + __ reset_last_Java_frame(/* check_last_java_sp = */ !maybe_preempted); // Unpack oop result, e.g. JNIHandles::resolve result. if (is_reference_type(ret_type)) { @@ -2317,6 +2786,8 @@ void SharedRuntime::gen_i2c_adapter(MacroAssembler *masm, } } + __ push_cont_fastpath(); // Set JavaThread::_cont_fastpath to the sp of the oldest interpreted frame we know about + // Jump to the compiled code just as if compiled code was doing it. // load target address from method: __ z_lg(Z_R1_scratch, Address(Z_method, Method::from_compiled_offset())); @@ -2416,8 +2887,7 @@ uint SharedRuntime::out_preserve_stack_slots() { } VMReg SharedRuntime::thread_register() { - Unimplemented(); - return nullptr; + return Z_thread->as_VMReg(); } // @@ -2678,6 +3148,13 @@ void SharedRuntime::generate_deopt_blob() { // stack: (caller_of_deoptee, ...). + // Freezing continuation frames requires that the caller is trimmed to unextended sp if compiled. + // If not compiled the loaded value is equal to the current SP (see frame::initial_deoptimization_info()) + // and the frame is effectively not resized. + Register caller_sp = Z_R1_scratch; + __ z_lg(caller_sp, Address(unroll_block_reg, Deoptimization::UnrollBlock::initial_info_offset())); + __ resize_frame_absolute(caller_sp, Z_R0, true); + // loop through the `UnrollBlock' info and create interpreter frames. push_skeleton_frames(masm, true/*deopt*/, unroll_block_reg, @@ -2809,6 +3286,13 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() { __ zap_from_to(Z_SP, Z_SP, Z_R0_scratch, Z_R1, 500, -1); + // Freezing continuation frames requires that the caller is trimmed to unextended sp if compiled. + // If not compiled the loaded value is equal to the current SP (see frame::initial_deoptimization_info()) + // and the frame is effectively not resized. + Register caller_sp = Z_R1_scratch; + __ z_lg(caller_sp, Address(unroll_block_reg, Deoptimization::UnrollBlock::initial_info_offset())); + __ resize_frame_absolute(caller_sp, Z_R0, true); + // allocate new interpreter frame(s) and possibly resize the caller's frame // (no more adapters !) push_skeleton_frames(masm, false/*deopt*/, @@ -3387,16 +3871,76 @@ int SpinPause() { } #if INCLUDE_JFR + +// For c2: c_rarg0 is junk, call to runtime to write a checkpoint. +// It returns a jobject handle to the event writer. +// The handle is dereferenced and the return value is the event writer oop. RuntimeStub* SharedRuntime::generate_jfr_write_checkpoint() { - if (!Continuations::enabled()) return nullptr; - Unimplemented(); - return nullptr; + const char* name = SharedRuntime::stub_name(StubId::shared_jfr_write_checkpoint_id); + CodeBuffer code(name, 512, 64); + MacroAssembler* masm = new MacroAssembler(&code); + + int framesize = frame::z_abi_160_size / VMRegImpl::stack_slot_size; + address start = __ pc(); + __ save_return_pc(); // save return_pc (Z_R14) + __ push_frame_abi160(0); + int frame_complete = __ pc() - start; + __ set_last_Java_frame(Z_SP, noreg); + + __ call_VM_leaf(CAST_FROM_FN_PTR(address, JfrIntrinsicSupport::write_checkpoint), Z_thread); + address calls_return_pc = __ last_calls_return_pc(); + __ reset_last_Java_frame(); + + // The handle is dereferenced through a load barrier. + __ resolve_global_jobject(Z_ARG1, Z_tmp_1, Z_tmp_2); + __ pop_frame(); + __ restore_return_pc(); + __ z_br(Z_R14); + + OopMapSet* oop_maps = new OopMapSet(); + OopMap* map = new OopMap(framesize, 0); + oop_maps->add_gc_map(calls_return_pc - start, map); + + RuntimeStub* stub = // codeBlob framesize is in words (not VMRegImpl::slot_size) + RuntimeStub::new_runtime_stub(name, &code, frame_complete, + (framesize >> (LogBytesPerWord - LogBytesPerInt)), + oop_maps, false); + + return stub; } +// For c2: call to return a leased buffer. RuntimeStub* SharedRuntime::generate_jfr_return_lease() { - if (!Continuations::enabled()) return nullptr; - Unimplemented(); - return nullptr; + const char* name = SharedRuntime::stub_name(StubId::shared_jfr_return_lease_id); + CodeBuffer code(name, 512, 64); + MacroAssembler* masm = new MacroAssembler(&code); + + int framesize = frame::z_abi_160_size / VMRegImpl::stack_slot_size; + address start = __ pc(); + __ save_return_pc(); // save return_pc (Z_R14) + __ push_frame_abi160(0); + int frame_complete = __ pc() - start; + __ set_last_Java_frame(Z_SP, noreg); + + __ call_VM_leaf(CAST_FROM_FN_PTR(address, JfrIntrinsicSupport::return_lease), Z_thread); + address calls_return_pc = __ last_calls_return_pc(); + + __ reset_last_Java_frame(); + + __ pop_frame(); + __ restore_return_pc(); + __ z_br(Z_R14); + + OopMapSet* oop_maps = new OopMapSet(); + OopMap* map = new OopMap(framesize, 0); + oop_maps->add_gc_map(calls_return_pc - start, map); + + RuntimeStub* stub = // codeBlob framesize is in words (not VMRegImpl::slot_size) + RuntimeStub::new_runtime_stub(name, &code, frame_complete, + (framesize >> (LogBytesPerWord - LogBytesPerInt)), + oop_maps, false); + + return stub; } #endif // INCLUDE_JFR diff --git a/src/hotspot/cpu/s390/smallRegisterMap_s390.inline.hpp b/src/hotspot/cpu/s390/smallRegisterMap_s390.inline.hpp index f338fb192ad..630a9516831 100644 --- a/src/hotspot/cpu/s390/smallRegisterMap_s390.inline.hpp +++ b/src/hotspot/cpu/s390/smallRegisterMap_s390.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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 @@ -30,7 +30,7 @@ class SmallRegisterMap; -// Java frames don't have callee saved registers (except for rfp), so we can use a smaller SmallRegisterMapType +// Java frames don't have callee saved registers, so we can use a smaller RegisterMap template class SmallRegisterMapType { friend SmallRegisterMap; @@ -39,8 +39,6 @@ class SmallRegisterMapType { ~SmallRegisterMapType() = default; NONCOPYABLE(SmallRegisterMapType); - static void assert_is_rfp(VMReg r) NOT_DEBUG_RETURN - DEBUG_ONLY({ Unimplemented(); }) public: // as_RegisterMap is used when we didn't want to templatize and abstract over RegisterMap type to support SmallRegisterMap // Consider enhancing SmallRegisterMap to support those cases @@ -48,20 +46,21 @@ class SmallRegisterMapType { RegisterMap* as_RegisterMap() { return nullptr; } RegisterMap* copy_to_RegisterMap(RegisterMap* map, intptr_t* sp) const { - Unimplemented(); + map->clear(); + map->set_include_argument_oops(this->include_argument_oops()); return map; } inline address location(VMReg reg, intptr_t* sp) const { - Unimplemented(); + assert(false, "Reg: %s", reg->name()); return nullptr; } - inline void set_location(VMReg reg, address loc) { assert_is_rfp(reg); } + inline void set_location(VMReg reg, address loc) { assert(false, "Reg: %s", reg->name()); } JavaThread* thread() const { #ifndef ASSERT - guarantee (false, ""); + guarantee (false, "unreachable"); #endif return nullptr; } @@ -76,7 +75,7 @@ class SmallRegisterMapType { #ifdef ASSERT bool should_skip_missing() const { return false; } VMReg find_register_spilled_here(void* p, intptr_t* sp) { - Unimplemented(); + assert(false, "Shouldn't reach here! p:" PTR_FORMAT " sp:" PTR_FORMAT, p2i(p), p2i(p)); return nullptr; } void print() const { print_on(tty); } diff --git a/src/hotspot/cpu/s390/stackChunkFrameStream_s390.inline.hpp b/src/hotspot/cpu/s390/stackChunkFrameStream_s390.inline.hpp index e598117fe7d..3a5b860b7a7 100644 --- a/src/hotspot/cpu/s390/stackChunkFrameStream_s390.inline.hpp +++ b/src/hotspot/cpu/s390/stackChunkFrameStream_s390.inline.hpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 IBM Corporation. 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 @@ -32,75 +33,120 @@ #ifdef ASSERT template inline bool StackChunkFrameStream::is_in_frame(void* p0) const { - Unimplemented(); - return true; + assert(!is_done(), ""); + assert(is_compiled(), ""); + intptr_t* p = (intptr_t*)p0; + int argsize = (_cb->as_nmethod()->num_stack_arg_slots() * VMRegImpl::stack_slot_size) >> LogBytesPerWord; + int frame_size = _cb->frame_size() + (argsize > 0 ? argsize + frame::metadata_words_at_top : 0); + return (p - unextended_sp()) >= 0 && (p - unextended_sp()) < frame_size; } #endif template inline frame StackChunkFrameStream::to_frame() const { - Unimplemented(); - return frame(); + if (is_done()) { + return frame(_sp, _sp, nullptr, nullptr, nullptr, nullptr, true); + } else { + // Compiled frames on heap don't have back links on s390. The back link is redundant + // and gets computed as unextended_sp + frame_size. In debug builds, FreezeBase::patch_pd() + // explicitly sets it to badAddress. + return frame(sp(), unextended_sp(), Interpreter::contains(pc()) ? fp() : nullptr, pc(), cb(), _oopmap, true); + } } template inline address StackChunkFrameStream::get_pc() const { - Unimplemented(); - return nullptr; + assert(!is_done(), ""); + return (address)((frame::z_common_abi*) _sp)->return_pc; } template inline intptr_t* StackChunkFrameStream::fp() const { - Unimplemented(); - return nullptr; + // See FreezeBase::patch_pd() and frame::setup() + assert((frame_kind == ChunkFrames::Mixed && is_interpreted()), ""); + intptr_t* fp_addr = (intptr_t*)&((frame::z_common_abi*)_sp)->callers_sp; + assert(*(intptr_t**)fp_addr != nullptr, ""); + // derelativize + return fp_addr + *fp_addr; } template inline intptr_t* StackChunkFrameStream::derelativize(int offset) const { - Unimplemented(); - return nullptr; + intptr_t* fp = this->fp(); + assert(fp != nullptr, ""); + return fp + fp[offset]; } template inline intptr_t* StackChunkFrameStream::unextended_sp_for_interpreter_frame() const { - Unimplemented(); - return nullptr; + assert_is_interpreted_and_frame_type_mixed(); + // Compute the unextended SP (stack pointer before any extension for arguments). + // On s390, esp points to the next free slot above the operand stack, so we add 1 + // to get the actual top of the operand stack, then subtract metadata_words to + // account for the frame metadata (callers_sp and return_pc) at the top of the frame. + return derelativize(_z_ijava_idx(esp)) + 1 - frame::metadata_words; } template inline void StackChunkFrameStream::next_for_interpreter_frame() { - Unimplemented(); + assert_is_interpreted_and_frame_type_mixed(); + if (derelativize(_z_ijava_idx(locals)) + 1 >= _end) { + _unextended_sp = _end; + _sp = _end; + } else { + _unextended_sp = derelativize(_z_ijava_idx(sender_sp)); + _sp = this->fp(); + } } template inline int StackChunkFrameStream::interpreter_frame_size() const { - Unimplemented(); - return 0; + assert_is_interpreted_and_frame_type_mixed(); + intptr_t* top = unextended_sp(); // later subtract argsize if callee is interpreted + intptr_t* bottom = derelativize(_z_ijava_idx(locals)) + 1; + return (int)(bottom - top); } +// Size of stack args in words (P0..Pn above). Only valid if the caller is also +// interpreted. The function is also called if the caller is compiled but the +// result is not used in that case (same on x86). +// See also setting of sender_sp in ContinuationHelper::InterpretedFrame::patch_sender_sp() template inline int StackChunkFrameStream::interpreter_frame_stack_argsize() const { - Unimplemented(); - return 0; + assert_is_interpreted_and_frame_type_mixed(); + frame::z_ijava_state* state = (frame::z_ijava_state*)((uintptr_t)fp() - frame::z_ijava_state_size); + int diff = (int)(state->locals - (state->sender_sp + frame::metadata_words_at_top) + 1); + assert(diff == -frame::metadata_words_at_top || ((Method*)state->method)->size_of_parameters() == diff, + "size_of_parameters(): %d diff: %d sp: " PTR_FORMAT " fp:" PTR_FORMAT, + ((Method*)state->method)->size_of_parameters(), diff, p2i(sp()), p2i(fp())); + return diff; } template template inline int StackChunkFrameStream::interpreter_frame_num_oops(RegisterMapT* map) const { - Unimplemented(); - return 0; + assert_is_interpreted_and_frame_type_mixed(); + ResourceMark rm; + frame f = to_frame(); + InterpreterOopCount closure; + f.oops_interpreted_do(&closure, map); + return closure.count(); } template<> template<> inline void StackChunkFrameStream::update_reg_map_pd(RegisterMap* map) { - Unimplemented(); + // No register map update needed for s390. + // In the Java calling convention on s390, all registers are volatile (caller-saved), + // so there are no non-volatile (callee-saved) registers that need to be tracked. } template<> template<> inline void StackChunkFrameStream::update_reg_map_pd(RegisterMap* map) { - Unimplemented(); + // No register map update needed for s390. + // In the Java calling convention on s390, all registers are volatile (caller-saved), + // so there are no non-volatile (callee-saved) registers that need to be tracked. } template diff --git a/src/hotspot/cpu/s390/stackChunkOop_s390.inline.hpp b/src/hotspot/cpu/s390/stackChunkOop_s390.inline.hpp index dfd3562c9d9..c97751d0d1e 100644 --- a/src/hotspot/cpu/s390/stackChunkOop_s390.inline.hpp +++ b/src/hotspot/cpu/s390/stackChunkOop_s390.inline.hpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 IBM Corporation. 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,11 +27,15 @@ #define CPU_S390_STACKCHUNKOOP_S390_INLINE_HPP inline void stackChunkOopDesc::relativize_frame_pd(frame& fr) const { - Unimplemented(); + if (fr.is_interpreted_frame()) { + fr.set_offset_fp(relativize_address(fr.fp())); + } } inline void stackChunkOopDesc::derelativize_frame_pd(frame& fr) const { - Unimplemented(); + if (fr.is_interpreted_frame()) { + fr.set_fp(derelativize_address(fr.offset_fp())); + } } #endif // CPU_S390_STACKCHUNKOOP_S390_INLINE_HPP diff --git a/src/hotspot/cpu/s390/stubDeclarations_s390.hpp b/src/hotspot/cpu/s390/stubDeclarations_s390.hpp index d0e26beedab..d773b6ce759 100644 --- a/src/hotspot/cpu/s390/stubDeclarations_s390.hpp +++ b/src/hotspot/cpu/s390/stubDeclarations_s390.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -47,7 +47,7 @@ do_arch_entry, \ do_arch_entry_init, \ do_arch_entry_array) \ - do_arch_blob(continuation, 2000) \ + do_arch_blob(continuation, 5000) \ #define STUBGEN_COMPILER_BLOBS_ARCH_DO(do_stub, \ diff --git a/src/hotspot/cpu/s390/stubGenerator_s390.cpp b/src/hotspot/cpu/s390/stubGenerator_s390.cpp index 5309158fc74..d1601d4f147 100644 --- a/src/hotspot/cpu/s390/stubGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/stubGenerator_s390.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. + * Copyright (c) 2026 IBM Corporation. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,6 +38,8 @@ #include "oops/oop.inline.hpp" #include "prims/methodHandles.hpp" #include "prims/upcallLinker.hpp" +#include "runtime/continuation.hpp" +#include "runtime/continuationEntry.inline.hpp" #include "runtime/frame.inline.hpp" #include "runtime/handles.inline.hpp" #include "runtime/javaThread.hpp" @@ -330,6 +333,8 @@ class StubGenerator: public StubCodeGenerator { // Pop frame. Done here to minimize stalls. __ pop_frame(); + __ pop_cont_fastpath(); + // Reload some volatile registers which we've spilled before the call // to template interpreter / native entry. // Access all locals via frame pointer, because we know nothing about @@ -3223,28 +3228,180 @@ class StubGenerator: public StubCodeGenerator { return start; } - address generate_cont_thaw(bool return_barrier, bool exception) { + address generate_cont_thaw(StubId stub_id) { if (!Continuations::enabled()) return nullptr; - Unimplemented(); - return nullptr; + + Continuation::thaw_kind kind; + bool return_barrier; + bool return_barrier_exception; + + switch (stub_id) { + case StubId::stubgen_cont_thaw_id: + kind = Continuation::thaw_top; + return_barrier = false; + return_barrier_exception = false; + break; + case StubId::stubgen_cont_returnBarrier_id: + kind = Continuation::thaw_return_barrier; + return_barrier = true; + return_barrier_exception = false; + break; + case StubId::stubgen_cont_returnBarrierExc_id: + kind = Continuation::thaw_return_barrier_exception; + return_barrier = true; + return_barrier_exception = true; + break; + default: + ShouldNotReachHere(); + } + + StubCodeMark mark(this, stub_id); + address start = __ pc(); + + // TODO: Handle Valhalla return types. May require generating different return barriers. + + if (kind == Continuation::thaw_top) { + __ clobber_nonvolatile_registers(); // Except Z_thread + } + + if (return_barrier) { + // Save return values in non-volatile float registers to preserve them across VM calls. + // Z_F8 and Z_F9 are non-volatile (callee-saved) registers on s390 (F8-F15 are non-volatile). + // They are safe to use here because: + // 1. clobber_nonvolatile_registers() is NOT called for return_barrier cases (only for thaw_top) + // 2. These registers are preserved across the VM leaf calls (prepare_thaw, thaw_entry) + __ z_ldgr(Z_F8, Z_RET); // Save integer return value in non-volatile float register + __ z_ldr(Z_F9, Z_FRET); // Save float return value in non-volatile float register + + DEBUG_ONLY(__ z_lg(Z_R1_scratch, _z_common_abi(callers_sp), Z_SP);) + __ z_lg(Z_SP, Address(Z_thread, JavaThread::cont_entry_offset())); +#ifdef ASSERT + __ z_cg(Z_R1_scratch, _z_common_abi(callers_sp), Z_SP); + __ asm_assert(/* check_equal=*/ true, FILE_AND_LINE ": callers sp is corrupt at thaw entry", 69); +#endif + + } + +#ifdef ASSERT + __ z_cg(Z_SP, Address(Z_thread, JavaThread::cont_entry_offset())); + __ asm_assert(/* check_equal=*/ true, FILE_AND_LINE ": incorrect Z_SP", 70); +#endif + + __ z_lghi(Z_ARG2, return_barrier ? 1 : 0); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, Continuation::prepare_thaw), Z_thread, Z_ARG2); + +#ifdef ASSERT + __ z_cg(Z_SP, Address(Z_thread, JavaThread::cont_entry_offset())); + __ asm_assert(/* check equal = */ true, FILE_AND_LINE ": incorrect Z_SP after prepare_thaw", 48); +#endif // ASSERT + + // Z_RET contains the size of the frames to thaw, 0 if overflow or no more frames + NearLabel L_thaw_success; + __ z_ltgr(Z_RET, Z_RET); + __ branch_optimized(Assembler::bcondNotEqual, L_thaw_success); + __ load_const_optimized(Z_R1_scratch, (SharedRuntime::throw_StackOverflowError_entry())); + __ call(Z_R1_scratch); + __ bind(L_thaw_success); + + // Make room for the thawed frames and align the stack. + __ add64(Z_RET, frame::z_abi_160_size); + + { // stack alignment + __ z_lcgr(Z_RET, Z_RET); // negate Z_RET value + __ z_nill(Z_RET, -frame::alignment_in_bytes); + } + __ resize_frame( /* offset = */ Z_RET,/* fp = */ Z_R1, /* load_fp = */ true); + + __ z_lghi(Z_ARG2, kind); + __ add64(Z_SP, -frame::z_abi_160_size); // Register save area for Continuation::thaw + __ call_VM_leaf(Continuation::thaw_entry(), Z_thread, Z_ARG2); + __ z_lgr(Z_SP, Z_RET); // Z_RET contains the SP of the thawed top frame + + if (return_barrier) { + // we're now in the caller of the frame that returned to the barrier + // restore return value (no safepoint in the call to thaw, so even an oop return value should be OK) + + __ z_lgdr(Z_RET, Z_F8); // Restore integer return value + __ z_ldr(Z_FRET, Z_F9); // Restore float return value + } else { + // we're now on the yield frame (which is in an address above us b/c rsp has been pushed down) + __ z_lghi(Z_RET, 0); // return 0 (success) from doYield + } + + if (return_barrier_exception) { + Register handler = Z_R1_scratch; + __ z_lg(Z_ARG2, _z_common_abi(return_pc), Z_SP); // exception pc + __ save_return_pc(); + __ push_frame_abi160(0 + 2 * BytesPerWord); + __ z_stg(Z_RET , 0 * BytesPerWord + frame::z_abi_160_size, Z_SP); // save return value containing the exception oop + + __ z_stg(Z_ARG2, 1 * BytesPerWord + frame::z_abi_160_size, Z_SP); // save exception_pc + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), Z_thread, Z_ARG2); + + // Copy handler's address. + __ z_lgr(handler, Z_RET); + + // Set up the arguments for the exception handler: + // - Z_ARG1: exception oop + // - Z_ARG2: exception pc + __ z_lg(Z_ARG1, 0 * BytesPerWord + frame::z_abi_160_size, Z_SP); // load the exception oop + __ z_lg(Z_ARG2, 1 * BytesPerWord + frame::z_abi_160_size, Z_SP); // load the exception pc + __ pop_frame(); + __ restore_return_pc(); + } else { + // We're "returning" into the topmost thawed frame; see Thaw::push_return_frame + __ z_lg(Z_R1_scratch, _z_common_abi(return_pc), Z_SP); + } + __ z_br(Z_R1_scratch); + + return start; } address generate_cont_thaw() { - if (!Continuations::enabled()) return nullptr; - Unimplemented(); - return nullptr; + return generate_cont_thaw(StubId::stubgen_cont_thaw_id); } address generate_cont_returnBarrier() { - if (!Continuations::enabled()) return nullptr; - Unimplemented(); - return nullptr; + return generate_cont_thaw(StubId::stubgen_cont_returnBarrier_id); } address generate_cont_returnBarrier_exception() { + return generate_cont_thaw(StubId::stubgen_cont_returnBarrierExc_id); + } + + address generate_cont_preempt_stub() { if (!Continuations::enabled()) return nullptr; - Unimplemented(); - return nullptr; + StubId stub_id = StubId::stubgen_cont_preempt_id; + StubCodeMark mark(this, stub_id); + address start = __ pc(); + + __ clobber_nonvolatile_registers(); // Except Z_thread + + __ reset_last_Java_frame(/*check_last_java_sp=*/ false); + + // Set sp to enterSpecial frame, i.e. remove all frames copied into the heap. + __ z_lg(Z_SP, Address(Z_thread, JavaThread::cont_entry_offset())); + + Label preemption_cancelled; + + __ z_cli(in_bytes(JavaThread::preemption_cancelled_offset()), Z_thread, 0); + __ z_brne(preemption_cancelled); + + // Remove enterSpecial frame from the stack and return to Continuation.run() to unmount. + SharedRuntime::continuation_enter_cleanup(_masm); + __ pop_frame(); + __ restore_return_pc(); + __ z_br(Z_R14); + + // We acquired the monitor after freezing the frames so call thaw to continue execution. + __ bind(preemption_cancelled); + __ z_mvi(in_bytes(JavaThread::preemption_cancelled_offset()), Z_thread, 0); + + __ load_const_optimized(Z_R1, ContinuationEntry::thaw_call_pc_address()); + __ z_lg(Z_R1, Address(Z_R1)); + __ z_br(Z_R1); + + return start; } // exception handler for upcall stubs @@ -3327,9 +3484,10 @@ class StubGenerator: public StubCodeGenerator { if (!Continuations::enabled()) return; // Continuation stubs: - StubRoutines::_cont_thaw = generate_cont_thaw(); - StubRoutines::_cont_returnBarrier = generate_cont_returnBarrier(); + StubRoutines::_cont_thaw = generate_cont_thaw(); + StubRoutines::_cont_returnBarrier = generate_cont_returnBarrier(); StubRoutines::_cont_returnBarrierExc = generate_cont_returnBarrier_exception(); + StubRoutines::_cont_preempt_stub = generate_cont_preempt_stub(); } void generate_final_stubs() { diff --git a/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp b/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp index dba04fc0e85..03470597ab5 100644 --- a/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -717,13 +717,31 @@ address TemplateInterpreterGenerator::generate_safept_entry_for (TosState state, address runtime_entry) { address entry = __ pc(); __ push(state); + __ push_cont_fastpath(); __ call_VM(noreg, runtime_entry); + __ pop_cont_fastpath(); __ dispatch_via(vtos, Interpreter::_normal_table.table_for (vtos)); return entry; } address TemplateInterpreterGenerator::generate_cont_resume_interpreter_adapter() { - return nullptr; + if (!Continuations::enabled()) return nullptr; + address start = __ pc(); + __ z_lg(Z_fp, _z_common_abi(callers_sp), Z_SP); + { + Register top_frame_sp = Z_R1_scratch; // anyway going to load it with correct value + __ z_lg(top_frame_sp, Address(Z_fp, _z_ijava_state_neg(top_frame_sp))); + __ z_slag(top_frame_sp, top_frame_sp, Interpreter::logStackElementSize); + __ z_agr(top_frame_sp, Z_fp); + + __ resize_frame_absolute(top_frame_sp, /* temp = */ Z_R0, /* load_fp = */ true); + } + __ restore_bcp(); + __ restore_locals(); + __ restore_esp(); + + __ z_br(Z_R14); + return start; } @@ -1468,8 +1486,13 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { __ bind(call_signature_handler); + bool support_vthread_preemption = Continuations::enabled(); + // We have a TOP_IJAVA_FRAME here, which belongs to us. - __ set_top_ijava_frame_at_SP_as_last_Java_frame(Z_SP, Z_R1/*tmp*/); + Label last_java_pc; + Label *resume_pc = support_vthread_preemption ? &last_java_pc : nullptr; + + __ set_top_ijava_frame_at_SP_as_last_Java_frame(Z_SP, Z_R1/*tmp*/, resume_pc); // Call signature handler and pass locals address in Z_ARG1. __ z_lgr(Z_ARG1, Z_locals); @@ -1526,7 +1549,18 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { // overwritten since "__ call_stub(signature_handler);" (except for // ARG1 and ARG2 for static methods). + if (support_vthread_preemption) { + // Rresult_handler is a nonvolatile register. Its value will be preserved across + // the native call but only if the call isn't preempted. To preserve its value even + // in the case of preemption we save it in the lresult slot. It is restored at + // resume_pc if, and only if the call was preempted. This works because only + // j.l.Object::wait calls are preempted which don't return a result. + + __ z_stg(Rresult_handler, _z_ijava_state_neg(lresult), Z_fp); + } + __ push_cont_fastpath(); __ call_c(Z_R1/*native_method_entry*/); + __ pop_cont_fastpath(); // NOTE: frame::interpreter_frame_result() depends on these stores. __ z_stg(Z_RET, _z_ijava_state_neg(lresult), Z_fp); @@ -1610,6 +1644,32 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { __ z_lg(Z_bcp, Address(Rmethod, Method::const_offset())); // get constMethod __ add2reg(Z_bcp, in_bytes(ConstMethod::codes_offset())); // get codebase + if (support_vthread_preemption) { + // Check preemption for Object.wait() + Label not_preempted; + __ z_ltg(Z_R1_scratch, Address(Z_thread, JavaThread::preempt_alternate_return_offset())); + __ z_brz(not_preempted); // if 0, jump to not_preempted + __ z_mvghi(Address(Z_thread, JavaThread::preempt_alternate_return_offset()), 0); + __ z_br(Z_R1_scratch); + + // Execution will be resumed here when the vthread becomes runnable again. + __ bind(*resume_pc); + __ restore_after_resume(); + // We saved the result handler before the call + __ z_lg(Rresult_handler, _z_ijava_state_neg(lresult), Z_fp); +#ifdef ASSERT + // Clobber result slots. Only native methods returning void can be preemted currently. + __ load_const(Z_RET, UCONST64(0xbad01001)); + __ z_stg(Z_RET, _z_ijava_state_neg(lresult), Z_fp); + __ z_stg(Z_RET, _z_ijava_state_neg(fresult), Z_fp); + // reset_last_Java_frame() below asserts that a last java sp is set + __ asm_assert_mem8_is_zero(in_bytes(JavaThread::last_Java_sp_offset()), + Z_thread, FILE_AND_LINE ": Last java sp should not be set when resuming", 69); + __ z_stg(Z_RET, in_bytes(JavaThread::last_Java_sp_offset()), Z_thread); +#endif + __ bind(not_preempted); + } + if (CheckJNICalls) { // clear_pending_jni_exception_check __ clear_mem(Address(Z_thread, JavaThread::pending_jni_exception_check_fn_offset()), sizeof(oop)); @@ -2030,7 +2090,7 @@ address TemplateInterpreterGenerator::generate_CRC32C_updateBytes_entry(Abstract address TemplateInterpreterGenerator::generate_currentThread() { uint64_t entry_off = __ offset(); - __ z_lg(Z_RET, Address(Z_thread, JavaThread::threadObj_offset())); + __ z_lg(Z_RET, Address(Z_thread, JavaThread::vthread_offset())); __ resolve_oop_handle(Z_RET, Z_R0_scratch, Z_R1_scratch); // Restore caller sp for c2i case. @@ -2176,6 +2236,7 @@ void TemplateInterpreterGenerator::generate_throw_exception() { JavaThread::popframe_force_deopt_reexecution_bit, Z_tmp_1, false); + __ pop_cont_fastpath(); // Continue in deoptimization handler. __ z_br(Z_R14); @@ -2191,6 +2252,7 @@ void TemplateInterpreterGenerator::generate_throw_exception() { false, // install_monitor_exception false); // notify_jvmdi __ z_lg(Z_fp, _z_abi(callers_sp), Z_SP); // Restore frame pointer. + __ pop_cont_fastpath(); { Register top_frame_sp = Z_R1_scratch; __ z_lg(top_frame_sp, Address(Z_fp, _z_ijava_state_neg(top_frame_sp))); @@ -2264,6 +2326,7 @@ void TemplateInterpreterGenerator::generate_throw_exception() { // Remove the activation (without doing throws on illegalMonitorExceptions). __ remove_activation(vtos, noreg/*ret.pc already loaded*/, false/*throw exc*/, true/*install exc*/, false/*notify jvmti*/); __ z_lg(Z_fp, _z_abi(callers_sp), Z_SP); // Restore frame pointer. + __ pop_cont_fastpath(); __ get_vm_result_oop(Z_ARG1); // Restore exception. __ verify_oop(Z_ARG1); diff --git a/src/hotspot/cpu/s390/templateTable_s390.cpp b/src/hotspot/cpu/s390/templateTable_s390.cpp index 3b0929608a3..1da24c0378c 100644 --- a/src/hotspot/cpu/s390/templateTable_s390.cpp +++ b/src/hotspot/cpu/s390/templateTable_s390.cpp @@ -2336,7 +2336,9 @@ void TemplateTable::_return(TosState state) { __ z_tm(poll_byte_addr, SafepointMechanism::poll_bit()); __ z_braz(no_safepoint); __ push(state); + __ push_cont_fastpath(); __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)); + __ pop_cont_fastpath(); __ pop(state); __ bind(no_safepoint); } @@ -2395,7 +2397,7 @@ void TemplateTable::resolve_cache_and_index_for_method(int byte_no, // Class initialization barrier slow path lands here as well. address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_from_cache); __ load_const_optimized(Z_ARG2, (int)code); - __ call_VM(noreg, entry, Z_ARG2); + __ call_VM_preemptable(noreg, entry, Z_ARG2); // Update registers with resolved info. __ load_method_entry(Rcache, index); @@ -2445,7 +2447,7 @@ void TemplateTable::resolve_cache_and_index_for_field(int byte_no, // Class initialization barrier slow path lands here as well. address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_from_cache); __ load_const_optimized(Z_ARG2, (int)code); - __ call_VM(noreg, entry, Z_ARG2); + __ call_VM_preemptable(noreg, entry, Z_ARG2); // Update registers with resolved info. __ load_field_entry(cache, index); @@ -4022,7 +4024,7 @@ void TemplateTable::_new() { __ bind(slow_case); __ get_constant_pool(Z_ARG2); __ get_2_byte_integer_at_bcp(Z_ARG3/*dest*/, 1, InterpreterMacroAssembler::Unsigned); - call_VM(Z_tos, CAST_FROM_FN_PTR(address, InterpreterRuntime::_new), Z_ARG2, Z_ARG3); + __ call_VM_preemptable(Z_tos, CAST_FROM_FN_PTR(address, InterpreterRuntime::_new), Z_ARG2, Z_ARG3); __ verify_oop(Z_tos); // continue diff --git a/src/hotspot/cpu/s390/upcallLinker_s390.cpp b/src/hotspot/cpu/s390/upcallLinker_s390.cpp index 23ac80ddf48..de57e5e0cc4 100644 --- a/src/hotspot/cpu/s390/upcallLinker_s390.cpp +++ b/src/hotspot/cpu/s390/upcallLinker_s390.cpp @@ -220,9 +220,13 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Symbol* signature, __ call(call_target_address); // load taget Method* into Z_method __ block_comment("} load_target"); + __ push_cont_fastpath(); + __ z_lg(call_target_address, Address(Z_method, in_bytes(Method::from_compiled_offset()))); __ call(call_target_address); + __ pop_cont_fastpath(); + // return value shuffle assert(!needs_return_buffer, "unexpected needs_return_buffer"); // CallArranger can pick a return type that goes in the same reg for both CCs. diff --git a/src/hotspot/share/oops/stackChunkOop.inline.hpp b/src/hotspot/share/oops/stackChunkOop.inline.hpp index d0ddbe8dfe6..3ad28190d02 100644 --- a/src/hotspot/share/oops/stackChunkOop.inline.hpp +++ b/src/hotspot/share/oops/stackChunkOop.inline.hpp @@ -369,7 +369,7 @@ inline void stackChunkOopDesc::copy_from_stack_to_chunk(intptr_t* from, intptr_t assert(to >= start_address(), "Chunk underflow"); assert(to + size <= end_address(), "Chunk overflow"); -#if !(defined(AMD64) || defined(AARCH64) || defined(RISCV64) || defined(PPC64)) || defined(ZERO) +#if !(defined(AMD64) || defined(AARCH64) || defined(RISCV64) || defined(PPC64) || defined(S390)) || defined(ZERO) // Suppress compilation warning-as-error on unimplemented architectures // that stub out arch-specific methods. Some compilers are smart enough // to figure out the argument is always null and then warn about it. @@ -388,7 +388,7 @@ inline void stackChunkOopDesc::copy_from_chunk_to_stack(intptr_t* from, intptr_t assert(from >= start_address(), ""); assert(from + size <= end_address(), ""); -#if !(defined(AMD64) || defined(AARCH64) || defined(RISCV64) || defined(PPC64)) || defined(ZERO) +#if !(defined(AMD64) || defined(AARCH64) || defined(RISCV64) || defined(PPC64) || defined(S390)) || defined(ZERO) // Suppress compilation warning-as-error on unimplemented architectures // that stub out arch-specific methods. Some compilers are smart enough // to figure out the argument is always null and then warn about it. diff --git a/src/hotspot/share/runtime/continuation.cpp b/src/hotspot/share/runtime/continuation.cpp index f8af2545c37..e80720072c5 100644 --- a/src/hotspot/share/runtime/continuation.cpp +++ b/src/hotspot/share/runtime/continuation.cpp @@ -317,7 +317,7 @@ frame Continuation::continuation_parent_frame(RegisterMap* map) { map->set_stack_chunk(nullptr); -#if (defined(X86) || defined(AARCH64) || defined(RISCV64) || defined(PPC64)) && !defined(ZERO) +#if (defined(X86) || defined(AARCH64) || defined(RISCV64) || defined(PPC64) || defined(S390)) && !defined(ZERO) frame sender(cont.entrySP(), cont.entryFP(), cont.entryPC()); #else frame sender = frame(); diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp index d76652edf36..e9b6325d03b 100644 --- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp +++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp @@ -220,7 +220,6 @@ template static inline freeze_result freeze_inte static inline int prepare_thaw_internal(JavaThread* thread, bool return_barrier); template static inline intptr_t* thaw_internal(JavaThread* thread, const Continuation::thaw_kind kind); - // Entry point to freeze. Transitions are handled manually // Called from gen_continuation_yield() in sharedRuntime_.cpp through Continuation::freeze_entry(); template @@ -507,13 +506,7 @@ FreezeBase::FreezeBase(JavaThread* thread, ContinuationWrapper& cont, intptr_t* assert(!Interpreter::contains(_cont.entryPC()), ""); - _bottom_address = _cont.entrySP() - _cont.entry_frame_extension(); -#ifdef _LP64 - if (((intptr_t)_bottom_address & 0xf) != 0) { - _bottom_address--; - } - assert(is_aligned(_bottom_address, frame::frame_alignment), ""); -#endif + _bottom_address = align_down(_cont.entrySP() - _cont.entry_frame_extension(), frame::frame_alignment); log_develop_trace(continuations)("bottom_address: " INTPTR_FORMAT " entrySP: " INTPTR_FORMAT " argsize: " PTR_FORMAT, p2i(_bottom_address), p2i(_cont.entrySP()), (_cont.entrySP() - _bottom_address) << LogBytesPerWord); @@ -523,13 +516,17 @@ FreezeBase::FreezeBase(JavaThread* thread, ContinuationWrapper& cont, intptr_t* assert(_cont.chunk_invariant(), ""); assert(!Interpreter::contains(_cont.entryPC()), ""); -#if !defined(PPC64) || defined(ZERO) - static const int doYield_stub_frame_size = frame::metadata_words; -#else +#if defined(PPC64) && !defined(ZERO) static const int doYield_stub_frame_size = frame::native_abi_reg_args_size >> LogBytesPerWord; +#elif defined(S390) && !defined(ZERO) + static const int doYield_stub_frame_size = frame::z_abi_160_base_size >> LogBytesPerWord; +#else + static const int doYield_stub_frame_size = frame::metadata_words; #endif // With preemption doYield() might not have been resolved yet - assert(_preempt || SharedRuntime::cont_doYield_stub()->frame_size() == doYield_stub_frame_size, ""); + assert(_preempt || SharedRuntime::cont_doYield_stub()->frame_size() == doYield_stub_frame_size, + "_preempt = %d, cont_doYield_stub()->frame_size() = %d, doYield_stub_frame_size = %d", + (_preempt ? 1 : 0), SharedRuntime::cont_doYield_stub()->frame_size(), doYield_stub_frame_size); if (preempt) { _last_frame = _thread->last_frame(); @@ -2597,7 +2594,13 @@ inline void ThawBase::patch(frame& f, const frame& caller, bool bottom) { } else if (_should_patch_caller_pc) { // Caller was deoptimized during thaw but we've overwritten the return address when copying f from the heap. // Also, on some platforms, if the caller is interpreted but the callee not we also need to patch. - assert(caller.is_deoptimized_frame() PPC64_ONLY(|| caller.is_interpreted_frame()), ""); + +#if defined(PPC64) || defined(S390) + assert(caller.is_deoptimized_frame() || caller.is_interpreted_frame(), ""); +#else + assert(caller.is_deoptimized_frame(), ""); +#endif + ContinuationHelper::Frame::patch_pc(caller, caller.raw_pc()); _should_patch_caller_pc = false; } diff --git a/src/hotspot/share/runtime/frame.cpp b/src/hotspot/share/runtime/frame.cpp index ae04d398043..3e45b6fe310 100644 --- a/src/hotspot/share/runtime/frame.cpp +++ b/src/hotspot/share/runtime/frame.cpp @@ -1685,13 +1685,13 @@ void FrameValues::print_on(outputStream* st, int min_index, int max_index, intpt // 4. Recognize it as being part of the "fixed frame". if (*fv.location != 0 && *fv.location > -100 && *fv.location < 100 && fp != nullptr && *fv.description != '#' -#if !defined(PPC64) +#if !defined(PPC64) && !defined(S390) && (strncmp(fv.description, "interpreter_frame_", 18) == 0 || strstr(fv.description, " method ")) -#else // !defined(PPC64) +#else // !defined(PPC64) && !defined(S390) && (strcmp(fv.description, "sender_sp") == 0 || strcmp(fv.description, "top_frame_sp") == 0 || strcmp(fv.description, "esp") == 0 || strcmp(fv.description, "monitors") == 0 || strcmp(fv.description, "locals") == 0 || strstr(fv.description, " method ")) -#endif //!defined(PPC64) +#endif // !defined(PPC64) && !defined(S390) ) { st->print_cr(" " INTPTR_FORMAT ": " INTPTR_FORMAT " %-32s (relativized: fp%+d)", p2i(fv.location), p2i(&fp[*fv.location]), fv.description, (int)*fv.location); diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index 5489735da39..bcb7f5488f5 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -3104,14 +3104,16 @@ void AdapterHandlerLibrary::create_native_wrapper(const methodHandle& method) { struct { double data[20]; } locs_buf; struct { double data[20]; } stubs_locs_buf; buffer.insts()->initialize_shared_locs((relocInfo*)&locs_buf, sizeof(locs_buf) / sizeof(relocInfo)); -#if defined(AARCH64) || defined(PPC64) +#if defined(AARCH64) // On AArch64 with ZGC and nmethod entry barriers, we need all oops to be // in the constant pool to ensure ordering between the barrier and oops // accesses. For native_wrappers we need a constant. - // On PPC64 the continuation enter intrinsic needs the constant pool for the compiled + buffer.initialize_consts_size(8); +#elif defined(PPC64) || defined(S390) + // On PPC64/S390 the continuation enter intrinsic needs the constant pool for the compiled // static java call that is resolved in the runtime. - if (PPC64_ONLY(method->is_continuation_enter_intrinsic() &&) true) { - buffer.initialize_consts_size(8 PPC64_ONLY(+ 24)); + if (method->is_continuation_enter_intrinsic()) { + buffer.initialize_consts_size(8 PPC64_ONLY(+ 24) S390_ONLY(+ 17)); } #endif buffer.stubs()->initialize_shared_locs((relocInfo*)&stubs_locs_buf, sizeof(stubs_locs_buf) / sizeof(relocInfo)); diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index a9f70fc97a4..e0005bfde07 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -64,7 +64,6 @@ compiler/floatingpoint/TestSubnormalDouble.java 8317810 generic-i586 compiler/codecache/CodeCacheFullCountTest.java 8332954 generic-all compiler/interpreter/Test6833129.java 8335266 generic-i586 -compiler/intrinsics/TestReturnOopSetForJFRWriteCheckpoint.java 8286300 linux-s390x compiler/c2/aarch64/TestStaticCallStub.java 8359963 generic-aarch64 @@ -105,7 +104,6 @@ runtime/ErrorHandling/MachCodeFramesInErrorFile.java 8313315 linux-ppc64le runtime/NMT/VirtualAllocCommitMerge.java 8309698 linux-s390x runtime/Thread/TestAlwaysPreTouchStacks.java 8383372 macosx-aarch64 -applications/ctw/modules/jdk_jfr.java 8286300 linux-s390x applications/jcstress/copy.java 8229852 linux-all containers/docker/TestJFREvents.java 8327723 linux-x64 diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index fcde1d9c01d..8879aa2e5b6 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -612,24 +612,6 @@ jdk/incubator/vector/LoadJsvmlTest.java 8305390 windows- # jdk_jfr -jdk/jfr/api/consumer/TestRecordingFileWrite.java 8286300 linux-s390x -jdk/jfr/api/consumer/streaming/TestCrossProcessStreaming.java 8286300 linux-s390x -jdk/jfr/api/consumer/streaming/TestFilledChunks.java 8286300 linux-s390x -jdk/jfr/api/consumer/streaming/TestRemovedChunks.java 8286300 linux-s390x -jdk/jfr/api/recording/misc/TestGetStreamWithFailure.java 8286300 linux-s390x -jdk/jfr/api/settings/TestSettingControl.java 8286300 linux-s390x -jdk/jfr/event/runtime/TestBackToBackSensitive.java 8286300 linux-s390x -jdk/jfr/event/runtime/TestSyncOnValueBasedClassEvent.java 8286300 linux-s390x -jdk/jfr/event/tracing/TestMultipleThreads.java 8286300 linux-s390x -jdk/jfr/event/tracing/TestTracedString.java 8286300 linux-s390x -jdk/jfr/javaagent/TestLoadedAgent.java 8286300 linux-s390x -jdk/jfr/javaagent/TestPremainAgent.java 8286300 linux-s390x -jdk/jfr/jmx/streaming/TestClose.java 8286300 linux-s390x -jdk/jfr/jmx/streaming/TestMaxSize.java 8286300 linux-s390x -jdk/jfr/jvm/TestChunkIntegrity.java 8286300 linux-s390x -jdk/jfr/jvm/TestJFRIntrinsic.java 8286300 linux-s390x -jdk/jfr/tool/TestDisassemble.java 8286300 linux-s390x -jdk/jfr/tool/TestScrub.java 8286300 linux-s390x jdk/jfr/event/compiler/TestCodeSweeper.java 8338127 generic-all jdk/jfr/event/oldobject/TestShenandoah.java 8342951 generic-all jdk/jfr/event/runtime/TestResidentSetSizeEvent.java 8309846 aix-ppc64 diff --git a/test/jdk/java/util/concurrent/tck/JSR166TestCase.java b/test/jdk/java/util/concurrent/tck/JSR166TestCase.java index 641fbf2e495..f1f32bee310 100644 --- a/test/jdk/java/util/concurrent/tck/JSR166TestCase.java +++ b/test/jdk/java/util/concurrent/tck/JSR166TestCase.java @@ -37,9 +37,7 @@ /* * @test id=default * @summary Conformance testing variant of JSR-166 tck tests. - * @library /test/lib * @build * - * @build jdk.test.lib.Platform * @modules java.management java.base/jdk.internal.util * @run junit/othervm/timeout=1000 JSR166TestCase */ @@ -48,9 +46,7 @@ * @test id=forkjoinpool-common-parallelism * @summary Test implementation details variant of JSR-166 * tck tests with ForkJoinPool common parallelism. - * @library /test/lib * @build * - * @build jdk.test.lib.Platform * @modules java.management java.base/jdk.internal.util * @run junit/othervm/timeout=1000 * --add-opens java.base/java.util.concurrent=ALL-UNNAMED @@ -72,9 +68,7 @@ * @summary Remaining test implementation details variant of * JSR-166 tck tests apart from ForkJoinPool common * parallelism. - * @library /test/lib * @build * - * @build jdk.test.lib.Platform * @modules java.management java.base/jdk.internal.util * @run junit/othervm/timeout=1000 * --add-opens java.base/java.util.concurrent=ALL-UNNAMED @@ -141,7 +135,6 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Pattern; -import jdk.test.lib.Platform; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestResult; @@ -631,13 +624,6 @@ public static Test suite() { "SynchronousQueue20Test", "ReentrantReadWriteLock20Test" }; - - if (Platform.isS390x()) { - java20TestClassNames = new String[] { - "ForkJoinPool20Test", - }; - } - addNamedTestClasses(suite, java20TestClassNames); } From ab116d00a88046d662210539b4bc12db3a364c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Thu, 9 Jul 2026 13:45:41 +0000 Subject: [PATCH 189/707] 8385945: Deprecate the CompilationMode flag Reviewed-by: dholmes, ayang --- src/hotspot/share/compiler/compiler_globals.hpp | 2 +- src/hotspot/share/runtime/arguments.cpp | 1 + .../hotspot/jtreg/runtime/CommandLine/VMDeprecatedOptions.java | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/compiler/compiler_globals.hpp b/src/hotspot/share/compiler/compiler_globals.hpp index e1f8d9f8922..effe6cc0725 100644 --- a/src/hotspot/share/compiler/compiler_globals.hpp +++ b/src/hotspot/share/compiler/compiler_globals.hpp @@ -273,7 +273,7 @@ "mode if posssible") \ \ product(ccstr, CompilationMode, "default", \ - "Compilation modes: " \ + "(Deprecated) Compilation modes: " \ "default: normal tiered compilation; " \ "quick-only: C1-only mode; " \ "high-only: C2-only mode.") \ diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 2804224ed01..269a8b39e6b 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -529,6 +529,7 @@ static SpecialFlag const special_jvm_flags[] = { { "DynamicDumpSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() }, { "RequireSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() }, { "UseSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() }, + { "CompilationMode", JDK_Version::jdk(28), JDK_Version::jdk(29), JDK_Version::jdk(30)}, // --- Deprecated alias flags (see also aliased_jvm_flags) - sorted by obsolete_in then expired_in: { "CreateMinidumpOnCrash", JDK_Version::jdk(9), JDK_Version::undefined(), JDK_Version::undefined() }, { "InitiatingHeapOccupancyPercent", JDK_Version::jdk(27), JDK_Version::jdk(28), JDK_Version::jdk(29) }, diff --git a/test/hotspot/jtreg/runtime/CommandLine/VMDeprecatedOptions.java b/test/hotspot/jtreg/runtime/CommandLine/VMDeprecatedOptions.java index 99c2d27f8d3..8c530936065 100644 --- a/test/hotspot/jtreg/runtime/CommandLine/VMDeprecatedOptions.java +++ b/test/hotspot/jtreg/runtime/CommandLine/VMDeprecatedOptions.java @@ -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 @@ -58,6 +58,7 @@ public class VMDeprecatedOptions { // { , } // deprecated non-alias flags: {"AllowRedefinitionToAddDeleteMethods", "true"}, + {"CompilationMode", "default"}, // deprecated alias flags (see also aliased_jvm_flags): {"CreateMinidumpOnCrash", "false"} From 095cf06e734dad42ffec82292f23cf85bcfae62e Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Thu, 9 Jul 2026 16:02:22 +0000 Subject: [PATCH 190/707] 8387404: Make ClassLoaderData::oops_do inlineable Reviewed-by: coleenp, xpeng --- .../share/classfile/classLoaderData.cpp | 19 +------------------ .../share/classfile/classLoaderData.hpp | 5 +++-- .../classfile/classLoaderData.inline.hpp | 19 +++++++++++++++++++ 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/hotspot/share/classfile/classLoaderData.cpp b/src/hotspot/share/classfile/classLoaderData.cpp index d1ea9c09d4c..95e1ef3b877 100644 --- a/src/hotspot/share/classfile/classLoaderData.cpp +++ b/src/hotspot/share/classfile/classLoaderData.cpp @@ -289,19 +289,6 @@ void ClassLoaderData::verify_not_claimed(int claim) { } #endif -bool ClassLoaderData::try_claim(int claim) { - for (;;) { - int old_claim = AtomicAccess::load(&_claim); - if ((old_claim & claim) == claim) { - return false; - } - int new_claim = old_claim | claim; - if (AtomicAccess::cmpxchg(&_claim, old_claim, new_claim) == old_claim) { - return true; - } - } -} - void ClassLoaderData::demote_strong_roots() { // The oop handle area contains strong roots that the GC traces from. We are about // to demote them to strong native oops that the GC does *not* trace from. Conceptually, @@ -369,11 +356,7 @@ void ClassLoaderData::dec_keep_alive_ref_count() { } } -void ClassLoaderData::oops_do(OopClosure* f, int claim_value, bool clear_mod_oops) { - if (claim_value != ClassLoaderData::_claim_none && !try_claim(claim_value)) { - return; - } - +void ClassLoaderData::oops_do_slow(OopClosure* f, bool clear_mod_oops) { // Only clear modified_oops after the ClassLoaderData is claimed. if (clear_mod_oops) { clear_modified_oops(); diff --git a/src/hotspot/share/classfile/classLoaderData.hpp b/src/hotspot/share/classfile/classLoaderData.hpp index 64fcfb7519f..3a0a05126af 100644 --- a/src/hotspot/share/classfile/classLoaderData.hpp +++ b/src/hotspot/share/classfile/classLoaderData.hpp @@ -242,7 +242,7 @@ class ClassLoaderData : public CHeapObj { void verify_not_claimed(int claim) NOT_DEBUG_RETURN; bool claimed() const { return _claim != 0; } bool claimed(int claim) const { return (_claim & claim) == claim; } - bool try_claim(int claim); + inline bool try_claim(int claim); // Computes if the CLD is alive or not. This is safe to call in concurrent // contexts. @@ -305,7 +305,8 @@ class ClassLoaderData : public CHeapObj { void initialize_holder(Handle holder); - void oops_do(OopClosure* f, int claim_value, bool clear_modified_oops = false); + inline void oops_do(OopClosure* f, int claim_value, bool clear_modified_oops = false); + void oops_do_slow(OopClosure* f, bool clear_modified_oops); void classes_do(KlassClosure* klass_closure); Klass* klasses() { return _klasses; } diff --git a/src/hotspot/share/classfile/classLoaderData.inline.hpp b/src/hotspot/share/classfile/classLoaderData.inline.hpp index 4c4427b19e1..df29dca053b 100644 --- a/src/hotspot/share/classfile/classLoaderData.inline.hpp +++ b/src/hotspot/share/classfile/classLoaderData.inline.hpp @@ -85,4 +85,23 @@ inline ClassLoaderData* ClassLoaderData::class_loader_data(oop loader) { return loader_data; } +inline bool ClassLoaderData::try_claim(int claim) { + for (;;) { + int old_claim = AtomicAccess::load(&_claim); + if ((old_claim & claim) == claim) { + return false; + } + int new_claim = old_claim | claim; + if (AtomicAccess::cmpxchg(&_claim, old_claim, new_claim) == old_claim) { + return true; + } + } +} + +inline void ClassLoaderData::oops_do(OopClosure* f, int claim_value, bool clear_mod_oops) { + if (claim_value == _claim_none || try_claim(claim_value)) { + oops_do_slow(f, clear_mod_oops); + } +} + #endif // SHARE_CLASSFILE_CLASSLOADERDATA_INLINE_HPP From a230a6099e2ec1b2f8f35515964c8ccb7c1523ec Mon Sep 17 00:00:00 2001 From: Naoto Sato Date: Thu, 9 Jul 2026 16:28:56 +0000 Subject: [PATCH 191/707] 8387795: Remove hard coded set of locales in LocaleData Reviewed-by: jlu --- .../ResourceBundleGenerator.java | 40 +++++++++++++++++-- .../util/cldr/CLDRLocaleProviderAdapter.java | 6 ++- .../provider/JRELocaleProviderAdapter.java | 4 ++ .../provider/ResourceBundleBasedAdapter.java | 12 +++++- .../sun/util/resources/LocaleData.java | 21 +++++----- 5 files changed, 66 insertions(+), 17 deletions(-) diff --git a/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java b/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java index 84657ae94f0..8e9635ab519 100644 --- a/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java +++ b/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java @@ -29,6 +29,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.Arrays; +import java.util.Comparator; import java.util.Formatter; import java.util.HashSet; import java.util.HashMap; @@ -39,6 +40,7 @@ import java.util.Set; import java.util.SortedSet; import java.util.stream.Collectors; +import static java.util.ResourceBundle.Control; class ResourceBundleGenerator implements BundleGenerator { // preferred timezones - keeping compatibility with JDK1.1 3 letter abbreviations @@ -69,6 +71,9 @@ class ResourceBundleGenerator implements BundleGenerator { // For duplicated values private static final String META_VALUE_PREFIX = "metaValue_"; + // locales in the base module + private final Set baseModuleLocales = new HashSet<>(); + @Override public void generateBundle(String packageName, String baseName, String localeID, Map map, BundleType type) throws IOException { @@ -80,8 +85,15 @@ public void generateBundle(String packageName, String baseName, String localeID, return; } - // Assume that non-base resources go into jdk.localedata - if (!CLDRConverter.isBaseModule) { + if (CLDRConverter.isBaseModule) { + if (!localeID.equals("root")) { + baseModuleLocales.addAll( + Control.getControl(Control.FORMAT_DEFAULT) + .getCandidateLocales("", + Locale.forLanguageTag(CLDRConverter.toLanguageTag(localeID)))); + } + } else { + // Assume that non-base resources go into jdk.localedata dirName = dirName + File.separator + "ext"; packageName = packageName + ".ext"; } @@ -284,6 +296,7 @@ public void generateMetaInfo(Map> metaInfo) throws IOE import java.util.HashMap; import java.util.Locale; import java.util.Map; + import java.util.Set; import sun.util.locale.provider.LocaleDataMetaInfo; import sun.util.locale.provider.LocaleProviderAdapter; @@ -296,6 +309,7 @@ public class %s implements LocaleDataMetaInfo { out.printf(""" private static final Map parentLocalesMap = HashMap.newHashMap(%d); private static final Map languageAliasMap = HashMap.newHashMap(%d); + private static final Set baseModuleLocales; static final boolean nonlikelyScript = %s; // package access from CLDRLocaleProviderAdapter static { @@ -322,7 +336,23 @@ public class %s implements LocaleDataMetaInfo { CLDRConverter.handlerSupplMeta.getLanguageAliasData().forEach((key, value) -> { out.printf(" languageAliasMap.put(\"%s\", \"%s\");\n", CLDRConverter.escape(key), CLDRConverter.escape(value)); }); - out.printf(" }\n\n"); + out.println(); + + // for baseModuleLocales + out.printf(" baseModuleLocales = Set.of(\n"); + out.printf(" %s", + baseModuleLocales.stream() + .map(Locale::toLanguageTag) + .sorted(Comparator.comparing(l -> l.equals("und") ? "" : l)) + .map(l -> switch(l) { + case "und" -> "Locale.ROOT"; + case "en" -> "Locale.ENGLISH"; + case "en-US" -> "Locale.US"; + default -> "Locale.forLanguageTag(\"" + l + "\")"; + }) + .collect(Collectors.joining(",\n "))); + out.printf("\n );"); + out.println("\n }\n"); // end of static initializer block. @@ -391,6 +421,10 @@ public Map parentLocales() { return parentLocalesMap; } + public Set baseModuleLocales() { + return baseModuleLocales; + } + // package access from CLDRLocaleProviderAdapter Map likelyScriptMap() { return CLDRMapHolder.likelyScriptMap; diff --git a/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java b/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java index 573187ba3d0..1e80bce3839 100644 --- a/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java +++ b/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -288,6 +288,10 @@ public boolean isSupportedProviderLocale(Locale locale, Set langtags) { || langtags.contains(getEquivalentLoc(locale).toLanguageTag()); } + public Set baseModuleLocales() { + return baseMetaInfo.baseModuleLocales(); + } + /** * Returns the canonical ID for the given ID */ diff --git a/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java b/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java index 7b8b3b06eb3..2d6d95b509f 100644 --- a/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java +++ b/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java @@ -488,4 +488,8 @@ public boolean isSupportedProviderLocale(Locale locale, Set langtags) { "th-TH-TH".equals(oldname) || "no-NO-NY".equals(oldname); } + + public Set baseModuleLocales() { + return Set.of(Locale.ROOT); + } } diff --git a/src/java.base/share/classes/sun/util/locale/provider/ResourceBundleBasedAdapter.java b/src/java.base/share/classes/sun/util/locale/provider/ResourceBundleBasedAdapter.java index 613b1ee5158..48d9b832b13 100644 --- a/src/java.base/share/classes/sun/util/locale/provider/ResourceBundleBasedAdapter.java +++ b/src/java.base/share/classes/sun/util/locale/provider/ResourceBundleBasedAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -27,6 +27,8 @@ import java.util.List; import java.util.Locale; +import java.util.Set; + import sun.util.resources.LocaleData; /** @@ -40,5 +42,11 @@ public interface ResourceBundleBasedAdapter { /** * candidate locales customization */ - public List getCandidateLocales(String baseName, Locale locale); + List getCandidateLocales(String baseName, Locale locale); + + /** + * Returns the locales whose resource bundles are resolved from + * the java.base module for this adapter. + */ + Set baseModuleLocales(); } diff --git a/src/java.base/share/classes/sun/util/resources/LocaleData.java b/src/java.base/share/classes/sun/util/resources/LocaleData.java index 20e8e0f8fe9..884f9610ca7 100644 --- a/src/java.base/share/classes/sun/util/resources/LocaleData.java +++ b/src/java.base/share/classes/sun/util/resources/LocaleData.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -45,7 +45,6 @@ import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.spi.ResourceBundleProvider; import sun.util.locale.provider.JRELocaleProviderAdapter; @@ -180,9 +179,6 @@ protected String toOtherBundleName(String baseName, String bundleName, Locale lo private static class LocaleDataStrategy implements Bundles.Strategy { private static final LocaleDataStrategy INSTANCE = new LocaleDataStrategy(); - // TODO: avoid hard-coded Locales - private static final Set JAVA_BASE_LOCALES - = Set.of(Locale.ROOT, Locale.ENGLISH, Locale.US, Locale.of("en", "US", "POSIX")); private LocaleDataStrategy() { } @@ -202,11 +198,8 @@ public List getCandidateLocales(String baseName, Locale locale) { String key = baseName + '-' + locale.toLanguageTag(); List candidates = CANDIDATES_MAP.get(key); if (candidates == null) { - LocaleProviderAdapter.Type type = baseName.contains(DOTCLDR) ? CLDR : JRE; - LocaleProviderAdapter adapter = LocaleProviderAdapter.forType(type); - candidates = adapter instanceof ResourceBundleBasedAdapter rbba ? - rbba.getCandidateLocales(baseName, locale) : - defaultControl.getCandidateLocales(baseName, locale); + var adapter = getAdapter(baseName); + candidates = adapter.getCandidateLocales(baseName, locale); // Weed out Locales which are known to have no resource bundles int lastDot = baseName.lastIndexOf('.'); @@ -227,7 +220,13 @@ public List getCandidateLocales(String baseName, Locale locale) { } boolean inJavaBaseModule(String baseName, Locale locale) { - return JAVA_BASE_LOCALES.contains(locale); + return getAdapter(baseName).baseModuleLocales().contains(locale); + } + + private static ResourceBundleBasedAdapter getAdapter(String baseName) { + return (ResourceBundleBasedAdapter)(baseName.contains(DOTCLDR) ? + LocaleProviderAdapter.forType(CLDR) : + LocaleProviderAdapter.forType(JRE)); } @Override From 7544c91a81858b8738e06a611f93bd95a7a8197c Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Thu, 9 Jul 2026 16:46:29 +0000 Subject: [PATCH 192/707] 8387961: Shenandoah: Rework marked objects loop prefetch Reviewed-by: xpeng, wkemper, kdnilsen --- .../gc/shenandoah/shenandoahHeap.inline.hpp | 88 ++++++------------- .../gc/shenandoah/shenandoah_globals.hpp | 5 -- 2 files changed, 27 insertions(+), 66 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp index 69eaf1589d2..b3c847cadaf 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp @@ -45,6 +45,7 @@ #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegionSet.inline.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" +#include "gc/shenandoah/shenandoahPrefetch.inline.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" #include "gc/shenandoah/shenandoahWorkGroup.hpp" #include "oops/compressedOops.inline.hpp" @@ -515,74 +516,35 @@ inline void ShenandoahHeap::marked_object_iterate(ShenandoahHeapRegion* region, template inline void ShenandoahHeap::marked_object_iterate(ShenandoahHeapRegion* region, T* cl, HeapWord* limit) { - assert(! region->is_humongous_continuation(), "no humongous continuation regions here"); + assert(!region->is_humongous_continuation(), "no humongous continuation regions here"); + assert(limit <= region->top(), "sanity"); ShenandoahMarkingContext* const ctx = marking_context(); HeapWord* tams = ctx->top_at_mark_start(region); - - size_t skip_bitmap_delta = 1; - HeapWord* start = region->bottom(); - HeapWord* end = MIN2(tams, region->end()); - - // Step 1. Scan below the TAMS based on bitmap data. HeapWord* limit_bitmap = MIN2(limit, tams); + // Step 1. Scan below the TAMS based on bitmap data. // Try to scan the initial candidate. If the candidate is above the TAMS, it would // fail the subsequent "< limit_bitmap" checks, and fall through to Step 2. - HeapWord* cb = ctx->get_next_marked_addr(start, end); - - intx dist = ShenandoahMarkScanPrefetch; - if (dist > 0) { - // Batched scan that prefetches the oop data, anticipating the access to - // either header, oop field, or forwarding pointer. Not that we cannot - // touch anything in oop, while it still being prefetched to get enough - // time for prefetch to work. This is why we try to scan the bitmap linearly, - // disregarding the object size. However, since we know forwarding pointer - // precedes the object, we can skip over it. Once we cannot trust the bitmap, - // there is no point for prefetching the oop contents, as oop->size() will - // touch it prematurely. - - // No variable-length arrays in standard C++, have enough slots to fit - // the prefetch distance. - static const int SLOT_COUNT = 256; - guarantee(dist <= SLOT_COUNT, "adjust slot count"); - HeapWord* slots[SLOT_COUNT]; - - int avail; - do { - avail = 0; - for (int c = 0; (c < dist) && (cb < limit_bitmap); c++) { - Prefetch::read(cb, oopDesc::mark_offset_in_bytes()); - slots[avail++] = cb; - cb += skip_bitmap_delta; - if (cb < limit_bitmap) { - cb = ctx->get_next_marked_addr(cb, limit_bitmap); - } - } - - for (int c = 0; c < avail; c++) { - assert (slots[c] < tams, "only objects below TAMS here: " PTR_FORMAT " (" PTR_FORMAT ")", p2i(slots[c]), p2i(tams)); - assert (slots[c] < limit, "only objects below limit here: " PTR_FORMAT " (" PTR_FORMAT ")", p2i(slots[c]), p2i(limit)); - oop obj = cast_to_oop(slots[c]); - assert(oopDesc::is_oop(obj), "sanity"); - assert(ctx->is_marked(obj), "object expected to be marked"); - cl->do_object(obj); - } - } while (avail > 0); - } else { - while (cb < limit_bitmap) { - assert (cb < tams, "only objects below TAMS here: " PTR_FORMAT " (" PTR_FORMAT ")", p2i(cb), p2i(tams)); - assert (cb < limit, "only objects below limit here: " PTR_FORMAT " (" PTR_FORMAT ")", p2i(cb), p2i(limit)); - oop obj = cast_to_oop(cb); - assert(oopDesc::is_oop(obj), "sanity"); - assert(ctx->is_marked(obj), "object expected to be marked"); - cl->do_object(obj); - cb += skip_bitmap_delta; - if (cb < limit_bitmap) { - cb = ctx->get_next_marked_addr(cb, limit_bitmap); - } + HeapWord* cb = ctx->get_next_marked_addr(region->bottom(), limit_bitmap); + while (cb < limit_bitmap) { + assert (cb < tams, "only objects below TAMS here: " PTR_FORMAT " (" PTR_FORMAT ")", p2i(cb), p2i(tams)); + assert (cb < limit, "only objects below limit here: " PTR_FORMAT " (" PTR_FORMAT ")", p2i(cb), p2i(limit)); + oop obj = cast_to_oop(cb); + assert(oopDesc::is_oop(obj), "sanity"); + assert(ctx->is_marked(obj), "object expected to be marked"); + + // Compute the next object address and initiate prefetches for it, + // while we are processing current object. + constexpr size_t skip_bitmap_delta = 1; + cb += skip_bitmap_delta; + if (cb < limit_bitmap) { + cb = ctx->get_next_marked_addr(cb, limit_bitmap); } + ShenandoahPrefetch::prefetch(cast_to_oop(cb)); + + cl->do_object(obj); } // Step 2. Accurate size-based traversal, happens past the TAMS. @@ -595,9 +557,13 @@ inline void ShenandoahHeap::marked_object_iterate(ShenandoahHeapRegion* region, oop obj = cast_to_oop(cs); assert(oopDesc::is_oop(obj), "sanity"); assert(ctx->is_marked(obj), "object expected to be marked"); - size_t size = ShenandoahForwarding::size(obj); + + // Compute the next object address and initiate prefetches for it, + // while we are processing current object. + cs += ShenandoahForwarding::size(obj); + ShenandoahPrefetch::prefetch(cast_to_oop(cs)); + cl->do_object(obj); - cs += size; } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp index 793b2f3b6d1..d76348b030a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp @@ -476,11 +476,6 @@ "evacuated.") \ range(0, 100) \ \ - product(intx, ShenandoahMarkScanPrefetch, 32, EXPERIMENTAL, \ - "How many objects to prefetch ahead when traversing mark bitmaps."\ - "Set to 0 to disable prefetching.") \ - range(0, 256) \ - \ product(uintx, ShenandoahMarkLoopStride, 1000, EXPERIMENTAL, \ "How many items to process during one marking iteration before " \ "checking for cancellation, yielding, etc. Larger values improve "\ From 4d3723b802bf582572e9d71d1ff1660e68637e04 Mon Sep 17 00:00:00 2001 From: Vladimir Ivanov Date: Thu, 9 Jul 2026 16:59:16 +0000 Subject: [PATCH 193/707] 8382536: C2: sharpen_type_after_if: assert(val->find_edge(con) > 0) failed: mismatch Reviewed-by: chagedorn, mchevalier --- src/hotspot/share/opto/parse2.cpp | 34 ++++++++----- .../types/TestSubTypeCheckConstantCastII.java | 51 +++++++++++++++++++ 2 files changed, 72 insertions(+), 13 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/types/TestSubTypeCheckConstantCastII.java diff --git a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp index 8ac4cf47558..9cb20cfcd00 100644 --- a/src/hotspot/share/opto/parse2.cpp +++ b/src/hotspot/share/opto/parse2.cpp @@ -1749,6 +1749,10 @@ static bool match_type_check(PhaseGVN& gvn, Node* con, const Type* tcon, Node* val, const Type* tval, Node** obj, const TypeOopPtr** cast_type) { // out-parameters + assert(tcon->singleton(), "not a constant: %s", Type::str(tcon)); + assert(tcon == gvn.type(con), "mismatch: %s != %s", Type::str(tcon), Type::str(gvn.type(con))); + assert(tval == gvn.type(val), "mismatch: %s != %s", Type::str(tval), Type::str(gvn.type(val))); + // Look for opportunities to sharpen the type of a node whose klass is compared with a constant klass. // The constant klass being tested against can come from many bytecode instructions (implicitly or explicitly), // and also from profile data used by speculative casts. @@ -1783,14 +1787,14 @@ static bool match_type_check(PhaseGVN& gvn, // Region // \ ConI ConI // \ | / - // val -> Phi ConI <- con - // \ / - // CmpI - // | - // Bool [btest] - // | + // val -> Phi ConI|CastII <- con + // \ / + // CmpI + // | + // Bool [btest] + // | // - if (tval->isa_int() && val->is_Phi() && val->in(0)->as_Region()->is_diamond()) { + if (tcon->isa_int() && val->is_Phi() && val->in(0)->as_Region()->is_diamond()) { RegionNode* diamond = val->in(0)->as_Region(); IfNode* if1 = diamond->in(1)->in(0)->as_If(); BoolNode* b1 = if1->in(1)->isa_Bool(); @@ -1799,12 +1803,16 @@ static bool match_type_check(PhaseGVN& gvn, b1->_test._test == BoolTest::ne, "%d", b1->_test._test); ProjNode* success_proj = if1->proj_out(b1->_test._test == BoolTest::eq ? 1 : 0); - int idx = diamond->find_edge(success_proj); - assert(idx == 1 || idx == 2, ""); - Node* vcon = val->in(idx); - - if ((btest == BoolTest::eq && vcon == con) || (btest == BoolTest::ne && vcon != con)) { - assert(val->find_edge(con) > 0, "mismatch"); + int success_idx = diamond->find_edge(success_proj); + assert(success_idx == 1 || success_idx == 2, ""); + assert(val->req() == 3, "not a diamond"); + + // gen_instanceof() emits 1 on success and 0 on failure. + // Check whether current comparison selects the success value. + const Type* success_tval = gvn.type(val->in(success_idx)); + assert(success_tval->isa_int(), "not an int: %s", Type::str(success_tval)); + if ((btest == BoolTest::eq && tcon == success_tval) || + (btest == BoolTest::ne && tcon->join(success_tval)->empty())) { SubTypeCheckNode* sub = b1->in(1)->as_SubTypeCheck(); Node* obj_or_subklass = sub->in(SubTypeCheckNode::ObjOrSubKlass); Node* superklass = sub->in(SubTypeCheckNode::SuperKlass); diff --git a/test/hotspot/jtreg/compiler/types/TestSubTypeCheckConstantCastII.java b/test/hotspot/jtreg/compiler/types/TestSubTypeCheckConstantCastII.java new file mode 100644 index 00000000000..d3ce317ed30 --- /dev/null +++ b/test/hotspot/jtreg/compiler/types/TestSubTypeCheckConstantCastII.java @@ -0,0 +1,51 @@ +/* + * 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 8382536 + * @summary C2: sharpen_type_after_if: assert(val->find_edge(con) > 0) failed: mismatch + * + * @run main/othervm -Xcomp -XX:CompileCommand=compileonly,${test.main.class}::test ${test.main.class} + */ +package compiler.types; + +public class TestSubTypeCheckConstantCastII { + static class A {} + + static boolean isInstanceOfA(Object obj) { + return (obj instanceof A); + } + + static void test(boolean b, Object obj) { + if (b) { + return; + } + // b == false + if (b != isInstanceOfA(obj)) {} + } + + public static void main(String[] args) { + test(true, new A()); + } +} From cd7b5fc7a5c294c8572f644d4bbf7451f8cfbec2 Mon Sep 17 00:00:00 2001 From: Patrick Fontanilla Date: Thu, 9 Jul 2026 22:34:25 +0000 Subject: [PATCH 194/707] 8386872: Test gc/shenandoah/generational/TestOldGrowthTriggers still fails intermittently Reviewed-by: wkemper, kdnilsen --- .../generational/TestOldGrowthTriggers.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/test/hotspot/jtreg/gc/shenandoah/generational/TestOldGrowthTriggers.java b/test/hotspot/jtreg/gc/shenandoah/generational/TestOldGrowthTriggers.java index 2af784fd034..fe3c8a5a476 100644 --- a/test/hotspot/jtreg/gc/shenandoah/generational/TestOldGrowthTriggers.java +++ b/test/hotspot/jtreg/gc/shenandoah/generational/TestOldGrowthTriggers.java @@ -41,9 +41,11 @@ public class TestOldGrowthTriggers { public static void makeOldAllocations() { - // Expect most of the BitSet entries placed into array to be promoted, and most will eventually become garbage within old + // Keep the majority of BitSet entries (5/8, 960) long-lived so they promote and grow old generation + // well past the old GC trigger threshold. A smaller long-lived set can fall just short and only + // intermittently trigger an old GC, so don't reduce the array size or the promoted fraction. - final int ArraySize = 1024; // 1K entries + final int ArraySize = 1536; // 1536 entries (1024 + 512) final int RefillIterations = 128; BitSet[] array = new BitSet[ArraySize]; @@ -57,8 +59,10 @@ public static void makeOldAllocations() { int replaceIndex = i; int deriveIndex = i-1; + // 3/8 entries are replaced each pass to trigger young gcs. + // 5/8 entries are never touched, so they age each cycle. switch (i & 0x7) { - case 0,1,2 -> { + case 0,1 -> { // creates new BitSet, releases old BitSet, // create ephemeral data while computing BitSet result = (BitSet) array[deriveIndex].clone(); @@ -67,12 +71,12 @@ public static void makeOldAllocations() { } array[replaceIndex] = result; } - case 3,4 -> { + case 2 -> { // creates new BitSet, releases old BitSet BitSet result = (BitSet) array[deriveIndex].clone(); array[replaceIndex] = result; } - case 5,6,7 -> { + default -> { // do nothing, let all objects in the array age to increase pressure on old generation } } @@ -110,6 +114,8 @@ public static void main(String[] args) throws Exception { "-XX:ShenandoahMinOldGenGrowthRemainingHeapPercent=100", "-XX:ShenandoahGuaranteedYoungGCInterval=0", "-XX:ShenandoahGuaranteedOldGCInterval=0", + "-XX:ShenandoahGenerationalMinTenuringAge=2", + "-XX:ShenandoahGenerationalMaxTenuringAge=2", "-XX:-UseCompactObjectHeaders" ); @@ -127,6 +133,8 @@ public static void main(String[] args) throws Exception { "-XX:ShenandoahMinOldGenGrowthRemainingHeapPercent=100", "-XX:ShenandoahGuaranteedYoungGCInterval=0", "-XX:ShenandoahGuaranteedOldGCInterval=0", + "-XX:ShenandoahGenerationalMinTenuringAge=2", + "-XX:ShenandoahGenerationalMaxTenuringAge=2", "-XX:+UseCompactObjectHeaders" ); } From 05be7e5439ceee17419ede343799dd4367b34fc5 Mon Sep 17 00:00:00 2001 From: Xiaolong Peng Date: Fri, 10 Jul 2026 02:40:40 +0000 Subject: [PATCH 195/707] 8387806: Shenandoah: Reduce allocation-path contention from ShenandoahAllocRate byte accounting Reviewed-by: shade, wkemper, kdnilsen --- .../gc/shenandoah/shenandoahAllocRate.hpp | 51 +++++-- .../shenandoah/shenandoahAllocRate.inline.hpp | 57 +++++--- .../shenandoah/shenandoahStripedCounter.cpp | 38 ++++++ .../shenandoah/shenandoahStripedCounter.hpp | 79 +++++++++++ .../shenandoahStripedCounter.inline.hpp | 74 ++++++++++ .../test_shenandoahAllocationRate.cpp | 128 ++++++++++++++++++ .../test_shenandoahStripedCounter.cpp | 118 ++++++++++++++++ 7 files changed, 512 insertions(+), 33 deletions(-) create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahStripedCounter.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahStripedCounter.hpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahStripedCounter.inline.hpp create mode 100644 test/hotspot/gtest/gc/shenandoah/test_shenandoahStripedCounter.cpp diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAllocRate.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAllocRate.hpp index 24221e504fd..ca94b91200a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAllocRate.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAllocRate.hpp @@ -25,7 +25,7 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHALLOCRATE_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHALLOCRATE_HPP -#include "gc/shenandoah/shenandoahPadding.hpp" +#include "gc/shenandoah/shenandoahStripedCounter.hpp" #include "gc/shenandoah/shenandoahWeightedSeq.hpp" #include "runtime/atomic.hpp" #include "runtime/mutex.hpp" @@ -111,12 +111,26 @@ class ShenandoahAllocRate { static constexpr size_t ALLOC_SAMPLE_MAX = G; PaddedMonitor _sample_lock; - shenandoah_padding(0); - Atomic _allocated_bytes_since_last_sample; - shenandoah_padding(1); - Atomic _minimum_sample_size; // bytes, read by mutator, updated by gc + ShenandoahStripedCounter _unsampled; + // Packed minimum_sample_size and log_per_stripe_threshold for one alloc-path load. + Atomic _sample_params; jlong _last_sample_time; + static uint64_t encode_sample_params(const uint32_t minimum_sample_size, const uint32_t log_per_stripe_threshold) { + return (static_cast(log_per_stripe_threshold) << 32) | + minimum_sample_size; + } + + static size_t decode_min_sample_size(const uint64_t params) { + return static_cast(params); + } + + static uint32_t decode_log_per_stripe_threshold(const uint64_t params) { + return static_cast(params >> 32); + } + + void maybe_take_sample(size_t minimum_sample_size, size_t striped_unsampled); + ShenandoahWeightedSeq _baseline; ShenandoahWeightedSeq _recent; ShenandoahWeightedSeq _momentary; @@ -127,22 +141,19 @@ class ShenandoahAllocRate { const uint recent_window_size = ShenandoahRecentAllocRateSampleWindow, const uint momentary_window_size = ShenandoahMomentaryAllocRateSampleWindow) : _sample_lock(Mutex::nosafepoint - 2, "ShenandoahAllocSample_lock", true) - , _allocated_bytes_since_last_sample(0) - , _minimum_sample_size(minimum_sample_size) , _last_sample_time(Clock::elapsed_counter()) , _baseline(baseline_window_size) , _recent(recent_window_size) , _momentary(momentary_window_size) { + set_minimum_sample_size(minimum_sample_size); } // Update minimum sample size based on the given available bytes void update_minimum_sample_size(size_t available); - // Set minimum sample size in bytes - void set_minimum_sample_size(const size_t minimum_sample_size) { - _minimum_sample_size.store_relaxed(minimum_sample_size); - } + // Set minimum sample size and its per-stripe trigger shift. + void set_minimum_sample_size(size_t minimum_sample_size); // Indicate that this many bytes have been allocated (by the mutator). void allocated(size_t allocated_bytes); @@ -173,6 +184,24 @@ class ShenandoahAllocRate { } private: + // Log2 of the per-stripe trigger threshold. + uint32_t log_per_stripe_threshold_for(size_t minimum_sample_size) const; + + // Fast, lock-free: did this add carry the calling thread's stripe across a per-stripe threshold + // multiple? The threshold is a power of two, so a crossing is a change in the bits above it. + static bool striped_threshold_exceeded(size_t striped_unsampled, size_t previous_striped_unsampled, uint32_t log_per_stripe_threshold) { + return (striped_unsampled >> log_per_stripe_threshold) > (previous_striped_unsampled >> log_per_stripe_threshold); + } + + // Whether the unsampled bytes are still below the sampling floor. Must be called under the sample + // lock: drains only happen under the lock, so reading the live stripe value and sum() here filters + // out false positives from a concurrent drain that already reset the counter. + bool unsampled_below_floor(size_t minimum_sample_size, size_t striped_unsampled) const { + assert(_sample_lock.owned_by_self(), "Caller must hold lock"); + return (_unsampled.num_stripes() > 1 && _unsampled.current_stripe_value() < striped_unsampled) || + _unsampled.sum() < minimum_sample_size; + } + // Record the sample under the sample lock void take_sample(jlong now, jlong elapsed, size_t unsampled); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAllocRate.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAllocRate.inline.hpp index 9ffad0d312c..e317721cd7b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAllocRate.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAllocRate.inline.hpp @@ -27,8 +27,10 @@ #include "gc/shenandoah/shenandoahAllocRate.hpp" +#include "gc/shenandoah/shenandoahStripedCounter.inline.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" #include "logging/log.hpp" +#include "utilities/powerOfTwo.hpp" inline size_t ShenandoahAnticipatedConsumption::baseline_consumption() const { @@ -56,38 +58,54 @@ void ShenandoahAllocRate::update_minimum_sample_size(const size_t availab } template -void ShenandoahAllocRate::allocated(const size_t allocated_bytes) { - size_t unsampled = _allocated_bytes_since_last_sample.add_then_fetch(allocated_bytes, memory_order_relaxed); - const size_t minimum_sample_size = _minimum_sample_size.load_relaxed(); - if (unsampled < minimum_sample_size) { - // Not enough to sample yet - return; - } +uint32_t ShenandoahAllocRate::log_per_stripe_threshold_for(const size_t minimum_sample_size) const { + // Floor-log2 of the per-stripe share. Clamps to 0 for a 1-byte trigger. + const int log_threshold = log2i(minimum_sample_size) - (int) _unsampled.log_num_stripes(); + return log_threshold > 0 ? (uint32_t) log_threshold : 0u; +} + +template +void ShenandoahAllocRate::set_minimum_sample_size(const size_t minimum_sample_size) { + assert(minimum_sample_size > 0, "minimum sample size must be non-zero"); + _sample_params.store_relaxed(encode_sample_params(checked_cast(minimum_sample_size), log_per_stripe_threshold_for(minimum_sample_size))); +} +template +void ShenandoahAllocRate::maybe_take_sample(const size_t minimum_sample_size, const size_t striped_unsampled) { if (!_sample_lock.try_lock()) { - // Another thread has the lock and will take the sample + // Another thread has the lock and will take the sample. return; } - unsampled = _allocated_bytes_since_last_sample.load_relaxed(); - if (unsampled < minimum_sample_size) { - // Another thread has sampled and reset the allocated bytes under the lock + if (unsampled_below_floor(minimum_sample_size, striped_unsampled)) { + // Either another thread already sampled and drained, or this thread's stripe crossed its share + // while the aggregate is still short (skewed distribution). Wait for more. _sample_lock.unlock(); return; } - const jlong now = Clock::elapsed_counter(); const jlong elapsed = now - _last_sample_time; - if (elapsed <= 0) { - // Avoid sampling nonsense allocation rates + // Avoid sampling nonsense allocation rates. _sample_lock.unlock(); return; } + take_sample(now, elapsed, _unsampled.drain()); + _sample_lock.unlock(); +} + +template +void ShenandoahAllocRate::allocated(const size_t allocated_bytes) { + const size_t striped_unsampled = _unsampled.add(allocated_bytes); + const size_t previous_striped_unsampled = striped_unsampled - allocated_bytes; - take_sample(now, elapsed, unsampled); + const uint64_t params = _sample_params.load_relaxed(); + const uint32_t log_per_stripe_threshold = decode_log_per_stripe_threshold(params); - _sample_lock.unlock(); + // Re-arm the trigger at every per-stripe threshold crossing. + if (striped_threshold_exceeded(striped_unsampled, previous_striped_unsampled, log_per_stripe_threshold)) { + maybe_take_sample(decode_min_sample_size(params), striped_unsampled); + } } template @@ -97,7 +115,6 @@ void ShenandoahAllocRate::force_update() { return; } - const size_t unsampled = _allocated_bytes_since_last_sample.load_relaxed(); const jlong now = Clock::elapsed_counter(); const jlong elapsed = now - _last_sample_time; @@ -107,7 +124,7 @@ void ShenandoahAllocRate::force_update() { return; } - take_sample(now, elapsed, unsampled); + take_sample(now, elapsed, _unsampled.drain()); _sample_lock.unlock(); } @@ -118,10 +135,6 @@ void ShenandoahAllocRate::take_sample(jlong now, jlong elapsed, size_t un _last_sample_time = now; - // We are recording this sample, deduct it from the counter. It may be increased - // concurrently by other threads outside the lock, so we still use an atomic access. - _allocated_bytes_since_last_sample.sub_then_fetch(unsampled, memory_order_relaxed); - const double timestamp = static_cast(_last_sample_time) / Clock::elapsed_frequency(); const double rate_seconds = static_cast(unsampled) * Clock::elapsed_frequency() / elapsed; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahStripedCounter.cpp b/src/hotspot/share/gc/shenandoah/shenandoahStripedCounter.cpp new file mode 100644 index 00000000000..d6e55d06248 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahStripedCounter.cpp @@ -0,0 +1,38 @@ +/* + * Copyright Amazon.com Inc. 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 "gc/shenandoah/shenandoahStripedCounter.hpp" +#include "memory/padded.inline.hpp" +#include "runtime/os.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/powerOfTwo.hpp" + +ShenandoahStripedCounter::ShenandoahStripedCounter() + : _num_stripes(round_down_power_of_2((uint32_t) MAX2(os::processor_count(), 1))) + , _stripe_mask(_num_stripes - 1) + , _log_num_stripes(log2i_exact(_num_stripes)) { + _stripes = PaddedArray, mtGC>::create_unfreeable(_num_stripes); +} + +ShenandoahStripedCounter::~ShenandoahStripedCounter() { } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahStripedCounter.hpp b/src/hotspot/share/gc/shenandoah/shenandoahStripedCounter.hpp new file mode 100644 index 00000000000..ad1086005a6 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahStripedCounter.hpp @@ -0,0 +1,79 @@ +/* + * Copyright Amazon.com Inc. 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_GC_SHENANDOAH_SHENANDOAHSTRIPEDCOUNTER_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHSTRIPEDCOUNTER_HPP + +#include "memory/allocation.hpp" +#include "memory/padded.hpp" +#include "runtime/atomic.hpp" +#include "utilities/globalDefinitions.hpp" + +// A contended-counter optimized for many concurrent writers and infrequent reads. +// Each writer accumulates into a stripe chosen by its thread hash, each on its own cache line to +// avoid false sharing. Stripes are shared when live writers outnumber stripes (num_stripes <= CPU +// count). The value of the counter is always sum(stripes). +// +// Reads (sum) are approximate under concurrent writes and exact when quiescent. +// This counter is monotonic per epoch: add() only increases it; drain() atomically reads and resets +// to begin a new epoch (0), preserving concurrent adds that race with the drain. +class ShenandoahStripedCounter : public CHeapObj { + typedef PaddedEnd> PaddedCounter; + + PaddedCounter* _stripes; // _num_stripes entries + // Number of stripes: a power of two, rounded down from the CPU count. Keeping it a power of two + // lets current_stripe() map a thread hash into range with a mask (& _stripe_mask) instead of a + // modulo on the hot path. + uint32_t const _num_stripes; + uint32_t const _stripe_mask; // _num_stripes - 1 + uint32_t const _log_num_stripes; + + // The stripe this thread uses. + uint32_t current_stripe() const; + +public: + ShenandoahStripedCounter(); + ~ShenandoahStripedCounter(); + + // Add `bytes` to the current stripe of the counter and return the resulting total of the current stripe. + size_t add(size_t bytes); + + // Current total of all stripes of the counter. No reset. + // Approximate under concurrent writes. + size_t sum() const; + + // Current value of the calling thread's own stripe. O(1), no reset. + size_t current_stripe_value() const; + + // Read the total and atomically reset it to zero, returning the amount consumed. + // Concurrent adds racing with the drain accumulate toward the next epoch rather than being lost. + size_t drain(); + + // Number of stripes (a power of two, <= CPU count), and its base-2 log. Exposed so a caller can + // scale a threshold to a per-stripe share with a shift (>> log_num_stripes) instead of a divide. + uint32_t num_stripes() const; + uint32_t log_num_stripes() const; +}; + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHSTRIPEDCOUNTER_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahStripedCounter.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahStripedCounter.inline.hpp new file mode 100644 index 00000000000..58c40089324 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahStripedCounter.inline.hpp @@ -0,0 +1,74 @@ +/* + * Copyright Amazon.com Inc. 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_GC_SHENANDOAH_SHENANDOAHSTRIPEDCOUNTER_INLINE_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHSTRIPEDCOUNTER_INLINE_HPP + +#include "gc/shenandoah/shenandoahStripedCounter.hpp" + +#include "runtime/thread.hpp" + +inline uint32_t ShenandoahStripedCounter::current_stripe() const { + if (_num_stripes == 1u) { + return 0u; + } + // Per-thread probe into [0, _num_stripes). Hashing the thread pointer spreads threads across + // stripes. This is a pure, stable function of (thread pointer, _num_stripes) + const uintptr_t t = (uintptr_t) Thread::current(); + return (uint32_t) ((t ^ (t >> 20) ^ (t >> 9)) & _stripe_mask); +} + +inline uint32_t ShenandoahStripedCounter::num_stripes() const { + return _num_stripes; +} + +inline uint32_t ShenandoahStripedCounter::log_num_stripes() const { + return _log_num_stripes; +} + +inline size_t ShenandoahStripedCounter::add(const size_t bytes) { + return _stripes[current_stripe()].add_then_fetch(bytes, memory_order_relaxed); +} + +inline size_t ShenandoahStripedCounter::sum() const { + size_t total = 0; + for (uint32_t i = 0; i < _num_stripes; i++) { + total += _stripes[i].load_relaxed(); + } + return total; +} + +inline size_t ShenandoahStripedCounter::current_stripe_value() const { + return _stripes[current_stripe()].load_relaxed(); +} + +inline size_t ShenandoahStripedCounter::drain() { + size_t total = 0; + for (uint32_t i = 0; i < _num_stripes; i++) { + total += _stripes[i].exchange(0, memory_order_relaxed); + } + return total; +} + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHSTRIPEDCOUNTER_INLINE_HPP diff --git a/test/hotspot/gtest/gc/shenandoah/test_shenandoahAllocationRate.cpp b/test/hotspot/gtest/gc/shenandoah/test_shenandoahAllocationRate.cpp index af066657377..a4169ff6ba6 100644 --- a/test/hotspot/gtest/gc/shenandoah/test_shenandoahAllocationRate.cpp +++ b/test/hotspot/gtest/gc/shenandoah/test_shenandoahAllocationRate.cpp @@ -26,6 +26,9 @@ #include "gc/shared/gc_globals.hpp" #include "gc/shenandoah/shenandoahAllocRate.inline.hpp" +#include "gc/shenandoah/shenandoahStripedCounter.inline.hpp" +#include "runtime/atomic.hpp" +#include "threadHelper.inline.hpp" class ShenandoahMockClock { public: @@ -120,6 +123,131 @@ TEST_VM_F(ShenandoahAllocationRateTest, accelerated_consumption_momentary_spike) EXPECT_EQ(consumption.accelerated_consumption(), 0UL); } +TEST_VM_F(ShenandoahAllocationRateTest, event_driven_sampling_single_dominant_allocator) { + // Single mutator: one stripe allocates, other stripes stay empty. + ShenandoahStripedCounter stripes; + if (stripes.num_stripes() == 1) { + // Regression requires multiple stripes. + return; + } + + ShenandoahAllocRate rate(MINIMUM_SAMPLE_SIZE, BASELINE_SAMPLES, RECENT_SAMPLES, MOMENTARY_SAMPLES); + // Multiple epochs prove the allocation-path trigger re-fires without force_update(). + constexpr size_t alloc_size = 64; + constexpr size_t epochs = 4; + for (size_t allocated = 0; allocated < MINIMUM_SAMPLE_SIZE * epochs; allocated += alloc_size) { + allocate(rate, alloc_size); + } + + // Old one-shot trigger left the average at zero until force_update(). + EXPECT_GT(rate.weighted_average(), 0.0); +} + +TEST_VM_F(ShenandoahAllocationRateTest, event_driven_sampling_rearms_when_floor_lowered) { + // Lowering the floor must re-arm a stripe that crossed the old share. + constexpr size_t high_floor = 1 * M; + constexpr size_t low_floor = 1024; + constexpr size_t alloc_size = 64; + + ShenandoahAllocRate rate(high_floor, BASELINE_SAMPLES, RECENT_SAMPLES, MOMENTARY_SAMPLES); + + // Accumulate below the high floor, but above the later lowered share. + constexpr size_t phase1_bytes = high_floor / 4; + for (size_t allocated = 0; allocated < phase1_bytes; allocated += alloc_size) { + allocate(rate, alloc_size); + } + EXPECT_DOUBLE_EQ(rate.weighted_average(), 0.0); // nothing drained yet + + // A GC lowers the floor. + rate.set_minimum_sample_size(low_floor); + + // New crossings under the lowered floor must sample without force_update(). + for (size_t allocated = 0; allocated < low_floor * 16; allocated += alloc_size) { + allocate(rate, alloc_size); + } + + EXPECT_GT(rate.weighted_average(), 0.0); +} + +// Concurrent multi-threaded sampling. Many threads drive allocated() past the aggregate floor at +// the same time, so distinct JavaThreads spread across stripes and stay hot simultaneously. This is +// the regime the sampling guard is written for: contended try_lock (multiple threads cross their +// per-stripe share at once, only one wins the lock), multi-stripe sum() aggregation (the floor is +// reached by several occupied stripes, not one), and the drain-race clause (one thread's add() +// captures a stripe value that another thread drains before the first takes the lock). +class ConcurrentAllocators { +public: + static constexpr int kThreads = 8; + static constexpr size_t kPerThreadEpochs = 500; + static constexpr size_t kAllocSize = 64; + // Every thread allocates this many bytes; the grand total spans many minimum-sample-size epochs. + static constexpr size_t kPerThreadBytes = MINIMUM_SAMPLE_SIZE * kPerThreadEpochs; +}; + +TEST_VM_F(ShenandoahAllocationRateTest, event_driven_sampling_concurrent_allocators) { + ShenandoahAllocRate rate(MINIMUM_SAMPLE_SIZE, BASELINE_SAMPLES, RECENT_SAMPLES, MOMENTARY_SAMPLES); + + auto worker = [&](Thread*, int) { + for (size_t allocated = 0; allocated < ConcurrentAllocators::kPerThreadBytes; + allocated += ConcurrentAllocators::kAllocSize) { + rate.allocated(ConcurrentAllocators::kAllocSize); + } + }; + TestThreadGroup ttg(worker, ConcurrentAllocators::kThreads); + ttg.doit(); + ttg.join(); + + // No force_update() was called: every sample came from the contended allocation path. Across + // thousands of epochs driven by all threads, sampling must have fired and drained repeatedly. + EXPECT_GT(rate.weighted_average(), 0.0); +} + +// Concurrent skew: a few threads hold their stripes just below the per-stripe share and keep them +// hot (spinning at the barrier), while a heavy thread pushes the aggregate over the floor. The +// sample can then only be taken because sum() aggregates the heavy stripe with the held stripes -- +// exercising the multi-stripe floor crossing, not a single dominant stripe. +class ConcurrentSkew { +public: + static constexpr int kHolderThreads = 6; + static constexpr size_t kHeavyEpochs = 300; + static constexpr size_t kAllocSize = 64; +}; + +TEST_VM_F(ShenandoahAllocationRateTest, event_driven_sampling_concurrent_skew) { + ShenandoahStripedCounter stripes; + if (stripes.num_stripes() == 1) { + // A multi-stripe aggregate crossing is only meaningful with more than one stripe. + return; + } + + ShenandoahAllocRate rate(MINIMUM_SAMPLE_SIZE, BASELINE_SAMPLES, RECENT_SAMPLES, MOMENTARY_SAMPLES); + + // Each holder adds just under the per-stripe share once, then stays live for the whole run, so + // several stripes remain simultaneously occupied below their individual share. Their adds never + // cross a share alone, but they contend on the counter and feed sum(). + Atomic stop(false); + const size_t per_stripe_share = MINIMUM_SAMPLE_SIZE / stripes.num_stripes(); + const size_t holder_target = per_stripe_share > 2 ? per_stripe_share - 1 : 1; + auto holder = [&](Thread*, int) { + rate.allocated(holder_target); + while (!stop.load_relaxed()) { /* keep the thread (and its stripe) live */ } + }; + TestThreadGroup holders(holder, ConcurrentSkew::kHolderThreads); + holders.doit(); + + // Heavy stream on the main thread's own stripe. Its crossings, added to the held stripes, take + // sum() over the floor; the re-armed trigger must sample every epoch off the allocation path. + const size_t heavy_bytes = MINIMUM_SAMPLE_SIZE * ConcurrentSkew::kHeavyEpochs; + for (size_t allocated = 0; allocated < heavy_bytes; allocated += ConcurrentSkew::kAllocSize) { + allocate(rate, ConcurrentSkew::kAllocSize); + } + + stop.store_relaxed(true); + holders.join(); + + EXPECT_GT(rate.weighted_average(), 0.0); +} + TEST_VM_F(ShenandoahAllocationRateTest, accelerated_consumption_accelerating) { ShenandoahAllocRate rate(256, BASELINE_SAMPLES, RECENT_SAMPLES, MOMENTARY_SAMPLES); for (uint i = 0; i < BASELINE_SAMPLES; ++i) { diff --git a/test/hotspot/gtest/gc/shenandoah/test_shenandoahStripedCounter.cpp b/test/hotspot/gtest/gc/shenandoah/test_shenandoahStripedCounter.cpp new file mode 100644 index 00000000000..db4933a9264 --- /dev/null +++ b/test/hotspot/gtest/gc/shenandoah/test_shenandoahStripedCounter.cpp @@ -0,0 +1,118 @@ +/* + * Copyright Amazon.com Inc. 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 "gc/shenandoah/shenandoahStripedCounter.inline.hpp" +#include "runtime/atomic.hpp" +#include "threadHelper.inline.hpp" +#include "unittest.hpp" + +// Single thread: every add() maps to the same stripe, so add() returns the running total and +// sum()/drain() are exact. +TEST_VM(ShenandoahStripedCounter, single_thread_exact) { + ShenandoahStripedCounter c; + size_t expected = 0; + for (size_t i = 1; i <= 1000; i++) { + const size_t got = c.add(i); + expected += i; + // A lone writer owns one stripe, so its stripe total is the whole total. + EXPECT_EQ(got, expected); + EXPECT_EQ(c.sum(), expected); + } + // drain() returns everything and resets to zero; a second drain sees nothing. + EXPECT_EQ(c.drain(), expected); + EXPECT_EQ(c.sum(), (size_t) 0); + EXPECT_EQ(c.drain(), (size_t) 0); +} + +// Draining mid-stream starts a fresh epoch, and sum()/drain() stay exact across the boundary. +TEST_VM(ShenandoahStripedCounter, drain_epochs) { + ShenandoahStripedCounter c; + size_t expected = 0; + for (size_t i = 0; i < 500; i++) { + c.add(7); + expected += 7; + } + EXPECT_EQ(c.sum(), expected); + // Drain (starts a new epoch), then keep adding. + EXPECT_EQ(c.drain(), expected); + expected = 0; + for (size_t i = 0; i < 500; i++) { + c.add(13); + expected += 13; + } + EXPECT_EQ(c.sum(), expected); + EXPECT_EQ(c.drain(), expected); +} + +// Multi-threaded stress. N threads each add a fixed number of bytes; when quiescent, sum() must +// equal the grand total, and the periodic-drain variant must lose nothing (every byte lands in +// exactly one drain or the final sum). Distinct JavaThreads make current_stripe() actually spread +// writers across stripes. +class StripedCounterStress { +public: + static constexpr int kThreads = 8; + static constexpr size_t kPerThreadAdds = 20000; + static constexpr size_t kBytesPerAdd = 8; + static constexpr size_t kGrandTotal = (size_t) kThreads * kPerThreadAdds * kBytesPerAdd; +}; + +TEST_VM(ShenandoahStripedCounter, mt_quiescent_sum_exact) { + ShenandoahStripedCounter c; + auto worker = [&](Thread*, int) { + for (size_t i = 0; i < StripedCounterStress::kPerThreadAdds; i++) { + c.add(StripedCounterStress::kBytesPerAdd); + } + }; + TestThreadGroup ttg(worker, StripedCounterStress::kThreads); + ttg.doit(); + ttg.join(); + // All writers quiesced: sum() is now exact and must account for every byte. + EXPECT_EQ(c.sum(), StripedCounterStress::kGrandTotal); + EXPECT_EQ(c.drain(), StripedCounterStress::kGrandTotal); + EXPECT_EQ(c.sum(), (size_t) 0); +} + +TEST_VM(ShenandoahStripedCounter, mt_concurrent_drain_loses_nothing) { + ShenandoahStripedCounter c; + Atomic drained(0); + Atomic done(0); + auto worker = [&](Thread*, int) { + for (size_t i = 0; i < StripedCounterStress::kPerThreadAdds; i++) { + c.add(StripedCounterStress::kBytesPerAdd); + } + done.add_then_fetch(1); + }; + TestThreadGroup ttg(worker, StripedCounterStress::kThreads); + ttg.doit(); + // Drain concurrently with the adds; each drain moves bytes to a new epoch without losing them. + while (done.load_relaxed() < StripedCounterStress::kThreads) { + drained.add_then_fetch(c.drain()); + } + ttg.join(); + // Final drain sweeps up whatever raced the last concurrent drain. + drained.add_then_fetch(c.drain()); + // Every byte added landed in exactly one drain. + EXPECT_EQ(drained.load_relaxed(), StripedCounterStress::kGrandTotal); + EXPECT_EQ(c.sum(), (size_t) 0); +} From 3354ad3b6f599c603d55447d4f747e98020e773a Mon Sep 17 00:00:00 2001 From: Amit Kumar Date: Fri, 10 Jul 2026 06:05:42 +0000 Subject: [PATCH 196/707] 8386586: [s390x] TestSyncOnValueBasedClassEvent.java fails due to incorrect branch Reviewed-by: aph, hdhiman --- src/hotspot/cpu/s390/macroAssembler_s390.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp index 5d5c7570e27..6eb14452401 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp @@ -6178,7 +6178,7 @@ void MacroAssembler::fast_lock(Register basic_lock, Register obj, Register temp1 if (DiagnoseSyncOnValueBasedClasses != 0) { load_klass(temp1, obj); z_tm(Address(temp1, Klass::misc_flags_offset()), KlassFlags::_misc_is_value_based_class); - z_brne(slow); + z_brnaz(slow); } // First we need to check if the lock-stack has room for pushing the object reference. From 3e2366e7f5d29be03c8d5ddb4f62e5e1f7185550 Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Fri, 10 Jul 2026 06:36:00 +0000 Subject: [PATCH 197/707] 8387742: Reclaim CodeCache nmethods more promptly Reviewed-by: tschatzl, shade --- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 7 +- src/hotspot/share/gc/g1/g1CollectedHeap.hpp | 1 - src/hotspot/share/gc/g1/g1ConcurrentMark.cpp | 5 +- src/hotspot/share/gc/g1/g1FullCollector.cpp | 3 + .../share/gc/parallel/psParallelCompact.cpp | 2 +- src/hotspot/share/gc/serial/serialFullGC.cpp | 1 + src/hotspot/share/gc/serial/serialHeap.cpp | 1 - .../hotspot/jtreg/gc/TestCodeCacheUnload.java | 177 ++++++++++++++++++ ...stCodeCacheUnloadDuringConcurrentMark.java | 115 ++++++++++++ 9 files changed, 302 insertions(+), 10 deletions(-) create mode 100644 test/hotspot/jtreg/gc/TestCodeCacheUnload.java create mode 100644 test/hotspot/jtreg/gc/g1/TestCodeCacheUnloadDuringConcurrentMark.java diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 3c41133e572..f60ce9b15b4 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -868,7 +868,7 @@ void G1CollectedHeap::prepare_for_mutator_after_full_collection(size_t allocatio // Rebuild the code root lists for each region rebuild_code_roots(); - finish_codecache_marking_cycle(); + CodeCache::arm_all_nmethods(); start_new_collection_set(); _allocator->init_mutator_alloc_regions(); @@ -3342,8 +3342,3 @@ void G1CollectedHeap::start_codecache_marking_cycle_if_inactive(bool concurrent_ CodeCache::arm_all_nmethods(); } } - -void G1CollectedHeap::finish_codecache_marking_cycle() { - CodeCache::on_gc_marking_cycle_finish(); - CodeCache::arm_all_nmethods(); -} diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index cb466a5e120..672dea9b7b0 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -953,7 +953,6 @@ class G1CollectedHeap : public CollectedHeap { void fill_with_dummy_object(HeapWord* start, HeapWord* end, bool zap) override; static void start_codecache_marking_cycle_if_inactive(bool concurrent_mark_start); - static void finish_codecache_marking_cycle(); // The shared block offset table array. G1BlockOffsetTable* bot() const { return _bot; } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 233901c30f8..73a697f8c51 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -24,6 +24,7 @@ #include "classfile/classLoaderData.hpp" #include "classfile/classLoaderDataGraph.hpp" +#include "code/codeCache.hpp" #include "cppstdlib/new.hpp" #include "gc/g1/g1BarrierSet.hpp" #include "gc/g1/g1BatchedTask.hpp" @@ -1378,6 +1379,8 @@ void G1ConcurrentMark::remark() { if (mark_finished) { weak_refs_work(); + CodeCache::on_gc_marking_cycle_finish(); + // Unload Klasses, String, Code Cache, etc. if (ClassUnloadingWithConcurrentMark) { G1CMIsAliveClosure is_alive(this); @@ -1446,7 +1449,7 @@ void G1ConcurrentMark::remark() { // Completely reset the marking state (except bitmaps) since marking completed. reset_at_marking_complete(); - G1CollectedHeap::finish_codecache_marking_cycle(); + CodeCache::arm_all_nmethods(); { GCTraceTime(Debug, gc, phases) debug("Report Object Count", _gc_timer_cm); diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp index c5af4a8220b..1e838b344b7 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.cpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp @@ -23,6 +23,7 @@ */ #include "classfile/classLoaderDataGraph.hpp" +#include "code/codeCache.hpp" #include "cppstdlib/new.hpp" #include "gc/g1/g1CollectedHeap.hpp" #include "gc/g1/g1FullCollector.inline.hpp" @@ -330,6 +331,8 @@ void G1FullCollector::phase1_mark_live_objects() { assert(marker(0)->task_queue()->is_empty(), "Should be no oops on the stack"); } + CodeCache::on_gc_marking_cycle_finish(); + { GCTraceTime(Debug, gc, phases) debug("Phase 1: Flush Mark Stats Cache", scope()->timer()); for (uint i = 0; i < workers(); i++) { diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp index ff757f205a2..777b734c59e 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp @@ -644,7 +644,6 @@ void PSParallelCompact::post_compact() GCTraceTime(Info, gc, phases) tm("Post Compact", &_gc_timer); ParCompactionManager::remove_all_shadow_regions(); - CodeCache::on_gc_marking_cycle_finish(); CodeCache::arm_all_nmethods(); // Need to clear claim bits for the next full-gc (marking and adjust-pointers). @@ -1216,6 +1215,7 @@ void PSParallelCompact::marking_phase(ParallelOldTracer *gc_tracer) { // This is the point where the entire marking should have completed. ParCompactionManager::verify_all_marking_stack_empty(); + CodeCache::on_gc_marking_cycle_finish(); { GCTraceTime(Debug, gc, phases) tm("Weak Processing", &_gc_timer); diff --git a/src/hotspot/share/gc/serial/serialFullGC.cpp b/src/hotspot/share/gc/serial/serialFullGC.cpp index 13532dea07d..a88a0878305 100644 --- a/src/hotspot/share/gc/serial/serialFullGC.cpp +++ b/src/hotspot/share/gc/serial/serialFullGC.cpp @@ -512,6 +512,7 @@ void SerialFullGC::phase1_mark(bool clear_all_softrefs) { // This is the point where the entire marking should have completed. assert(_marking_stack.is_empty(), "Marking should have completed"); + CodeCache::on_gc_marking_cycle_finish(); { GCTraceTime(Debug, gc, phases) tm_m("Weak Processing", gc_timer()); diff --git a/src/hotspot/share/gc/serial/serialHeap.cpp b/src/hotspot/share/gc/serial/serialHeap.cpp index 3de562e886d..eb2bed109b5 100644 --- a/src/hotspot/share/gc/serial/serialHeap.cpp +++ b/src/hotspot/share/gc/serial/serialHeap.cpp @@ -589,7 +589,6 @@ void SerialHeap::do_full_collection(bool clear_all_soft_refs) { gc_timer->register_gc_end(); gc_tracer->report_gc_end(gc_timer->gc_end(), gc_timer->time_partitions()); - CodeCache::on_gc_marking_cycle_finish(); CodeCache::arm_all_nmethods(); COMPILER2_PRESENT(DerivedPointerTable::update_pointers()); diff --git a/test/hotspot/jtreg/gc/TestCodeCacheUnload.java b/test/hotspot/jtreg/gc/TestCodeCacheUnload.java new file mode 100644 index 00000000000..03c857dba1e --- /dev/null +++ b/test/hotspot/jtreg/gc/TestCodeCacheUnload.java @@ -0,0 +1,177 @@ +/* + * 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 gc; + +/* + * @test id=serial + * @summary Tests that one full GC unloads a freshly not-entrant nmethod. + * @requires vm.gc.Serial + * @requires vm.compiler1.enabled + * @requires vm.opt.ClassUnloading != false + * @requires vm.opt.MethodFlushing != false + * @library /test/lib / + * @modules java.base/jdk.internal.misc + * java.management + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI -Xbatch -XX:-BackgroundCompilation + * -XX:+UseSerialGC gc.TestCodeCacheUnload + */ + +/* + * @test id=parallel + * @summary Tests that one full GC unloads a freshly not-entrant nmethod. + * @requires vm.gc.Parallel + * @requires vm.compiler1.enabled + * @requires vm.opt.ClassUnloading != false + * @requires vm.opt.MethodFlushing != false + * @library /test/lib / + * @modules java.base/jdk.internal.misc + * java.management + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI -Xbatch -XX:-BackgroundCompilation + * -XX:+UseParallelGC gc.TestCodeCacheUnload + */ + +/* + * @test id=g1 + * @summary Tests that one full GC unloads a freshly not-entrant nmethod. + * @requires vm.gc.G1 + * @requires vm.compiler1.enabled + * @requires vm.opt.ClassUnloading != false + * @requires vm.opt.MethodFlushing != false + * @library /test/lib / + * @modules java.base/jdk.internal.misc + * java.management + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI -Xbatch -XX:-BackgroundCompilation + * -XX:+UseG1GC gc.TestCodeCacheUnload + */ + +/* + * @test id=shenandoah + * @summary Tests that one full GC unloads a freshly not-entrant nmethod. + * @requires vm.gc.Shenandoah + * @requires vm.compiler1.enabled + * @requires vm.opt.ClassUnloading != false + * @requires vm.opt.MethodFlushing != false + * @library /test/lib / + * @modules java.base/jdk.internal.misc + * java.management + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI -Xbatch -XX:-BackgroundCompilation + * -XX:+UseShenandoahGC gc.TestCodeCacheUnload + */ + +/* + * @test id=z + * @summary Tests that one full GC unloads a freshly not-entrant nmethod. + * @requires vm.gc.Z + * @requires vm.compiler1.enabled + * @requires vm.opt.ClassUnloading != false + * @requires vm.opt.MethodFlushing != false + * @library /test/lib / + * @modules java.base/jdk.internal.misc + * java.management + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI -Xbatch -XX:-BackgroundCompilation + * -XX:+UseZGC gc.TestCodeCacheUnload + */ + +import java.lang.reflect.Method; + +import jdk.test.lib.dcmd.JMXExecutor; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.whitebox.WhiteBox; + +public class TestCodeCacheUnload { + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + public static class Target { + public static int test(int value) { + return value + 1; + } + } + + private static void compileAndMakeNotEntrant() throws Exception { + Method method = Target.class.getDeclaredMethod("test", int.class); + + method.invoke(null, 1); + if (!WB.enqueueMethodForCompilation(method, 1 /* compLevel */)) { + throw new AssertionError("Failed to enqueue target for compilation"); + } + while (WB.isMethodQueuedForCompilation(method)) { + Thread.sleep(50); + } + if (!WB.isMethodCompiled(method)) { + throw new AssertionError("Target is not compiled"); + } + + int deoptimized = WB.deoptimizeMethod(method); + if (deoptimized == 0) { + throw new AssertionError("No target nmethod was made not-entrant"); + } + } + + private static int countNotEntrantEntries() { + OutputAnalyzer output = new JMXExecutor().execute("Compiler.codelist"); + String target = "gc.TestCodeCacheUnload$Target.test"; + int result = 0; + + for (String line : output.asLines()) { + if (!line.contains(target)) { + continue; + } + + System.out.println("Found codelist entry: " + line); + String[] parts = line.trim().split("\\s+"); + int codeState = Integer.parseInt(parts[2]); + if (codeState == 1 /* not_entrant */) { + result++; + } + } + + return result; + } + + public static void main(String[] args) throws Exception { + compileAndMakeNotEntrant(); + WB.fullGC(); + + int notEntrantEntries = countNotEntrantEntries(); + System.out.println("Target not-entrant entries after 1 full GC: " + notEntrantEntries); + if (notEntrantEntries != 0) { + throw new AssertionError("Expected one full GC to unload the not-entrant nmethod"); + } + } +} diff --git a/test/hotspot/jtreg/gc/g1/TestCodeCacheUnloadDuringConcurrentMark.java b/test/hotspot/jtreg/gc/g1/TestCodeCacheUnloadDuringConcurrentMark.java new file mode 100644 index 00000000000..0c2d473b021 --- /dev/null +++ b/test/hotspot/jtreg/gc/g1/TestCodeCacheUnloadDuringConcurrentMark.java @@ -0,0 +1,115 @@ +/* + * 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 gc.g1; + +/* + * @test TestCodeCacheUnloadDuringConcurrentMark + * @summary Tests that G1 concurrent marking unloads a freshly not-entrant nmethod. + * @requires vm.gc.G1 + * @requires vm.compiler1.enabled + * @requires vm.opt.ClassUnloading != false + * @requires vm.opt.ClassUnloadingWithConcurrentMark != false + * @requires vm.opt.MethodFlushing != false + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI -Xbatch -XX:-BackgroundCompilation + * -XX:+UseG1GC + * -XX:+ClassUnloadingWithConcurrentMark + * gc.g1.TestCodeCacheUnloadDuringConcurrentMark + */ + +import java.lang.reflect.Method; + +import jdk.test.lib.dcmd.JMXExecutor; +import jdk.test.whitebox.WhiteBox; + +public class TestCodeCacheUnloadDuringConcurrentMark { + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + static class Target { + public static int test(int value) { + return value + 1; + } + } + + private static void compileAndMakeNotEntrant(Method method) throws Exception { + Target.test(1); + if (!WB.enqueueMethodForCompilation(method, 1 /* compLevel */)) { + throw new AssertionError("Failed to enqueue target for compilation"); + } + while (WB.isMethodQueuedForCompilation(method)) { + Thread.sleep(50); + } + if (!WB.isMethodCompiled(method)) { + throw new AssertionError("Target is not compiled"); + } + + int deoptimized = WB.deoptimizeMethod(method); + if (deoptimized == 0) { + throw new AssertionError("No target nmethod was made not-entrant"); + } + } + + private static int countNotEntrantEntries() { + String target = TestCodeCacheUnloadDuringConcurrentMark.class.getName() + "$Target.test"; + int result = 0; + + for (String line : new JMXExecutor().execute("Compiler.codelist", true).asLines()) { + if (!line.contains(target)) { + continue; + } + + System.out.println("Found codelist entry: " + line); + String[] parts = line.trim().split("\\s+"); + int codeState = Integer.parseInt(parts[2]); + if (codeState == 1 /* not_entrant */) { + result++; + } + } + + return result; + } + + public static void main(String[] args) throws Exception { + compileAndMakeNotEntrant(Target.class.getDeclaredMethod("test", int.class)); + + int notEntrantEntries = countNotEntrantEntries(); + System.out.println("Target not-entrant entries before concurrent mark: " + notEntrantEntries); + if (notEntrantEntries == 0) { + throw new AssertionError("Expected a not-entrant target nmethod before concurrent mark"); + } + + WB.g1RunConcurrentGC(); + + notEntrantEntries = countNotEntrantEntries(); + System.out.println("Target not-entrant entries after concurrent mark: " + notEntrantEntries); + if (notEntrantEntries != 0) { + throw new AssertionError("Expected concurrent mark to unload the not-entrant target nmethod"); + } + } +} From 978dfecb6545166aee93a8e68c9c82535b0cb3e2 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Fri, 10 Jul 2026 07:07:20 +0000 Subject: [PATCH 198/707] 8387580: [S390x] OpenJDK build crashes with SIGSEGV in HashMap::resize() Reviewed-by: amitkumar, hdhiman --- src/hotspot/cpu/s390/templateTable_s390.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/cpu/s390/templateTable_s390.cpp b/src/hotspot/cpu/s390/templateTable_s390.cpp index 1da24c0378c..1db9c54aef5 100644 --- a/src/hotspot/cpu/s390/templateTable_s390.cpp +++ b/src/hotspot/cpu/s390/templateTable_s390.cpp @@ -1055,7 +1055,7 @@ void TemplateTable::lstore() { void TemplateTable::fstore() { transition(ftos, vtos); locals_index(Z_R1_scratch); - __ freg2mem_opt(Z_ftos, faddress(_masm, Z_R1_scratch)); + __ freg2mem_opt(Z_ftos, faddress(_masm, Z_R1_scratch), false); } void TemplateTable::dstore() { @@ -3506,7 +3506,7 @@ void TemplateTable::fast_xaccess(TosState state) { __ verify_oop(Z_tos); break; case ftos: - __ mem2freg_opt(Z_ftos, field); + __ mem2freg_opt(Z_ftos, field, false); break; default: ShouldNotReachHere(); From 7295b8aa2cdb3b47126986239a24488520816d20 Mon Sep 17 00:00:00 2001 From: Artur Barashev Date: Fri, 10 Jul 2026 14:27:29 +0000 Subject: [PATCH 199/707] 8386953: sun/security/ssl/CertificateCompression/CompressedCertMsgCache.java failing Reviewed-by: mullan --- .../ssl/CertificateCompression/CompressedCertMsgCache.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jdk/sun/security/ssl/CertificateCompression/CompressedCertMsgCache.java b/test/jdk/sun/security/ssl/CertificateCompression/CompressedCertMsgCache.java index 42e9701c6d0..be0f0154e32 100644 --- a/test/jdk/sun/security/ssl/CertificateCompression/CompressedCertMsgCache.java +++ b/test/jdk/sun/security/ssl/CertificateCompression/CompressedCertMsgCache.java @@ -52,7 +52,7 @@ * java.base/sun.security.util * @library /javax/net/ssl/templates * /test/lib - * @run main/othervm CompressedCertMsgCache + * @run main/othervm -Djdk.tls.server.newSessionTicketCount=0 CompressedCertMsgCache */ public class CompressedCertMsgCache extends SSLSocketTemplate { From 6eccdd862aa27c64a5c2c41b220edcfca390fd10 Mon Sep 17 00:00:00 2001 From: Artur Barashev Date: Fri, 10 Jul 2026 14:33:11 +0000 Subject: [PATCH 200/707] 8387578: Test sun/security/ssl/SSLSessionImpl/ResumeChecksServer.java failed: Existing session was used: FAIL Reviewed-by: mullan --- .../security/ssl/SSLSessionImpl/ResumeChecksServer.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/jdk/sun/security/ssl/SSLSessionImpl/ResumeChecksServer.java b/test/jdk/sun/security/ssl/SSLSessionImpl/ResumeChecksServer.java index d1918aab7f1..87c032728dc 100644 --- a/test/jdk/sun/security/ssl/SSLSessionImpl/ResumeChecksServer.java +++ b/test/jdk/sun/security/ssl/SSLSessionImpl/ResumeChecksServer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -93,6 +93,9 @@ private void test() throws Exception { System.err.println("firstSession.getCreationTime() = " + firstSession.getCreationTime()); + // Sleep 100ms between 2 connections to avoid test flakiness. + Thread.sleep(100); + long secondStartTime = System.currentTimeMillis(); secondSession = c.test(); @@ -128,7 +131,8 @@ private void test() throws Exception { case SIGNATURE_SCHEME: case LOCAL_CERTS: // fail if a new session is not created - if (secondSession.getCreationTime() < secondStartTime) { + if (secondSession.getCreationTime() == + firstSession.getCreationTime()) { throw new AssertionError("Existing session was used: FAIL"); } System.out.println("secondSession not resumed: PASS"); From 119fe211c8fb04afc5459cca7508b3066dfb1f08 Mon Sep 17 00:00:00 2001 From: Lawrence Andrews Date: Fri, 10 Jul 2026 16:43:32 +0000 Subject: [PATCH 201/707] 8388001: Test java/awt/Frame/PackTwiceTest.java fails because the frame title is displayed as 'PackTwiceTest TestFrame' instead of 'TestFrame' Reviewed-by: azvegint, prr --- test/jdk/java/awt/Frame/PackTwiceTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/jdk/java/awt/Frame/PackTwiceTest.java b/test/jdk/java/awt/Frame/PackTwiceTest.java index 63cd20612f0..ee948665d15 100644 --- a/test/jdk/java/awt/Frame/PackTwiceTest.java +++ b/test/jdk/java/awt/Frame/PackTwiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -36,7 +36,7 @@ public class PackTwiceTest { public static void main(String[] args) throws Exception { String INSTRUCTIONS = """ - 1. You would see a Frame titled 'TestFrame' + 1. You would see a Frame titled 'PackTwiceTest TestFrame' 2. The Frame displays a text as below: 'I am a lengthy sentence...can you see me?' 3. If you can see the full text without resizing the frame From c0a3082cf1a84962eba3c3b1b48f98e1f9b3fc64 Mon Sep 17 00:00:00 2001 From: Evgeny Astigeevich Date: Fri, 10 Jul 2026 17:29:07 +0000 Subject: [PATCH 202/707] 8388008: AArch64: data race accessing CodeHeap::high in CodeCache::max_distance_to_non_nmethod Reviewed-by: aph, shade, mhaessig, bulasevich --- src/hotspot/share/code/codeCache.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index 94cf8ebdec1..efe5d6549eb 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -1182,8 +1182,8 @@ size_t CodeCache::max_distance_to_non_nmethod() { CodeHeap* blob = get_code_heap(CodeBlobType::NonNMethod); // the max distance is minimized by placing the NonNMethod segment // in between MethodProfiled and MethodNonProfiled segments - size_t dist1 = (size_t)blob->high() - (size_t)_low_bound; - size_t dist2 = (size_t)_high_bound - (size_t)blob->low(); + size_t dist1 = (size_t)blob->high_boundary() - (size_t)_low_bound; + size_t dist2 = (size_t)_high_bound - (size_t)blob->low_boundary(); return dist1 > dist2 ? dist1 : dist2; } } From d3e5304c0f70aa03a52f5449cb38645a184b23dc Mon Sep 17 00:00:00 2001 From: April Ivy Date: Fri, 10 Jul 2026 21:48:11 +0000 Subject: [PATCH 203/707] 8382841: Revert annotation parsing changes from libgraal Reviewed-by: liach, darcy --- .../classes/sun/reflect/annotation/AnnotationParser.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/sun/reflect/annotation/AnnotationParser.java b/src/java.base/share/classes/sun/reflect/annotation/AnnotationParser.java index b40ed946648..ba804757e45 100644 --- a/src/java.base/share/classes/sun/reflect/annotation/AnnotationParser.java +++ b/src/java.base/share/classes/sun/reflect/annotation/AnnotationParser.java @@ -77,14 +77,14 @@ public static Map, Annotation> parseAnnotations( * Like {@link #parseAnnotations(byte[], sun.reflect.ConstantPool, Class)} * with an additional parameter {@code selectAnnotationClasses} which selects the * annotation types to parse (other than selected are quickly skipped).

    - * This method is used to parse select meta annotations in the construction + * This method is only used to parse select meta annotations in the construction * phase of {@link AnnotationType} instances to prevent infinite recursion. * * @param selectAnnotationClasses an array of annotation types to select when parsing */ @SafeVarargs @SuppressWarnings("varargs") // selectAnnotationClasses is used safely - public static Map, Annotation> parseSelectAnnotations( + static Map, Annotation> parseSelectAnnotations( byte[] rawAnnotations, ConstantPool constPool, Class container, From 7a5e6ef6aaaac5681df007a75b823af44a0b745e Mon Sep 17 00:00:00 2001 From: zifeihan Date: Mon, 13 Jul 2026 01:50:44 +0000 Subject: [PATCH 204/707] 8388035: RISC-V: Auto-enable Zfa extension features Reviewed-by: fyang, dzhang --- src/hotspot/cpu/riscv/globals_riscv.hpp | 2 +- src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/hotspot/cpu/riscv/globals_riscv.hpp b/src/hotspot/cpu/riscv/globals_riscv.hpp index dc3915aa398..d399bc13082 100644 --- a/src/hotspot/cpu/riscv/globals_riscv.hpp +++ b/src/hotspot/cpu/riscv/globals_riscv.hpp @@ -103,7 +103,7 @@ define_pd_global(intx, InlineSmallCode, 1000); product(bool, UseZbb, false, DIAGNOSTIC, "Use Zbb instructions") \ product(bool, UseZbkb, false, EXPERIMENTAL, "Use Zbkb instructions") \ product(bool, UseZbs, false, DIAGNOSTIC, "Use Zbs instructions") \ - product(bool, UseZfa, false, EXPERIMENTAL, "Use Zfa instructions") \ + product(bool, UseZfa, false, DIAGNOSTIC, "Use Zfa instructions") \ product(bool, UseZfh, false, DIAGNOSTIC, "Use Zfh instructions") \ product(bool, UseZfhmin, false, DIAGNOSTIC, "Use Zfhmin instructions") \ product(bool, UseZacas, false, EXPERIMENTAL, "Use Zacas instructions") \ diff --git a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp index 3ede62e14cd..fe555ec5ffb 100644 --- a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp +++ b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp @@ -215,11 +215,9 @@ void RiscvHwprobe::add_features_from_query_result() { if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZBS)) { VM_Version::ext_Zbs.enable_feature(); } -#ifndef PRODUCT if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZFA)) { VM_Version::ext_Zfa.enable_feature(); } -#endif if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZFH)) { VM_Version::ext_Zfh.enable_feature(); } From b3d98954d1f18bd7cd0804deea0c34f1d3dbc9be Mon Sep 17 00:00:00 2001 From: April Ivy Date: Mon, 13 Jul 2026 04:31:35 +0000 Subject: [PATCH 205/707] 8387996: Remove reference to -d64 from serviceability tool manpages Reviewed-by: dholmes --- src/jdk.jcmd/share/man/jinfo.md | 7 ++----- src/jdk.jcmd/share/man/jstack.md | 5 ++--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/jdk.jcmd/share/man/jinfo.md b/src/jdk.jcmd/share/man/jinfo.md index b70bc4c45ee..8365c5af8a5 100644 --- a/src/jdk.jcmd/share/man/jinfo.md +++ b/src/jdk.jcmd/share/man/jinfo.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 2004, 2024, 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 @@ -50,10 +50,7 @@ jinfo - generate Java configuration information for a specified Java process The `jinfo` command prints Java configuration information for a specified Java process. The configuration information includes Java system properties and JVM -command-line flags. If the specified process is running on a 64-bit JVM, then -you might need to specify the `-J-d64` option, for example: - -> `jinfo -J-d64 -sysprops` *pid* +command-line flags. This command is unsupported and might not be available in future releases of the JDK. In Windows Systems where `dbgeng.dll` is not present, the Debugging diff --git a/src/jdk.jcmd/share/man/jstack.md b/src/jdk.jcmd/share/man/jstack.md index 15849502d8c..2e95abf36c4 100644 --- a/src/jdk.jcmd/share/man/jstack.md +++ b/src/jdk.jcmd/share/man/jstack.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 2004, 2024, 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 @@ -52,8 +52,7 @@ The `jstack` command prints Java stack traces of Java threads for a specified Java process. For each Java frame, the full class name, method name, byte code index (BCI), and line number, when available, are printed. C++ mangled names aren't demangled. To demangle C++ names, the output of this command can be -piped to `c++filt`. When the specified process is running on a 64-bit JVM, you -might need to specify the `-J-d64` option, for example: `jstack -J-d64` *pid*. +piped to `c++filt`. **Note:** From f6d897614d9dbe07448fbeb04d168814de588eba Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Mon, 13 Jul 2026 07:08:31 +0000 Subject: [PATCH 206/707] 8387702: Linux/clang: enable linktime-gc on libjvm.so too, when it is configured Reviewed-by: mdoerr, lucy, clanger --- make/autoconf/flags-ldflags.m4 | 1 + 1 file changed, 1 insertion(+) diff --git a/make/autoconf/flags-ldflags.m4 b/make/autoconf/flags-ldflags.m4 index 7876511328b..1da98f5cdeb 100644 --- a/make/autoconf/flags-ldflags.m4 +++ b/make/autoconf/flags-ldflags.m4 @@ -81,6 +81,7 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER], fi if test "x$ENABLE_LINKTIME_GC" = xtrue; then + BASIC_LDFLAGS_JVM_ONLY="$BASIC_LDFLAGS_JVM_ONLY -Wl,--gc-sections -Wl,--undefined=_ZTV8Metadata" BASIC_LDFLAGS_JDK_ONLY="$BASIC_LDFLAGS_JDK_ONLY -Wl,--gc-sections" fi fi From 6247550cec70f76420ccf7e3a8aaa57e93315439 Mon Sep 17 00:00:00 2001 From: Amit Kumar Date: Mon, 13 Jul 2026 08:09:54 +0000 Subject: [PATCH 207/707] 8388117: [s390x] is_z_illtrap should recognise all forms Reviewed-by: lucy, hdhiman --- src/hotspot/cpu/s390/assembler_s390.hpp | 2 +- .../gtest/s390/test_assembler_s390.cpp | 69 +++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 test/hotspot/gtest/s390/test_assembler_s390.cpp diff --git a/src/hotspot/cpu/s390/assembler_s390.hpp b/src/hotspot/cpu/s390/assembler_s390.hpp index 95ae442bb49..c834a71ec0c 100644 --- a/src/hotspot/cpu/s390/assembler_s390.hpp +++ b/src/hotspot/cpu/s390/assembler_s390.hpp @@ -3280,7 +3280,7 @@ class Assembler : public AbstractAssembler { return is_z_nop(* (short *) x); } static bool is_z_illtrap(address x) { - return *(uint16_t*)x == 0u; + return *(uint8_t*)x == 0u; } static bool is_z_br(long x) { return is_z_bcr(x) && ((x & 0x00f0) == 0x00f0); diff --git a/test/hotspot/gtest/s390/test_assembler_s390.cpp b/test/hotspot/gtest/s390/test_assembler_s390.cpp new file mode 100644 index 00000000000..2e677508a8d --- /dev/null +++ b/test/hotspot/gtest/s390/test_assembler_s390.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2025, IBM Corporation. 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. + */ + +#if defined(S390) && !defined(ZERO) + +#include "asm/assembler.hpp" +#include "asm/assembler.inline.hpp" +#include "unittest.hpp" + +// --------------------------------------------------------------------------- +// Tests for Assembler::is_z_illtrap +// +// The three emitter forms and what they write into memory (big-endian): +// +// z_illtrap() -> 0x00 0x00 (id == 0) +// z_illtrap(int id) -> 0x00 (e.g. 0x00 0xba) +// z_illtrap_eyecatcher(...) -> ends with z_illtrap(xpattern) -> 0x00 +// +// All forms share: high byte (first byte in memory) == 0x00. +// is_z_illtrap must recognise all of them, not just 0x0000. +// --------------------------------------------------------------------------- + +TEST(AssemblerS390, is_z_illtrap_no_id) { + // z_illtrap() emits 0x0000 — must be detected. + uint8_t buf[] = { 0x00, 0x00 }; + EXPECT_TRUE(Assembler::is_z_illtrap((address)buf)) + << "z_illtrap() (0x0000) must be recognised as illtrap"; +} + +TEST(AssemblerS390, is_z_illtrap_with_id) { + // z_illtrap(id) emits 0x00 — must also be detected. + // Tests a representative set of ids actually used in the source. + const uint8_t ids[] = { 0x22, 0x55, 0x66, 0x99, 0xba, 0xd1, 0xd2, 0xee }; + for (uint8_t id : ids) { + uint8_t buf[] = { 0x00, id }; + EXPECT_TRUE(Assembler::is_z_illtrap((address)buf)) + << "z_illtrap(0x" << std::hex << (int)id << ") must be recognised as illtrap"; + } +} + +TEST(AssemblerS390, is_z_illtrap_false_positive) { + // A non-zero high byte must NOT be recognised as an illtrap. + uint8_t buf[] = { 0x07, 0x00 }; // BCR 0,0 (a NOP — not an illtrap) + EXPECT_FALSE(Assembler::is_z_illtrap((address)buf)) + << "BCR 0,0 (0x0700) must not be recognised as illtrap"; +} + +#endif // S390 && !ZERO + From bc1bd75e7bc6cea24bbc77c45410338b0b110218 Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Mon, 13 Jul 2026 12:32:57 +0000 Subject: [PATCH 208/707] 8388122: NMT: Remove unused comm_size variable from RegionsTree::visit_committed_regions Reviewed-by: stuefe --- src/hotspot/share/nmt/regionsTree.inline.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/hotspot/share/nmt/regionsTree.inline.hpp b/src/hotspot/share/nmt/regionsTree.inline.hpp index 793a5c5f1fa..7f1714fb939 100644 --- a/src/hotspot/share/nmt/regionsTree.inline.hpp +++ b/src/hotspot/share/nmt/regionsTree.inline.hpp @@ -32,7 +32,6 @@ template void RegionsTree::visit_committed_regions(const VirtualMemoryRegion& rgn, F func) { position start = (position)rgn.base(); size_t end = reinterpret_cast(rgn.end()) + 1; - size_t comm_size = 0; NodeHelper prev; visit_range_in_order(start, end, [&](Node* node) { From 0dcfa722aa02cfa9c097c459bab0c1233fbd1897 Mon Sep 17 00:00:00 2001 From: April Ivy Date: Mon, 13 Jul 2026 13:36:26 +0000 Subject: [PATCH 209/707] 8388120: [s390x] c2: c_return_value is redundant Reviewed-by: amitkumar, rrich --- src/hotspot/cpu/s390/s390.ad | 39 +++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index 6cdf40cda9c..256e39b03c2 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -2617,28 +2617,31 @@ frame %{ // stack slot. return_addr(REG Z_R14); - // Location of native (C/C++) and interpreter return values. This - // is specified to be the same as Java. In the 32-bit VM, long - // values are actually returned from native calls in O0:O1 and - // returned to the interpreter in I0:I1. The copying to and from - // the register pairs is done by the appropriate call and epilog - // opcodes. This simplifies the register allocator. - // - // Use register pair for c return value. - c_return_value %{ - assert(ideal_reg >= Op_RegI && ideal_reg <= Op_RegL, "only return normal values"); - static int typeToRegLo[Op_RegL+1] = { 0, 0, Z_R2_num, Z_R2_num, Z_R2_num, Z_F0_num, Z_F0_num, Z_R2_num }; - static int typeToRegHi[Op_RegL+1] = { 0, 0, OptoReg::Bad, OptoReg::Bad, Z_R2_H_num, OptoReg::Bad, Z_F0_H_num, Z_R2_H_num }; - return OptoRegPair(typeToRegHi[ideal_reg], typeToRegLo[ideal_reg]); - %} - // Use register pair for return value. // Location of compiled Java return values. Same as C return_value %{ assert(ideal_reg >= Op_RegI && ideal_reg <= Op_RegL, "only return normal values"); - static int typeToRegLo[Op_RegL+1] = { 0, 0, Z_R2_num, Z_R2_num, Z_R2_num, Z_F0_num, Z_F0_num, Z_R2_num }; - static int typeToRegHi[Op_RegL+1] = { 0, 0, OptoReg::Bad, OptoReg::Bad, Z_R2_H_num, OptoReg::Bad, Z_F0_H_num, Z_R2_H_num }; - return OptoRegPair(typeToRegHi[ideal_reg], typeToRegLo[ideal_reg]); + static const int lo[Op_RegL + 1] = { + 0, + 0, + Z_R2_num, // Op_RegN + Z_R2_num, // Op_RegI + Z_R2_num, // Op_RegP + Z_F0_num, // Op_RegF + Z_F0_num, // Op_RegD + Z_R2_num // Op_RegL + }; + static const int hi[Op_RegL + 1] = { + 0, + 0, + OptoReg::Bad, // Op_RegN + OptoReg::Bad, // Op_RegI + Z_R2_H_num, // Op_RegP + OptoReg::Bad, // Op_RegF + Z_F0_H_num, // Op_RegD + Z_R2_H_num // Op_RegL + }; + return OptoRegPair(hi[ideal_reg], lo[ideal_reg]); %} %} From d343e6c854f2851c3bd09d850898b0d79950e718 Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Mon, 13 Jul 2026 16:23:47 +0000 Subject: [PATCH 210/707] 8387455: Restrict legacy Locale compatibility handling to exact matches Reviewed-by: naoto --- .../share/classes/java/util/Locale.java | 32 ++++++++----- .../classes/java/util/ResourceBundle.java | 2 +- .../util/locale/InternalLocaleBuilder.java | 48 ++++++++++--------- .../classes/sun/util/locale/LanguageTag.java | 3 +- .../provider/LocaleServiceProviderPool.java | 14 ++---- .../java/util/Locale/LocaleEnhanceTest.java | 47 +++++++++++++++++- .../Control/DefaultControlTest.java | 29 +++++++++-- 7 files changed, 124 insertions(+), 51 deletions(-) diff --git a/src/java.base/share/classes/java/util/Locale.java b/src/java.base/share/classes/java/util/Locale.java index 462cd5755c2..f727d301954 100644 --- a/src/java.base/share/classes/java/util/Locale.java +++ b/src/java.base/share/classes/java/util/Locale.java @@ -523,25 +523,24 @@ *

    For compatibility reasons, two * non-conforming locales are treated as special cases. These are * {@code ja_JP_JP} and {@code th_TH_TH}. These are ill-formed - * in BCP 47 since the {@linkplain ##def_variant variants} are too short. To ease migration to BCP 47, - * these are treated specially during construction. These two cases (and only - * these) cause a constructor to generate an extension, all other values behave - * exactly as they did prior to Java 7. + * in BCP 47 since the {@linkplain ##def_variant variants} are too short. To ease + * migration to BCP 47, these are treated specially during creation. Creation + * of these two cases generates a compatibility extension. * *

    Java has used {@code ja_JP_JP} to represent Japanese as used in * Japan together with the Japanese Imperial calendar. This is now * representable using a Unicode locale extension, by specifying the * Unicode locale key {@code ca} (for "calendar") and type - * {@code japanese}. When the Locale constructor is called with the - * arguments "ja", "JP", "JP", the extension "u-ca-japanese" is - * automatically added. + * {@code japanese}. When a {@code Locale} is created with language "ja", an + * empty script, country "JP", variant "JP", and no extensions, the extension + * "u-ca-japanese" is automatically added. * *

    Java has used {@code th_TH_TH} to represent Thai as used in * Thailand together with Thai digits. This is also now representable using * a Unicode locale extension, by specifying the Unicode locale key - * {@code nu} (for "number") and value {@code thai}. When the Locale - * constructor is called with the arguments "th", "TH", "TH", the - * extension "u-nu-thai" is automatically added. + * {@code nu} (for "number") and value {@code thai}. When a {@code Locale} is + * created with language "th", an empty script, country "TH", variant "TH", and + * no extensions, the extension "u-nu-thai" is automatically added. * *

    Legacy language codes

    * @@ -1612,9 +1611,9 @@ public final String toString() { *
  2. Deprecated ISO language codes "iw", "ji", and "in" are * converted to "he", "yi", and "id", respectively. * - *
  3. A locale with language "no", country "NO", and variant - * "NY", representing Norwegian Nynorsk (Norway), is converted - * to a language tag "nn-NO".
  4. + *
  5. A locale with language "no", an empty script, country "NO", variant + * "NY", and no extensions, representing Norwegian Nynorsk (Norway), is + * converted to a language tag "nn-NO".
  6. * *

    Note: Although the language tag obtained by this * method is well-formed (satisfies the syntax requirements @@ -2693,6 +2692,13 @@ public Builder() { *

  7. Locale("th", "TH", "TH") is treated as "th-TH-u-nu-thai" *
  8. Locale("no", "NO", "NY") is treated as "nn-NO" * + *

    For all three cases, compatibility handling only applies when the script + * is empty. Additionally, the Japanese case requires exactly the + * {@code u-ca-japanese} extension, the Thai case requires + * exactly the {@code u-nu-thai} extension, and the Norwegian case + * requires no extensions. If these conditions are not met, the two-letter + * variant is treated as ill-formed, and an {@code IllformedLocaleException} is thrown. + * * @param locale the locale * @return This builder. * @throws IllformedLocaleException if {@code locale} has diff --git a/src/java.base/share/classes/java/util/ResourceBundle.java b/src/java.base/share/classes/java/util/ResourceBundle.java index f91db79891b..2483e184e2e 100644 --- a/src/java.base/share/classes/java/util/ResourceBundle.java +++ b/src/java.base/share/classes/java/util/ResourceBundle.java @@ -2842,7 +2842,7 @@ private static List createCandidateList(BaseLocale base) { boolean isNorwegianBokmal = false; boolean isNorwegianNynorsk = false; if (language.equals("no")) { - if (region.equals("NO") && variant.equals("NY")) { + if (region.equals("NO") && variant.equals("NY") && script.isEmpty()) { variant = ""; isNorwegianNynorsk = true; } else { diff --git a/src/java.base/share/classes/sun/util/locale/InternalLocaleBuilder.java b/src/java.base/share/classes/sun/util/locale/InternalLocaleBuilder.java index 5da725d59c8..499cb757125 100644 --- a/src/java.base/share/classes/sun/util/locale/InternalLocaleBuilder.java +++ b/src/java.base/share/classes/sun/util/locale/InternalLocaleBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 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 @@ -380,27 +380,31 @@ public InternalLocaleBuilder setLocale(BaseLocale base, LocaleExtensions localeE String variant = base.getVariant(); // Special backward compatibility support - - // Exception 1 - ja_JP_JP - if (language.equals("ja") && region.equals("JP") && variant.equals("JP")) { - // When locale ja_JP_JP is created, ca-japanese is always there. - // The builder ignores the variant "JP" - assert("japanese".equals(localeExtensions.getUnicodeLocaleType("ca"))); - variant = ""; - } - // Exception 2 - th_TH_TH - else if (language.equals("th") && region.equals("TH") && variant.equals("TH")) { - // When locale th_TH_TH is created, nu-thai is always there. - // The builder ignores the variant "TH" - assert("thai".equals(localeExtensions.getUnicodeLocaleType("nu"))); - variant = ""; - } - // Exception 3 - no_NO_NY - else if (language.equals("no") && region.equals("NO") && variant.equals("NY")) { - // no_NO_NY is a valid locale and used by Java 6 or older versions. - // The build ignores the variant "NY" and change the language to "nn". - language = "nn"; - variant = ""; + if (script.isEmpty()) { + // Exception 1 - ja_JP_JP + if (language.equals("ja") && region.equals("JP") && variant.equals("JP") + && LocaleExtensions.CALENDAR_JAPANESE.equals(localeExtensions)) { + // When locale ja_JP_JP is created, ca-japanese is always added. + // If the extension exists, the builder ignores the variant "JP" + // otherwise "JP" is merely an ill-formed variant + variant = ""; + } + // Exception 2 - th_TH_TH + else if (language.equals("th") && region.equals("TH") && variant.equals("TH") + && LocaleExtensions.NUMBER_THAI.equals(localeExtensions)){ + // When locale th_TH_TH is created, nu-thai is always added. + // If the extension exists, the builder ignores the variant "TH" + // otherwise "TH" is merely an ill-formed variant + variant = ""; + } + // Exception 3 - no_NO_NY + else if (language.equals("no") && region.equals("NO") && variant.equals("NY") + && localeExtensions == null) { + // no_NO_NY is a valid locale and used by Java 6 or older versions. + // The builder ignores the variant "NY" and changes the language to "nn". + language = "nn"; + variant = ""; + } } // Validate base locale fields before updating internal state. diff --git a/src/java.base/share/classes/sun/util/locale/LanguageTag.java b/src/java.base/share/classes/sun/util/locale/LanguageTag.java index 5ce62a275cc..485fb7f5ca6 100644 --- a/src/java.base/share/classes/sun/util/locale/LanguageTag.java +++ b/src/java.base/share/classes/sun/util/locale/LanguageTag.java @@ -418,7 +418,8 @@ public static LanguageTag parseLocale(BaseLocale baseLocale, LocaleExtensions lo } // Special handling for no_NO_NY - use nn_NO for language tag - if (language.equals("no") && region.equals("NO") && baseVariant.equals("NY")) { + if (language.equals("no") && region.equals("NO") && baseVariant.equals("NY") + && script.isEmpty() && localeExtensions == null) { language = "nn"; baseVariant = EMPTY_SUBTAG; } diff --git a/src/java.base/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java b/src/java.base/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java index cc9a805fe0d..f3d0990429d 100644 --- a/src/java.base/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java +++ b/src/java.base/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java @@ -370,16 +370,10 @@ static Locale getLookupLocale(Locale locale) { locbld.clearExtensions(); lookupLocale = locbld.build(); } catch (IllformedLocaleException e) { - // A Locale with non-empty extensions - // should have well-formed fields except - // for ja_JP_JP and th_TH_TH. Therefore, - // it should never enter in this catch clause. - System.getLogger(LocaleServiceProviderPool.class.getCanonicalName()) - .log(System.Logger.Level.INFO, - "A locale(" + locale + ") has non-empty extensions, but has illformed fields."); - - // Fallback - script field will be lost. - lookupLocale = Locale.of(locale.getLanguage(), locale.getCountry(), locale.getVariant()); + // E.g. "en-Latn-US-a-foo-x-lvariant-xy" + // Extensions can exist while variant is ill-formed + // Simply strip the extensions so that all fields are preserved + lookupLocale = lookupLocale.stripExtensions(); } } return lookupLocale; diff --git a/test/jdk/java/util/Locale/LocaleEnhanceTest.java b/test/jdk/java/util/Locale/LocaleEnhanceTest.java index 3fe3745034d..a4c056d3a77 100644 --- a/test/jdk/java/util/Locale/LocaleEnhanceTest.java +++ b/test/jdk/java/util/Locale/LocaleEnhanceTest.java @@ -58,7 +58,7 @@ * @test * @bug 6875847 6992272 7002320 7015500 7023613 7032820 7033504 7004603 * 7044019 8008577 8176853 8255086 8263202 8287868 8174269 8369452 - * 8369590 8387185 8387253 + * 8369590 8387185 8387253 8387455 * @summary test API changes to Locale * @modules jdk.localedata * @run junit/othervm -esa LocaleEnhanceTest @@ -498,6 +498,23 @@ public void testToLanguageTag() { // private use only language tag is preserved (no extra "und") {"x-elmer", "x-elmer"}, {"x-lvariant-JP", "x-lvariant-JP"}, + // Legacy locale cases + // no/NO/NY case is normalized during `toLanguageTag` + // ja/JP/JP & th/TH/TH case is normalized during `forLanguageTag` + // Script prevents the legacy conversions + {"no-Latn-NO-x-lvariant-NY", + "no-Latn-NO-x-lvariant-NY"}, + {"ja-Jpan-JP-x-lvariant-JP", + "ja-Jpan-JP-x-lvariant-JP"}, + {"th-Thai-TH-x-lvariant-TH", + "th-Thai-TH-x-lvariant-TH"}, + // Unexpected extensions prevent the legacy conversions + {"no-NO-a-foo-x-lvariant-NY", + "no-NO-a-foo-x-lvariant-NY"}, + {"ja-JP-a-foo-x-lvariant-JP", + "ja-JP-a-foo-x-lvariant-JP"}, + {"th-TH-a-foo-x-lvariant-TH", + "th-TH-a-foo-x-lvariant-TH"}, }; for (String[] test : tests1) { Locale locale = Locale.forLanguageTag(test[0]); @@ -729,6 +746,34 @@ public void testBuilderSetLocale() { assertEquals("nn", locale.getLanguage(), "no_NO_NY language"); assertEquals("", locale.getVariant(), "no_NO_NY variant"); + // Legacy locales that stripped their compatibility extensions are invalid + assertThrows(IllformedLocaleException.class, + () -> new Builder().setLocale(Locale.of("ja", "JP", "JP").stripExtensions())); + assertThrows(IllformedLocaleException.class, + () -> new Builder().setLocale(Locale.of("th", "TH", "TH").stripExtensions())); + + // Legacy locales without the correct Unicode locale extension value are invalid + assertThrows(IllformedLocaleException.class, + () -> new Builder().setLocale(Locale.forLanguageTag("ja-JP-u-ca-foobar-x-lvariant-JP"))); + assertThrows(IllformedLocaleException.class, + () -> new Builder().setLocale(Locale.forLanguageTag("th-TH-u-nu-foobar-x-lvariant-TH"))); + + // Legacy locales with additional extensions are invalid + assertThrows(IllformedLocaleException.class, + () -> new Builder().setLocale(Locale.forLanguageTag("no-NO-a-foo-x-lvariant-NY"))); + assertThrows(IllformedLocaleException.class, + () -> new Builder().setLocale(Locale.forLanguageTag("ja-JP-a-foo-x-lvariant-JP"))); + assertThrows(IllformedLocaleException.class, + () -> new Builder().setLocale(Locale.forLanguageTag("th-TH-a-foo-x-lvariant-TH"))); + + // Legacy locales with non-empty script are invalid + assertThrows(IllformedLocaleException.class, + () -> new Builder().setLocale(Locale.forLanguageTag("ja-Jpan-JP-u-ca-japanese-x-lvariant-JP"))); + assertThrows(IllformedLocaleException.class, + () -> new Builder().setLocale(Locale.forLanguageTag("th-Thai-TH-u-nu-thai-x-lvariant-TH"))); + assertThrows(IllformedLocaleException.class, + () -> new Builder().setLocale(Locale.forLanguageTag("no-Latn-NO-x-lvariant-NY"))); + // non-canonical, non-legacy locales are invalid assertThrows(IllformedLocaleException.class, () -> new Builder().setLocale(Locale.of("123", "4567", "89")), "123_4567_89"); diff --git a/test/jdk/java/util/ResourceBundle/Control/DefaultControlTest.java b/test/jdk/java/util/ResourceBundle/Control/DefaultControlTest.java index b8b49406c05..a70ebf9e527 100644 --- a/test/jdk/java/util/ResourceBundle/Control/DefaultControlTest.java +++ b/test/jdk/java/util/ResourceBundle/Control/DefaultControlTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -22,7 +22,7 @@ */ /* * @test - * @bug 5102289 6278334 8261179 + * @bug 5102289 6278334 8261179 8387455 * @summary Test the default Control implementation. The expiration * functionality of newBundle, getTimeToLive, and needsReload is * tested by ExpirationTest.sh. The factory methods are tested @@ -171,6 +171,15 @@ private static void testGetCandidateLocales() { candidateData.put(Locale.ROOT, new Locale[] { Locale.ROOT }); + // Norwegian Nynorsk + candidateData.put(Locale.of("no", "NO", "NY"), new Locale[] { + Locale.of("nn", "NO"), + Locale.of("nn"), + Locale.of("no", "NO", "NY"), + Locale.of("no", "NO"), + Locale.of("no"), + Locale.ROOT}); + // Norwegian Bokmal candidateData.put(Locale.forLanguageTag("nb-NO-POSIX"), new Locale[] { Locale.forLanguageTag("nb-NO-POSIX"), @@ -188,7 +197,21 @@ private static void testGetCandidateLocales() { Locale.forLanguageTag("no"), Locale.forLanguageTag("nb"), Locale.ROOT}); - + // Appears as no-NO-NY legacy locale (but contains script) so treat as Norwegian Bokmal + candidateData.put(Locale.forLanguageTag("no-Latn-NO-x-lvariant-NY"), new Locale[] { + Locale.forLanguageTag("no-Latn-NO-x-lvariant-NY"), + Locale.forLanguageTag("nb-Latn-NO-x-lvariant-NY"), + Locale.forLanguageTag("no-Latn-NO"), + Locale.forLanguageTag("nb-Latn-NO"), + Locale.forLanguageTag("no-Latn"), + Locale.forLanguageTag("nb-Latn"), + Locale.forLanguageTag("no-NO-x-lvariant-NY"), + Locale.forLanguageTag("nb-NO-x-lvariant-NY"), + Locale.forLanguageTag("no-NO"), + Locale.forLanguageTag("nb-NO"), + Locale.forLanguageTag("no"), + Locale.forLanguageTag("nb"), + Locale.ROOT}); for (Locale locale : candidateData.keySet()) { List candidates = CONTROL.getCandidateLocales("any", locale); From 151516fae22ee71c12ea76a51fcf5be69f2e5dbf Mon Sep 17 00:00:00 2001 From: Elif Aslan Date: Mon, 13 Jul 2026 16:46:50 +0000 Subject: [PATCH 211/707] 8387985: sun/tools/jstat shell tests fail on platforms that do not support ParallelGC Reviewed-by: cjplummer, dholmes --- test/jdk/sun/tools/jstat/jstatGcCapacityOutput1.sh | 3 ++- test/jdk/sun/tools/jstat/jstatGcMetaCapacityOutput1.sh | 3 ++- test/jdk/sun/tools/jstat/jstatGcNewCapacityOutput1.sh | 3 ++- test/jdk/sun/tools/jstat/jstatGcNewOutput1.sh | 3 ++- test/jdk/sun/tools/jstat/jstatGcOldCapacityOutput1.sh | 5 +++-- test/jdk/sun/tools/jstat/jstatGcOldOutput1.sh | 3 ++- test/jdk/sun/tools/jstat/jstatGcOutput1.sh | 3 ++- test/jdk/sun/tools/jstat/jstatLineCounts1.sh | 3 ++- test/jdk/sun/tools/jstat/jstatLineCounts2.sh | 3 ++- test/jdk/sun/tools/jstat/jstatLineCounts3.sh | 3 ++- test/jdk/sun/tools/jstat/jstatLineCounts4.sh | 3 ++- test/jdk/sun/tools/jstat/jstatTimeStamp1.sh | 3 ++- 12 files changed, 25 insertions(+), 13 deletions(-) diff --git a/test/jdk/sun/tools/jstat/jstatGcCapacityOutput1.sh b/test/jdk/sun/tools/jstat/jstatGcCapacityOutput1.sh index c1908855ea7..6e184e9dc31 100644 --- a/test/jdk/sun/tools/jstat/jstatGcCapacityOutput1.sh +++ b/test/jdk/sun/tools/jstat/jstatGcCapacityOutput1.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2004, 2025, 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 @@ -23,6 +23,7 @@ # @test # @bug 4990825 +# @requires vm.gc.Parallel # @run shell jstatGcCapacityOutput1.sh # @summary Test that output of 'jstat -gccapacity 0' has expected line counts diff --git a/test/jdk/sun/tools/jstat/jstatGcMetaCapacityOutput1.sh b/test/jdk/sun/tools/jstat/jstatGcMetaCapacityOutput1.sh index b16d0e38d02..b5caccb1768 100644 --- a/test/jdk/sun/tools/jstat/jstatGcMetaCapacityOutput1.sh +++ b/test/jdk/sun/tools/jstat/jstatGcMetaCapacityOutput1.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2013, 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,6 +23,7 @@ # @test # @bug 4990825 +# @requires vm.gc.Parallel # @run shell jstatGcMetaCapacityOutput1.sh # @summary Test that output of 'jstat -gcmetacapacity 0' has expected line counts diff --git a/test/jdk/sun/tools/jstat/jstatGcNewCapacityOutput1.sh b/test/jdk/sun/tools/jstat/jstatGcNewCapacityOutput1.sh index 64ce2efd455..96f0722a9e1 100644 --- a/test/jdk/sun/tools/jstat/jstatGcNewCapacityOutput1.sh +++ b/test/jdk/sun/tools/jstat/jstatGcNewCapacityOutput1.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2004, 2025, 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 @@ -23,6 +23,7 @@ # @test # @bug 4990825 +# @requires vm.gc.Parallel # @run shell jstatGcNewCapacityOutput1.sh # @summary Test that output of 'jstat -gcnewcapacity 0' has expected line counts diff --git a/test/jdk/sun/tools/jstat/jstatGcNewOutput1.sh b/test/jdk/sun/tools/jstat/jstatGcNewOutput1.sh index b15ec02d2b0..96e2db61488 100644 --- a/test/jdk/sun/tools/jstat/jstatGcNewOutput1.sh +++ b/test/jdk/sun/tools/jstat/jstatGcNewOutput1.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2004, 2025, 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 @@ -23,6 +23,7 @@ # @test # @bug 4990825 +# @requires vm.gc.Parallel # @run shell jstatGcNewOutput1.sh # @summary Test that output of 'jstat -gcnew 0' has expected line counts diff --git a/test/jdk/sun/tools/jstat/jstatGcOldCapacityOutput1.sh b/test/jdk/sun/tools/jstat/jstatGcOldCapacityOutput1.sh index 1c13d6f916d..0c5e2e19894 100644 --- a/test/jdk/sun/tools/jstat/jstatGcOldCapacityOutput1.sh +++ b/test/jdk/sun/tools/jstat/jstatGcOldCapacityOutput1.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2004, 2025, 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 @@ -23,8 +23,9 @@ # @test # @bug 4990825 +# @requires vm.gc.Parallel # @run shell jstatGcOldCapacityOutput1.sh -# @summary Test that output of 'jstat -gcoldcapcaity 0' has expected line counts +# @summary Test that output of 'jstat -gcoldcapacity 0' has expected line counts . ${TESTSRC-.}/../../jvmstat/testlibrary/utils.sh diff --git a/test/jdk/sun/tools/jstat/jstatGcOldOutput1.sh b/test/jdk/sun/tools/jstat/jstatGcOldOutput1.sh index 7f505228b12..0f857ccb1e6 100644 --- a/test/jdk/sun/tools/jstat/jstatGcOldOutput1.sh +++ b/test/jdk/sun/tools/jstat/jstatGcOldOutput1.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2004, 2025, 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 @@ -23,6 +23,7 @@ # @test # @bug 4990825 +# @requires vm.gc.Parallel # @run shell jstatGcOldOutput1.sh # @summary Test that output of 'jstat -gcold 0' has expected line counts diff --git a/test/jdk/sun/tools/jstat/jstatGcOutput1.sh b/test/jdk/sun/tools/jstat/jstatGcOutput1.sh index dfffa2d1a55..5862fda3fd7 100644 --- a/test/jdk/sun/tools/jstat/jstatGcOutput1.sh +++ b/test/jdk/sun/tools/jstat/jstatGcOutput1.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2004, 2025, 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 @@ -23,6 +23,7 @@ # @test # @bug 4990825 +# @requires vm.gc.Parallel # @run shell jstatGcOutput1.sh # @summary Test that output of 'jstat -gc 0' has expected line counts diff --git a/test/jdk/sun/tools/jstat/jstatLineCounts1.sh b/test/jdk/sun/tools/jstat/jstatLineCounts1.sh index 97338b8e793..ca6adce96a5 100644 --- a/test/jdk/sun/tools/jstat/jstatLineCounts1.sh +++ b/test/jdk/sun/tools/jstat/jstatLineCounts1.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2004, 2025, 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 @@ -23,6 +23,7 @@ # @test # @bug 4990825 +# @requires vm.gc.Parallel # @run shell jstatLineCounts1.sh # @summary Test that output of 'jstat -gcutil 0 250 5' has expected line counts diff --git a/test/jdk/sun/tools/jstat/jstatLineCounts2.sh b/test/jdk/sun/tools/jstat/jstatLineCounts2.sh index eab19f3931e..a668df72e0e 100644 --- a/test/jdk/sun/tools/jstat/jstatLineCounts2.sh +++ b/test/jdk/sun/tools/jstat/jstatLineCounts2.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2004, 2025, 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 @@ -23,6 +23,7 @@ # @test # @bug 4990825 +# @requires vm.gc.Parallel # @run shell jstatLineCounts2.sh # @summary Test that output of 'jstat -gcutil 0' has expected line counts diff --git a/test/jdk/sun/tools/jstat/jstatLineCounts3.sh b/test/jdk/sun/tools/jstat/jstatLineCounts3.sh index 9a769a92464..bffffc8a38e 100644 --- a/test/jdk/sun/tools/jstat/jstatLineCounts3.sh +++ b/test/jdk/sun/tools/jstat/jstatLineCounts3.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2004, 2025, 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 @@ -23,6 +23,7 @@ # @test # @bug 4990825 +# @requires vm.gc.Parallel # @run shell jstatLineCounts3.sh # @summary Test that output of 'jstat -gcutil -h 10 250 10' has expected line counts diff --git a/test/jdk/sun/tools/jstat/jstatLineCounts4.sh b/test/jdk/sun/tools/jstat/jstatLineCounts4.sh index 817c3b14f62..9ad1f57a5d5 100644 --- a/test/jdk/sun/tools/jstat/jstatLineCounts4.sh +++ b/test/jdk/sun/tools/jstat/jstatLineCounts4.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2004, 2025, 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 @@ -23,6 +23,7 @@ # @test # @bug 4990825 +# @requires vm.gc.Parallel # @run shell jstatLineCounts4.sh # @summary Test that output of 'jstat -gcutil -h 10 250 11' has expected line counts diff --git a/test/jdk/sun/tools/jstat/jstatTimeStamp1.sh b/test/jdk/sun/tools/jstat/jstatTimeStamp1.sh index db71314fcf9..4e4cb8df426 100644 --- a/test/jdk/sun/tools/jstat/jstatTimeStamp1.sh +++ b/test/jdk/sun/tools/jstat/jstatTimeStamp1.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2004, 2020, 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 @@ -23,6 +23,7 @@ # @test # @bug 4990825 +# @requires vm.gc.Parallel # @run shell jstatTimeStamp1.sh # @summary Test that output of 'jstat -gcutil -t 0' has expected format From 30abe0b3a6ee2d9a8ef992e58e8e81d5aadaf49f Mon Sep 17 00:00:00 2001 From: Vladimir Ivanov Date: Mon, 13 Jul 2026 17:51:11 +0000 Subject: [PATCH 212/707] 8373487: Out-of-bounds access in AlignmentGapAccess test Reviewed-by: dlong, ayang --- test/hotspot/jtreg/ProblemList.txt | 2 -- test/hotspot/jtreg/compiler/unsafe/AlignmentGapAccess.java | 7 ++++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index e0005bfde07..0a98477be69 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -67,8 +67,6 @@ compiler/interpreter/Test6833129.java 8335266 generic-i586 compiler/c2/aarch64/TestStaticCallStub.java 8359963 generic-aarch64 -compiler/unsafe/AlignmentGapAccess.java 8373487 generic-all - compiler/escapeAnalysis/TestBCEscapeAnalyzerOverflow.java 8387392 windows-aarch64 ############################################################################# diff --git a/test/hotspot/jtreg/compiler/unsafe/AlignmentGapAccess.java b/test/hotspot/jtreg/compiler/unsafe/AlignmentGapAccess.java index ac3c4b0278a..8b2ee067140 100644 --- a/test/hotspot/jtreg/compiler/unsafe/AlignmentGapAccess.java +++ b/test/hotspot/jtreg/compiler/unsafe/AlignmentGapAccess.java @@ -38,17 +38,22 @@ public class AlignmentGapAccess { static class A { int fa; } static class B extends A { byte fb; } + static class C extends B { int fc; } static final long FA_OFFSET = UNSAFE.objectFieldOffset(A.class, "fa"); static final long FB_OFFSET = UNSAFE.objectFieldOffset(B.class, "fb"); + static final long FC_OFFSET = UNSAFE.objectFieldOffset(C.class, "fc"); static int test(B obj) { return UNSAFE.getInt(obj, FB_OFFSET + 1); } public static void main(String[] args) { + System.out.printf("Layout: +%d: fa; +%d: fb; +%d: fc\n", + FA_OFFSET, FB_OFFSET, FC_OFFSET); + for (int i = 0; i < 20_000; i++) { - test(new B()); + test(new C()); } System.out.println("TEST PASSED"); } From 6ec04bb20423110d6a991c827104f37571e4ead0 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Mon, 13 Jul 2026 18:04:04 +0000 Subject: [PATCH 213/707] 8387675: IS_WINVISTA macro is obsolete Reviewed-by: aivanov, stuefe, prr --- .../sun/awt/Win32GraphicsEnvironment.java | 9 +---- .../sun/awt/windows/WComponentPeer.java | 10 ++---- .../classes/sun/awt/windows/WWindowPeer.java | 36 +++++-------------- .../libawt/java2d/windows/WindowsFlags.cpp | 5 ++- .../windows/native/libawt/windows/awt.h | 6 +--- .../libawt/windows/awt_DesktopProperties.cpp | 14 +------- .../native/libawt/windows/awt_MenuItem.cpp | 8 ++--- .../native/libawt/windows/awt_TextArea.cpp | 4 +-- .../native/libawt/windows/awt_TextField.cpp | 4 +-- .../libawt/windows/awt_Win32GraphicsEnv.cpp | 19 +--------- 10 files changed, 22 insertions(+), 93 deletions(-) diff --git a/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java b/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java index 9d09f13e525..8bb7f04420c 100644 --- a/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java +++ b/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java @@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -255,11 +255,4 @@ public static boolean isDWMCompositionEnabled() { private static void dwmCompositionChanged(boolean enabled) { isDWMCompositionEnabled = enabled; } - - /** - * Used to find out if the OS is Windows Vista or later. - * - * @return {@code true} if the OS is Vista or later, {@code false} otherwise - */ - public static native boolean isVistaOS(); } diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java index 00ad60c8bb3..b288d5beb07 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java @@ -1082,16 +1082,10 @@ public void setBoundsOperation(int operation) { */ public boolean isAccelCapable() { if (!isAccelCapable || - !isContainingTopLevelAccelCapable((Component)target)) - { + !isContainingTopLevelAccelCapable((Component)target)) { return false; } - - boolean isTranslucent = - SunToolkit.isContainingTopLevelTranslucent((Component)target); - // D3D/OGL and translucent windows interacted poorly in Windows XP; - // these problems are no longer present in Vista - return !isTranslucent || Win32GraphicsEnvironment.isVistaOS(); + return true; } /** diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java index 9c1c7665f4b..3c8e4de23cb 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java @@ -683,16 +683,6 @@ public void setOpacity(float opacity) { throw new IllegalArgumentException( "The value of opacity should be in the range [0.0f .. 1.0f]."); } - - if (((this.opacity == 1.0f && opacity < 1.0f) || - (this.opacity < 1.0f && opacity == 1.0f)) && - !Win32GraphicsEnvironment.isVistaOS()) - { - // non-Vista OS: only replace the surface data if opacity status - // changed (see WComponentPeer.isAccelCapable() for more) - replaceSurfaceDataRecursively((Component)getTarget()); - } - this.opacity = opacity; final int maxOpacity = 0xff; @@ -734,14 +724,6 @@ public void setOpaque(boolean isOpaque) { } } - boolean isVistaOS = Win32GraphicsEnvironment.isVistaOS(); - - if (this.isOpaque != isOpaque && !isVistaOS) { - // non-Vista OS: only replace the surface data if the opacity - // status changed (see WComponentPeer.isAccelCapable() for more) - replaceSurfaceDataRecursively(target); - } - synchronized (getStateLock()) { this.isOpaque = isOpaque; setOpaqueImpl(isOpaque); @@ -756,16 +738,14 @@ public void setOpaque(boolean isOpaque) { } } - if (isVistaOS) { - // On Vista: setting the window non-opaque makes the window look - // rectangular, though still catching the mouse clicks within - // its shape only. To restore the correct visual appearance - // of the window (i.e. w/ the correct shape) we have to reset - // the shape. - Shape shape = target.getShape(); - if (shape != null) { - target.setShape(shape); - } + // Since Vista: setting the window non-opaque makes the window look + // rectangular, though still catching the mouse clicks within + // its shape only. To restore the correct visual appearance + // of the window (i.e. w/ the correct shape) we have to reset + // the shape. + Shape shape = target.getShape(); + if (shape != null) { + target.setShape(shape); } if (target.isVisible()) { diff --git a/src/java.desktop/windows/native/libawt/java2d/windows/WindowsFlags.cpp b/src/java.desktop/windows/native/libawt/java2d/windows/WindowsFlags.cpp index 189525c39a1..c294303f4c1 100644 --- a/src/java.desktop/windows/native/libawt/java2d/windows/WindowsFlags.cpp +++ b/src/java.desktop/windows/native/libawt/java2d/windows/WindowsFlags.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -88,8 +88,7 @@ void GetFlagValues(JNIEnv *env, jclass wFlagsClass) } useD3D = d3dEnabled; forceD3DUsage = d3dSet; - setHighDPIAware = - (IS_WINVISTA && GetStaticBoolean(env, wFlagsClass, "setHighDPIAware")); + setHighDPIAware = GetStaticBoolean(env, wFlagsClass, "setHighDPIAware"); JNU_CHECK_EXCEPTION(env); J2dTraceLn(J2D_TRACE_INFO, "WindowsFlags (native):"); diff --git a/src/java.desktop/windows/native/libawt/windows/awt.h b/src/java.desktop/windows/native/libawt/windows/awt.h index c367471afa9..8a09d6af994 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt.h +++ b/src/java.desktop/windows/native/libawt/windows/awt.h @@ -154,12 +154,8 @@ typedef AwtObject* PDATA; JNI_TRUE) /* /NEW JNI */ -/* - * IS_WINVISTA returns TRUE on Vista - */ -#define IS_WINVISTA (LOBYTE(LOWORD(::GetVersion())) >= 6) #define IS_WIN8 ( \ - (IS_WINVISTA && (HIBYTE(LOWORD(::GetVersion())) >= 2)) || \ + (LOBYTE(LOWORD(::GetVersion())) == 6 && (HIBYTE(LOWORD(::GetVersion())) >= 2)) || \ (LOBYTE(LOWORD(::GetVersion())) > 6)) #define IS_WINVER_ATLEAST(maj, min) \ diff --git a/src/java.desktop/windows/native/libawt/windows/awt_DesktopProperties.cpp b/src/java.desktop/windows/native/libawt/windows/awt_DesktopProperties.cpp index d5ad022c1e0..a00938f764f 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_DesktopProperties.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_DesktopProperties.cpp @@ -272,20 +272,8 @@ void AwtDesktopProperties::GetNonClientParameters() { // general window properties // NONCLIENTMETRICS ncmetrics; + ncmetrics.cbSize = sizeof(ncmetrics); - // Fix for 6944516: specify correct size for ncmetrics on WIN2K/XP - // Microsoft recommend to subtract the size of 'iPaddedBorderWidth' field - // when running on XP. However this can't be referenced at compile time - // with the older SDK, so there use 'lfMessageFont' plus its size. - if (!IS_WINVISTA) { -#if defined(_MSC_VER) - ncmetrics.cbSize = offsetof(NONCLIENTMETRICS, iPaddedBorderWidth); -#else - ncmetrics.cbSize = offsetof(NONCLIENTMETRICS,lfMessageFont) + sizeof(LOGFONT); -#endif - } else { - ncmetrics.cbSize = sizeof(ncmetrics); - } VERIFY( SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncmetrics.cbSize, &ncmetrics, FALSE) ); float invScaleX; diff --git a/src/java.desktop/windows/native/libawt/windows/awt_MenuItem.cpp b/src/java.desktop/windows/native/libawt/windows/awt_MenuItem.cpp index ff7e01df3e8..d1a4fc68d03 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_MenuItem.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_MenuItem.cpp @@ -395,9 +395,7 @@ AwtMenuItem::DrawSelf(DRAWITEMSTRUCT& drawInfo) //draw check mark int checkWidth = ::GetSystemMetrics(SM_CXMENUCHECK); // Workaround for CR#6401956 - if (IS_WINVISTA) { - AdjustCheckWidth(checkWidth); - } + AdjustCheckWidth(checkWidth); if (IsCheckbox()) { // means that target is a java.awt.CheckboxMenuItem @@ -558,9 +556,7 @@ void AwtMenuItem::MeasureSelf(HDC hDC, MEASUREITEMSTRUCT& measureInfo) if (!IsTopMenu()) { int checkWidth = ::GetSystemMetrics(SM_CXMENUCHECK); // Workaround for CR#6401956 - if (IS_WINVISTA) { - AdjustCheckWidth(checkWidth); - } + AdjustCheckWidth(checkWidth); measureInfo.itemWidth += checkWidth; // Add in shortcut width, if one exists. diff --git a/src/java.desktop/windows/native/libawt/windows/awt_TextArea.cpp b/src/java.desktop/windows/native/libawt/windows/awt_TextArea.cpp index 6dc21c5adda..282bd194f2f 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_TextArea.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_TextArea.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -78,7 +78,7 @@ void AwtTextArea::EditSetSel(CHARRANGE &cr) { SendMessage(EM_EXSETSEL, 0, reinterpret_cast(&cr)); SendMessage(EM_HIDESELECTION, TRUE, TRUE); // 6417581: force expected drawing - if (IS_WINVISTA && cr.cpMin == cr.cpMax) { + if (cr.cpMin == cr.cpMax) { ::InvalidateRect(GetHWnd(), NULL, TRUE); } } diff --git a/src/java.desktop/windows/native/libawt/windows/awt_TextField.cpp b/src/java.desktop/windows/native/libawt/windows/awt_TextField.cpp index 5518ab91145..c1b682ffa46 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_TextField.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_TextField.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -55,7 +55,7 @@ void AwtTextField::EditSetSel(CHARRANGE &cr) { SendMessage(EM_EXSETSEL, 0, reinterpret_cast(&cr)); // 6417581: force expected drawing - if (IS_WINVISTA && cr.cpMin == cr.cpMax) { + if (cr.cpMin == cr.cpMax) { ::InvalidateRect(GetHWnd(), NULL, TRUE); } diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsEnv.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsEnv.cpp index cc53f4a3322..5aa7731c8a6 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsEnv.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsEnv.cpp @@ -90,20 +90,13 @@ void DWMResetCompositionEnabled() { } /** - * Returns true if dwm composition is enabled, false if it is not applicable - * (if the OS is not Vista) or dwm composition is disabled. + * Returns true if DWM composition is enabled, false if DWM composition is disabled. */ BOOL DWMIsCompositionEnabled() { - // cheaper to check than whether it's vista or not if (dwmIsCompositionEnabled != DWM_COMP_UNDEFINED) { return (BOOL)dwmIsCompositionEnabled; } - if (!IS_WINVISTA) { - dwmIsCompositionEnabled = FALSE; - return FALSE; - } - BOOL bRes = FALSE; try { @@ -337,13 +330,3 @@ Java_sun_awt_Win32GraphicsEnvironment_getYResolution(JNIEnv *env, jobject wge) CATCH_BAD_ALLOC_RET(0); } -/* - * Class: sun_awt_Win32GraphicsEnvironment - * Method: isVistaOS - * Signature: ()Z - */ -JNIEXPORT jboolean JNICALL Java_sun_awt_Win32GraphicsEnvironment_isVistaOS - (JNIEnv *env, jclass wgeclass) -{ - return IS_WINVISTA; -} From be40b6bcdab37368ea3c769e575b36e290d0c6a1 Mon Sep 17 00:00:00 2001 From: Ashay Rane Date: Tue, 14 Jul 2026 09:12:36 +0000 Subject: [PATCH 214/707] 8387188: JImageExtractTest.java test should deny APPEND_DATA ACL in `testExtractToReadOnlyDir` Reviewed-by: alanb, dbalek --- test/jdk/tools/jimage/JImageExtractTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/jdk/tools/jimage/JImageExtractTest.java b/test/jdk/tools/jimage/JImageExtractTest.java index eec10439cf5..e5f62232097 100644 --- a/test/jdk/tools/jimage/JImageExtractTest.java +++ b/test/jdk/tools/jimage/JImageExtractTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -188,7 +188,8 @@ public void testExtractToReadOnlyDir() throws IOException { AclEntry entry = AclEntry.newBuilder() .setType(AclEntryType.DENY) .setPrincipal(fileOwner) - .setPermissions(AclEntryPermission.WRITE_DATA) + .setPermissions(AclEntryPermission.WRITE_DATA, + AclEntryPermission.APPEND_DATA) .setFlags(AclEntryFlag.FILE_INHERIT, AclEntryFlag.DIRECTORY_INHERIT) .build(); List acl = view.getAcl(); From 6e2a4f847fc91ab25a167ca833bdeaaad5ee8d48 Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Tue, 14 Jul 2026 12:57:49 +0000 Subject: [PATCH 215/707] 8386322: Float16Vector.toString should render lane values using Float16.toString Reviewed-by: psandoz, rgiulietti --- .../jdk/incubator/vector/Float16Vector.java | 12 ++++++++---- .../jdk/incubator/vector/X-Vector.java.template | 16 ++++++++++++++++ .../incubator/vector/Float16Vector128Tests.java | 3 ++- .../incubator/vector/Float16Vector256Tests.java | 3 ++- .../incubator/vector/Float16Vector512Tests.java | 3 ++- .../incubator/vector/Float16Vector64Tests.java | 3 ++- .../incubator/vector/Float16VectorMaxTests.java | 3 ++- .../vector/templates/Unit-Miscellaneous.template | 7 ++++++- 8 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector.java index ce3a67357f9..a42fb44dd02 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector.java @@ -3707,8 +3707,10 @@ public final ShortVector viewAsIntegralLanes() { * in lane order. * * The string is produced as if by a call to {@link - * java.util.Arrays#toString(short[]) Arrays.toString()}, - * as appropriate to the {@code short} array returned by + * java.util.Arrays#toString(Object[]) Arrays.toString()}, + * as appropriate to a {@code Float16} array whose elements + * are obtained by applying {@link Float16#shortBitsToFloat16(short)} + * to each element of the {@code short[]} array returned by * {@link #toArray this.toArray()}. * * @return a string of the form {@code "[0,1,2...]"} @@ -3718,8 +3720,10 @@ public final ShortVector viewAsIntegralLanes() { @ForceInline public final String toString() { - // now that toArray is strongly typed, we can define this - return Arrays.toString(toArray()); + // Render the lanes as Float16 values; Float16.toString produces + // human-readable text and canonicalizes NaN, Infinity and -0.0 + // independent of the underlying bit encoding. + return Arrays.toString(toFloat16Array()); } /** diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template index f11c6283685..00445cc8ac5 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template @@ -5723,10 +5723,19 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp * {@code "[0,1,2...]"}, reporting the lane values of this vector, * in lane order. * +#if[FP16] + * The string is produced as if by a call to {@link + * java.util.Arrays#toString(Object[]) Arrays.toString()}, + * as appropriate to a {@code Float16} array whose elements + * are obtained by applying {@link Float16#shortBitsToFloat16(short)} + * to each element of the {@code short[]} array returned by + * {@link #toArray this.toArray()}. +#else[FP16] * The string is produced as if by a call to {@link * java.util.Arrays#toString($type$[]) Arrays.toString()}, * as appropriate to the {@code $type$} array returned by * {@link #toArray this.toArray()}. +#end[FP16] * * @return a string of the form {@code "[0,1,2...]"} * reporting the lane values of this vector @@ -5735,8 +5744,15 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp @ForceInline public final String toString() { +#if[FP16] + // Render the lanes as Float16 values; Float16.toString produces + // human-readable text and canonicalizes NaN, Infinity and -0.0 + // independent of the underlying bit encoding. + return Arrays.toString(toFloat16Array()); +#else[FP16] // now that toArray is strongly typed, we can define this return Arrays.toString(toArray()); +#end[FP16] } /** diff --git a/test/jdk/jdk/incubator/vector/Float16Vector128Tests.java b/test/jdk/jdk/incubator/vector/Float16Vector128Tests.java index a33e83d14ea..215f6ef9c47 100644 --- a/test/jdk/jdk/incubator/vector/Float16Vector128Tests.java +++ b/test/jdk/jdk/incubator/vector/Float16Vector128Tests.java @@ -5412,7 +5412,8 @@ static void toStringFloat16Vector128TestsSmokeTest(IntFunction fa) { String str = av.toString(); short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); - Assert.assertTrue(str.equals(Arrays.toString(subarr)), "at index " + i + ", string should be = " + Arrays.toString(subarr) + ", but is = " + str); + String expectedStr = Arrays.toString(toFloat16Array(subarr)); + Assert.assertTrue(str.equals(expectedStr), "at index " + i + ", string should be = " + expectedStr + ", but is = " + str); } } diff --git a/test/jdk/jdk/incubator/vector/Float16Vector256Tests.java b/test/jdk/jdk/incubator/vector/Float16Vector256Tests.java index 99b167d4024..f198a4a970f 100644 --- a/test/jdk/jdk/incubator/vector/Float16Vector256Tests.java +++ b/test/jdk/jdk/incubator/vector/Float16Vector256Tests.java @@ -5412,7 +5412,8 @@ static void toStringFloat16Vector256TestsSmokeTest(IntFunction fa) { String str = av.toString(); short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); - Assert.assertTrue(str.equals(Arrays.toString(subarr)), "at index " + i + ", string should be = " + Arrays.toString(subarr) + ", but is = " + str); + String expectedStr = Arrays.toString(toFloat16Array(subarr)); + Assert.assertTrue(str.equals(expectedStr), "at index " + i + ", string should be = " + expectedStr + ", but is = " + str); } } diff --git a/test/jdk/jdk/incubator/vector/Float16Vector512Tests.java b/test/jdk/jdk/incubator/vector/Float16Vector512Tests.java index 1c391497015..3d2b7de23f9 100644 --- a/test/jdk/jdk/incubator/vector/Float16Vector512Tests.java +++ b/test/jdk/jdk/incubator/vector/Float16Vector512Tests.java @@ -5412,7 +5412,8 @@ static void toStringFloat16Vector512TestsSmokeTest(IntFunction fa) { String str = av.toString(); short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); - Assert.assertTrue(str.equals(Arrays.toString(subarr)), "at index " + i + ", string should be = " + Arrays.toString(subarr) + ", but is = " + str); + String expectedStr = Arrays.toString(toFloat16Array(subarr)); + Assert.assertTrue(str.equals(expectedStr), "at index " + i + ", string should be = " + expectedStr + ", but is = " + str); } } diff --git a/test/jdk/jdk/incubator/vector/Float16Vector64Tests.java b/test/jdk/jdk/incubator/vector/Float16Vector64Tests.java index 6ef651860ad..c44f0f7c576 100644 --- a/test/jdk/jdk/incubator/vector/Float16Vector64Tests.java +++ b/test/jdk/jdk/incubator/vector/Float16Vector64Tests.java @@ -5412,7 +5412,8 @@ static void toStringFloat16Vector64TestsSmokeTest(IntFunction fa) { String str = av.toString(); short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); - Assert.assertTrue(str.equals(Arrays.toString(subarr)), "at index " + i + ", string should be = " + Arrays.toString(subarr) + ", but is = " + str); + String expectedStr = Arrays.toString(toFloat16Array(subarr)); + Assert.assertTrue(str.equals(expectedStr), "at index " + i + ", string should be = " + expectedStr + ", but is = " + str); } } diff --git a/test/jdk/jdk/incubator/vector/Float16VectorMaxTests.java b/test/jdk/jdk/incubator/vector/Float16VectorMaxTests.java index 61efa3de9a0..92945d74ceb 100644 --- a/test/jdk/jdk/incubator/vector/Float16VectorMaxTests.java +++ b/test/jdk/jdk/incubator/vector/Float16VectorMaxTests.java @@ -5418,7 +5418,8 @@ static void toStringFloat16VectorMaxTestsSmokeTest(IntFunction fa) { String str = av.toString(); short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); - Assert.assertTrue(str.equals(Arrays.toString(subarr)), "at index " + i + ", string should be = " + Arrays.toString(subarr) + ", but is = " + str); + String expectedStr = Arrays.toString(toFloat16Array(subarr)); + Assert.assertTrue(str.equals(expectedStr), "at index " + i + ", string should be = " + expectedStr + ", but is = " + str); } } diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-Miscellaneous.template b/test/jdk/jdk/incubator/vector/templates/Unit-Miscellaneous.template index 0ae9342539f..b673e5e1fab 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-Miscellaneous.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-Miscellaneous.template @@ -87,7 +87,12 @@ String str = av.toString(); $type$ subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); - Assert.assertTrue(str.equals(Arrays.toString(subarr)), "at index " + i + ", string should be = " + Arrays.toString(subarr) + ", but is = " + str); +#if[FP16] + String expectedStr = Arrays.toString(toFloat16Array(subarr)); +#else[FP16] + String expectedStr = Arrays.toString(subarr); +#end[FP16] + Assert.assertTrue(str.equals(expectedStr), "at index " + i + ", string should be = " + expectedStr + ", but is = " + str); } } From feb944c6c9396c7aa2b34e70366402d02b42693f Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 14 Jul 2026 13:29:26 +0000 Subject: [PATCH 216/707] 8387637: Dead code for upwards interpreter stacks Reviewed-by: coleenp, dholmes, shade --- src/hotspot/cpu/aarch64/frame_aarch64.hpp | 2 -- src/hotspot/cpu/arm/frame_arm.hpp | 2 -- src/hotspot/cpu/ppc/frame_ppc.hpp | 2 -- src/hotspot/cpu/riscv/frame_riscv.hpp | 2 -- src/hotspot/cpu/s390/frame_s390.hpp | 2 -- src/hotspot/cpu/x86/frame_x86.hpp | 2 -- src/hotspot/cpu/zero/frame_zero.hpp | 2 -- .../share/interpreter/abstractInterpreter.hpp | 6 ++--- src/hotspot/share/runtime/frame.cpp | 25 +++++-------------- src/hotspot/share/runtime/vframe.cpp | 6 +---- src/hotspot/share/runtime/vframeArray.cpp | 7 +----- 11 files changed, 11 insertions(+), 47 deletions(-) diff --git a/src/hotspot/cpu/aarch64/frame_aarch64.hpp b/src/hotspot/cpu/aarch64/frame_aarch64.hpp index ac4740645b8..55a6dde38f5 100644 --- a/src/hotspot/cpu/aarch64/frame_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/frame_aarch64.hpp @@ -186,8 +186,6 @@ // deoptimization support void interpreter_frame_set_last_sp(intptr_t* sp); - static jint interpreter_frame_expression_stack_direction() { return -1; } - // returns the sending frame, without applying any barriers inline frame sender_raw(RegisterMap* map) const; diff --git a/src/hotspot/cpu/arm/frame_arm.hpp b/src/hotspot/cpu/arm/frame_arm.hpp index 2ef44414e1c..8ca7a555b93 100644 --- a/src/hotspot/cpu/arm/frame_arm.hpp +++ b/src/hotspot/cpu/arm/frame_arm.hpp @@ -122,6 +122,4 @@ // helper to update a map with callee-saved FP static void update_map_with_saved_link(RegisterMap* map, intptr_t** link_addr); - static jint interpreter_frame_expression_stack_direction() { return -1; } - #endif // CPU_ARM_FRAME_ARM_HPP diff --git a/src/hotspot/cpu/ppc/frame_ppc.hpp b/src/hotspot/cpu/ppc/frame_ppc.hpp index bf49bbb7e01..43d5fd41068 100644 --- a/src/hotspot/cpu/ppc/frame_ppc.hpp +++ b/src/hotspot/cpu/ppc/frame_ppc.hpp @@ -407,8 +407,6 @@ align_wiggle = 1 }; - static jint interpreter_frame_expression_stack_direction() { return -1; } - // returns the sending frame, without applying any barriers inline frame sender_raw(RegisterMap* map) const; diff --git a/src/hotspot/cpu/riscv/frame_riscv.hpp b/src/hotspot/cpu/riscv/frame_riscv.hpp index d5f04ee3ff7..5cf341aa21b 100644 --- a/src/hotspot/cpu/riscv/frame_riscv.hpp +++ b/src/hotspot/cpu/riscv/frame_riscv.hpp @@ -218,8 +218,6 @@ // deoptimization support void interpreter_frame_set_last_sp(intptr_t* last_sp); - static jint interpreter_frame_expression_stack_direction() { return -1; } - // returns the sending frame, without applying any barriers inline frame sender_raw(RegisterMap* map) const; diff --git a/src/hotspot/cpu/s390/frame_s390.hpp b/src/hotspot/cpu/s390/frame_s390.hpp index 36fc5970cd8..8a99bdb8df5 100644 --- a/src/hotspot/cpu/s390/frame_s390.hpp +++ b/src/hotspot/cpu/s390/frame_s390.hpp @@ -572,8 +572,6 @@ align_wiggle = 0 }; - static jint interpreter_frame_expression_stack_direction() { return -1; } - // returns the sending frame, without applying any barriers inline frame sender_raw(RegisterMap* map) const; diff --git a/src/hotspot/cpu/x86/frame_x86.hpp b/src/hotspot/cpu/x86/frame_x86.hpp index d97e6b847b4..50f0c6b7eb6 100644 --- a/src/hotspot/cpu/x86/frame_x86.hpp +++ b/src/hotspot/cpu/x86/frame_x86.hpp @@ -170,8 +170,6 @@ // deoptimization support void interpreter_frame_set_last_sp(intptr_t* sp); - static jint interpreter_frame_expression_stack_direction() { return -1; } - // returns the sending frame, without applying any barriers inline frame sender_raw(RegisterMap* map) const; diff --git a/src/hotspot/cpu/zero/frame_zero.hpp b/src/hotspot/cpu/zero/frame_zero.hpp index 45d1cb82e82..514b134e36b 100644 --- a/src/hotspot/cpu/zero/frame_zero.hpp +++ b/src/hotspot/cpu/zero/frame_zero.hpp @@ -82,8 +82,6 @@ char* buf, int buflen) const; - static jint interpreter_frame_expression_stack_direction() { return -1; } - inline address* sender_pc_addr() const; template diff --git a/src/hotspot/share/interpreter/abstractInterpreter.hpp b/src/hotspot/share/interpreter/abstractInterpreter.hpp index 23618cb037e..6c555f0c008 100644 --- a/src/hotspot/share/interpreter/abstractInterpreter.hpp +++ b/src/hotspot/share/interpreter/abstractInterpreter.hpp @@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -262,8 +262,8 @@ class AbstractInterpreter: AllStatic { #endif // Local values relative to locals[n] - static int local_offset_in_bytes(int n) { - return ((frame::interpreter_frame_expression_stack_direction() * n) * stackElementSize); + static int local_offset_in_bytes(int n) { + return -n * stackElementSize; } // access to stacked values according to type: diff --git a/src/hotspot/share/runtime/frame.cpp b/src/hotspot/share/runtime/frame.cpp index 3e45b6fe310..2b0dd59deba 100644 --- a/src/hotspot/share/runtime/frame.cpp +++ b/src/hotspot/share/runtime/frame.cpp @@ -500,23 +500,16 @@ intptr_t* frame::interpreter_frame_local_at(int index) const { } intptr_t* frame::interpreter_frame_expression_stack_at(jint offset) const { - const int i = offset * interpreter_frame_expression_stack_direction(); - const int n = i * Interpreter::stackElementWords; - return &(interpreter_frame_expression_stack()[n]); + const int n = offset * Interpreter::stackElementWords; + return interpreter_frame_expression_stack() - n; } jint frame::interpreter_frame_expression_stack_size() const { // Number of elements on the interpreter expression stack // Callers should span by stackElementWords int element_size = Interpreter::stackElementWords; - size_t stack_size = 0; - if (frame::interpreter_frame_expression_stack_direction() < 0) { - stack_size = (interpreter_frame_expression_stack() - - interpreter_frame_tos_address() + 1)/element_size; - } else { - stack_size = (interpreter_frame_tos_address() - - interpreter_frame_expression_stack() + 1)/element_size; - } + size_t stack_size = (interpreter_frame_expression_stack() - + interpreter_frame_tos_address() + 1)/element_size; assert(stack_size <= (size_t)max_jint, "stack size too big"); return (jint)stack_size; } @@ -791,14 +784,8 @@ class InterpreterFrameClosure : public OffsetClosure { } else { addr = (oop*) _fr->interpreter_frame_expression_stack_at((offset - _max_locals)); // In case of exceptions, the expression stack is invalid and the esp will be reset to express - // this condition. Therefore, we call f only if addr is 'inside' the stack (i.e., addr >= esp for Intel). - bool in_stack; - if (frame::interpreter_frame_expression_stack_direction() > 0) { - in_stack = (intptr_t*)addr <= _fr->interpreter_frame_tos_address(); - } else { - in_stack = (intptr_t*)addr >= _fr->interpreter_frame_tos_address(); - } - if (in_stack) { + // this condition. Therefore, we call f only if addr is 'inside' the stack (i.e., addr >= esp). + if ((intptr_t*)addr >= _fr->interpreter_frame_tos_address()) { _f->do_oop(addr); } } diff --git a/src/hotspot/share/runtime/vframe.cpp b/src/hotspot/share/runtime/vframe.cpp index c9628255e45..02386537004 100644 --- a/src/hotspot/share/runtime/vframe.cpp +++ b/src/hotspot/share/runtime/vframe.cpp @@ -318,13 +318,9 @@ static StackValue* create_stack_value_from_oop_map(const InterpreterOopMap& oop_ static bool is_in_expression_stack(const frame& fr, const intptr_t* const addr) { assert(addr != nullptr, "invariant"); - // Ensure to be 'inside' the expression stack (i.e., addr >= sp for Intel). + // Ensure to be 'inside' the expression stack (i.e., addr >= sp). // In case of exceptions, the expression stack is invalid and the sp // will be reset to express this condition. - if (frame::interpreter_frame_expression_stack_direction() > 0) { - return addr <= fr.interpreter_frame_tos_address(); - } - return addr >= fr.interpreter_frame_tos_address(); } diff --git a/src/hotspot/share/runtime/vframeArray.cpp b/src/hotspot/share/runtime/vframeArray.cpp index 6810d7bb8d3..0885262eefb 100644 --- a/src/hotspot/share/runtime/vframeArray.cpp +++ b/src/hotspot/share/runtime/vframeArray.cpp @@ -473,12 +473,7 @@ void vframeArrayElement::unpack_on_stack(int caller_actual_parameters, "expression stack size should have been extended"); #endif // ASSERT int top_element = iframe()->interpreter_frame_expression_stack_size()-1; - intptr_t* base; - if (frame::interpreter_frame_expression_stack_direction() < 0) { - base = iframe()->interpreter_frame_expression_stack_at(top_element); - } else { - base = iframe()->interpreter_frame_expression_stack(); - } + intptr_t* base = iframe()->interpreter_frame_expression_stack_at(top_element); Copy::conjoint_jbytes(saved_args, base, popframe_preserved_args_size_in_bytes); From f71c37bdb3370d56e830de2957bdcaf4879c25cb Mon Sep 17 00:00:00 2001 From: Mark Powers Date: Tue, 14 Jul 2026 14:47:16 +0000 Subject: [PATCH 217/707] =?UTF-8?q?8358549:=20O(n=C2=B2)=20time=20complexi?= =?UTF-8?q?ty=20in=20java.security.Provider.parseLegacy()=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: mullan, djelinski --- .../share/classes/java/security/Provider.java | 13 ++++++++++--- .../java/security/Provider/SupportsParameter.java | 8 +++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/java.base/share/classes/java/security/Provider.java b/src/java.base/share/classes/java/security/Provider.java index f95caa1d920..e4b6109bfb0 100644 --- a/src/java.base/share/classes/java/security/Provider.java +++ b/src/java.base/share/classes/java/security/Provider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -1084,9 +1084,16 @@ private void parseLegacy(String name, String value, OPType opType) { String stdAlg = attrString.substring(0, i).intern(); String attrName = attrString.substring(i + 1); // kill additional spaces - while (attrName.startsWith(" ")) { - attrName = attrName.substring(1); + int pos = 0; + for (; pos < attrName.length(); pos++) { + if (attrName.charAt(pos) != ' ') { + break; + } } + if (pos > 0) { + attrName = attrName.substring(pos); + } + attrName = attrName.intern(); ServiceKey stdKey = new ServiceKey(type, stdAlg, true); Service stdService = legacyMap.get(stdKey); diff --git a/test/jdk/java/security/Provider/SupportsParameter.java b/test/jdk/java/security/Provider/SupportsParameter.java index 039fb3d0797..3325ad9680a 100644 --- a/test/jdk/java/security/Provider/SupportsParameter.java +++ b/test/jdk/java/security/Provider/SupportsParameter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 4911081 8130181 + * @bug 4911081 8130181 8358549 * @library /test/lib * @summary verify that Provider.Service.supportsParameter() works * @author Andreas Sterbenz @@ -112,7 +112,9 @@ private static class MyProvider extends Provider { put("Signature.DSA0", "foo.DSA0"); put("Signature.DSA", "foo.DSA"); - put("Signature.DSA SupportedKeyClasses", + // Extra spaces between "Signature.DSA" and "SupportedKeyClasses" + // are used to verify that whitespace is trimmed. + put("Signature.DSA SupportedKeyClasses", "java.security.interfaces.DSAPublicKey" + "|java.security.interfaces.DSAPrivateKey"); From e452e6c8b06e7ff3d47d0b3cd6fa8d73eb85b655 Mon Sep 17 00:00:00 2001 From: Vladimir Ivanov Date: Tue, 14 Jul 2026 16:13:18 +0000 Subject: [PATCH 218/707] 8374783: C2 compilation asserts with "slice of address and input slice don't match" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vladimir Ivanov Co-authored-by: Roberto Castañeda Lozano Reviewed-by: chagedorn, qamai --- src/hotspot/share/opto/callGenerator.cpp | 36 +-- src/hotspot/share/opto/classes.hpp | 1 + src/hotspot/share/opto/compile.cpp | 8 + src/hotspot/share/opto/graphKit.hpp | 2 +- src/hotspot/share/opto/opaquenode.cpp | 8 + src/hotspot/share/opto/opaquenode.hpp | 13 + .../TestLateInliningWithSliceNarrowing.java | 225 ++++++++++++++++++ 7 files changed, 277 insertions(+), 16 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/inlining/TestLateInliningWithSliceNarrowing.java diff --git a/src/hotspot/share/opto/callGenerator.cpp b/src/hotspot/share/opto/callGenerator.cpp index d0b48982b0f..10df25abcb8 100644 --- a/src/hotspot/share/opto/callGenerator.cpp +++ b/src/hotspot/share/opto/callGenerator.cpp @@ -717,26 +717,32 @@ void CallGenerator::do_late_inline_helper() { C->inline_printer()->record(method(), jvms, InliningResult::SUCCESS, "late inline succeeded"); } - // Capture any exceptional control flow - GraphKit kit(new_jvms); - - // Find the result object - Node* result = C->top(); - int result_size = method()->return_type()->size(); - if (result_size != 0 && !kit.stopped()) { - result = (result_size == 1) ? kit.pop() : kit.pop_pair(); - } - - if (call->is_CallStaticJava() && call->as_CallStaticJava()->is_boxing_method()) { - result = kit.must_be_not_null(result, false); - } - if (inline_cg()->is_inline()) { C->set_has_loops(C->has_loops() || inline_cg()->method()->has_loops()); C->env()->notice_inlined_method(inline_cg()->method()); } C->set_inlining_progress(true); - C->set_do_cleanup(kit.stopped()); // path is dead; needs cleanup + + // Find the result object and capture any exceptional control flow. + GraphKit kit(new_jvms); + Node* result = C->top(); + + assert(!C->do_cleanup(), "already set"); + if (kit.stopped()) { + C->set_do_cleanup(true); // path is dead; needs cleanup + } else { + result = kit.pop_node(method()->return_type()->basic_type()); + if (result != C->top() && !result_not_used) { + if (call->is_CallStaticJava() && + call->as_CallStaticJava()->is_boxing_method()) { + result = kit.must_be_not_null(result, false); + } + // Limit result type propagation until next IGVN cleanup. + const Type* result_type = kit.gvn().type(callprojs.resproj); + result = kit.gvn().transform(new OpaqueParseNode(C, result, result_type)); + } + } + kit.replace_call(call, result, true, do_asserts); } } diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index 4d06e20875a..53a72f979db 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -288,6 +288,7 @@ macro(OpaqueZeroTripGuard) macro(OpaqueConstantBool) macro(OpaqueInitializedAssertionPredicate) macro(OpaqueTemplateAssertionPredicate) +macro(OpaqueParse) macro(PowD) macro(ProfileBoolean) macro(OrI) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index e5f91875516..0c7083ed723 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -2051,10 +2051,13 @@ void Compile::inline_string_calls(bool parse_time) { _late_inlines_pos = _late_inlines.length(); } + assert(!do_cleanup(), "already set"); + while (_string_late_inlines.length() > 0) { CallGenerator* cg = _string_late_inlines.pop(); cg->do_late_inline(); if (failing()) return; + set_do_cleanup(false); // ignore and reset } _string_late_inlines.trunc_to(0); } @@ -2070,10 +2073,13 @@ void Compile::inline_boxing_calls(PhaseIterGVN& igvn) { _late_inlines_pos = _late_inlines.length(); + assert(!do_cleanup(), "already set"); + while (_boxing_late_inlines.length() > 0) { CallGenerator* cg = _boxing_late_inlines.pop(); cg->do_late_inline(); if (failing()) return; + set_do_cleanup(false); // ignore and reset } _boxing_late_inlines.trunc_to(0); @@ -2647,12 +2653,14 @@ void Compile::check_no_dead_use() const { #endif void Compile::inline_vector_reboxing_calls() { + assert(!do_cleanup(), "already set"); if (C->_vector_reboxing_late_inlines.length() > 0) { _late_inlines_pos = C->_late_inlines.length(); while (_vector_reboxing_late_inlines.length() > 0) { CallGenerator* cg = _vector_reboxing_late_inlines.pop(); cg->do_late_inline(); if (failing()) return; + assert(!do_cleanup(), "should not be set"); print_method(PHASE_INLINE_VECTOR_REBOX, 3, cg->call_node()); } _vector_reboxing_late_inlines.trunc_to(0); diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index d371dfb2e32..ef160ac6f1a 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -500,7 +500,7 @@ class GraphKit : public Phase { int n_size = type2size[n_type]; if (n_size == 1) return pop(); else if (n_size == 2) return pop_pair(); - else return nullptr; + else return C->top(); } Node* control() const { return map_not_null()->control(); } diff --git a/src/hotspot/share/opto/opaquenode.cpp b/src/hotspot/share/opto/opaquenode.cpp index 428379e84ae..a3b50d269fb 100644 --- a/src/hotspot/share/opto/opaquenode.cpp +++ b/src/hotspot/share/opto/opaquenode.cpp @@ -183,6 +183,14 @@ void OpaqueInitializedAssertionPredicateNode::dump_spec(outputStream* st) const } #endif // NOT PRODUCT +// Do NOT remove the opaque node until subsequent IGVN pass. +Node* OpaqueParseNode::Identity(PhaseGVN* phase) { + if (phase->is_IterGVN()) { + return in(1); + } + return this; +} + uint ProfileBooleanNode::hash() const { return NO_HASH; } bool ProfileBooleanNode::cmp( const Node &n ) const { return (&n == this); diff --git a/src/hotspot/share/opto/opaquenode.hpp b/src/hotspot/share/opto/opaquenode.hpp index bb3da2aa65f..7ec7e23144a 100644 --- a/src/hotspot/share/opto/opaquenode.hpp +++ b/src/hotspot/share/opto/opaquenode.hpp @@ -258,6 +258,19 @@ class OpaqueInitializedAssertionPredicateNode : public Node { NOT_PRODUCT(void dump_spec(outputStream* st) const); }; +// The node is used during late inlining to limit type propagation between cleanup phases. +// It avoids type paradoxes caused by divergence between recorded type and IR shapes +// during successive late inlining attempts. +class OpaqueParseNode : public TypeNode { + public: + OpaqueParseNode(Compile* C, Node* n, const Type* t) : TypeNode(t, 2) { + init_req(1, n); + C->record_for_igvn(this); + } + virtual int Opcode() const; + virtual Node* Identity(PhaseGVN* phase); +}; + //------------------------------ProfileBooleanNode------------------------------- // A node represents value profile for a boolean during parsing. // Once parsing is over, the node goes away (during IGVN). diff --git a/test/hotspot/jtreg/compiler/inlining/TestLateInliningWithSliceNarrowing.java b/test/hotspot/jtreg/compiler/inlining/TestLateInliningWithSliceNarrowing.java new file mode 100644 index 00000000000..fbae0454d07 --- /dev/null +++ b/test/hotspot/jtreg/compiler/inlining/TestLateInliningWithSliceNarrowing.java @@ -0,0 +1,225 @@ +/* + * 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.inlining; + +import java.lang.reflect.Field; +import jdk.internal.misc.Unsafe; +import jdk.test.lib.Asserts; + +/** + * @test + * @bug 8374783 + * @summary Test that address type refinements after an incremental inlining + * step are propagated by IGVN before the next step. Failing to + * propagate such refinements could lead to slice mismatches between + * field-derived and IGVN-recorded address types when parsing bytecode + * in subsequent inlining steps. + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @run main ${test.main.class} + * @run main/othervm -Xbatch + -XX:CompileCommand=compileonly,${test.main.class}::test* + -XX:CompileCommand=dontinline,${test.main.class}::notInlined* + -XX:CompileCommand=delayinline,${test.main.class}::late* + ${test.main.class} + */ + +class A { + int f; +} + +public class TestLateInliningWithSliceNarrowing { + + private static Unsafe UNSAFE = Unsafe.getUnsafe(); + private static final long F_OFFSET; + private static final long INT_ARRAY_OFFSET; + + static { + try { + Field fField = A.class.getDeclaredField("f"); + F_OFFSET = UNSAFE.objectFieldOffset(fField); + } catch (Exception e) { + throw new RuntimeException(e); + } + INT_ARRAY_OFFSET = UNSAFE.arrayBaseOffset(int[].class); + } + + static A notInlinedId(A a) { + return a; + } + + static long lateOffset() { + return F_OFFSET; + } + + static long lateOffsetMinusFour() { + return F_OFFSET - 4; + } + + static long lateOffsetDividedByTwo() { + return F_OFFSET / 2; + } + + static long lateArrayOffset() { + return INT_ARRAY_OFFSET; + } + + static void lateStore(A a) { + a.f = 42; + } + + static void lateArrayStore(int[] a) { + a[0] = 42; + } + + static int lateLoad(A a) { + return a.f; + } + + static Object lateBase(A a) { + return a; + } + + // Test that when lateStore() is inlined, the IGVN-recorded type of the + // accessed memory address (captured by an AddP) has been updated to reflect + // the compiler-known offset discovered by inlining lateOffset(). Failure to + // do so leads to a slice mismatch when parsing the inlined store. + static int testLoadFromLateDiscoveredOffsetThenStoreAtConstOffset(A a) { + long o = lateOffset(); + int val = UNSAFE.getInt(a, o); + lateStore(a); + return val; + } + + // Test that when lateLoad() is inlined, the IGVN-recorded type of the + // accessed memory address (captured by an AddP) has been updated to reflect + // the compiler-known offset discovered by inlining lateOffset(). Failure to + // do so leads to a slice mismatch when parsing the inlined load. + static int testLoadFromLateDiscoveredOffsetThenLoadFromConstOffset(A a) { + long o = lateOffset(); + int val = UNSAFE.getInt(a, o); + lateLoad(a); + return val; + } + + // Test a variation of the above where lateOffsetMinusFour() is not used + // directly by an AddP node. This test does not require updating the + // IGVN-recorded type of the accessed memory address for correctness, + // because lateStore() does not reuse the corresponding AddP node. + static int testLoadFromLateDiscoveredOffsetPlusFourThenStoreAtConstOffset(A a) { + long o = lateOffsetMinusFour(); + int val = UNSAFE.getInt(a, o + 4); + lateStore(a); + return val; + } + + // Test a variation of the above using a different arithmetic operation, + // with the same expectations. + static int testLoadFromLateDiscoveredOffsetTimesTwoThenStoreAtConstOffset(A a) { + long o = lateOffsetDividedByTwo(); + int val = UNSAFE.getInt(a, o * 2); + lateStore(a); + return val; + } + + // Test a variation of the first test where failing to update the + // IGVN-recorded type of the accessed memory address would result in a slice + // mismatch that will lead to an incorrect memory graph (the memory input of + // the last load would bypass the memory output of the store). + static int testLoadFromLateDiscoveredOffsetThenStoreAtConstOffsetThenReloadFromConstOffset(A a) { + A a2 = notInlinedId(a); + long o = lateOffset(); + int val = UNSAFE.getInt(a, o); + lateStore(a); + return a2.f + val; + } + + // Test a variation of the first test where the offset is compiler-known + // from the beginning, but the unsafe base address is only discovered by + // inlining lateBase(). This variation does not require a cleanup between + // the late inlining of lateBase() and lateLoad() for correctness: a slice + // mismatch cannot occur because the memory access within lateLoad() does + // not reuse the same address node (AddP) as the unsafe load. The unsafe + // load address node is not reusable by the lateLoad() access because it is + // obscured by casts by the time lateLoad() is late inlined. Making the + // address node reusable by both loads would require a cleanup round, which + // would prevent the mismatch from happening in the first place. + static int testLoadFromLateDiscoveredBaseThenLoadFromKnownBase(A a) { + Object obj = lateBase(a); + int val = UNSAFE.getInt(obj, F_OFFSET); + lateLoad(a); + return val; + } + + // Test a variation of the first test using an array instead of a class + // instance. No slice mismatch occurs because the address types for both + // memory accesses lead to the same slice, regardless of whether the offset + // is compiler-known. + static int testArrayLoadFromLateDiscoveredOffsetThenStoreAtConstOffset(int[] a) { + long o = lateArrayOffset(); + int val = UNSAFE.getInt(a, o); + lateArrayStore(a); + return val; + } + + public static void main(String[] args) { + for (int i = 0; i < 10_000; i++) { + { + A a = new A(); + int result = testLoadFromLateDiscoveredOffsetThenStoreAtConstOffset(a); + Asserts.assertEquals(0, result); + } + { + A a = new A(); + int result = testLoadFromLateDiscoveredOffsetThenLoadFromConstOffset(a); + Asserts.assertEquals(0, result); + } + { + A a = new A(); + int result = testLoadFromLateDiscoveredOffsetPlusFourThenStoreAtConstOffset(a); + Asserts.assertEquals(0, result); + } + { + A a = new A(); + int result = testLoadFromLateDiscoveredOffsetTimesTwoThenStoreAtConstOffset(a); + Asserts.assertEquals(0, result); + } + { + A a = new A(); + int result = testLoadFromLateDiscoveredOffsetThenStoreAtConstOffsetThenReloadFromConstOffset(a); + Asserts.assertEquals(42, result); + } + { + A a = new A(); + int result = testLoadFromLateDiscoveredBaseThenLoadFromKnownBase(a); + Asserts.assertEquals(0, result); + } + { + int[] a = new int[1]; + int result = testArrayLoadFromLateDiscoveredOffsetThenStoreAtConstOffset(a); + Asserts.assertEquals(0, result); + } + } + } +} From 499a25b2d05194461c2a4ed1d7bdb745b115f98f Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Tue, 14 Jul 2026 16:47:49 +0000 Subject: [PATCH 219/707] 8386503: C2: assert(adr_type == nullptr || adr_type->isa_aryptr() != nullptr) failed: unexpected type-unsafe store Reviewed-by: epeter, vlivanov --- src/hotspot/share/opto/memnode.cpp | 9 +- .../parsing/TestTypeUnsafeFieldStore.java | 240 ++++++++++++++++++ 2 files changed, 246 insertions(+), 3 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/parsing/TestTypeUnsafeFieldStore.java diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 00ccb3e3dbc..165eb9e430c 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -1290,9 +1290,12 @@ Node* MemNode::can_see_stored_value(Node* st, PhaseValues* phase) const { return res; } - // Type-unsafe stores must be due to array polymorphism - const TypePtr* adr_type = this->adr_type(); - assert(adr_type == nullptr || adr_type->isa_aryptr() != nullptr, "unexpected type-unsafe store"); + // There are some cases in which the Type of the load is narrower than the Type of the value + // that is stored into that location. The most common case is array polymorphism, when the + // type of an array element depends on the type of the array. In addition, there are some + // corner cases, the first one is concurrent class loading, when CHA can result in a narrower + // Type than what is declared only after the child class is loaded, and the second case is + // unsafe accesses when we do not check for type safety. See JDK-8388184. return nullptr; } diff --git a/test/hotspot/jtreg/compiler/parsing/TestTypeUnsafeFieldStore.java b/test/hotspot/jtreg/compiler/parsing/TestTypeUnsafeFieldStore.java new file mode 100644 index 00000000000..d6bd097eec0 --- /dev/null +++ b/test/hotspot/jtreg/compiler/parsing/TestTypeUnsafeFieldStore.java @@ -0,0 +1,240 @@ +/* + * 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.parsing; + +import jdk.internal.misc.Unsafe; +import jdk.test.whitebox.WhiteBox; + +/* + * @test + * @bug 8386503 + * @summary Test load folding from a field store with a less precise type + * @library /test/lib + * @requires vm.compiler2.enabled + * @modules java.base/jdk.internal.misc + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:-TieredCompilation + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:CompileOnly=${test.main.class}::test* + * -XX:CompileCommand=inline,${test.main.class}::inline* + * ${test.main.class} + */ +public class TestTypeUnsafeFieldStore { + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + private static volatile Throwable failure; + + public static void main(String[] args) throws Exception { + for (int i = 0; i <= 10; i++) { + testConcurrentClassLoading(i); + } + Holder h = new Holder(); + Integer obj = 0; + for (int i = 0; i < 20000; i++) { + testUnsafeAccess(h, obj); + } + } + + // It's hard to coordinate the compiler thread with the thread that load the child class, so we + // randomly delay one of the threads + private static void testConcurrentClassLoading(int idx) throws Exception { + var parentClass = Class.forName("compiler.parsing.TestTypeUnsafeFieldStore$P" + idx); + var testMethod = TestTypeUnsafeFieldStore.class.getDeclaredMethod("testMethod" + idx, parentClass); + Thread compiler = new Thread(() -> { + try { + if (idx < 5) { + Thread.sleep((5 - idx) * 10L); + } + WHITE_BOX.markMethodProfiled(testMethod); + if (!WHITE_BOX.enqueueMethodForCompilation(testMethod, 4)) { + throw new RuntimeException("Could not enqueue the test method for C2 compilation"); + } + while (WHITE_BOX.isMethodQueuedForCompilation(testMethod)) { + Thread.yield(); + } + } catch (Throwable t) { + failure = t; + } + }); + compiler.start(); + if (idx > 5) { + Thread.sleep((idx - 5) * 10L); + } + Class.forName("compiler.parsing.TestTypeUnsafeFieldStore$C" + idx); + compiler.join(); + if (failure != null) { + throw new RuntimeException(failure); + } + } + + private static Integer testUnsafeAccess(Holder h, Object obj) { + UNSAFE.putReference(h, Holder.V_OFFSET, obj); + return h.v; + } + + private static class Holder { + private static final long V_OFFSET = UNSAFE.objectFieldOffset(Holder.class, "v"); + Integer v; + } + + // When the compiler parses the store, C has not been loaded, so obj is of type P. However, + // when the compiler parses the load, C has been loaded and is observed to be the unique + // concrete subclass of P, so the result of the load is of type C. Folding the load to obj will + // drop this information, thus is incorrect. + private static abstract class P0 {} + private static class C0 extends P0 {} + private static P0 staticField0; + private static P0 testMethod0(P0 obj) { + staticField0 = obj; + inline0(); + return staticField0; + } + + private static abstract class P1 {} + private static class C1 extends P1 {} + private static P1 staticField1; + private static P1 testMethod1(P1 obj) { + staticField1 = obj; + inline0(); + return staticField1; + } + + private static abstract class P2 {} + private static class C2 extends P2 {} + private static P2 staticField2; + private static P2 testMethod2(P2 obj) { + staticField2 = obj; + inline0(); + return staticField2; + } + + private static abstract class P3 {} + private static class C3 extends P3 {} + private static P3 staticField3; + private static P3 testMethod3(P3 obj) { + staticField3 = obj; + inline0(); + return staticField3; + } + + private static abstract class P4 {} + private static class C4 extends P4 {} + private static P4 staticField4; + private static P4 testMethod4(P4 obj) { + staticField4 = obj; + inline0(); + return staticField4; + } + + private static abstract class P5 {} + private static class C5 extends P5 {} + private static P5 staticField5; + private static P5 testMethod5(P5 obj) { + staticField5 = obj; + inline0(); + return staticField5; + } + + private static abstract class P6 {} + private static class C6 extends P6 {} + private static P6 staticField6; + private static P6 testMethod6(P6 obj) { + staticField6 = obj; + inline0(); + return staticField6; + } + + private static abstract class P7 {} + private static class C7 extends P7 {} + private static P7 staticField7; + private static P7 testMethod7(P7 obj) { + staticField7 = obj; + inline0(); + return staticField7; + } + + private static abstract class P8 {} + private static class C8 extends P8 {} + private static P8 staticField8; + private static P8 testMethod8(P8 obj) { + staticField8 = obj; + inline0(); + return staticField8; + } + + private static abstract class P9 {} + private static class C9 extends P9 {} + private static P9 staticField9; + private static P9 testMethod9(P9 obj) { + staticField9 = obj; + inline0(); + return staticField9; + } + + private static abstract class P10 {} + private static class C10 extends P10 {} + private static P10 staticField10; + private static P10 testMethod10(P10 obj) { + staticField10 = obj; + inline0(); + return staticField10; + } + + private static void inline0() { + inline1(); + inline1(); + inline1(); + inline1(); + } + + private static void inline1() { + inline2(); + inline2(); + inline2(); + inline2(); + } + + private static void inline2() { + inline3(); + inline3(); + inline3(); + inline3(); + } + + private static void inline3() { + inline4(); + inline4(); + inline4(); + inline4(); + } + + private static void inline4() { + inline5(); + inline5(); + inline5(); + inline5(); + } + + private static void inline5() {} +} From 5d24cecccd9d39d22c9d736b0f5283c49d8bc440 Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Tue, 14 Jul 2026 17:25:01 +0000 Subject: [PATCH 220/707] 8387048: NMTCommittedVirtualMemoryTracker.test_committed_virtualmemory_region_vm fails due to found_stack_top Reviewed-by: rtoyonaga, stuefe --- .../gtest/runtime/test_committed_virtualmemory.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/test/hotspot/gtest/runtime/test_committed_virtualmemory.cpp b/test/hotspot/gtest/runtime/test_committed_virtualmemory.cpp index 5d475d2f955..fcc1354c773 100644 --- a/test/hotspot/gtest/runtime/test_committed_virtualmemory.cpp +++ b/test/hotspot/gtest/runtime/test_committed_virtualmemory.cpp @@ -58,16 +58,15 @@ class CommittedVirtualMemoryTest { address i_addr = (address)&i; bool found_i_addr = false; - // stack grows downward + // Stack grows downward. address stack_top = stack_end + stack_size; - bool found_stack_top = false; { MemTracker::NmtVirtualMemoryLocker vml; + // For thread stacks, this historically named API visits resident ranges. + // Not all committed pages have to be resident. VirtualMemoryTracker::Instance::tree()->visit_committed_regions(rgn_found, [&](const VirtualMemoryRegion& rgn) { - if (rgn.base() + rgn.size() == stack_top) { - EXPECT_TRUE(rgn.size() <= stack_size); - found_stack_top = true; - } + EXPECT_GE(rgn.base(), stack_end); + EXPECT_LE(rgn.end(), stack_top); if (i_addr < stack_top && i_addr >= rgn.base()) { found_i_addr = true; } @@ -76,10 +75,9 @@ class CommittedVirtualMemoryTest { }); } - // stack and guard pages may be contiguous as one region + // Stack and guard pages may be contiguous as one region. ASSERT_TRUE(i >= 1); ASSERT_TRUE(found_i_addr); - ASSERT_TRUE(found_stack_top); } static const int PAGE_CONTAINED_IN_RANGE_TAG = -1; From 0efadf5f5401e1b3f7230c19e832d0acb35bd9d0 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Tue, 14 Jul 2026 17:58:31 +0000 Subject: [PATCH 221/707] 8388139: Shenandoah: -XX:+VerifyOops fails on forwarded objects with COH Reviewed-by: wkemper, xpeng --- .../shenandoahBarrierSetAssembler_aarch64.cpp | 26 +++++++++++++++++++ .../shenandoahBarrierSetAssembler_aarch64.hpp | 1 + .../shenandoahBarrierSetAssembler_ppc.cpp | 20 ++++++++++++++ .../shenandoahBarrierSetAssembler_ppc.hpp | 2 ++ .../shenandoahBarrierSetAssembler_riscv.cpp | 26 +++++++++++++++++++ .../shenandoahBarrierSetAssembler_riscv.hpp | 1 + .../shenandoahBarrierSetAssembler_x86.cpp | 26 +++++++++++++++++++ .../shenandoahBarrierSetAssembler_x86.hpp | 1 + 8 files changed, 103 insertions(+) diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp index 7406aa0c1c4..19c82ed77ef 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp @@ -420,6 +420,32 @@ void ShenandoahBarrierSetAssembler::try_peek_weak_handle_in_nmethod(MacroAssembl __ bind(done); } +void ShenandoahBarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& L_error) { + // Check if the oop is in the right area of memory + __ mov(tmp2, (intptr_t) Universe::verify_oop_mask()); + __ andr(tmp1, obj, tmp2); + __ mov(tmp2, (intptr_t) Universe::verify_oop_bits()); + + // Compare tmp1 and tmp2. We don't use a compare + // instruction here because the flags register is live. + __ eor(tmp1, tmp1, tmp2); + __ cbnz(tmp1, L_error); + + // This routine is sometimes called before applying GC barriers. + // With +COH, loading the klass may end up loading forwarding pointer instead. + Label L_skip; + if (UseCompactObjectHeaders) { + Address gc_state(rthread, in_bytes(ShenandoahThreadLocalData::gc_state_offset())); + __ ldrb(tmp1, gc_state); + __ tbnz(tmp1, ShenandoahHeap::HAS_FORWARDED_BITPOS, L_skip); + } + + // Make sure klass is 'reasonable', which is not zero. + __ load_narrow_klass(tmp1, obj); + __ cbz(tmp1, L_error); + __ bind(L_skip); +} + void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, Register start, Register count, Register scratch) { assert(ShenandoahCardBarrier, "Should have been checked by caller"); diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp index d25dd8871f9..62273a44da2 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp @@ -74,6 +74,7 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { Register obj, Register tmp, Label& slowpath); virtual void try_peek_weak_handle_in_nmethod(MacroAssembler* masm, Register weak_handle, Register obj, Register tmp, Label& slow_path); + virtual void check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& L_error); #ifdef COMPILER1 void keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub); diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp index b17f0f924ae..7dbb0182266 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp @@ -659,6 +659,26 @@ void ShenandoahBarrierSetAssembler::try_peek_weak_handle_in_nmethod(MacroAssembl __ block_comment("} try_peek_weak_handle_in_nmethod (shenandoahgc)"); } +void ShenandoahBarrierSetAssembler::check_oop(MacroAssembler *masm, Register obj, const char* msg) { + if (!VerifyOops) { + return; + } + + __ mr(R0, obj); + + // This routine is sometimes called before applying GC barriers. + // With +COH, verification can touch the klass that may end up loading forwarding pointer instead. + Label L_skip; + if (UseCompactObjectHeaders) { + __ lbz(R0, in_bytes(ShenandoahThreadLocalData::gc_state_offset()), R16_thread); + __ andi_(R0, R0, ShenandoahHeap::HAS_FORWARDED); + __ bne(CR0, L_skip); + } + + __ verify_oop(R0, msg); + __ bind(L_skip); +} + void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count, Register preserve) { assert(ShenandoahCardBarrier, "Should have been checked by caller"); diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp index 8d741e6104b..0784c8b7148 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp @@ -125,6 +125,8 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { virtual void try_peek_weak_handle_in_nmethod(MacroAssembler* masm, Register weak_handle, Register obj, Register tmp, Label& slow_path); + virtual void check_oop(MacroAssembler *masm, Register obj, const char* msg); + #ifdef COMPILER2 // Entry points from Matcher void load_c2(const MachNode* node, MacroAssembler* masm, Register dst, Register addr, int disp, Register tmp1, Register tmp2, bool narrow, bool acquire); diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp index 574c70c8ea4..d7cfcb11205 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp @@ -433,6 +433,32 @@ void ShenandoahBarrierSetAssembler::try_peek_weak_handle_in_nmethod(MacroAssembl __ bind(done); } +void ShenandoahBarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& L_error) { + // Check if the oop is in the right area of memory + __ mv(tmp2, (intptr_t) Universe::verify_oop_mask()); + __ andr(tmp1, obj, tmp2); + __ mv(tmp2, (intptr_t) Universe::verify_oop_bits()); + + // Compare tmp1 and tmp2. + __ bne(tmp1, tmp2, L_error); + + // This routine is sometimes called before applying GC barriers. + // With +COH, loading the klass may end up loading forwarding pointer instead. + Label L_skip; + if (UseCompactObjectHeaders) { + Address gc_state(xthread, ShenandoahThreadLocalData::gc_state_offset()); + __ lbu(tmp1, gc_state); + __ test_bit(tmp1, tmp1, ShenandoahHeap::HAS_FORWARDED_BITPOS); + __ bnez(tmp1, L_skip); + } + + // Make sure klass is 'reasonable', which is not zero. + __ load_narrow_klass(tmp1, obj); + __ beqz(tmp1, L_error); + + __ bind(L_skip); +} + void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, Register start, Register count, Register tmp) { assert(ShenandoahCardBarrier, "Did you mean to enable ShenandoahCardBarrier?"); diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp index ecb63e68a01..eb8ac653e2e 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp @@ -79,6 +79,7 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { Register obj, Register tmp, Label& slowpath); virtual void try_peek_weak_handle_in_nmethod(MacroAssembler* masm, Register weak_handle, Register obj, Register tmp, Label& slow_path); + virtual void check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& L_error); #ifdef COMPILER1 void keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub); diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp index bdb98d4b4c0..9ee1d2c0704 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp @@ -507,6 +507,32 @@ void ShenandoahBarrierSetAssembler::try_peek_weak_handle_in_nmethod(MacroAssembl __ bind(done); } +void ShenandoahBarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& L_error) { + // Check if the oop is in the right area of memory + __ movptr(tmp1, obj); + __ movptr(tmp2, (intptr_t) Universe::verify_oop_mask()); + __ andptr(tmp1, tmp2); + __ movptr(tmp2, (intptr_t) Universe::verify_oop_bits()); + __ cmpptr(tmp1, tmp2); + __ jcc(Assembler::notZero, L_error); + + // This routine is sometimes called before applying GC barriers. + // With +COH, loading the klass may end up loading forwarding pointer instead. + Label L_skip; + if (UseCompactObjectHeaders) { + Address gc_state(r15_thread, ShenandoahThreadLocalData::gc_state_offset()); + __ testb(gc_state, ShenandoahHeap::HAS_FORWARDED); + __ jcc(Assembler::notZero, L_skip); + } + + // Make sure klass is 'reasonable', which is not zero. + __ load_narrow_klass(tmp1, obj); + __ testl(tmp1, tmp1); + __ jcc(Assembler::zero, L_error); + + __ bind(L_skip); +} + #ifdef PRODUCT #define BLOCK_COMMENT(str) /* nothing */ #else diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp index 7f417d3c262..7c1a89b74f5 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp @@ -71,6 +71,7 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env, Register obj, Register tmp, Label& slowpath); virtual void try_peek_weak_handle_in_nmethod(MacroAssembler* masm, Register weak_handle, Register obj, Label& slowpath); + virtual void check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& L_error); #ifdef COMPILER1 void keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub); From 189dde7dfe55ba6c41eda9e7020abdfb50638935 Mon Sep 17 00:00:00 2001 From: William Kemper Date: Tue, 14 Jul 2026 18:42:48 +0000 Subject: [PATCH 222/707] 8388173: Shenandoah: Overly strict assertion failure in CAS barrier Reviewed-by: shade, xpeng --- .../gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp index 9ee1d2c0704..480e484f4b1 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp @@ -705,10 +705,9 @@ void ShenandoahBarrierSetAssembler::compare_and_set_c2(const MachNode* node, Mac assert(oldval == rax, "must be in rax for implicit use in cmpxchg"); - // Oldval and newval can be in the same register, but all other registers should be - // distinct for extra safety, as we shuffle register values around. - assert_different_registers(oldval, tmp, addr.base(), addr.index()); - assert_different_registers(newval, tmp, addr.base(), addr.index()); + // Oldval and newval cannot be clobbered by aliasing with tmp. + assert_different_registers(oldval, tmp); + assert_different_registers(newval, tmp); ShenandoahBarrierStubC2::load_store_pre(masm, node, addr, tmp, noreg, noreg, narrow); @@ -729,7 +728,7 @@ void ShenandoahBarrierSetAssembler::compare_and_set_c2(const MachNode* node, Mac } void ShenandoahBarrierSetAssembler::get_and_set_c2(const MachNode* node, MacroAssembler* masm, Register newval, Address addr, Register tmp, bool narrow) { - assert_different_registers(newval, tmp, addr.base(), addr.index()); + assert_different_registers(newval, tmp); ShenandoahBarrierStubC2::load_store_pre(masm, node, addr, tmp, noreg, noreg, narrow); From fe430ad121464d731402ef4add6d57dadffe723e Mon Sep 17 00:00:00 2001 From: Vladimir Ivanov Date: Tue, 14 Jul 2026 19:14:06 +0000 Subject: [PATCH 223/707] 8387940: C2: Stress allocation elimination failures Reviewed-by: chagedorn, kvn, qamai --- src/hotspot/share/opto/c2_globals.hpp | 8 +++ src/hotspot/share/opto/compile.cpp | 3 +- src/hotspot/share/opto/macro.cpp | 26 ++++--- .../compiler/arguments/TestStressOptions.java | 6 +- .../StressEliminateAllocationsIRTest.java | 67 +++++++++++++++++++ 5 files changed, 100 insertions(+), 10 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/escapeAnalysis/StressEliminateAllocationsIRTest.java diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index cbe149fd01a..9ff88e8c310 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -601,6 +601,14 @@ "Number of fields in instance limit for scalar replacement") \ range(0, max_jint) \ \ + product(bool, StressEliminateAllocations, false, DIAGNOSTIC, \ + "Randomly fail allocation elimination attempts") \ + \ + product(uint, StressEliminateAllocationsMean, 20, DIAGNOSTIC, \ + "The expected number of elimination checks made until " \ + "a random failure.") \ + range(1, max_juint) \ + \ product(bool, OptimizePtrCompare, true, \ "Use escape analysis to optimize pointers compare") \ \ diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 0c7083ed723..93d8e4c425d 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -748,7 +748,8 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, if (StressLCM || StressGCM || StressIGVN || StressCCP || StressIncrementalInlining || StressMacroExpansion || StressMacroElimination || StressUnstableIfTraps || - StressBailout || StressLoopPeeling || StressCountedLoop) { + StressBailout || StressLoopPeeling || StressCountedLoop || + StressEliminateAllocations) { initialize_stress_seed(directive); } diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 0ee073e8b06..a4d03970fcf 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -931,7 +931,9 @@ SafePointScalarObjectNode* PhaseMacroExpand::create_scalarized_object_descriptio // We weren't able to find a value for this field, // give up on eliminating this allocation. - if (field_val == nullptr) { + bool force_scalarization_failure = StressEliminateAllocations && + (C->random() % StressEliminateAllocationsMean == 0); + if (field_val == nullptr || force_scalarization_failure) { uint last = sfpt->req() - 1; for (int k = 0; k < j; k++) { sfpt->del_req(last--); @@ -940,13 +942,21 @@ SafePointScalarObjectNode* PhaseMacroExpand::create_scalarized_object_descriptio #ifndef PRODUCT if (PrintEliminateAllocations) { - if (field != nullptr) { - tty->print("=== At SafePoint node %d can't find value of field: ", sfpt->_idx); - field->print(); - int field_idx = C->get_alias_index(field_addr_type); - tty->print(" (alias_idx=%d)", field_idx); - } else { // Array's element - tty->print("=== At SafePoint node %d can't find value of array element [%d]", sfpt->_idx, j); + tty->print("=== At SafePoint node %d ", sfpt->_idx); + if (field_val == nullptr) { + tty->print_raw("can't find value of "); + + if (field != nullptr) { + tty->print_raw("field: "); + field->print(); + int field_idx = C->get_alias_index(field_addr_type); + tty->print(" (alias_idx=%d)", field_idx); + } else { // Array's element + tty->print("array element [%d]", j); + } + } else { + assert(force_scalarization_failure, "sanity"); + tty->print_raw("forcibly abort elimination"); } tty->print(", which prevents elimination of: "); if (res == nullptr) diff --git a/test/hotspot/jtreg/compiler/arguments/TestStressOptions.java b/test/hotspot/jtreg/compiler/arguments/TestStressOptions.java index 534ec9d2d97..99cf06110d6 100644 --- a/test/hotspot/jtreg/compiler/arguments/TestStressOptions.java +++ b/test/hotspot/jtreg/compiler/arguments/TestStressOptions.java @@ -24,7 +24,7 @@ /* * @test * @key stress randomness - * @bug 8252219 8256535 8317349 8319879 8335334 8325478 + * @bug 8252219 8256535 8317349 8319879 8335334 8325478 8387940 * @requires vm.compiler2.enabled * @summary Tests that different combinations of stress options and * -XX:StressSeed=N are accepted. @@ -60,6 +60,10 @@ * compiler.arguments.TestStressOptions * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+StressMacroElimination -XX:StressSeed=42 * compiler.arguments.TestStressOptions + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+StressEliminateAllocations + * compiler.arguments.TestStressOptions + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+StressEliminateAllocations -XX:StressSeed=42 + * compiler.arguments.TestStressOptions */ package compiler.arguments; diff --git a/test/hotspot/jtreg/compiler/escapeAnalysis/StressEliminateAllocationsIRTest.java b/test/hotspot/jtreg/compiler/escapeAnalysis/StressEliminateAllocationsIRTest.java new file mode 100644 index 00000000000..b97847b9b28 --- /dev/null +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/StressEliminateAllocationsIRTest.java @@ -0,0 +1,67 @@ +/* + * 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 8387940 + * @requires vm.compiler2.enabled + * @summary C2: Stress allocation elimination failures + * + * @library /test/lib / + * @run driver ${test.main.class} + */ + +package compiler.escapeAnalysis; + +import compiler.lib.ir_framework.*; + +public class StressEliminateAllocationsIRTest { + public static void main(String[] args) { + TestFramework.runWithFlags("-XX:+UnlockDiagnosticVMOptions", + "-XX:+StressEliminateAllocations", + "-XX:StressEliminateAllocationsMean=1"); + } + + static class A { + final int i; + A(int i) { + this.i = i; + } + } + + @Test + @IR(counts = {IRNode.ALLOC, "1"}) + @Arguments(values = Argument.NUMBER_42) + private static int test(int i) { + // Even though the object is scalar replaceable, + // allocation elimination unconditionally fails in stress mode. + A a = new A(i); + + dontInline(); + + return a.i; + } + + @DontInline + private static void dontInline() {} +} From 0381286adedbd7daf1eb01a820ad8179dbb7da15 Mon Sep 17 00:00:00 2001 From: Hai-May Chao Date: Tue, 14 Jul 2026 20:46:53 +0000 Subject: [PATCH 224/707] 8376748: Emit runtime warnings for JCE algorithms that will be disabled Reviewed-by: mullan --- .../share/classes/java/security/KeyStore.java | 128 +++++++-- .../classes/java/security/MessageDigest.java | 75 ++++- .../classes/java/security/Signature.java | 74 ++++- .../share/classes/javax/crypto/Cipher.java | 81 +++++- .../util/CryptoAlgorithmConstraints.java | 94 +++++-- .../share/conf/security/java.security | 35 ++- .../KeyStore/TestLegacyAlgorithms.java | 259 ++++++++++++++++++ .../MessageDigest/TestLegacyAlgorithms.java | 212 ++++++++++++++ .../Signature/TestLegacyAlgorithms.java | 212 ++++++++++++++ .../crypto/Cipher/TestLegacyAlgorithms.java | 221 +++++++++++++++ 10 files changed, 1312 insertions(+), 79 deletions(-) create mode 100644 test/jdk/java/security/KeyStore/TestLegacyAlgorithms.java create mode 100644 test/jdk/java/security/MessageDigest/TestLegacyAlgorithms.java create mode 100644 test/jdk/java/security/Signature/TestLegacyAlgorithms.java create mode 100644 test/jdk/javax/crypto/Cipher/TestLegacyAlgorithms.java diff --git a/src/java.base/share/classes/java/security/KeyStore.java b/src/java.base/share/classes/java/security/KeyStore.java index 434aa57e3ac..f7adfcbcd62 100644 --- a/src/java.base/share/classes/java/security/KeyStore.java +++ b/src/java.base/share/classes/java/security/KeyStore.java @@ -37,6 +37,9 @@ import javax.security.auth.DestroyFailedException; import javax.security.auth.callback.*; +import jdk.internal.reflect.CallerSensitive; +import jdk.internal.reflect.Reflection; + import sun.security.util.Debug; import sun.security.util.CryptoAlgorithmConstraints; @@ -854,8 +857,18 @@ private String getProviderName() { *

  9. the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified keystore type is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + *
  10. + *
  11. the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified keystore type is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the keystore type. This warning is shown once per caller for + * each legacy keystore type. If the keystore type is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. *
  12. * * @@ -876,6 +889,7 @@ private String getProviderName() { * * @see Provider */ + @CallerSensitive public static KeyStore getInstance(String type) throws KeyStoreException { @@ -885,6 +899,11 @@ public static KeyStore getInstance(String type) throw new KeyStoreException(type + " is disabled"); } + if (CryptoAlgorithmConstraints.isLegacy("KeyStore", type)) { + CryptoAlgorithmConstraints.warn("KeyStore", type, + Reflection.getCallerClass()); + } + try { Object[] objs = Security.getImpl(type, "KeyStore", (String)null); return new KeyStore((KeyStoreSpi)objs[0], (Provider)objs[1], type); @@ -906,11 +925,24 @@ public static KeyStore getInstance(String type) * * @implNote * The JDK Reference Implementation additionally uses - * the {@code jdk.crypto.disabledAlgorithms} + *
      + *
    • the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified keystore type is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + *
    • + *
    • the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified keystore type is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the keystore type. This warning is shown once per caller for + * each legacy keystore type. If the keystore type is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. + *
    • + *
    * * @param type the type of keystore. * See the KeyStore section in the + *
  13. the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified keystore type is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + *
  14. + *
  15. the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified keystore type is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the keystore type. This warning is shown once per caller for + * each legacy keystore type. If the keystore type is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. + *
  16. + * * * @param type the type of keystore. * See the KeyStore section in the
    + *
  17. the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified keystore type is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. Disallowed type will be skipped. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + * Disallowed type will be skipped. + *
  18. + *
  19. the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified keystore type is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the keystore type. This warning is shown once per caller for + * each legacy keystore type. If the keystore type is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. + *
  20. + * * * @param file the keystore file * @param password the keystore password, which may be {@code null} @@ -1785,10 +1856,12 @@ public final void setEntry(String alias, Entry entry, * * @since 9 */ + @CallerSensitive public static final KeyStore getInstance(File file, char[] password) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { - return getInstance(file, password, null, true); + return getInstance(file, password, null, true, + Reflection.getCallerClass()); } /** @@ -1815,11 +1888,25 @@ public static final KeyStore getInstance(File file, char[] password) * * @implNote * The JDK Reference Implementation additionally uses - * the {@code jdk.crypto.disabledAlgorithms} + *
      + *
    • the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified keystore type is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. Disallowed type will be skipped. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + * Disallowed type will be skipped. + *
    • + *
    • the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified keystore type is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the keystore type. This warning is shown once per caller for + * each legacy keystore type. If the keystore type is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. + *
    • + *
    * * @param file the keystore file * @param param the {@code LoadStoreParameter} that specifies how to load @@ -1847,15 +1934,17 @@ public static final KeyStore getInstance(File file, char[] password) * * @since 9 */ + @CallerSensitive public static final KeyStore getInstance(File file, LoadStoreParameter param) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { - return getInstance(file, null, param, false); + return getInstance(file, null, param, false, + Reflection.getCallerClass()); } // Used by getInstance(File, char[]) & getInstance(File, LoadStoreParameter) private static final KeyStore getInstance(File file, char[] password, - LoadStoreParameter param, boolean hasPassword) + LoadStoreParameter param, boolean hasPassword, Class callerClass) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { @@ -1893,6 +1982,11 @@ private static final KeyStore getInstance(File file, char[] password, String ksAlgo = s.getAlgorithm(); if (CryptoAlgorithmConstraints.permits( "KEYSTORE", ksAlgo)) { + if (CryptoAlgorithmConstraints.isLegacy( + "KeyStore", ksAlgo)) { + CryptoAlgorithmConstraints.warn( + "KeyStore", ksAlgo, callerClass); + } keystore = new KeyStore(impl, p, ksAlgo); } else { matched = ksAlgo; diff --git a/src/java.base/share/classes/java/security/MessageDigest.java b/src/java.base/share/classes/java/security/MessageDigest.java index 6e8f64f7ebe..943459b4bf7 100644 --- a/src/java.base/share/classes/java/security/MessageDigest.java +++ b/src/java.base/share/classes/java/security/MessageDigest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -30,6 +30,9 @@ import java.io.PrintStream; import java.nio.ByteBuffer; +import jdk.internal.reflect.CallerSensitive; +import jdk.internal.reflect.Reflection; + import sun.security.jca.GetInstance; import sun.security.util.Debug; import sun.security.util.MessageDigestSpi2; @@ -168,8 +171,18 @@ private MessageDigest(String algorithm, Provider p) { *
  21. the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified algorithm is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + *
  22. + *
  23. the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified algorithm is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the algorithm. This warning is shown once per caller for + * each legacy algorithm. If the algorithm is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. *
  24. * * @@ -191,6 +204,7 @@ private MessageDigest(String algorithm, Provider p) { * * @see Provider */ + @CallerSensitive public static MessageDigest getInstance(String algorithm) throws NoSuchAlgorithmException { @@ -200,6 +214,11 @@ public static MessageDigest getInstance(String algorithm) throw new NoSuchAlgorithmException(algorithm + " is disabled"); } + if (CryptoAlgorithmConstraints.isLegacy("MessageDigest", algorithm)) { + CryptoAlgorithmConstraints.warn("MessageDigest", algorithm, + Reflection.getCallerClass()); + } + GetInstance.Instance instance = GetInstance.getInstance("MessageDigest", MessageDigestSpi.class, algorithm); MessageDigest md; @@ -233,11 +252,24 @@ public static MessageDigest getInstance(String algorithm) * * @implNote * The JDK Reference Implementation additionally uses - * the {@code jdk.crypto.disabledAlgorithms} + *
      + *
    • the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified algorithm is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + *
    • + *
    • the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified algorithm is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the algorithm. This warning is shown once per caller for + * each legacy algorithm. If the algorithm is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. + *
    • + *
    * * @param algorithm the name of the algorithm requested. * See the MessageDigest section in the
    + *
  25. the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified algorithm is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + *
  26. + *
  27. the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified algorithm is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the algorithm. This warning is shown once per caller for + * each legacy algorithm. If the algorithm is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. + *
  28. + * * * @param algorithm the name of the algorithm requested. * See the MessageDigest section in the
    the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified algorithm is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + * + *
  29. the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified algorithm is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the algorithm. This warning is shown once per caller for + * each legacy algorithm. If the algorithm is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. *
  30. * * @@ -259,6 +271,7 @@ protected Signature(String algorithm) { * * @see Provider */ + @CallerSensitive public static Signature getInstance(String algorithm) throws NoSuchAlgorithmException { Objects.requireNonNull(algorithm, "null algorithm name"); @@ -267,6 +280,11 @@ public static Signature getInstance(String algorithm) throw new NoSuchAlgorithmException(algorithm + " is disabled"); } + if (CryptoAlgorithmConstraints.isLegacy("Signature", algorithm)) { + CryptoAlgorithmConstraints.warn("Signature", algorithm, + Reflection.getCallerClass()); + } + Iterator t = GetInstance.getServices("Signature", algorithm); if (!t.hasNext()) { throw new NoSuchAlgorithmException @@ -362,11 +380,24 @@ private static boolean isSpi(Service s) { * * @implNote * The JDK Reference Implementation additionally uses - * the {@code jdk.crypto.disabledAlgorithms} + *
      + *
    • the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified algorithm is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + *
    • + *
    • the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified algorithm is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the algorithm. This warning is shown once per caller for + * each legacy algorithm. If the algorithm is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. + *
    • + *
    * * @param algorithm the name of the algorithm requested. * See the Signature section in the
    + *
  31. the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified algorithm is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + *
  32. + *
  33. the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified algorithm is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the algorithm. This warning is shown once per caller for + * each legacy algorithm. If the algorithm is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. + *
  34. + * * * @param algorithm the name of the algorithm requested. * See the Signature section in the
    the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified algorithm is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + * + *
  35. the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified algorithm is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the algorithm. This warning is shown once per caller for + * each legacy algorithm. If the algorithm is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. *
  36. * * @@ -541,6 +553,7 @@ private static Transform getTransform(Service s, * * @see java.security.Provider */ + @CallerSensitive public static final Cipher getInstance(String transformation) throws NoSuchAlgorithmException, NoSuchPaddingException { @@ -554,6 +567,11 @@ public static final Cipher getInstance(String transformation) " is disabled"); } + if (CryptoAlgorithmConstraints.isLegacy("Cipher", transformation)) { + CryptoAlgorithmConstraints.warn("Cipher", transformation, + Reflection.getCallerClass()); + } + List transforms = getTransforms(transformation); List cipherServices = new ArrayList<>(transforms.size()); for (Transform transform : transforms) { @@ -623,11 +641,24 @@ public static final Cipher getInstance(String transformation) * * @implNote * The JDK Reference Implementation additionally uses - * the {@code jdk.crypto.disabledAlgorithms} + *
      + *
    • the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified algorithm is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + *
    • + *
    • the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified algorithm is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the algorithm. This warning is shown once per caller for + * each legacy algorithm. If the algorithm is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. + *
    • + *
    * * @param transformation the name of the transformation, * e.g., AES/CBC/PKCS5Padding. @@ -660,6 +691,7 @@ public static final Cipher getInstance(String transformation) * * @see java.security.Provider */ + @CallerSensitive public static final Cipher getInstance(String transformation, String provider) throws NoSuchAlgorithmException, NoSuchProviderException, @@ -676,7 +708,7 @@ public static final Cipher getInstance(String transformation, throw new NoSuchProviderException("No such provider: " + provider); } - return getInstance(transformation, p); + return getInstance(transformation, p, Reflection.getCallerClass()); } private String getProviderName() { @@ -705,11 +737,24 @@ private String getProviderName() { * * @implNote * The JDK Reference Implementation additionally uses - * the {@code jdk.crypto.disabledAlgorithms} + *
      + *
    • the {@code jdk.crypto.disabledAlgorithms} * {@link Security#getProperty(String) Security} property to determine * if the specified algorithm is allowed. If the - * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes - * the security property value. + * {@systemProperty jdk.crypto.disabledAlgorithms} system property + * is set, it supersedes the security property value. + *
    • + *
    • the {@code jdk.crypto.legacyAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified algorithm is considered legacy. + * If so, a warning is emitted at runtime when this method is called + * with the algorithm. This warning is shown once per caller for + * each legacy algorithm. If the algorithm is also disabled, + * the warning will not be shown. + * If the {@systemProperty jdk.crypto.legacyAlgorithms} system property + * is set, it supersedes the security property value. + *
    • + *
    * * @param transformation the name of the transformation, * e.g., AES/CBC/PKCS5Padding. @@ -739,6 +784,7 @@ private String getProviderName() { * * @see java.security.Provider */ + @CallerSensitive public static final Cipher getInstance(String transformation, Provider provider) throws NoSuchAlgorithmException, NoSuchPaddingException @@ -750,12 +796,27 @@ public static final Cipher getInstance(String transformation, throw new IllegalArgumentException("Missing provider"); } + return getInstance(transformation, provider, Reflection.getCallerClass()); + } + + private static Cipher getInstance(String transformation, Provider provider, + Class callerClass) + throws NoSuchAlgorithmException, NoSuchPaddingException { + if (provider == null) { + throw new IllegalArgumentException("Missing provider"); + } + // throws NoSuchAlgorithmException if java.security disables it if (!CryptoAlgorithmConstraints.permits("Cipher", transformation)) { throw new NoSuchAlgorithmException(transformation + " is disabled"); } + if (CryptoAlgorithmConstraints.isLegacy("Cipher", transformation)) { + CryptoAlgorithmConstraints.warn("Cipher", transformation, + callerClass); + } + Exception failure = null; List transforms = getTransforms(transformation); boolean providerChecked = false; diff --git a/src/java.base/share/classes/sun/security/util/CryptoAlgorithmConstraints.java b/src/java.base/share/classes/sun/security/util/CryptoAlgorithmConstraints.java index ad3beab350f..781c1ab2cd2 100644 --- a/src/java.base/share/classes/sun/security/util/CryptoAlgorithmConstraints.java +++ b/src/java.base/share/classes/sun/security/util/CryptoAlgorithmConstraints.java @@ -26,7 +26,9 @@ package sun.security.util; import java.lang.ref.SoftReference; +import java.net.URL; import java.security.AlgorithmParameters; +import java.security.CodeSource; import java.security.CryptoPrimitive; import java.security.Key; import java.util.Arrays; @@ -36,9 +38,10 @@ /** * This class implements the algorithm constraints for the - * "jdk.crypto.disabledAlgorithms" security property. This security property - * can be overridden by the system property of the same name. See the - * java.security file for the syntax of the property value. + * "jdk.crypto.disabledAlgorithms" and "jdk.crypto.legacyAlgorithms" security + * properties. Each security property can be overridden by a system property + * of the same name. See the java.security file for the syntax of the property + * values. */ public class CryptoAlgorithmConstraints extends AbstractAlgorithmConstraints { private static final Debug debug = Debug.getInstance("jca"); @@ -51,11 +54,20 @@ public class CryptoAlgorithmConstraints extends AbstractAlgorithmConstraints { private static final String PROPERTY_CRYPTO_DISABLED_ALGS = "jdk.crypto.disabledAlgorithms"; - private static class CryptoHolder { - static final CryptoAlgorithmConstraints CONSTRAINTS = + // Legacy algorithm security property for JCE crypto services + private static final String PROPERTY_CRYPTO_LEGACY_ALGS = + "jdk.crypto.legacyAlgorithms"; + + private static class DisabledHolder { + private static final CryptoAlgorithmConstraints DISABLED_CONSTRAINTS = new CryptoAlgorithmConstraints(PROPERTY_CRYPTO_DISABLED_ALGS); } + private static class LegacyHolder { + private static final CryptoAlgorithmConstraints LEGACY_CONSTRAINTS = + new CryptoAlgorithmConstraints(PROPERTY_CRYPTO_LEGACY_ALGS); + } + private static void debug(String msg) { if (debug != null) { debug.println("CryptoAlgoConstraints: ", msg); @@ -63,11 +75,47 @@ private static void debug(String msg) { } public static boolean permits(String service, String algo) { - return CryptoHolder.CONSTRAINTS.cachedCheckAlgorithm( + return DisabledHolder.DISABLED_CONSTRAINTS.cachedCheckAlgorithm( service + "." + algo); } - private final Set disabledServices; // syntax is . + public static boolean isLegacy(String service, String alg) { + return !LegacyHolder.LEGACY_CONSTRAINTS.cachedCheckAlgorithm( + service + "." + alg); + } + + private static class CallersHolder { + static final ClassValue> callers = new ClassValue<>() { + @Override + protected Set computeValue(Class type) { + return ConcurrentHashMap.newKeySet(); + } + }; + } + + public static void warn(String service, String alg, Class callerClass) { + if (callerClass == null) { + callerClass = CryptoAlgorithmConstraints.class; + } + String serviceAndAlg = service + "." + alg; + Set warnedAlgorithms = CallersHolder.callers.get(callerClass); + if (warnedAlgorithms.add(serviceAndAlg)) { + URL url = codeSource(callerClass); + String source = (url == null) ? callerClass.getName() : + callerClass.getName() + " (" + url + ")"; + System.err.printf(""" + WARNING: An outdated %s algorithm has been called by %s + WARNING: %s will be disabled by default in a future release + """, service, source, alg); + } + } + + private static URL codeSource(Class clazz) { + CodeSource cs = clazz.getProtectionDomain().getCodeSource(); + return (cs != null) ? cs.getLocation() : null; + } + + private final Set affectedServices; // syntax is . private volatile SoftReference> cacheRef = new SoftReference<>(null); @@ -76,42 +124,42 @@ public static boolean permits(String service, String algo) { * {@code propertyName}. Note that if a system property of the same name * is set, it overrides the security property. * - * @param propertyName the security property name that define the disabled + * @param propertyName the security property name that defines the * algorithm constraints */ CryptoAlgorithmConstraints(String propertyName) { super(null); - disabledServices = getAlgorithms(propertyName, true); - String[] entries = disabledServices.toArray(new String[0]); + affectedServices = getAlgorithms(propertyName, true); + String[] entries = affectedServices.toArray(new String[0]); debug("Before " + Arrays.deepToString(entries)); - for (String dk : entries) { - int idx = dk.indexOf("."); - if (idx < 1 || idx == dk.length() - 1) { + for (String k : entries) { + int idx = k.indexOf("."); + if (idx < 1 || idx == k.length() - 1) { // wrong syntax: missing "." or empty service or algorithm - throw new IllegalArgumentException("Invalid entry: " + dk); + throw new IllegalArgumentException("Invalid entry: " + k); } - String service = dk.substring(0, idx); - String algo = dk.substring(idx + 1); + String service = k.substring(0, idx); + String algo = k.substring(idx + 1); if (SUPPORTED_SERVICES.stream().anyMatch(e -> e.equalsIgnoreCase (service))) { KnownOIDs oid = KnownOIDs.findMatch(algo); if (oid != null) { debug("Add oid: " + oid.value()); - disabledServices.add(service + "." + oid.value()); + affectedServices.add(service + "." + oid.value()); debug("Add oid stdName: " + oid.stdName()); - disabledServices.add(service + "." + oid.stdName()); + affectedServices.add(service + "." + oid.stdName()); for (String a : oid.aliases()) { debug("Add oid alias: " + a); - disabledServices.add(service + "." + a); + affectedServices.add(service + "." + a); } } } else { // unsupported service - throw new IllegalArgumentException("Invalid entry: " + dk); + throw new IllegalArgumentException("Invalid entry: " + k); } } - debug("After " + Arrays.deepToString(disabledServices.toArray())); + debug("After " + Arrays.deepToString(affectedServices.toArray())); } @Override @@ -131,7 +179,7 @@ public final boolean permits(Set primitives, throw new UnsupportedOperationException("Unsupported permits() method"); } - // Return false if algorithm is found in the disabledServices Set. + // Return false if algorithm is found in the affectedServices Set. // Otherwise, return true. private boolean cachedCheckAlgorithm(String serviceDesc) { Map cache; @@ -147,7 +195,7 @@ private boolean cachedCheckAlgorithm(String serviceDesc) { if (result != null) { return result; } - result = checkAlgorithm(disabledServices, serviceDesc, null); + result = checkAlgorithm(affectedServices, serviceDesc, null); cache.put(serviceDesc, result); return result; } diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security index 976604b5cbc..26842d0c845 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -777,8 +777,8 @@ jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, DTLSv1.0, RC4, DES, \ # In some environments, certain algorithms may be undesirable for certain # cryptographic services. For example, "MD2" is generally no longer considered # to be a secure hash algorithm. This section describes the mechanism for -# disabling algorithms at the JCA/JCE level based on service name and algorithm -# name. +# disabling algorithms and identifying legacy algorithms at the JCA/JCE +# level based on service name and algorithm name. # # If a system property of the same name is also specified, it supersedes the # security property value defined here. @@ -786,7 +786,10 @@ jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, DTLSv1.0, RC4, DES, \ # The syntax of the disabled services string is described as follows: # "DisabledService {, DisabledService}" # -# DisabledService: +# The syntax of the legacy services string is described as follows: +# "LegacyService {, LegacyService}" +# +# DisabledService and LegacyService: # Service.AlgorithmName # # Service: (one of the following, more services may be added later) @@ -795,7 +798,7 @@ jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, DTLSv1.0, RC4, DES, \ # AlgorithmName: # (see below) # -# The "AlgorithmName" is the standard algorithm name of the disabled +# The "AlgorithmName" is the standard algorithm name of the affected # service. See the Java Security Standard Algorithm Names Specification # for information about Standard Algorithm Names. Matching is # performed using a case-insensitive exact matching rule. For Cipher service, @@ -805,18 +808,28 @@ jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, DTLSv1.0, RC4, DES, \ # unsupported services at the time of checking, an ExceptionInInitializerError # with a cause of IllegalArgumentException will be thrown. # -# Note: The restriction is applied in the various getInstance(...) methods -# of the supported Service classes, i.e. Cipher, KeyStore, MessageDigest, -# and Signature. If the algorithm is disabled, a NoSuchAlgorithmException will -# be thrown by the getInstance methods of Cipher, MessageDigest, and Signature -# and a KeyStoreException by the getInstance methods of KeyStore. +# Note: The jdk.crypto.disabledAlgorithms property is enforced in the various +# getInstance(...) methods of the supported Service classes, i.e. Cipher, +# KeyStore, MessageDigest, and Signature. If the algorithm is disabled, a +# NoSuchAlgorithmException will be thrown by the getInstance methods of +# Cipher, MessageDigest, and Signature and a KeyStoreException by the +# getInstance methods of KeyStore. # -# Note: This property is currently used by the JDK Reference implementation. -# It is not guaranteed to be examined and used by other implementations. +# Note: The jdk.crypto.legacyAlgorithms property is checked in the +# getInstance(...) methods of the supported Service classes, i.e. Cipher, +# KeyStore, MessageDigest, and Signature. If the algorithm is considered legacy, the +# JDK emits a warning at runtime when the algorithm is requested. +# This warning is shown once per caller for each legacy algorithm. +# If the algorithm is also disabled, the warning will not be shown. +# +# Note: These properties are currently used by the JDK Reference implementation. +# They are not guaranteed to be examined and used by other implementations. # # Example: # jdk.crypto.disabledAlgorithms=Cipher.RSA/ECB/PKCS1Padding, MessageDigest.MD2 +# jdk.crypto.legacyAlgorithms=Cipher.RSA/ECB/PKCS1Padding, MessageDigest.MD2 # +#jdk.crypto.legacyAlgorithms= #jdk.crypto.disabledAlgorithms= # diff --git a/test/jdk/java/security/KeyStore/TestLegacyAlgorithms.java b/test/jdk/java/security/KeyStore/TestLegacyAlgorithms.java new file mode 100644 index 00000000000..516191cfa01 --- /dev/null +++ b/test/jdk/java/security/KeyStore/TestLegacyAlgorithms.java @@ -0,0 +1,259 @@ +/* + * 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 8376748 + * @summary Test JCE layer legacy algorithm warning for KeyStore + * @library /test/lib + * @run main/othervm TestLegacyAlgorithms KEYSTORE.JKs true + * @run main/othervm TestLegacyAlgorithms keySTORE.what false + * @run main/othervm TestLegacyAlgorithms kEYstoRe.jceKS false + * @run main/othervm -Djdk.crypto.legacyAlgorithms=KEYSTORE.JKS + * -Djdk.crypto.disabledAlgorithms=KEYSTORE.JKS + * TestLegacyAlgorithms KEYSTORE.JKS false true + */ + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.Provider; +import java.security.Security; +import java.util.List; + +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; + +public class TestLegacyAlgorithms { + + private static final String PROP_NAME = "jdk.crypto.legacyAlgorithms"; + private static final String DIR = System.getProperty("test.src", "."); + private static final char[] PASSWD = "passphrase".toCharArray(); + private static final String JKS_FN = "keystore.jks"; + + private static final List ALG_LIST = + List.of("JKS", "jkS"); + + private static String saveWarn(ThrowingRunnable action) throws Exception { + PrintStream origErr = System.err; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(bOut, true, StandardCharsets.UTF_8); + try { + System.setErr(ps); + action.run(); + } finally { + ps.flush(); + System.setErr(origErr); + } + return bOut.toString(StandardCharsets.UTF_8); + } + + private static int countWarn(String warnS, String msg) { + int num = 0; + int index = 0; + while ((index = warnS.indexOf(msg, index)) >= 0) { + num++; + index += msg.length(); + } + return num; + } + + private static void checkOneWarn(String warnS, String alg) { + String warn1 = + "WARNING: An outdated KeyStore algorithm has been called by"; + String warn2 = "WARNING: " + alg + + " will be disabled by default in a future release"; + + Asserts.assertEQ(countWarn(warnS, warn1), 1, + "Expected one legacy warning for KeyStore " + alg + + " but got:\n" + warnS); + Asserts.assertEQ(countWarn(warnS, warn2), 1, + "Expected one future-disable warning for KeyStore " + + alg + " but got:\n" + warnS); + Asserts.assertTrue(warnS.contains("TestLegacyAlgorithms"), + "Expected warning to preserve caller: " + warnS); + } + + private static void checkNoWarn(String warnS) { + String warn1 = + "WARNING: An outdated KeyStore algorithm has been called by"; + String warn2 = + "will be disabled by default in a future release"; + Asserts.assertFalse(warnS.contains(warn1), + "Unexpected legacy warning for KeyStore: " + warnS); + Asserts.assertFalse(warnS.contains(warn2), + "Unexpected future-disable warning for KeyStore: " + warnS); + } + + private static void checkWarn(String label, String alg, + boolean shouldWarn, ThrowingRunnable action) throws Exception { + System.out.println("Testing " + label); + String warnS = saveWarn(action); + System.out.println("Warning emitted:\n" + warnS); + if (shouldWarn) { + checkOneWarn(warnS, alg); + } else { + checkNoWarn(warnS); + } + } + + // Disable the algorithm and check that a warning is not emitted. + private static void warnDisabledTest() + throws Exception { + File jksFile = new File(DIR, JKS_FN); + checkWarn("no warning when the algorithm is disabled", + "JKS", false, () -> { + Utils.runAndCheckException( + () -> KeyStore.getInstance("JKS"), + KeyStoreException.class); + Utils.runAndCheckException( + () -> KeyStore.getInstance(jksFile, PASSWD), + KeyStoreException.class); + }); + } + + private static void runTests(boolean shouldWarn) throws Exception { + for (String a : ALG_LIST) { + checkWarn("default provider: alg " + a, a, shouldWarn, + () -> DefaultKS.run(a)); + } + + File jksFile = new File(DIR, JKS_FN); + + checkWarn("file with password: " + jksFile, "JKS", shouldWarn, + () -> PasswordKS.run(jksFile)); + + checkWarn("file with LoadStoreParameter: " + jksFile, "JKS", + shouldWarn, () -> LoadStoreParamKS.run(jksFile)); + + Provider[] providers = Security.getProviders("KeyStore.JKS"); + if (providers.length > 0) { + // First provider should warn, and later provider for the same + // algorithm will not warn. This is because warning is determined + // by caller class and algorithm string, not by provider. + Provider p = providers[0]; + for (String a : ALG_LIST) { + checkWarn("provider object " + p.getName() + ": alg " + a, + a, shouldWarn, () -> ProvObjKS.run(a, p)); + + checkWarn("provider name " + p.getName() + ": alg " + a, + a, shouldWarn, () -> ProvNameKS.run(a, p)); + } + } + } + + public static void main(String[] args) throws Exception { + String propValue = args[0]; + boolean shouldWarn = Boolean.parseBoolean(args[1]); + boolean warnDisabled = + args.length > 2 && Boolean.parseBoolean(args[2]); + System.out.println("Setting Security Prop " + PROP_NAME + " = " + + propValue); + Security.setProperty(PROP_NAME, propValue); + if (warnDisabled) { + warnDisabledTest(); + } else { + runTests(shouldWarn); + } + } + + @FunctionalInterface + private interface ThrowingRunnable { + void run() throws Exception; + } + + private static final class DefaultKS { + static void run(String alg) throws Exception { + KeyStore k = KeyStore.getInstance(alg); + System.out.println(" type lookup: got KeyStore w/ alg " + + k.getType()); + + // Call the method twice, and make sure that only get one + // warning per caller. + k = KeyStore.getInstance(alg); + System.out.println(" type lookup again: got KeyStore w/ alg " + + k.getType()); + } + } + + private static final class PasswordKS { + static void run(File jksFile) throws Exception { + KeyStore k = KeyStore.getInstance(jksFile, PASSWD); + System.out.println(" file+password: got KeyStore w/ alg " + + k.getType()); + + // Call the method twice, and make sure that only get one + // warning per caller. + k = KeyStore.getInstance(jksFile, PASSWD); + System.out.println(" file+password again: got KeyStore " + + "w/ alg " + k.getType()); + } + } + + private static final class LoadStoreParamKS { + static void run(File jksFile) throws Exception { + KeyStore k = KeyStore.getInstance(jksFile, + () -> new KeyStore.PasswordProtection(PASSWD)); + System.out.println(" file+LoadStoreParameter: got KeyStore " + + "w/ alg " + k.getType()); + + // Call the method twice, and make sure that only get one + // warning per caller. + k = KeyStore.getInstance(jksFile, + () -> new KeyStore.PasswordProtection(PASSWD)); + System.out.println(" file+LoadStoreParameter again: got " + + "KeyStore w/ alg " + k.getType()); + } + } + + private static final class ProvObjKS { + static void run(String alg, Provider provider) throws Exception { + KeyStore k = KeyStore.getInstance(alg, provider); + System.out.println(" provider object: got KeyStore w/ alg " + + k.getType()); + + // Call the method twice, and make sure that only get one + // warning per caller. + k = KeyStore.getInstance(alg, provider); + System.out.println(" provider object again: got KeyStore " + + "w/ alg " + k.getType()); + } + } + + private static final class ProvNameKS { + static void run(String alg, Provider provider) throws Exception { + KeyStore k = KeyStore.getInstance(alg, provider.getName()); + System.out.println(" provider name: got KeyStore w/ alg " + + k.getType()); + + // Call the method twice, and make sure that only get one + // warning per caller. + k = KeyStore.getInstance(alg, provider.getName()); + System.out.println(" provider name again: got KeyStore " + + "w/ alg " + k.getType()); + } + } +} diff --git a/test/jdk/java/security/MessageDigest/TestLegacyAlgorithms.java b/test/jdk/java/security/MessageDigest/TestLegacyAlgorithms.java new file mode 100644 index 00000000000..61275b03f5d --- /dev/null +++ b/test/jdk/java/security/MessageDigest/TestLegacyAlgorithms.java @@ -0,0 +1,212 @@ +/* + * 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 8376748 + * @summary Test JCE layer legacy algorithm warning for MessageDigest + * @library /test/lib + * @run main/othervm TestLegacyAlgorithms MESSAGEdigest.Sha-512 true + * @run main/othervm TestLegacyAlgorithms messageDIGest.what false + * @run main/othervm TestLegacyAlgorithms meSSagedIgest.sHA-512/224 false + * @run main/othervm -Djdk.crypto.legacyAlgorithms=MESSAGEdigest.Sha-512 + * -Djdk.crypto.disabledAlgorithms=MESSAGEdigest.Sha-512 + * TestLegacyAlgorithms MESSAGEdigest.Sha-512 false true + */ + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Security; +import java.util.List; + +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; + +public class TestLegacyAlgorithms { + + private static final String PROP_NAME = "jdk.crypto.legacyAlgorithms"; + private static final List ALG_LIST = + List.of("sHA-512", "shA-512", "2.16.840.1.101.3.4.2.3"); + + private static String saveWarn(ThrowingRunnable action) throws Exception { + PrintStream origErr = System.err; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(bOut, true, StandardCharsets.UTF_8); + try { + System.setErr(ps); + action.run(); + } finally { + ps.flush(); + System.setErr(origErr); + } + return bOut.toString(StandardCharsets.UTF_8); + } + + private static int countWarn(String warnS, String msg) { + int num = 0; + int index = 0; + while ((index = warnS.indexOf(msg, index)) >= 0) { + num++; + index += msg.length(); + } + return num; + } + + private static void checkOneWarn(String warnS, String alg) { + String warn1 = + "WARNING: An outdated MessageDigest algorithm has been called by"; + String warn2 = "WARNING: " + alg + + " will be disabled by default in a future release"; + + Asserts.assertEQ(countWarn(warnS, warn1), 1, + "Expected one legacy warning for MessageDigest " + alg + + " but got:\n" + warnS); + Asserts.assertEQ(countWarn(warnS, warn2), 1, + "Expected one future-disable warning for MessageDigest " + + alg + " but got:\n" + warnS); + Asserts.assertTrue(warnS.contains("TestLegacyAlgorithms"), + "Expected warning to preserve caller: " + warnS); + } + + private static void checkNoWarn(String warnS) { + String warn1 = + "WARNING: An outdated MessageDigest algorithm has been called by"; + String warn2 = + "will be disabled by default in a future release"; + Asserts.assertFalse(warnS.contains(warn1), + "Unexpected legacy warning for MessageDigest: " + warnS); + Asserts.assertFalse(warnS.contains(warn2), + "Unexpected future-disable warning for MessageDigest: " + warnS); + } + + private static void checkwarn(String label, String alg, + boolean shouldWarn, ThrowingRunnable action) throws Exception { + System.out.println("Testing " + label); + String warnS = saveWarn(action); + System.out.println("Warning emitted:\n" + warnS); + if (shouldWarn) { + checkOneWarn(warnS, alg); + } else { + checkNoWarn(warnS); + } + } + + // Disable the algorithm and check that a warning is not emitted. + private static void warnDisabledTest() + throws Exception { + checkwarn("no warning when the algorithm is disabled", + "SHA-512", false, () -> { + Utils.runAndCheckException( + () -> MessageDigest.getInstance("SHA-512"), + NoSuchAlgorithmException.class); + }); + } + + private static void runTests(boolean shouldWarn) throws Exception { + for (String a : ALG_LIST) { + checkwarn("default provider: alg " + a, a, shouldWarn, + () -> DefaultMD.run(a)); + } + + Provider[] providers = Security.getProviders("MessageDigest.SHA-512"); + if (providers.length > 0) { + // First provider should warn, and later provider for the same + // algorithm will not warn. This is because warning is determined + // by caller class and algorithm string, not by provider. + Provider p = providers[0]; + for (String a : ALG_LIST) { + checkwarn("provider object " + p.getName() + ": alg " + a, + a, shouldWarn, () -> ProvObjMD.run(a, p)); + + checkwarn("provider name " + p.getName() + ": alg " + a, + a, shouldWarn, () -> ProvNameMD.run(a, p)); + } + } + } + + public static void main(String[] args) throws Exception { + String propValue = args[0]; + boolean shouldWarn = Boolean.parseBoolean(args[1]); + boolean warnDisabled = + args.length > 2 && Boolean.parseBoolean(args[2]); + System.out.println("Setting Security Prop " + PROP_NAME + " = " + + propValue); + Security.setProperty(PROP_NAME, propValue); + if (warnDisabled) { + warnDisabledTest(); + } else { + runTests(shouldWarn); + } + } + + @FunctionalInterface + private interface ThrowingRunnable { + void run() throws Exception; + } + + private static final class DefaultMD { + static void run(String alg) throws Exception { + MessageDigest m = MessageDigest.getInstance(alg); + System.out.println(" type lookup: got MessageDigest w/ alg " + + m.getAlgorithm()); + + // Call the method twice, and make sure that only get one + // warning per caller. + m = MessageDigest.getInstance(alg); + System.out.println(" type lookup again: got MessageDigest w/ alg " + + m.getAlgorithm()); + } + } + + private static final class ProvObjMD { + static void run(String alg, Provider provider) throws Exception { + MessageDigest m = MessageDigest.getInstance(alg, provider); + System.out.println(" provider object: got MessageDigest w/ alg " + + m.getAlgorithm()); + + // Call the method twice, and make sure that only get one + // warning per caller. + m = MessageDigest.getInstance(alg, provider); + System.out.println(" provider object again: got MessageDigest " + + "w/ alg " + m.getAlgorithm()); + } + } + + private static final class ProvNameMD { + static void run(String alg, Provider provider) throws Exception { + MessageDigest m = MessageDigest.getInstance(alg, provider.getName()); + System.out.println(" provider name: got MessageDigest w/ alg " + + m.getAlgorithm()); + + // Call the method twice, and make sure that only get one + // warning per caller. + m = MessageDigest.getInstance(alg, provider.getName()); + System.out.println(" provider name again: got MessageDigest " + + "w/ alg " + m.getAlgorithm()); + } + } +} diff --git a/test/jdk/java/security/Signature/TestLegacyAlgorithms.java b/test/jdk/java/security/Signature/TestLegacyAlgorithms.java new file mode 100644 index 00000000000..bf745fba3e1 --- /dev/null +++ b/test/jdk/java/security/Signature/TestLegacyAlgorithms.java @@ -0,0 +1,212 @@ +/* + * 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 8376748 + * @summary Test JCE layer legacy algorithm warning for Signature + * @library /test/lib + * @run main/othervm TestLegacyAlgorithms SIGNATURe.sha512withRSA true + * @run main/othervm TestLegacyAlgorithms signaturE.what false + * @run main/othervm TestLegacyAlgorithms SiGnAtUrE.SHa512/224withRSA false + * @run main/othervm -Djdk.crypto.legacyAlgorithms=SIGNATURe.sha512withRSA + * -Djdk.crypto.disabledAlgorithms=SIGNATURe.sha512withRSA + * TestLegacyAlgorithms SIGNATURe.sha512withRSA false true + */ + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Security; +import java.security.Signature; +import java.util.List; + +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; + +public class TestLegacyAlgorithms { + + private static final String PROP_NAME = "jdk.crypto.legacyAlgorithms"; + private static final List ALG_LIST = + List.of("sha512withRsa", "1.2.840.113549.1.1.13"); + + private static String saveWarn(ThrowingRunnable action) throws Exception { + PrintStream origErr = System.err; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(bOut, true, StandardCharsets.UTF_8); + try { + System.setErr(ps); + action.run(); + } finally { + ps.flush(); + System.setErr(origErr); + } + return bOut.toString(StandardCharsets.UTF_8); + } + + private static int countWarn(String warnS, String msg) { + int num = 0; + int index = 0; + while ((index = warnS.indexOf(msg, index)) >= 0) { + num++; + index += msg.length(); + } + return num; + } + + private static void checkOneWarn(String warnS, String alg) { + String warn1 = + "WARNING: An outdated Signature algorithm has been called by"; + String warn2 = "WARNING: " + alg + + " will be disabled by default in a future release"; + + Asserts.assertEQ(countWarn(warnS, warn1), 1, + "Expected one legacy warning for Signature " + alg + + " but got:\n" + warnS); + Asserts.assertEQ(countWarn(warnS, warn2), 1, + "Expected one future-disable warning for Signature " + + alg + " but got:\n" + warnS); + Asserts.assertTrue(warnS.contains("TestLegacyAlgorithms"), + "Expected warning to preserve caller: " + warnS); + } + + private static void checkNoWarn(String warnS) { + String warn1 = + "WARNING: An outdated Signature algorithm has been called by"; + String warn2 = + "will be disabled by default in a future release"; + Asserts.assertFalse(warnS.contains(warn1), + "Unexpected legacy warning for Signature: " + warnS); + Asserts.assertFalse(warnS.contains(warn2), + "Unexpected future-disable warning for Signature: " + warnS); + } + + private static void checkWarn(String label, String alg, + boolean shouldWarn, ThrowingRunnable action) throws Exception { + System.out.println("Testing " + label); + String warnS = saveWarn(action); + System.out.println("Warning emitted:\n" + warnS); + if (shouldWarn) { + checkOneWarn(warnS, alg); + } else { + checkNoWarn(warnS); + } + } + + // Disable the algorithm and check that a warning is not emitted. + private static void warnDisabledTest() + throws Exception { + checkWarn("no warning when the algorithm is disabled", + "sha512withRSA", false, () -> { + Utils.runAndCheckException( + () -> Signature.getInstance("sha512withRSA"), + NoSuchAlgorithmException.class); + }); + } + + private static void runTests(boolean shouldWarn) throws Exception { + for (String a : ALG_LIST) { + checkWarn("default provider: alg " + a, a, shouldWarn, + () -> DefaultSig.run(a)); + } + + Provider[] providers = Security.getProviders("Signature.SHA512withRSA"); + if (providers.length > 0) { + // First provider should warn, and later provider for the same + // algorithm will not warn. This is because warning is determined + // by caller class and algorithm string, not by provider. + Provider p = providers[0]; + for (String a : ALG_LIST) { + checkWarn("provider object " + p.getName() + ": alg " + a, + a, shouldWarn, () -> ProvObjSig.run(a, p)); + + checkWarn("provider name " + p.getName() + ": alg " + a, + a, shouldWarn, () -> ProvNameSig.run(a, p)); + } + } + } + + public static void main(String[] args) throws Exception { + String propValue = args[0]; + boolean shouldWarn = Boolean.parseBoolean(args[1]); + boolean warnDisabled = + args.length > 2 && Boolean.parseBoolean(args[2]); + System.out.println("Setting Security Prop " + PROP_NAME + " = " + + propValue); + Security.setProperty(PROP_NAME, propValue); + if (warnDisabled) { + warnDisabledTest(); + } else { + runTests(shouldWarn); + } + } + + @FunctionalInterface + private interface ThrowingRunnable { + void run() throws Exception; + } + + private static final class DefaultSig { + static void run(String alg) throws Exception { + Signature s = Signature.getInstance(alg); + System.out.println(" type lookup: got Signature w/ alg " + + s.getAlgorithm()); + + // Call the method twice, and make sure that only get one + // warning per caller. + s = Signature.getInstance(alg); + System.out.println(" type lookup again: got Signature w/ alg " + + s.getAlgorithm()); + } + } + + private static final class ProvObjSig { + static void run(String alg, Provider provider) throws Exception { + Signature s = Signature.getInstance(alg, provider); + System.out.println(" provider object: got Signature w/ alg " + + s.getAlgorithm()); + + // Call the method twice, and make sure that only get one + // warning per caller. + s = Signature.getInstance(alg, provider); + System.out.println(" provider object again: got Signature " + + "w/ alg " + s.getAlgorithm()); + } + } + + private static final class ProvNameSig { + static void run(String alg, Provider provider) throws Exception { + Signature s = Signature.getInstance(alg, provider.getName()); + System.out.println(" provider name: got Signature w/ alg " + + s.getAlgorithm()); + + // Call the method twice, and make sure that only get one + // warning per caller. + s = Signature.getInstance(alg, provider.getName()); + System.out.println(" provider name again: got Signature " + + "w/ alg " + s.getAlgorithm()); + } + } +} diff --git a/test/jdk/javax/crypto/Cipher/TestLegacyAlgorithms.java b/test/jdk/javax/crypto/Cipher/TestLegacyAlgorithms.java new file mode 100644 index 00000000000..f4336646cbd --- /dev/null +++ b/test/jdk/javax/crypto/Cipher/TestLegacyAlgorithms.java @@ -0,0 +1,221 @@ +/* + * 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 8376748 + * @summary Test JCE layer legacy algorithm warning for Cipher + * @library /test/lib + * @run main/othervm TestLegacyAlgorithms CIPHEr.Rsa/ECB/PKCS1Padding true + * @run main/othervm TestLegacyAlgorithms cipheR.rsA true + * @run main/othervm TestLegacyAlgorithms CIPher.what false + * @run main/othervm TestLegacyAlgorithms cipHER.RSA/ECB/PKCS1Padding2 false + * @run main/othervm -Djdk.crypto.legacyAlgorithms=CIPHER.RSA + * -Djdk.crypto.disabledAlgorithms=CIPHER.RSA + * TestLegacyAlgorithms CIPHER.RSA false true + + */ + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Security; +import java.util.List; +import javax.crypto.Cipher; + +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; + +public class TestLegacyAlgorithms { + + private static final String PROP_NAME = "jdk.crypto.legacyAlgorithms"; + private static final List ALG_LIST = + List.of("Rsa/ECB/PKCS1Padding", "rSA"); + + private static String saveWarn(ThrowingRunnable action) throws Exception { + PrintStream origErr = System.err; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(bOut, true, StandardCharsets.UTF_8); + try { + System.setErr(ps); + action.run(); + } finally { + ps.flush(); + System.setErr(origErr); + } + return bOut.toString(StandardCharsets.UTF_8); + } + + private static int countWarn(String warnS, String msg) { + int num = 0; + int index = 0; + while ((index = warnS.indexOf(msg, index)) >= 0) { + num++; + index += msg.length(); + } + return num; + } + + private static void checkOneWarn(String warnS, String alg) { + String warn1 = + "WARNING: An outdated Cipher algorithm has been called by"; + String warn2 = "WARNING: " + alg + + " will be disabled by default in a future release"; + + Asserts.assertEQ(countWarn(warnS, warn1), 1, + "Expected one legacy warning for Cipher " + alg + + " but got:\n" + warnS); + Asserts.assertEQ(countWarn(warnS, warn2), 1, + "Expected one future-disable warning for Cipher " + + alg + " but got:\n" + warnS); + Asserts.assertTrue(warnS.contains("TestLegacyAlgorithms"), + "Expected warning to preserve caller: " + warnS); + } + + private static void checkNoWarn(String warnS) { + String warn1 = + "WARNING: An outdated Cipher algorithm has been called by"; + String warn2 = + "will be disabled by default in a future release"; + Asserts.assertFalse(warnS.contains(warn1), + "Unexpected legacy warning for Cipher: " + warnS); + Asserts.assertFalse(warnS.contains(warn2), + "Unexpected future-disable warning for Cipher: " + warnS); + } + + private static void checkWarn(String label, String alg, + boolean shouldWarn, ThrowingRunnable action) throws Exception { + System.out.println("Testing " + label); + String warnS = saveWarn(action); + System.out.println("Warning emitted:\n" + warnS); + if (shouldWarn) { + checkOneWarn(warnS, alg); + } else { + checkNoWarn(warnS); + } + } + + private static void warnDisabledTest() + throws Exception { + checkWarn("no warning when the algorithm is disabled", + "RSA", false, () -> { + Utils.runAndCheckException( + () -> Cipher.getInstance("RSA"), + NoSuchAlgorithmException.class); + }); + } + + private static void runTests(boolean shouldWarn) throws Exception { + for (String a : ALG_LIST) { + checkWarn("default provider: alg " + a, a, shouldWarn, + () -> DefaultCipher.run(a)); + } + + Provider provider = null; + for (Provider p : Security.getProviders()) { + // First provider should warn, and later provider for the same + // algorithm will not warn. This is because warning is determined + // by caller class and algorithm string, not by provider. + if (p.getService("Cipher", "RSA") != null) { + provider = p; + break; + } + } + if (provider != null) { + final Provider fp = provider; + for (String a : ALG_LIST) { + checkWarn("provider object " + fp.getName() + + ": alg " + a, a, shouldWarn, + () -> ProvObjCipher.run(a, fp)); + + checkWarn("provider name " + fp.getName() + + ": alg " + a, a, shouldWarn, + () -> ProvNameCipher.run(a, fp)); + } + } + } + + public static void main(String[] args) throws Exception { + String propValue = args[0]; + boolean shouldWarn = Boolean.parseBoolean(args[1]); + boolean warnDisabled = + args.length > 2 && Boolean.parseBoolean(args[2]); + System.out.println("Setting Security Prop " + PROP_NAME + " = " + + propValue); + Security.setProperty(PROP_NAME, propValue); + if (warnDisabled) { + warnDisabledTest(); + } else { + runTests(shouldWarn); + } + } + + @FunctionalInterface + private interface ThrowingRunnable { + void run() throws Exception; + } + + private static final class DefaultCipher { + static void run(String alg) throws Exception { + Cipher c = Cipher.getInstance(alg); + System.out.println(" type lookup: got Cipher w/ alg " + + c.getAlgorithm()); + + // Call the method twice, and make sure that only get one + // warning per caller. + c = Cipher.getInstance(alg); + System.out.println(" type lookup again: got Cipher w/ alg " + + c.getAlgorithm()); + } + } + + private static final class ProvObjCipher { + static void run(String alg, Provider provider) throws Exception { + Cipher c = Cipher.getInstance(alg, provider); + System.out.println(" provider object: got Cipher w/ alg " + + c.getAlgorithm()); + + // Call the method twice, and make sure that only get one + // warning per caller. + c = Cipher.getInstance(alg, provider); + System.out.println(" provider object again: got Cipher " + + "w/ alg " + c.getAlgorithm()); + } + } + + private static final class ProvNameCipher { + static void run(String alg, Provider provider) throws Exception { + Cipher c = Cipher.getInstance(alg, provider.getName()); + System.out.println(" provider name: got Cipher w/ alg " + + c.getAlgorithm()); + + // Call the method twice, and make sure that only get one + // warning per caller. + c = Cipher.getInstance(alg, provider.getName()); + System.out.println(" provider name again: got Cipher " + + "w/ alg " + c.getAlgorithm()); + } + } +} From 6ae23a0d6574dc8139aea93ea3c562a7410fcb34 Mon Sep 17 00:00:00 2001 From: William Kemper Date: Tue, 14 Jul 2026 23:41:25 +0000 Subject: [PATCH 225/707] 8388058: Shenandoah: Refactor arraycopy_work Reviewed-by: kdnilsen, shade --- .../gc/shenandoah/shenandoahBarrierSet.hpp | 10 +- .../shenandoahBarrierSet.inline.hpp | 113 +++++++++++------- 2 files changed, 76 insertions(+), 47 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp index 51b355e7042..ac896f24739 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp @@ -33,6 +33,7 @@ class ShenandoahHeap; class ShenandoahBarrierSetAssembler; class ShenandoahCardTable; +class ShenandoahMarkingContext; class ShenandoahBarrierSet: public BarrierSet { private: @@ -126,6 +127,10 @@ class ShenandoahBarrierSet: public BarrierSet { private: template void arraycopy_marking(T* dst, size_t count); + + template + bool is_above_tams(const ShenandoahMarkingContext* ctx, T* dst) const; + template inline void arraycopy_evacuation(T* src, size_t count); template @@ -134,10 +139,7 @@ class ShenandoahBarrierSet: public BarrierSet { template inline void clone_work(oop src); - template - inline void arraycopy_work(T* src, size_t count); - - inline bool need_bulk_update(HeapWord* dst); + inline bool need_bulk_update(HeapWord* dst) const; public: // Callbacks for runtime accesses. template diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp index f4b859afc44..af0622693fb 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp @@ -509,40 +509,6 @@ OopCopyResult ShenandoahBarrierSet::AccessBarrier::oop_ return result; } -template -void ShenandoahBarrierSet::arraycopy_work(T* src, size_t count) { - // Young cycles are allowed to run when old marking is in progress. When old marking is in progress, - // this barrier will be called with ENQUEUE=true and HAS_FWD=false, even though the young generation - // may have forwarded objects. - assert(HAS_FWD == _heap->has_forwarded_objects() || _heap->is_concurrent_old_mark_in_progress(), "Forwarded object status is sane"); - // This function cannot be called to handle marking and evacuation at the same time (they operate on - // different sides of the copy). - static_assert((HAS_FWD || EVAC) != ENQUEUE, "Cannot evacuate and mark both sides of copy."); - - Thread* thread = Thread::current(); - SATBMarkQueue& queue = ShenandoahThreadLocalData::satb_mark_queue(thread); - ShenandoahMarkingContext* ctx = _heap->marking_context(); - const ShenandoahCollectionSet* const cset = _heap->collection_set(); - T* end = src + count; - for (T* elem_ptr = src; elem_ptr < end; elem_ptr++) { - T o = RawAccess<>::oop_load(elem_ptr); - if (!CompressedOops::is_null(o)) { - oop obj = CompressedOops::decode_not_null(o); - if (HAS_FWD && cset->is_in(obj)) { - oop fwd = ShenandoahForwarding::get_forwardee(obj); - if (EVAC && obj == fwd) { - fwd = _heap->evacuate_object(obj, thread); - } - shenandoah_assert_forwarded_except(elem_ptr, obj, _heap->cancelled_gc()); - ShenandoahHeap::atomic_update_oop(fwd, elem_ptr, o); - } - if (ENQUEUE && !ctx->is_marked_strong(obj)) { - _satb_mark_queue_set.enqueue_known_active(queue, obj); - } - } - } -} - template void ShenandoahBarrierSet::arraycopy_barrier(T* src, T* dst, size_t count) { if (count == 0) { @@ -574,31 +540,92 @@ void ShenandoahBarrierSet::arraycopy_barrier(T* src, T* dst, size_t count) { template void ShenandoahBarrierSet::arraycopy_marking(T* dst, size_t count) { assert(_heap->is_concurrent_mark_in_progress(), "only during marking"); - if (ShenandoahSATBBarrier) { - if (!_heap->marking_context()->allocated_after_mark_start(reinterpret_cast(dst)) || - (IS_GENERATIONAL && _heap->heap_region_containing(dst)->is_old() && _heap->is_concurrent_young_mark_in_progress())) { - arraycopy_work(dst, count); + if (!ShenandoahSATBBarrier) { + return; + } + + const ShenandoahMarkingContext* ctx = _heap->marking_context(); + // Everything allocated above TAMS is alive and doesn't need the barrier to keep it that way + if (is_above_tams(ctx, dst)) { + return; + } + + assert(!_heap->has_forwarded_objects() || _heap->is_concurrent_old_mark_in_progress(), "Forwarded object status is sane"); + Thread* thread = Thread::current(); + SATBMarkQueue& queue = ShenandoahThreadLocalData::satb_mark_queue(thread); + T* end = dst + count; + for (T* elem_ptr = dst; elem_ptr < end; ++elem_ptr) { + T o = RawAccess<>::oop_load(elem_ptr); + if (!CompressedOops::is_null(o)) { + oop obj = CompressedOops::decode_not_null(o); + if (!ctx->is_marked_strong(obj)) { + _satb_mark_queue_set.enqueue_known_active(queue, obj); + } } } } -inline bool ShenandoahBarrierSet::need_bulk_update(HeapWord* ary) { +template +bool ShenandoahBarrierSet::is_above_tams(const ShenandoahMarkingContext* ctx, T* dst) const { + // TAMS for an old region is unreliable during a young-only mark, so overwritten pointers in old dst arrays must + // be enqueued to preserve old->young referents copied in and overwritten after init mark. See JDK-8373116. + return ctx->allocated_after_mark_start(reinterpret_cast(dst)) + && !(IS_GENERATIONAL + && _heap->heap_region_containing(dst)->is_old() + && _heap->is_concurrent_young_mark_in_progress()); +} + +inline bool ShenandoahBarrierSet::need_bulk_update(HeapWord* ary) const { return ary < _heap->heap_region_containing(ary)->get_update_watermark(); } template void ShenandoahBarrierSet::arraycopy_evacuation(T* src, size_t count) { assert(_heap->is_evacuation_in_progress(), "only during evacuation"); - if (need_bulk_update(reinterpret_cast(src))) { - arraycopy_work(src, count); + if (!need_bulk_update(reinterpret_cast(src))) { + return; + } + + assert(_heap->has_forwarded_objects(), "Forwarded object status is sane"); + Thread* thread = Thread::current(); + const ShenandoahCollectionSet* const cset = _heap->collection_set(); + T* end = src + count; + for (T* elem_ptr = src; elem_ptr < end; ++elem_ptr) { + T o = RawAccess<>::oop_load(elem_ptr); + if (!CompressedOops::is_null(o)) { + oop obj = CompressedOops::decode_not_null(o); + if (cset->is_in(obj)) { + oop fwd = ShenandoahForwarding::get_forwardee(obj); + if (obj == fwd) { + fwd = _heap->evacuate_object(obj, thread); + } + shenandoah_assert_forwarded_except(elem_ptr, obj, _heap->cancelled_gc()); + ShenandoahHeap::atomic_update_oop(fwd, elem_ptr, o); + } + } } } template void ShenandoahBarrierSet::arraycopy_update(T* src, size_t count) { assert(_heap->is_update_refs_in_progress(), "only during update-refs"); - if (need_bulk_update(reinterpret_cast(src))) { - arraycopy_work(src, count); + if (!need_bulk_update(reinterpret_cast(src))) { + return; + } + + assert(_heap->has_forwarded_objects(), "Forwarded object status is sane"); + const ShenandoahCollectionSet* const cset = _heap->collection_set(); + T* end = src + count; + for (T* elem_ptr = src; elem_ptr < end; ++elem_ptr) { + T o = RawAccess<>::oop_load(elem_ptr); + if (!CompressedOops::is_null(o)) { + oop obj = CompressedOops::decode_not_null(o); + if (cset->is_in(obj)) { + oop fwd = ShenandoahForwarding::get_forwardee(obj); + shenandoah_assert_forwarded_except(elem_ptr, obj, _heap->cancelled_gc()); + ShenandoahHeap::atomic_update_oop(fwd, elem_ptr, o); + } + } } } From 10ec62d643a0c0174cd9ca74041bf13fa127d20e Mon Sep 17 00:00:00 2001 From: Harshit Dhiman Date: Wed, 15 Jul 2026 04:17:27 +0000 Subject: [PATCH 226/707] 8388016: [s390x] Remove the alignment from stubGenerator Reviewed-by: aph, amitkumar --- src/hotspot/cpu/s390/stubGenerator_s390.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/hotspot/cpu/s390/stubGenerator_s390.cpp b/src/hotspot/cpu/s390/stubGenerator_s390.cpp index d1601d4f147..381d1c02277 100644 --- a/src/hotspot/cpu/s390/stubGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/stubGenerator_s390.cpp @@ -3306,10 +3306,12 @@ class StubGenerator: public StubCodeGenerator { // Make room for the thawed frames and align the stack. __ add64(Z_RET, frame::z_abi_160_size); - { // stack alignment - __ z_lcgr(Z_RET, Z_RET); // negate Z_RET value - __ z_nill(Z_RET, -frame::alignment_in_bytes); - } +#ifdef ASSERT + __ z_tmll(Z_RET, frame::alignment_in_bytes - 1); + __ asm_assert(Assembler::bcondAllZero, FILE_AND_LINE ": size is not aligned properly", 71); +#endif // ASSERT + + __ z_lcgr(Z_RET, Z_RET); // negate Z_RET value __ resize_frame( /* offset = */ Z_RET,/* fp = */ Z_R1, /* load_fp = */ true); __ z_lghi(Z_ARG2, kind); From 5c673a17c06d941ea0e058c05665abe9d08f158e Mon Sep 17 00:00:00 2001 From: Daniel Skantz Date: Wed, 15 Jul 2026 06:36:49 +0000 Subject: [PATCH 227/707] 8387414: Insufficient feature gate in vm_version_x86 for UseKyberIntrinsics Reviewed-by: semery, kvn --- .../cpu/x86/stubGenerator_x86_64_kyber.cpp | 16 +++++++--------- src/hotspot/cpu/x86/vm_version_x86.cpp | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp index c35a2a1bba6..840f848d3ba 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp @@ -1108,15 +1108,13 @@ address generate_kyberBarrettReduce_avx512(StubGenerator *stubgen, void StubGenerator::generate_kyber_stubs() { // Generate Kyber intrinsics code if (UseKyberIntrinsics) { - if (VM_Version::supports_evex()) { - StubRoutines::_kyberNtt = generate_kyberNtt_avx512(this, _masm); - StubRoutines::_kyberInverseNtt = generate_kyberInverseNtt_avx512(this, _masm); - StubRoutines::_kyberNttMult = generate_kyberNttMult_avx512(this, _masm); - StubRoutines::_kyberAddPoly_2 = generate_kyberAddPoly_2_avx512(this, _masm); - StubRoutines::_kyberAddPoly_3 = generate_kyberAddPoly_3_avx512(this, _masm); - StubRoutines::_kyber12To16 = generate_kyber12To16_avx512(this, _masm); - StubRoutines::_kyberBarrettReduce = generate_kyberBarrettReduce_avx512(this, _masm); - } + StubRoutines::_kyberNtt = generate_kyberNtt_avx512(this, _masm); + StubRoutines::_kyberInverseNtt = generate_kyberInverseNtt_avx512(this, _masm); + StubRoutines::_kyberNttMult = generate_kyberNttMult_avx512(this, _masm); + StubRoutines::_kyberAddPoly_2 = generate_kyberAddPoly_2_avx512(this, _masm); + StubRoutines::_kyberAddPoly_3 = generate_kyberAddPoly_3_avx512(this, _masm); + StubRoutines::_kyber12To16 = generate_kyber12To16_avx512(this, _masm); + StubRoutines::_kyberBarrettReduce = generate_kyberBarrettReduce_avx512(this, _masm); } } diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index 53696ee6ef3..6112c280a1d 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -1270,7 +1270,7 @@ void VM_Version::get_processor_features() { // Kyber Intrinsics // Currently we only have them for AVX512 - if (supports_evex() && supports_avx512bw()) { + if (supports_avx512vlbw()) { if (FLAG_IS_DEFAULT(UseKyberIntrinsics)) { UseKyberIntrinsics = true; } From 2b05a136cb80221a4252719510f817e268da5d5a Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Wed, 15 Jul 2026 07:08:24 +0000 Subject: [PATCH 228/707] 8387328: C2: A Phi must not have a narrower Type than its inputs Reviewed-by: thartmann, vlivanov --- src/hotspot/share/opto/parse.hpp | 1 + src/hotspot/share/opto/parse1.cpp | 31 ++- .../jtreg/compiler/parsing/TestNarrowPhi.java | 196 ++++++++++++++++++ 3 files changed, 225 insertions(+), 3 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/parsing/TestNarrowPhi.java diff --git a/src/hotspot/share/opto/parse.hpp b/src/hotspot/share/opto/parse.hpp index 5118019fc31..426720b5bba 100644 --- a/src/hotspot/share/opto/parse.hpp +++ b/src/hotspot/share/opto/parse.hpp @@ -480,6 +480,7 @@ class Parse : public GraphKit { // Helper: Merge the current mapping into the given basic block void merge_common(Block* target, int pnum); // Helper functions for merging individual cells. + Node* maybe_narrow_phi_input(Node* ctrl, Node* n, const Type* phi_type); PhiNode *ensure_phi( int idx, bool nocreate = false); PhiNode *ensure_memory_phi(int idx, bool nocreate = false); // Helper to merge the current memory state into the given basic block diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp index 6a400631bff..2d74866e570 100644 --- a/src/hotspot/share/opto/parse1.cpp +++ b/src/hotspot/share/opto/parse1.cpp @@ -1883,7 +1883,8 @@ void Parse::merge_common(Parse::Block* target, int pnum) { if (phi != nullptr) { assert(n != top() || r->in(pnum) == top(), "live value must not be garbage"); assert(phi->region() == r, ""); - phi->set_req(pnum, n); // Then add 'n' to the merge + phi->set_req(pnum, maybe_narrow_phi_input(r->in(pnum), n, _gvn.type(phi))); + if (pnum == PhiNode::Input) { // Last merge for this Phi. // So far, Phis have had a reasonable type from ciTypeFlow. @@ -2060,6 +2061,21 @@ int Parse::Block::add_new_path() { return pnum; } +// The verifier ensures that the ciType of phi is not narrower than its inputs. However, since +// TypeOopPtr::make_from_klass may be aggressive if it finds that the ciType has only a single +// concrete subtype, and concurrent class loading/unloading may change this property during the +// compilation process, it may be the case that the Type of phi is narrower than its inputs. In +// those cases, we need to insert a CheckCastPP, otherwise several PhiNode idealization may be +// unsound, as we may replace a Phi which has a narrower Type with one of its input which has a +// wider Type. +Node* Parse::maybe_narrow_phi_input(Node* ctrl, Node* n, const Type* phi_type) { + if (phi_type->isa_oopptr() != nullptr && !_gvn.type(n)->higher_equal(phi_type)) { + n = new CheckCastPPNode(ctrl, n, phi_type, ConstraintCastNode::DependencyType::NonFloatingNarrowing); + n = _gvn.transform(n); + } + return n; +} + //------------------------------ensure_phi------------------------------------- // Turn the idx'th entry of the current map into a Phi PhiNode *Parse::ensure_phi(int idx, bool nocreate) { @@ -2108,9 +2124,18 @@ PhiNode *Parse::ensure_phi(int idx, bool nocreate) { return nullptr; } - PhiNode* phi = PhiNode::make(region, o, t); + PhiNode* phi = new PhiNode(region, t); gvn().set_type(phi, t); - if (C->do_escape_analysis()) record_for_igvn(phi); + for (uint i = 1; i < phi->req(); i++) { + Node* ctrl = region->in(i); + if (ctrl != nullptr) { + phi->init_req(i, maybe_narrow_phi_input(ctrl, o, t)); + } + } + + if (C->do_escape_analysis()) { + record_for_igvn(phi); + } map->set_req(idx, phi); return phi; } diff --git a/test/hotspot/jtreg/compiler/parsing/TestNarrowPhi.java b/test/hotspot/jtreg/compiler/parsing/TestNarrowPhi.java new file mode 100644 index 00000000000..879234a62cf --- /dev/null +++ b/test/hotspot/jtreg/compiler/parsing/TestNarrowPhi.java @@ -0,0 +1,196 @@ +/* + * 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.parsing; + +import java.io.IOException; +import java.util.Objects; +import jdk.test.lib.Asserts; +import jdk.test.whitebox.WhiteBox; +import jdk.test.lib.process.ProcessTools; + +/* + * @test + * @bug 8387328 + * @summary A Phi having a narrower Type than its inputs may result in incorrect scheduling + * @library /test/lib + * @requires vm.compiler2.enabled + * @modules java.base/jdk.internal.misc + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * ${test.main.class} + */ +public class TestNarrowPhi { + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + private static volatile Throwable failure; + + private static abstract class P { + int u; + + private static P allocate() { + return new C(); + } + } + + private static class C extends P { + int v; + } + + public static void main(String[] args) throws IOException, InterruptedException, NoSuchMethodException { + if (args.length == 0) { + spawnTestProcesses(); + } else { + int idx = Integer.parseInt(args[0]); + runTest(idx); + } + } + + private static void spawnTestProcesses() throws IOException, InterruptedException { + String testClassName = TestNarrowPhi.class.getName(); + // Since we cannot reliably coordinate the compiler thread and the thread that load the + // child class, randomly delaying one of them + for (int i = 0; i <= 10; i++) { + var builder = ProcessTools.createTestJavaProcessBuilder( + "-Xbootclasspath/a:.", + "-Xbatch", + "-XX:-TieredCompilation", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-XX:CompileOnly=" + testClassName + "::test*", + "-XX:CompileCommand=inline," + testClassName + "::inline*", + "-XX:CompileCommand=dontinline," + testClassName + "::nonInline", + "-XX:CompileCommand=delayinline," + testClassName + "::inlineTestHelper", + testClassName, + Integer.toString(i)); + builder.redirectOutput(ProcessBuilder.Redirect.INHERIT); + builder.redirectError(ProcessBuilder.Redirect.INHERIT); + var process = builder.start(); + process.waitFor(); + Asserts.assertEQ(0, process.exitValue()); + } + } + + private static void runTest(int idx) throws InterruptedException, NoSuchMethodException { + var testMethod = TestNarrowPhi.class.getDeclaredMethod("testMethod", boolean.class, P.class, P.class, P.class); + var _ = Objects.class; + Thread loader = new Thread(() -> { + try { + if (idx < 5) { + Thread.sleep((5 - idx) * 10L); + } + var _ = C.class; + } catch (Exception e) { + failure = e; + } + }); + loader.start(); + + if (idx > 5) { + Thread.sleep((idx - 5) * 10L); + } + if (!WHITE_BOX.enqueueMethodForCompilation(testMethod, 4)) { + throw new RuntimeException("Could not enqueue the test method for C2 compilation"); + } + while (WHITE_BOX.isMethodQueuedForCompilation(testMethod)) { + Thread.yield(); + } + P p = P.allocate(); + Asserts.assertEQ(0, testMethod(true, p, p, p)); + loader.join(); + if (failure != null) { + throw new RuntimeException(failure); + } + } + + private static int testMethod(boolean b, P p1, P p2, P p3) { + // Arbitrarily delay the parser between generating the Type for P1 and for the loop Phi + // below + inline0(); + // This method is late-inlined, which increases the chance that C has been loaded then + return inlineTestHelper(b, p1, p2, p3); + } + + private static int inlineTestHelper(boolean b, P p1, P p2, P p3) { + // Random access that can be used as an implicit null-check, so that the load below can + // float freely + p1.u = 0; + P p = p1; + for (int i = 0; i < 1; i++) { + if (i % 2 != 0) { + p = p2; + } + } + + C cp = (C) Objects.requireNonNull(p); + C cp3 = (C) Objects.requireNonNull(p3); + int res = cp.v; + cp3.v = 1; + if (b) { + cp3.v = 2; + return res; + } else { + return nonInline(); + } + } + + private static int nonInline() { + return 0; + } + + private static void inline0() { + inline1(); + inline1(); + inline1(); + inline1(); + } + + private static void inline1() { + inline2(); + inline2(); + inline2(); + inline2(); + } + + private static void inline2() { + inline3(); + inline3(); + inline3(); + inline3(); + } + + private static void inline3() { + inline4(); + inline4(); + inline4(); + inline4(); + } + + private static void inline4() { + inline5(); + inline5(); + inline5(); + inline5(); + } + + private static void inline5() {} +} From f146847ca1289da313b3d07f14591ab669abedc1 Mon Sep 17 00:00:00 2001 From: EunHyunsu Date: Wed, 15 Jul 2026 07:16:10 +0000 Subject: [PATCH 229/707] 8380549: HttpCookie.expiryDate2DeltaSeconds returns 0 on parse failure, causing immediate cookie expiration Reviewed-by: vyazici, michaelm --- .../share/classes/java/net/HttpCookie.java | 21 ++++++------ test/jdk/java/net/CookieHandler/B6791927.java | 4 +-- .../net/HttpCookie/ExpiredCookieTest.java | 33 ++++++++++++++----- .../java.base/java/net/MaxAgeExpires.java | 23 +++++++++++++ 4 files changed, 60 insertions(+), 21 deletions(-) diff --git a/src/java.base/share/classes/java/net/HttpCookie.java b/src/java.base/share/classes/java/net/HttpCookie.java index 3c633522bdf..2b3a5cbb6a5 100644 --- a/src/java.base/share/classes/java/net/HttpCookie.java +++ b/src/java.base/share/classes/java/net/HttpCookie.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 @@ -1005,12 +1005,13 @@ private static void assignMaxAgeAttribute(HttpCookie cookie, } } catch (NumberFormatException ignored) {} - try { - if (expiresValue != null) { - long delta = cookie.expiryDate2DeltaSeconds(expiresValue); + if (expiresValue != null) { + Calendar cal = parseExpires(expiresValue); + if (cal != null) { + long delta = (cal.getTimeInMillis() - cookie.whenCreated) / 1000; cookie.maxAge = (delta > 0 ? delta : 0); } - } catch (NumberFormatException ignored) {} + } } private static void assignAttribute(HttpCookie cookie, @@ -1082,10 +1083,10 @@ private String toRFC2965HeaderString() { * @param dateString * a date string in one of the formats defined in Netscape cookie spec * - * @return delta seconds between this cookie's creation time and the time - * specified by dateString + * @return the parsed date as a Calendar, or null if none of the + * formats could parse the given date string */ - private long expiryDate2DeltaSeconds(String dateString) { + private static Calendar parseExpires(String dateString) { Calendar cal = new GregorianCalendar(GMT); for (int i = 0; i < COOKIE_DATE_FORMATS.length; i++) { SimpleDateFormat df = new SimpleDateFormat(COOKIE_DATE_FORMATS[i], @@ -1108,12 +1109,12 @@ private long expiryDate2DeltaSeconds(String dateString) { } cal.set(Calendar.YEAR, year); } - return (cal.getTimeInMillis() - whenCreated) / 1000; + return cal; } catch (Exception e) { // Ignore, try the next date format } } - return 0; + return null; } /* diff --git a/test/jdk/java/net/CookieHandler/B6791927.java b/test/jdk/java/net/CookieHandler/B6791927.java index bc5374b2a98..ceeff260665 100644 --- a/test/jdk/java/net/CookieHandler/B6791927.java +++ b/test/jdk/java/net/CookieHandler/B6791927.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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 @@ -24,7 +24,7 @@ /** * @test * @bug 6791927 8233886 - * @summary Wrong Locale in HttpCookie::expiryDate2DeltaSeconds + * @summary Wrong Locale in HttpCookie::parseExpires * @run main/othervm B6791927 */ diff --git a/test/jdk/java/net/HttpCookie/ExpiredCookieTest.java b/test/jdk/java/net/HttpCookie/ExpiredCookieTest.java index 5cf7208d2ca..e2769d8dd61 100644 --- a/test/jdk/java/net/HttpCookie/ExpiredCookieTest.java +++ b/test/jdk/java/net/HttpCookie/ExpiredCookieTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 8000525 + * @bug 8000525 8380549 * @library /test/lib */ @@ -33,6 +33,8 @@ import java.text.*; import jdk.test.lib.net.URIBuilder; +import static jdk.test.lib.Asserts.assertEquals; + public class ExpiredCookieTest { // lifted from HttpCookie.java private final static String[] COOKIE_DATE_FORMATS = { @@ -92,15 +94,28 @@ public static void main(String[] args) throws Exception { cm.put(uri, header); CookieStore cookieJar = cm.getCookieStore(); - List cookies = cookieJar.getCookies(); + Set names = new TreeSet<>(); + for (HttpCookie cookie : cookieJar.getCookies()) + names.add(cookie.getName()); + + Set expected; if (COOKIE_DATE_FORMATS[i].contains("yyyy")) { - if (cookies.size() != 2) - throw new RuntimeException( - "Incorrectly parsing a bad date"); - } else if (cookies.size() != 1) { - throw new RuntimeException( - "Incorrectly parsing a bad date"); + // Four-digit years parse unambiguously: TEST1 and TEST2 are + // in the past and expire, while TEST3 and TEST4 remain. + expected = new TreeSet<>(List.of("TEST3", "TEST4")); + } else { + // Two-digit years make TEST2 and TEST3 resolve to a mismatched + // day-of-week, so strict parsing rejects the Expires value; per + // RFC 6265 section 5.2.1 an unparseable Expires is ignored, so + // they remain as session cookies. TEST1 parses cleanly but is + // already expired, so it is dropped. TEST4's two-digit year + // round-trips to itself (69 -> 2069), so it parses and remains + // because its expiry is still in the future. + expected = new TreeSet<>(List.of("TEST2", "TEST3", "TEST4")); } + assertEquals(expected, names, + "Incorrectly parsing a bad date, format: " + + COOKIE_DATE_FORMATS[i]); } } } diff --git a/test/jdk/java/net/HttpCookie/whitebox/java.base/java/net/MaxAgeExpires.java b/test/jdk/java/net/HttpCookie/whitebox/java.base/java/net/MaxAgeExpires.java index 6704a290836..79139d69e50 100644 --- a/test/jdk/java/net/HttpCookie/whitebox/java.base/java/net/MaxAgeExpires.java +++ b/test/jdk/java/net/HttpCookie/whitebox/java.base/java/net/MaxAgeExpires.java @@ -33,6 +33,7 @@ import org.junit.jupiter.params.provider.MethodSource; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; public class MaxAgeExpires { @@ -138,4 +139,26 @@ public void test2() { cookie.setMaxAge(-2); assertEquals(-2, cookie.getMaxAge()); } + + public static Object[][] unparseableDates() { + return new Object[][] { + { "GARBAGE" }, + { "2024-01-01T00:00:00Z" }, // format not supported by RFC-6265 + { "January 1, 2099 00:00:00 GMT" } // format not supported by RFC-6265 + }; + } + + @ParameterizedTest + @MethodSource("unparseableDates") + public void testUnparseableExpires(String badDate) { + // RFC 6265 section 5.2.1: if the expires value fails to parse, + // the cookie-av should be ignored. + // That results in the HttpCookie implementation to have maxAge value of -1. + HttpCookie cookie = HttpCookie.parse( + "Set-Cookie: name=value; expires=" + badDate).get(0); + assertEquals(-1, cookie.getMaxAge(), + "Unparseable expires=\"" + badDate + "\" should be ignored"); + assertFalse(cookie.hasExpired(), + "Cookie with ignored expires should not be expired"); + } } From d6899460c7ca5daf402258e5d3ccb399a5607d3a Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Wed, 15 Jul 2026 09:25:58 +0000 Subject: [PATCH 230/707] 8380166: C2: crash in compiled code due to zero division because of widened CastII Reviewed-by: qamai, chagedorn --- src/hotspot/share/opto/c2_globals.hpp | 4 +- src/hotspot/share/opto/castnode.cpp | 3 - src/hotspot/share/opto/cfgnode.cpp | 14 +- src/hotspot/share/opto/classes.hpp | 1 + src/hotspot/share/opto/compile.cpp | 25 +- src/hotspot/share/opto/compile.hpp | 9 +- src/hotspot/share/opto/convertnode.cpp | 14 - src/hotspot/share/opto/divnode.cpp | 14 + src/hotspot/share/opto/divnode.hpp | 13 +- src/hotspot/share/opto/loopopts.cpp | 2 +- src/hotspot/share/opto/movenode.cpp | 5 - src/hotspot/share/opto/node.cpp | 37 +- src/hotspot/share/opto/node.hpp | 13 +- src/hotspot/share/opto/parse2.cpp | 2 +- src/hotspot/share/opto/phaseX.cpp | 123 +- src/hotspot/share/opto/phaseX.hpp | 6 +- src/hotspot/share/opto/rootnode.cpp | 45 + src/hotspot/share/opto/rootnode.hpp | 31 + src/hotspot/share/opto/vectornode.cpp | 2 +- .../c2/TestDeadPathManyDeadDataNodes.java | 1301 +++++++++++++++++ .../TestDivByZeroInLiveCFGPath.java | 64 + .../TestZeroDivModWidenedCastII.java | 1122 ++++++++++++++ ...yAccessAboveRCAfterRCCastIIEliminated.java | 24 +- 23 files changed, 2793 insertions(+), 81 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/c2/TestDeadPathManyDeadDataNodes.java create mode 100644 test/hotspot/jtreg/compiler/integerArithmetic/TestDivByZeroInLiveCFGPath.java create mode 100644 test/hotspot/jtreg/compiler/integerArithmetic/TestZeroDivModWidenedCastII.java diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 9ff88e8c310..dd9288f7617 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -922,8 +922,8 @@ "Use StoreStore barrier instead of Release barrier at the end " \ "of constructors") \ \ - develop(bool, KillPathsReachableByDeadTypeNode, true, \ - "When a Type node becomes top, make paths where the node is " \ + develop(bool, KillPathsReachableByDeadDataNode, true, \ + "When a data node becomes top, make paths where the node is " \ "used dead by replacing them with a Halt node. Turning this off " \ "could corrupt the graph in rare cases and should be used with " \ "care.") \ diff --git a/src/hotspot/share/opto/castnode.cpp b/src/hotspot/share/opto/castnode.cpp index 7bb6b1dcb77..076a95acfd8 100644 --- a/src/hotspot/share/opto/castnode.cpp +++ b/src/hotspot/share/opto/castnode.cpp @@ -111,9 +111,6 @@ Node* ConstraintCastNode::Ideal(PhaseGVN* phase, bool can_reshape) { if (in(0) != nullptr && remove_dead_region(phase, can_reshape)) { return this; } - if (in(1) != nullptr && phase->type(in(1)) != Type::TOP) { - return TypeNode::Ideal(phase, can_reshape); - } return nullptr; } diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index 828e5bf299f..ed5da046608 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -693,14 +693,13 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) { if (add_to_worklist) { igvn->add_users_to_worklist(this); // Check for further allowed opts } - for (DUIterator_Last imin, i = last_outs(imin); i >= imin; --i) { + uint edges_removed; + for (DUIterator_Last imin, i = last_outs(imin); i >= imin; i -= edges_removed) { + edges_removed = 1; Node* n = last_out(i); igvn->hash_delete(n); // Remove from worklist before modifying edges if (n->outcnt() == 0) { - int uses_found = n->replace_edge(this, phase->C->top(), igvn); - if (uses_found > 1) { // (--i) done at the end of the loop. - i -= (uses_found - 1); - } + edges_removed = n->replace_edge(this, phase->C->top(), igvn); continue; } if( n->is_Phi() ) { // Collapse all Phis @@ -719,10 +718,7 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) { } else if( n->is_Region() ) { // Update all incoming edges assert(n != this, "Must be removed from DefUse edges"); - int uses_found = n->replace_edge(this, parent_ctrl, igvn); - if (uses_found > 1) { // (--i) done at the end of the loop. - i -= (uses_found - 1); - } + edges_removed = n->replace_edge(this, parent_ctrl, igvn); } else { assert(n->in(0) == this, "Expect RegionNode to be control parent"); diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index 53a72f979db..c296237de37 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -121,6 +121,7 @@ macro(CompareAndExchangeI) macro(CompareAndExchangeL) macro(CompareAndExchangeP) macro(CompareAndExchangeN) +macro(DeadPath) macro(GetAndAddB) macro(GetAndAddS) macro(GetAndAddI) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 93d8e4c425d..db43c6fb1c4 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -312,6 +312,8 @@ void Compile::identify_useful_nodes(Unique_Node_List &useful) { // If 'top' is cached, declare it useful to preserve cached node if (cached_top_node()) { useful.push(cached_top_node()); } + if (dead_path()) { useful.push(dead_path()); } + // Push all useful nodes onto the list, breadthfirst for( uint next = 0; next < useful.size(); ++next ) { assert( next < unique(), "Unique useful nodes < total nodes"); @@ -388,7 +390,7 @@ void Compile::remove_useless_node(Node* dead) { // it reachable by adding use edges. So, we will NOT count Con nodes // as dead to be conservative about the dead node count at any // given time. - if (!dead->is_Con()) { + if (!dead->is_Con() && dead != dead_path()) { record_dead_node(dead->_idx); } if (dead->is_macro()) { @@ -684,6 +686,7 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, _node_arena_one(mtCompiler, Arena::Tag::tag_node), _node_arena_two(mtCompiler, Arena::Tag::tag_node), _node_arena(&_node_arena_one), + _dead_path(nullptr), _mach_constant_base_node(nullptr), _Compile_types(mtCompiler, Arena::Tag::tag_type), _initial_gvn(nullptr), @@ -754,6 +757,7 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, } Init(/*do_aliasing=*/ true); + set_dead_path(new DeadPathNode()); print_compile_messages(); @@ -963,6 +967,7 @@ Compile::Compile(ciEnv* ci_env, _node_arena_one(mtCompiler, Arena::Tag::tag_node), _node_arena_two(mtCompiler, Arena::Tag::tag_node), _node_arena(&_node_arena_one), + _dead_path(nullptr), _mach_constant_base_node(nullptr), _Compile_types(mtCompiler, Arena::Tag::tag_type), _initial_gvn(nullptr), @@ -2630,6 +2635,9 @@ void Compile::Optimize() { } } + // Unique DeadPath node should not be used anymore + _dead_path = nullptr; + print_method(PHASE_OPTIMIZE_FINISHED, 2); DEBUG_ONLY(set_phase_optimize_finished();) } @@ -3938,6 +3946,21 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f break; } #endif + case Op_DeadPath: { + // The CFG inputs are dead paths. Replace the DeadPath with a Region and insert a Halt node. + assert(n->req() > 1, "why not removed if no input other than itself?"); + RegionNode* r = new RegionNode(n->req()); + for (uint i = 1; i < n->req(); ++i) { + r->set_req(i, n->in(i)); + } + n->disconnect_inputs(this); + Node* frame = start()->proj_out(TypeFunc::FramePtr); + stringStream ss; + ss.print("dead path discovered by data nodes during igvn"); + Node* halt = new HaltNode(r, frame, ss.as_string(comp_arena())); + root()->set_req(root()->find_edge(n), halt); + break; + } default: assert(!n->is_Call(), ""); assert(!n->is_Mem(), ""); diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index ab36f59a28f..73e136787f8 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -57,6 +57,7 @@ class CallStaticJavaNode; class CloneMap; class CompilationFailureInfo; class ConnectionGraph; +class DeadPathNode; class IdealGraphPrinter; class InlineTree; class Matcher; @@ -427,7 +428,7 @@ class Compile : public Phase { private: RootNode* _root; // Unique root of compilation, or null after bail-out. Node* _top; // Unique top node. (Reset by various phases.) - + DeadPathNode* _dead_path; // Unique DeadPath node Node* _immutable_memory; // Initial memory state Node* _recent_alloc_obj; @@ -897,6 +898,12 @@ class Compile : public Phase { Arena* old_arena() { return (&_node_arena_one == _node_arena) ? &_node_arena_two : &_node_arena_one; } RootNode* root() const { return _root; } void set_root(RootNode* r) { _root = r; } + DeadPathNode* dead_path() const { return _dead_path; } + + void set_dead_path(DeadPathNode* dead_path) { + assert(_dead_path == nullptr, "can only set once"); + _dead_path = dead_path; + } StartNode* start() const; // (Derived from root.) void verify_start(StartNode* s) const NOT_DEBUG_RETURN; Node* immutable_memory(); diff --git a/src/hotspot/share/opto/convertnode.cpp b/src/hotspot/share/opto/convertnode.cpp index a495814da61..d706a13feb3 100644 --- a/src/hotspot/share/opto/convertnode.cpp +++ b/src/hotspot/share/opto/convertnode.cpp @@ -755,13 +755,6 @@ bool Compile::push_thru_add(PhaseGVN* phase, Node* z, const TypeInteger* tz, con //------------------------------Ideal------------------------------------------ Node* ConvI2LNode::Ideal(PhaseGVN* phase, bool can_reshape) { - if (in(1) != nullptr && phase->type(in(1)) != Type::TOP) { - Node* progress = TypeNode::Ideal(phase, can_reshape); - if (progress != nullptr) { - return progress; - } - } - const TypeLong* this_type = this->type()->is_long(); if (can_reshape && !phase->C->post_loop_opts_phase()) { // makes sure we run ::Value to potentially remove type assertion after loop opts @@ -864,13 +857,6 @@ const Type* ConvL2INode::Value(PhaseGVN* phase) const { // Return a node which is more "ideal" than the current node. // Blow off prior masking to int Node* ConvL2INode::Ideal(PhaseGVN* phase, bool can_reshape) { - if (in(1) != nullptr && phase->type(in(1)) != Type::TOP) { - Node* progress = TypeNode::Ideal(phase, can_reshape); - if (progress != nullptr) { - return progress; - } - } - Node *andl = in(1); uint andl_op = andl->Opcode(); if( andl_op == Op_AndL ) { diff --git a/src/hotspot/share/opto/divnode.cpp b/src/hotspot/share/opto/divnode.cpp index 1687ff2cade..3b51491294e 100644 --- a/src/hotspot/share/opto/divnode.cpp +++ b/src/hotspot/share/opto/divnode.cpp @@ -1031,6 +1031,10 @@ const Type* UDivINode::Value(PhaseGVN* phase) const { if( t1 == Type::TOP ) return Type::TOP; if( t2 == Type::TOP ) return Type::TOP; + if (t2 == TypeInt::ZERO) { + return Type::TOP; + } + // x/x == 1 since we always generate the dynamic divisor check for 0. if (in(1) == in(2)) { return TypeInt::ONE; @@ -1067,6 +1071,10 @@ const Type* UDivLNode::Value(PhaseGVN* phase) const { if( t1 == Type::TOP ) return Type::TOP; if( t2 == Type::TOP ) return Type::TOP; + if (t2 == TypeLong::ZERO) { + return Type::TOP; + } + // x/x == 1 since we always generate the dynamic divisor check for 0. if (in(1) == in(2)) { return TypeLong::ONE; @@ -1380,6 +1388,9 @@ Node* UModINode::Ideal(PhaseGVN* phase, bool can_reshape) { } const Type* UModINode::Value(PhaseGVN* phase) const { + if (phase->type(in(2)) == TypeInt::ZERO) { + return Type::TOP; + } return unsigned_mod_value(phase, this); } @@ -1520,6 +1531,9 @@ Node *UModLNode::Ideal(PhaseGVN *phase, bool can_reshape) { } const Type* UModLNode::Value(PhaseGVN* phase) const { + if (phase->type(in(2)) == TypeLong::ZERO) { + return Type::TOP; + } return unsigned_mod_value(phase, this); } diff --git a/src/hotspot/share/opto/divnode.hpp b/src/hotspot/share/opto/divnode.hpp index 366e3fb882d..de89dcaad06 100644 --- a/src/hotspot/share/opto/divnode.hpp +++ b/src/hotspot/share/opto/divnode.hpp @@ -40,7 +40,9 @@ class DivModIntegerNode : public Node { bool _pinned; protected: - DivModIntegerNode(Node* c, Node* dividend, Node* divisor) : Node(c, dividend, divisor), _pinned(false) {} + DivModIntegerNode(Node* c, Node* dividend, Node* divisor) : Node(c, dividend, divisor), _pinned(false) { + init_class_id(Class_DivModInteger); + } private: virtual uint size_of() const override { return sizeof(DivModIntegerNode); } @@ -52,6 +54,15 @@ class DivModIntegerNode : public Node { res->_pinned = true; return res; } + +public: + const TypeInteger* zero() const { + if (bottom_type() == TypeInt::INT) { + return TypeInt::ZERO; + } + assert(bottom_type() == TypeLong::LONG, "should be int or long"); + return TypeLong::ZERO; + } }; //------------------------------DivINode--------------------------------------- diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index ccd53129a87..d525c274ef6 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -1725,7 +1725,7 @@ void PhaseIdealLoop::try_sink_out_of_loop(Node* n) { !n->is_OpaqueTemplateAssertionPredicate() && !is_raw_to_oop_cast && // don't extend live ranges of raw oops n->Opcode() != Op_CreateEx && - (KillPathsReachableByDeadTypeNode || !n->is_Type()) + (KillPathsReachableByDeadDataNode || !n->is_Type()) ) { Node *n_ctrl = get_ctrl(n); IdealLoopTree *n_loop = get_loop(n_ctrl); diff --git a/src/hotspot/share/opto/movenode.cpp b/src/hotspot/share/opto/movenode.cpp index 6b6becb434f..7d38238da2f 100644 --- a/src/hotspot/share/opto/movenode.cpp +++ b/src/hotspot/share/opto/movenode.cpp @@ -90,11 +90,6 @@ Node *CMoveNode::Ideal(PhaseGVN *phase, bool can_reshape) { phase->type(in(IfTrue)) == Type::TOP) { return nullptr; } - Node* progress = TypeNode::Ideal(phase, can_reshape); - if (progress != nullptr) { - return progress; - } - // Check for Min/Max patterns. This is called before constants are pushed to the right input, as that transform can // make BoolTests non-canonical. Node* minmax = Ideal_minmax(phase, this); diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index 726a3ea1b55..264216ddc6d 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -597,6 +597,7 @@ void Node::setup_is_top() { //------------------------------~Node------------------------------------------ // Fancy destructor; eagerly attempt to reclaim Node numberings and storage void Node::destruct(PhaseValues* phase) { + assert(this != Compile::current()->dead_path(), "we want to keep the unique DeadPath node around"); Compile* compile = (phase != nullptr) ? phase->C : Compile::current(); if (phase != nullptr && phase->is_IterGVN()) { phase->is_IterGVN()->_worklist.remove(this); @@ -735,11 +736,14 @@ void Node::out_grow(uint len) { //------------------------------is_dead---------------------------------------- bool Node::is_dead() const { // Mach and pinch point nodes may look like dead. - if( is_top() || is_Mach() || (Opcode() == Op_Node && _outcnt > 0) ) + if (is_top() || is_Mach() || (Opcode() == Op_Node && _outcnt > 0) || this == Compile::current()->dead_path()) { return false; - for( uint i = 0; i < _max; i++ ) - if( _in[i] != nullptr ) + } + for (uint i = 0; i < _max; i++) { + if (_in[i] != nullptr) { return false; + } + } return true; } @@ -3178,10 +3182,11 @@ uint TypeNode::ideal_reg() const { return _type->ideal_reg(); } -void TypeNode::make_path_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, Node* ctrl_use, uint j, const char* phase_str) { +void Node::make_path_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, Node* ctrl_use, uint j, const char* phase_str) { Node* c = ctrl_use->in(j); - if (igvn->type(c) != Type::TOP) { - igvn->replace_input_of(ctrl_use, j, igvn->C->top()); + Node* top = igvn->C->top(); + if (c != top) { + igvn->replace_input_of(ctrl_use, j, top); create_halt_path(igvn, c, loop, phase_str); } } @@ -3193,14 +3198,18 @@ void TypeNode::make_path_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, Node* ct // constant folds and the control flow that leads to the Type node becomes unreachable. There are cases where that // doesn't happen, however. They are handled here by following uses of the Type node until a CFG or a Phi to find dead // paths. The dead paths are then replaced by a Halt node. -void TypeNode::make_paths_from_here_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, const char* phase_str) { +void Node::make_paths_from_here_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, const char* phase_str) { Unique_Node_List wq; wq.push(this); for (uint i = 0; i < wq.size(); ++i) { Node* n = wq.at(i); + if (n->is_CFG()) { + n->remove_dead_region(igvn, true); + } for (DUIterator_Fast kmax, k = n->fast_outs(kmax); k < kmax; k++) { Node* u = n->fast_out(k); if (u->is_CFG()) { + wq.push(u); assert(!u->is_Region(), "Can't reach a Region without going through a Phi"); make_path_dead(igvn, loop, u, 0, phase_str); } else if (u->is_Phi()) { @@ -3220,7 +3229,7 @@ void TypeNode::make_paths_from_here_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loo } } -void TypeNode::create_halt_path(PhaseIterGVN* igvn, Node* c, PhaseIdealLoop* loop, const char* phase_str) const { +void Node::create_halt_path(PhaseIterGVN* igvn, Node* c, PhaseIdealLoop* loop, const char* phase_str) { Node* frame = new ParmNode(igvn->C->start(), TypeFunc::FramePtr); if (loop == nullptr) { igvn->register_new_node_with_optimizer(frame); @@ -3239,15 +3248,3 @@ void TypeNode::create_halt_path(PhaseIterGVN* igvn, Node* c, PhaseIdealLoop* loo } igvn->add_input_to(igvn->C->root(), halt); } - -Node* TypeNode::Ideal(PhaseGVN* phase, bool can_reshape) { - if (KillPathsReachableByDeadTypeNode && can_reshape && Value(phase) == Type::TOP) { - PhaseIterGVN* igvn = phase->is_IterGVN(); - Node* top = igvn->C->top(); - ResourceMark rm; - make_paths_from_here_dead(igvn, nullptr, "igvn"); - return top; - } - - return Node::Ideal(phase, can_reshape); -} diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index b3de7498e50..e593822c313 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -82,6 +82,7 @@ class CountedLoopEndNode; class DecodeNarrowPtrNode; class DecodeNNode; class DecodeNKlassNode; +class DivModIntegerNode; class EncodeNarrowPtrNode; class EncodePNode; class EncodePKlassNode; @@ -829,8 +830,9 @@ class Node { DEFINE_CLASS_ID(LShift, Node, 21) DEFINE_CLASS_ID(Neg, Node, 22) DEFINE_CLASS_ID(ReachabilityFence, Node, 23) + DEFINE_CLASS_ID(DivModInteger, Node, 24) - _max_classes = ClassMask_Neg + _max_classes = ClassMask_DivModInteger }; #undef DEFINE_CLASS_ID @@ -947,6 +949,7 @@ class Node { DEFINE_CLASS_QUERY(DecodeNarrowPtr) DEFINE_CLASS_QUERY(DecodeN) DEFINE_CLASS_QUERY(DecodeNKlass) + DEFINE_CLASS_QUERY(DivModInteger) DEFINE_CLASS_QUERY(EncodeNarrowPtr) DEFINE_CLASS_QUERY(EncodeP) DEFINE_CLASS_QUERY(EncodePKlass) @@ -1501,6 +1504,10 @@ class Node { uint _del_tick; // Bumped when a deletion happens.. #endif #endif + void make_paths_from_here_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, const char* phase_str); + + static void create_halt_path(PhaseIterGVN* igvn, Node* c, PhaseIdealLoop* loop, const char* phase_str); + void make_path_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, Node* ctrl_use, uint j, const char* phase_str); }; inline bool not_a_node(const Node* n) { @@ -2198,17 +2205,13 @@ class TypeNode : public Node { init_class_id(Class_Type); } virtual const Type* Value(PhaseGVN* phase) const; - virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); virtual const Type *bottom_type() const; virtual uint ideal_reg() const; - void make_path_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, Node* ctrl_use, uint j, const char* phase_str); #ifndef PRODUCT virtual void dump_spec(outputStream *st) const; virtual void dump_compact_spec(outputStream *st) const; #endif - void make_paths_from_here_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, const char* phase_str); - void create_halt_path(PhaseIterGVN* igvn, Node* c, PhaseIdealLoop* loop, const char* phase_str) const; }; #include "opto/opcodes.hpp" diff --git a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp index 9cb20cfcd00..6e58fae51e1 100644 --- a/src/hotspot/share/opto/parse2.cpp +++ b/src/hotspot/share/opto/parse2.cpp @@ -1843,7 +1843,7 @@ void Parse::sharpen_type_after_if(BoolTest::mask btest, const Type* obj_type = _gvn.type(obj); const Type* tboth = obj_type->filter_speculative(cast_type); assert(tboth->higher_equal(obj_type) && tboth->higher_equal(cast_type), "sanity"); - if (tboth == Type::TOP && KillPathsReachableByDeadTypeNode) { + if (tboth == Type::TOP && KillPathsReachableByDeadDataNode) { // Let dead type node cleaning logic prune effectively dead path for us. // CheckCastPP::Value() == TOP and it will trigger the cleanup during GVN. // Don't materialize the cast when cleanup is disabled, because diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index a4d6a6c33d0..c124f940a27 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -32,6 +32,7 @@ #include "opto/castnode.hpp" #include "opto/cfgnode.hpp" #include "opto/convertnode.hpp" +#include "opto/divnode.hpp" #include "opto/idealGraphPrinter.hpp" #include "opto/loopnode.hpp" #include "opto/machnode.hpp" @@ -2197,6 +2198,107 @@ Node *PhaseIterGVN::transform( Node *n ) { return transform_old(n); } +DeadPathNode* PhaseIterGVN::dead_path() { + DeadPathNode* dead_path_node = C->dead_path(); + if (!dead_path_node->is_active()) { + dead_path_node->activate(this); + } + assert(C->root()->find_edge(dead_path_node) > 0, "should be reachable from root"); + return dead_path_node; +} + + +// If dead_node is a data node, all CFG nodes reachable from dead_node are dead cfg paths. This method follows uses from +// dead_node until it encounters a cfg node or a phi and eagerly kills these dead cfg paths. This is needed because, in +// some corner cases, a data node dies but some data paths that use it (and are unreachable at runtime) are not proven +// dead by igvn, possibly leading to incorrect IR graphs. +// Also see comment at DeadPathNode declaration. +void PhaseIterGVN::make_dependent_paths_dead_if_top(Node* dead_node, const Type* t) { + if (t != Type::TOP) { + return; + } + if (!KillPathsReachableByDeadDataNode) { + return; + } + // dead_node is going dead, follow uses + ResourceMark rm; + Unique_Node_List wq; + wq.push(dead_node); + for (uint i = 0; i < wq.size(); i++) { + Node* n = wq.at(i); + if (n != dead_node && (n->is_Phi() || n->is_CFG())) { + continue; + } + for (DUIterator_Fast kmax, k = n->fast_outs(kmax); k < kmax; k++) { + Node* u = n->fast_out(k); + wq.push(u); + } + } + for (uint i = 0; i < wq.size(); i++) { + Node* n = wq.at(i); + if (n->is_Phi()) { + Node* region = n->in(0); + // Find out through which of the Phi's input, we reached that Phi and mark the corresponding CFG path dead + for (uint j = 1; j < n->req(); j++) { + Node* in = n->in(j); + // We don't follow uses beyond Phis so if 'in' is a Phi (unless it's dead_node), we couldn't reach this Phi through it + if (in == dead_node || (in != nullptr && !in->is_Phi() && wq.member(in))) { + if (!region->is_top() && region->in(j) != nullptr && !region->in(j)->is_top()) { + // We reached this CFG path through data nodes, record it in dead path to later insert a Halt node, if it + // doesn't die in the meantime + dead_path()->add_req(region->in(j)); + _worklist.push(dead_path()); + replace_input_of(region, j, C->top()); + } + replace_input_of(n, j, C->top()); + if (in->outcnt() == 0) { + remove_dead_node(in, NodeOrigin::Graph); + } + } + } + continue; + } + if (n == dead_node) { + continue; + } + // We don't want to follow CFG nodes but is_CFG() can return false for a cfg projection if its input is top. So + // there's no foolproof way of telling if dead_node is a cfg or not and as a consequence we can reach a Region. + if (n->is_Region()) { + // Find out through which of the Region's input, we reached that Region and mark it dead + for (uint j = 1; j < n->req(); j++) { + Node* in = n->in(j); + // We don't follow uses beyond Regions so if 'in' is a Region, we couldn't reach this Region through it + if (in != nullptr && !in->is_Region() && wq.member(in)) { + replace_input_of(n, j, C->top()); + in->remove_dead_region(this, true); + } + } + continue; + } + // If we reached this CFG node through a data input... + if (n->is_CFG()) { + Node* control_input = n->in(0); + if (control_input != nullptr && !control_input->is_top()) { + // record it in dead path to later insert a Halt node, if it doesn't die in the meantime + dead_path()->add_req(control_input); + _worklist.push(dead_path()); + replace_input_of(n, 0, C->top()); + } + n->remove_dead_region(this, true); + continue; + } + if (n->outcnt() == 0) { + remove_dead_node(n, NodeOrigin::Graph); + } + } +#ifdef ASSERT + for (uint i = 0; i < wq.size(); i++) { + Node* n = wq.at(i); + assert(n->is_Region() || n->is_Phi() || n->is_CFG() || n->outcnt() == 0, "node should be dead now"); + } +#endif +} + Node *PhaseIterGVN::transform_old(Node* n) { NOT_PRODUCT(set_transforms()); // Remove 'n' from hash table in case it gets modified @@ -2288,6 +2390,7 @@ Node *PhaseIterGVN::transform_old(Node* n) { } // If 'k' computes a constant, replace it with a constant if (t->singleton() && !k->is_Con()) { + make_dependent_paths_dead_if_top(k, t); set_progress(); Node* con = makecon(t); // Make a constant add_users_to_worklist(k); @@ -2957,10 +3060,14 @@ void PhaseCCP::analyze_step(Unique_Node_List& worklist, Node* n) { set_type(n, new_type); push_child_nodes_to_worklist(worklist, n); } - if (KillPathsReachableByDeadTypeNode && n->is_Type() && new_type == Type::TOP) { + if (KillPathsReachableByDeadDataNode && n->is_Type() && new_type == Type::TOP) { // Keep track of Type nodes to kill CFG paths that use Type // nodes that become dead. - _maybe_top_type_nodes.push(n); + _maybe_top_type_or_div_mod_nodes.push(n); + } + if (KillPathsReachableByDeadDataNode && new_type == Type::TOP && n->is_DivModInteger() && + type(n->in(2)) == n->as_DivModInteger()->zero()) { + _maybe_top_type_or_div_mod_nodes.push(n); } } @@ -3256,16 +3363,16 @@ Node *PhaseCCP::transform( Node *n ) { // track all visited nodes, so that we can remove the complement Unique_Node_List useful; - if (KillPathsReachableByDeadTypeNode) { - for (uint i = 0; i < _maybe_top_type_nodes.size(); ++i) { - Node* type_node = _maybe_top_type_nodes.at(i); - if (type(type_node) == Type::TOP) { + if (KillPathsReachableByDeadDataNode) { + for (uint i = 0; i < _maybe_top_type_or_div_mod_nodes.size(); ++i) { + Node* data_node = _maybe_top_type_or_div_mod_nodes.at(i); + if (type(data_node) == Type::TOP) { ResourceMark rm; - type_node->as_Type()->make_paths_from_here_dead(this, nullptr, "ccp"); + data_node->make_paths_from_here_dead(this, nullptr, "ccp"); } } } else { - assert(_maybe_top_type_nodes.size() == 0, "we don't need type nodes"); + assert(_maybe_top_type_or_div_mod_nodes.size() == 0, "we don't need type nodes"); } // Initialize the traversal. diff --git a/src/hotspot/share/opto/phaseX.hpp b/src/hotspot/share/opto/phaseX.hpp index 014d16f92f6..7ea7aa99142 100644 --- a/src/hotspot/share/opto/phaseX.hpp +++ b/src/hotspot/share/opto/phaseX.hpp @@ -501,6 +501,10 @@ class PhaseIterGVN : public PhaseGVN { // Usually returns new_type. Returns old_type if new_type is only a slight // improvement, such that it would take many (>>10) steps to reach 2**32. + DeadPathNode* dead_path(); + + void make_dependent_paths_dead_if_top(Node* dead_node, const Type* t); + public: PhaseIterGVN(PhaseIterGVN* igvn); // Used by CCP constructor @@ -695,7 +699,7 @@ class PhaseIterGVN : public PhaseGVN { // Should be replaced with combined CCP & GVN someday. class PhaseCCP : public PhaseIterGVN { Unique_Node_List _root_and_safepoints; - Unique_Node_List _maybe_top_type_nodes; + Unique_Node_List _maybe_top_type_or_div_mod_nodes; // Non-recursive. Use analysis to transform single Node. virtual Node* transform_once(Node* n); diff --git a/src/hotspot/share/opto/rootnode.cpp b/src/hotspot/share/opto/rootnode.cpp index 60167c5436a..1e5ef29e79c 100644 --- a/src/hotspot/share/opto/rootnode.cpp +++ b/src/hotspot/share/opto/rootnode.cpp @@ -90,3 +90,48 @@ const Type* HaltNode::Value(PhaseGVN* phase) const { const RegMask &HaltNode::out_RegMask() const { return RegMask::EMPTY; } + +Node* DeadPathNode::Ideal(PhaseGVN* phase, bool can_reshape) { + assert(unique_ctrl_out() == phase->C->root(), "only referenced from root"); + assert(can_reshape, "only used once igvn executes"); + bool modified = false; + for (uint i = 1; i < req(); i++) { // For all inputs + // Check for and remove dead inputs + if (phase->type(in(i)) == Type::TOP) { + del_req(i--); // Delete TOP inputs + modified = true; + } + } + if (req() == 1 && is_active()) { + assert(modified, "only if some inputs were removed"); + deactivate(); + } + return modified ? this : nullptr; +} + +const Type* DeadPathNode::Value(PhaseGVN* phase) const { + if (req() == 1) { + return Type::TOP; + } + return bottom_type(); +} + +void DeadPathNode::activate(PhaseIterGVN* igvn) { + assert(Compile::current()->root()->find_edge(this) < 0, "should be disconnected from root"); + set_req(0, this); + // If an entire subgraph died such as with Node::remove_dead_region(), some dead inputs to the DeadPath node will have + // been left behind + while (req() > 1) { + uint last = req() - 1; + assert(in(last) == nullptr || in(last)->is_top(), "only dead inputs should remain"); + del_req(last); + } + Node* root_node = Compile::current()->root(); + root_node->add_req(this); + igvn->_worklist.push(root_node); + igvn->set_type(this, bottom_type()); +} + +void DeadPathNode::deactivate() { + set_req(0, nullptr); +} diff --git a/src/hotspot/share/opto/rootnode.hpp b/src/hotspot/share/opto/rootnode.hpp index 76f0ec440a9..61ad317d455 100644 --- a/src/hotspot/share/opto/rootnode.hpp +++ b/src/hotspot/share/opto/rootnode.hpp @@ -69,4 +69,35 @@ class HaltNode : public Node { virtual uint match_edge(uint idx) const { return 0; } }; + +// This node collects paths that are found dead by PhaseIterGVN::make_dependent_paths_dead_if_top() + +// There is a single DeadPath node for the lifetime of optimizations. It's initially not active (i.e. unreachable from +// the IR graph). When a cfg path becomes dead it's added as an input to the unique DeadPath node. If after some +// optimizations run, the DeadPath node gets disconnected, it's not destroyed. It becomes inactive and can possibly be +// activated again on a subsequent igvn. When optimizations are over, the DeadPath node, if it is active, is expanded to +// a Region and Halt node in Compile::final_graph_reshaping(). + +// Rather than having this dedicated node, igvn could add a Halt node everytime it finds a dead cfg path from a data +// node. What's likely, however, is that as igvn progresses, that same cfg path is found dead by following cfg edges. +// The Halt node then becomes dead. To avoid this unnecessary cycle of creation of a Halt node only to have it be found +// dead shortly after, dead cfg paths are added to the unique DeadPath node. +class DeadPathNode : public RegionNode { +public: + DeadPathNode() : RegionNode(1) { + deactivate(); + assert(Compile::current()->dead_path() == nullptr, "only one"); + } + virtual int Opcode() const; + virtual const Type* bottom_type() const { return Type::BOTTOM; } + virtual Node* Identity(PhaseGVN* phase) { return this; } + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); + virtual const Type* Value(PhaseGVN* phase) const; + bool is_active() const { + return in(0) == this; + } + void activate(PhaseIterGVN* igvn); + void deactivate(); +}; + #endif // SHARE_OPTO_ROOTNODE_HPP diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index 20857eed35c..60eda1204b7 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -2391,7 +2391,7 @@ Node* VectorMaskOpNode::Ideal(PhaseGVN* phase, bool can_reshape) { if (n != nullptr) { return n; } - return TypeNode::Ideal(phase, can_reshape); + return nullptr; } Node* VectorMaskCastNode::Identity(PhaseGVN* phase) { diff --git a/test/hotspot/jtreg/compiler/c2/TestDeadPathManyDeadDataNodes.java b/test/hotspot/jtreg/compiler/c2/TestDeadPathManyDeadDataNodes.java new file mode 100644 index 00000000000..e9c5a8f7529 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestDeadPathManyDeadDataNodes.java @@ -0,0 +1,1301 @@ +/* + * Copyright (c) 2026 IBM Corporation. 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 8380166 + * @summary C2: crash in compiled code due to zero division because of widened CastII + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions + * -Xcomp -XX:CompileOnly=TestDeadPathManyDeadDataNodes::test1 + * -XX:CompileCommand=quiet + * -XX:CompileCommand=inline,TestDeadPathManyDeadDataNodes::inlined1 + * -XX:MaxRecursiveInlineLevel=1000 -XX:MaxInlineLevel=1000 + * -XX:-TieredCompilation -XX:+AlwaysIncrementalInline + * -XX:+DelayAfterInliningCutoff -XX:+IncrementalInlineForceCleanup + * -XX:NodeCountInliningCutoff=100000 -XX:+StressIGVN + * ${test.main.class} + * @run main ${test.main.class} + */ + +package compiler.c2; + +public class TestDeadPathManyDeadDataNodes { + private static int field; + private static boolean boolField2; + private static int arrayLengthField; + + public static void main(String[] args) { + Object o = new Object(); + try { + test1(false, 0); + } catch (NegativeArraySizeException nase) { + } + } + + private static int test1(boolean boolParam, int intParam) { + int length; + int res = 0; + length = -1; + for (int i = 0; i < 2; i++) { + if (boolParam) { + field = 42; + } + int[] array = new int[length]; + arrayLengthField = array.length; + while(true) { + Object o = new Object(); + int arrayLength = arrayLengthField; + arrayLengthField = 0; + switch (intParam) { + case 0: + if (boolField2) { + break; + } + field = 42; + continue; + case 1: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 2: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 3: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 4: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 5: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 6: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 7: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 8: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 9: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 10: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 11: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 12: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 13: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 14: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 15: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 16: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 17: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 18: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 19: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 20: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 21: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 22: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 23: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 24: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 25: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 26: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 27: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 28: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 29: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 30: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 31: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 32: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 33: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 34: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 35: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 36: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 37: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 38: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 39: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 40: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 41: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 42: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 43: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 44: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 45: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 46: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 47: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 48: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 49: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 50: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 51: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 52: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 53: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 54: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 55: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 56: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 57: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 58: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 59: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 60: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 61: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 62: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 63: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 64: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 65: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 66: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 67: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 68: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 69: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 70: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 71: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 72: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 73: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 74: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 75: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 76: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 77: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 78: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 79: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 80: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 81: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 82: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 83: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 84: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 85: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 86: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 87: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 88: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 89: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 90: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 91: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 92: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 93: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 94: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 95: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 96: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 97: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 98: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 99: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + default: + res += inlined1(boolParam, intParam/100, arrayLength, 92); + continue; + } + field = 42; + break; + } + length = lastInlined(); + } + return res; + } + + static int lastInlined() { + return -1; + } + + static int inlined1(boolean boolParam, int intParam, int arrayLength, int count) { + int res = 0; + switch (intParam) { + case 0: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 1: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 2: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 3: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 4: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 5: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 6: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 7: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 8: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 9: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 10: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 11: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 12: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 13: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 14: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 15: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 16: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 17: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 18: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 19: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 20: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 21: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 22: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 23: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 24: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 25: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 26: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 27: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 28: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 29: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 30: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 31: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 32: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 33: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 34: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 35: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 36: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 37: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 38: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 39: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 40: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 41: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 42: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 43: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 44: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 45: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 46: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 47: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 48: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 49: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 50: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 51: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 52: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 53: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 54: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 55: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 56: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 57: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 58: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 59: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 60: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 61: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 62: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 63: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 64: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 65: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 66: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 67: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 68: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 69: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 70: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 71: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 72: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 73: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 74: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 75: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 76: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 77: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 78: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 79: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 80: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 81: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 82: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 83: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 84: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 85: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 86: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 87: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 88: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 89: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 90: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 91: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 92: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 93: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 94: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 95: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 96: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 97: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 98: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 99: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + default: + if (count == 0) { + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + } else { + return inlined1(boolParam, intParam / 100, arrayLength, count-1); + } + } + } +} diff --git a/test/hotspot/jtreg/compiler/integerArithmetic/TestDivByZeroInLiveCFGPath.java b/test/hotspot/jtreg/compiler/integerArithmetic/TestDivByZeroInLiveCFGPath.java new file mode 100644 index 00000000000..6eaf3d86e71 --- /dev/null +++ b/test/hotspot/jtreg/compiler/integerArithmetic/TestDivByZeroInLiveCFGPath.java @@ -0,0 +1,64 @@ + +/* + * 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 8383815 + * @summary C2: assert(false) failed: malformed IfNode with 1 outputs + * @run main/othervm -XX:CompileCommand=compileonly,${test.main.class}*::* -XX:-TieredCompilation -Xbatch -XX:PerMethodTrapLimit=0 ${test.main.class} + * @run main ${test.main.class} + */ + +package compiler.integerArithmetic; + +public class TestDivByZeroInLiveCFGPath { + static long lFld; + static int iArr[] = new int[400]; + + public static void main(String[] strArr) { + for (int i = 0; i < 10; i++) { + test(); + } + } + + static void test() { + int x; + for (int i = 9; i < 100; ++i) { + int j = 100; + while (--j > 0) { + iArr[1] = (int) lFld; + } + try { + iArr[1] = (5 / j); + x = (i / iArr[8]); + } catch (ArithmeticException a_e) { + } + } + + for (int i = 18; i < 50; i++) { + iArr[2] += lFld; + } + } +} + diff --git a/test/hotspot/jtreg/compiler/integerArithmetic/TestZeroDivModWidenedCastII.java b/test/hotspot/jtreg/compiler/integerArithmetic/TestZeroDivModWidenedCastII.java new file mode 100644 index 00000000000..a5bc8fc9287 --- /dev/null +++ b/test/hotspot/jtreg/compiler/integerArithmetic/TestZeroDivModWidenedCastII.java @@ -0,0 +1,1122 @@ +/* + * Copyright (c) 2026 IBM Corporation. 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 8380166 + * @summary C2: crash in compiled code due to zero division because of widened CastII + * + * @run main/othervm -XX:-TieredCompilation -XX:-UseOnStackReplacement -XX:-BackgroundCompilation + * ${test.main.class} + * @run main ${test.main.class} + * + */ + +package compiler.integerArithmetic; + +public class TestZeroDivModWidenedCastII { + private static int intField; + private static long longField; + private static volatile int volatileField; + + public static void main(String[] args) { + for (int i = 0; i < 20_000; i++) { + test1(0, 9, 1, true, false); + test1(0, 9, 1, false, false); + inlined1_2(9, 1, 1, true, 0); + inlined1_3(0, 0); + test2(0, 9, 1, true, false); + test2(0, 9, 1, false, false); + inlined2_2(9, 1, 1, true, 0); + inlined2_3(0, 0); + test3(0, 9, 1, true, false); + test3(0, 9, 1, false, false); + inlined3_2(9, 1, 1, true, 0); + inlined3_3(0, 0); + test4(0, 9, 1, true, false); + test4(0, 9, 1, false, false); + inlined4_2(9, 1, 1, true, 0); + inlined4_3(0, 0); + test5(0, 9, 1, true, false); + test5(0, 9, 1, false, false); + inlined5_2(9, 1, 1, true, 0); + inlined5_3(0, 0); + test6(0, 9, 1, true, false); + test6(0, 9, 1, false, false); + inlined6_2(9, 1, 1, true, 0); + inlined6_3(0, 0); + test7(0, 9, 1, true, false); + test7(0, 9, 1, false, false); + inlined7_2(9, 1, 1, true, 0); + inlined7_3(0, 0); + test8(0, 9, 1, true, false); + test8(0, 9, 1, false, false); + inlined8_2(9, 1, 1, true, 0); + inlined8_3(0, 0); + test9(0, 9, 1, true, false); + test9(0, 9, 1, false, false); + inlined9_2(9, 1, 1, true, 0); + inlined9_3(0, 0); + test10(0, 9, 1, false); + inlined10_2(9, 1, 1, true, 0); + inlined10_3(0, 0); + test11(0, 9, 1, false); + inlined11_2(9, 1, 1, true, 0); + inlined11_3(0, 0); + test12(0, 9, 1, false); + inlined12_2(9, 1, 1, true, 0); + inlined12_3(0, 0); + test13(0, 9, 1, false); + inlined13_2(9, 1, 1, true, 0); + inlined13_3(0, 0); + test14(0, 9, 1, false); + inlined14_2(9, 1, 1, true, 0); + inlined14_3(0, 0); + test15(0, 9, 1, false); + inlined15_2(9, 1, 1, true, 0); + inlined15_3(0, 0); + test16(0, 9, 1, false); + inlined16_2(9, 1, 1, true, 0); + inlined16_3(0, 0); + test17(0, 9, 1, false); + inlined17_2(9, 1, 1, true, 0); + inlined17_3(0, 0); + } + } + + private static void test1(int k, int j, int flag, boolean flag2, boolean flag3) { + int l = 0; + for (; l < 10; l++); + int m = inlined1_3(j, l); + + int i = inlined1(k, flag2); + j = Integer.min(j, 9); + int[] array = new int[10]; + if (flag == 0) { + throw new RuntimeException("never taken"); + } + if (flag2) { + inlined1_2(j, flag, i, flag3, m); + } else { + inlined1_2(j, flag, i, flag3, m); + } + } + + private static int inlined1_3(int j, int l) { + if (l == 10) { + j = 1; + } + return j; + } + + private static void inlined1_2(int j, int flag, int i, boolean flag3, int m) { + if (flag3) { + float[] newArray = new float[j + 1]; // j + 1 in [0..10] + // RC i Date: Wed, 15 Jul 2026 09:57:38 +0000 Subject: [PATCH 231/707] 8387149: C2: assert(regs[i] != regs[j]) failed: regs[2] and regs[3] are both: v24 Reviewed-by: aph, fgao --- .../cpu/aarch64/c2_MacroAssembler_aarch64.cpp | 9 +- .../TestSelectFromTwoVectorSameOperand.java | 142 ++++++++++++++++++ 2 files changed, 148 insertions(+), 3 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/vectorapi/TestSelectFromTwoVectorSameOperand.java diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index eacfef9618a..fe9180bda5c 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -2728,7 +2728,8 @@ void C2_MacroAssembler::reconstruct_frame_pointer(Register rtmp) { void C2_MacroAssembler::select_from_two_vectors_neon(FloatRegister dst, FloatRegister src1, FloatRegister src2, FloatRegister index, FloatRegister tmp, unsigned vector_length_in_bytes) { - assert_different_registers(dst, src1, src2, tmp); + assert_different_registers(src2, tmp); + assert_different_registers(index, tmp); SIMD_Arrangement size = vector_length_in_bytes == 16 ? T16B : T8B; if (vector_length_in_bytes == 16) { @@ -2757,7 +2758,8 @@ void C2_MacroAssembler::select_from_two_vectors_sve(FloatRegister dst, FloatRegi FloatRegister src2, FloatRegister index, FloatRegister tmp, SIMD_RegVariant T, unsigned vector_length_in_bytes) { - assert_different_registers(dst, src1, src2, index, tmp); + assert_different_registers(src2, tmp); + assert_different_registers(index, tmp); if (vector_length_in_bytes == 8) { // We need to fit both the source vectors (src1, src2) in a single vector register because the @@ -2784,7 +2786,8 @@ void C2_MacroAssembler::select_from_two_vectors(FloatRegister dst, FloatRegister FloatRegister tmp, BasicType bt, unsigned vector_length_in_bytes) { - assert_different_registers(dst, src1, src2, index, tmp); + assert_different_registers(dst, src1, src2, tmp); + assert_different_registers(index, tmp); // The cases that can reach this method are - // - UseSVE = 0/1, vector_length_in_bytes = 8 or 16, excluding double and long types diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestSelectFromTwoVectorSameOperand.java b/test/hotspot/jtreg/compiler/vectorapi/TestSelectFromTwoVectorSameOperand.java new file mode 100644 index 00000000000..7c30e439515 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestSelectFromTwoVectorSameOperand.java @@ -0,0 +1,142 @@ +/* + * 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 id=SVE + * @bug 8387149 + * @summary Test case for SelectFromTwoVector with index operand same as other inputs. + * @requires vm.compiler2.enabled + * @requires os.arch == "aarch64" & vm.cpu.features ~= ".*sve.*" + * @modules jdk.incubator.vector + * @library /test/lib / + * @run main/othervm + * -XX:+UnlockDiagnosticVMOptions + * -XX:UseSVE=1 + * -XX:-TieredCompilation -Xbatch + * -XX:CompileCommand=dontinline,${test.main.class}::test* + * -XX:CompileCommand=compileonly,${test.main.class}::test* + * ${test.main.class} + */ + +/* + * @test id=NEON + * @bug 8387149 + * @summary Test case for SelectFromTwoVector with index operand same as other inputs. + * @requires vm.compiler2.enabled + * @requires os.arch == "aarch64" & vm.cpu.features ~= ".*asimd.*" + * @modules jdk.incubator.vector + * @library /test/lib / + * @run main/othervm + * -XX:+UnlockDiagnosticVMOptions + * -XX:UseSVE=0 + * -XX:-TieredCompilation -Xbatch + * -XX:CompileCommand=dontinline,${test.main.class}::test* + * -XX:CompileCommand=compileonly,${test.main.class}::test* + * ${test.main.class} + */ + +package compiler.vectorapi; + +import java.util.Random; +import jdk.incubator.vector.*; +import jdk.test.lib.Asserts; + +public class TestSelectFromTwoVectorSameOperand { + static final int SIZE = 8; + + static byte[] byte_input1 = new byte[SIZE]; + static byte[] byte_input2 = new byte[SIZE]; + static byte[] byte_output = new byte[SIZE]; + static final byte byte_index_mask = 15; + + static short[] short_input1 = new short[SIZE / 2]; + static short[] short_input2 = new short[SIZE / 2]; + static short[] short_output = new short[SIZE / 2]; + static final short short_index_mask = 7; + + static { + Random r = new Random(42); + r.nextBytes(byte_input1); + r.nextBytes(byte_input2); + + for (int i = 0; i < SIZE / 2; i++) { + short_input1[i] = byte_input1[i]; + short_input2[i] = byte_input2[i]; + } + } + + public static void main(String[] args) { + for (int i = 0; i < 100_000; ++i) { + test_byte_src1(); + verify_byte(byte_input1, byte_input2, byte_input1, byte_output); + test_byte_src2(); + verify_byte(byte_input1, byte_input2, byte_input2, byte_output); + test_short_src1(); + verify_short(short_input1, short_input2, short_input1, short_output); + test_short_src2(); + verify_short(short_input1, short_input2, short_input2, short_output); + } + } + + static void test_byte_src1() { + ByteVector src1 = ByteVector.fromArray(ByteVector.SPECIES_64, byte_input1, 0).and(byte_index_mask); + ByteVector src2 = ByteVector.fromArray(ByteVector.SPECIES_64, byte_input2, 0).and(byte_index_mask); + src1.selectFrom(src1, src2).intoArray(byte_output, 0); + } + + static void test_byte_src2() { + ByteVector src1 = ByteVector.fromArray(ByteVector.SPECIES_64, byte_input1, 0).and(byte_index_mask); + ByteVector src2 = ByteVector.fromArray(ByteVector.SPECIES_64, byte_input2, 0).and(byte_index_mask); + src2.selectFrom(src1, src2).intoArray(byte_output, 0); + } + + static void test_short_src1() { + ShortVector src1 = ShortVector.fromArray(ShortVector.SPECIES_64, short_input1, 0).and(short_index_mask); + ShortVector src2 = ShortVector.fromArray(ShortVector.SPECIES_64, short_input2, 0).and(short_index_mask); + src1.selectFrom(src1, src2).intoArray(short_output, 0); + } + + static void test_short_src2() { + ShortVector src1 = ShortVector.fromArray(ShortVector.SPECIES_64, short_input1, 0).and(short_index_mask); + ShortVector src2 = ShortVector.fromArray(ShortVector.SPECIES_64, short_input2, 0).and(short_index_mask); + src2.selectFrom(src1, src2).intoArray(short_output, 0); + } + + static void verify_byte(byte[] src1, byte[] src2, byte[] index, byte[] output) { + for (int i = 0; i < SIZE; i++) { + int index_value = index[i] & byte_index_mask; + byte element_value = (index_value < SIZE) ? src1[index_value] : src2[index_value - SIZE]; + byte masked_element_value = (byte) (element_value & byte_index_mask); + Asserts.assertEQ(masked_element_value, output[i]); + } + } + + static void verify_short(short[] src1, short[] src2, short[] index, short[] output) { + for (int i = 0; i < SIZE / 2; i++) { + int index_value = index[i] & short_index_mask; + short element_value = (index_value < SIZE / 2) ? src1[index_value] : src2[index_value - SIZE / 2]; + short masked_element_value = (short) (element_value & short_index_mask); + Asserts.assertEQ(masked_element_value, output[i]); + } + } +} From 4086d114ed3fe82edb9005521cc6ede340ea0299 Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Wed, 15 Jul 2026 10:10:31 +0000 Subject: [PATCH 232/707] 8388188: NMT: Remove unused local variable in RegionIterator::next_committed Reviewed-by: phubner, cnorrbin --- src/hotspot/share/nmt/virtualMemoryTracker.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/hotspot/share/nmt/virtualMemoryTracker.cpp b/src/hotspot/share/nmt/virtualMemoryTracker.cpp index e23076a12bf..08ea3699bc2 100644 --- a/src/hotspot/share/nmt/virtualMemoryTracker.cpp +++ b/src/hotspot/share/nmt/virtualMemoryTracker.cpp @@ -281,7 +281,6 @@ class RegionIterator : public StackObj { bool RegionIterator::next_committed(address& committed_start, size_t& committed_size) { if (end() <= _current_start) return false; - const size_t page_sz = os::vm_page_size(); const size_t current_size = end() - _current_start; if (os::first_resident_in_range(_current_start, current_size, committed_start, committed_size)) { assert(committed_start != nullptr, "Must be"); From 723826295aa50a88cbb702128da79a91e6d87c73 Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Wed, 15 Jul 2026 11:04:04 +0000 Subject: [PATCH 233/707] 8385957: JFR: Sensitive command-line arguments still in environment variable values Reviewed-by: mgronlun, rtoyonaga --- src/hotspot/share/jfr/dcmd/jfrDcmds.cpp | 5 +- .../share/jfr/periodic/jfrRedactedEvents.cpp | 176 ++++++++++++++---- .../share/jfr/periodic/jfrRedactedEvents.hpp | 4 + src/java.base/share/man/java.md | 8 +- test/jdk/jdk/jfr/startupargs/TestRedact.java | 90 +++++++-- 5 files changed, 228 insertions(+), 55 deletions(-) diff --git a/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp b/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp index a41515edfbb..58d6c029bc1 100644 --- a/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp +++ b/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp @@ -486,8 +486,9 @@ void JfrConfigureFlightRecorderDCmd::print_help(outputStream* out, bool startup) out->print_cr(" The option redact-argument is best-effort and applies only to"); out->print_cr(" command-line arguments in the jdk.JVMInformation event and to"); out->print_cr(" the java.command system property in the jdk.InitialSystemProperty"); - out->print_cr(" event. Other events, such as jdk.ProcessStart (child processes),"); - out->print_cr(" are not redacted."); + out->print_cr(" event, and to matching command-line argument text in the values"); + out->print_cr(" of jdk.InitialEnvironmentVariable events. Other events, such as"); + out->print_cr(" jdk.ProcessStart (child processes), are not redacted."); out->print_cr(""); out->print_cr(" If the redact-argument option is not specified, the following"); out->print_cr(" filters are used by default:"); diff --git a/src/hotspot/share/jfr/periodic/jfrRedactedEvents.cpp b/src/hotspot/share/jfr/periodic/jfrRedactedEvents.cpp index 331c28cffa2..652320c9904 100644 --- a/src/hotspot/share/jfr/periodic/jfrRedactedEvents.cpp +++ b/src/hotspot/share/jfr/periodic/jfrRedactedEvents.cpp @@ -29,6 +29,7 @@ #include "logging/logMessage.hpp" #include "runtime/arguments.hpp" #include "runtime/flags/jvmFlag.hpp" +#include "runtime/javaThread.hpp" #include "runtime/os.hpp" #include "runtime/vm_version.hpp" #include "services/diagnosticArgument.hpp" @@ -46,17 +47,20 @@ using StringFlag = JfrRedactedEvents::StringFlag; using StringKeyValueArray = GrowableArray*; static const char REDACTED[] = "[REDACTED]"; +static const char REDACTED_MARKER = (char)0xFF; static const char DELIMITER[] = " "; +static const char REDACT_ARGUMENT[] = "redact-argument"; static const char REDACT_ARGUMENT_EQUAL[] = "redact-argument="; static const size_t REDACTED_LENGTH = sizeof(REDACTED) -1; static const size_t DELIMITER_LENGTH = sizeof(DELIMITER) -1; -static const size_t REDACT_ARGUMENT_EQUAL_LENGTH = sizeof(REDACT_ARGUMENT_EQUAL) -1; +static const size_t REDACT_ARGUMENT_LENGTH = sizeof(REDACT_ARGUMENT) -1; String* JfrRedactedEvents::_redacted_java_command_line = nullptr; String* JfrRedactedEvents::_redacted_jvm_command_line = nullptr; String* JfrRedactedEvents::_redacted_flags_command_line = nullptr; String* JfrRedactedEvents::_redacted_flight_recorder_options = nullptr; +String* JfrRedactedEvents::_redacted_flight_recorder_options_with_marker = nullptr; StringKeyValueArray JfrRedactedEvents::_initial_environment_variables = nullptr; StringKeyValueArray JfrRedactedEvents::_initial_system_properties = nullptr; @@ -71,6 +75,10 @@ bool JfrRedactedEvents::_initialized = false; bool JfrRedactedEvents::set_argument_filter(const char* filters) { assert (_argument_filters == nullptr, "invariant"); assert (filters != nullptr, "invariant"); + if (strcmp(filters, "*") != 0 && strcmp(filters, "none") != 0) { + _redacted_arguments = new StringArray(); + _redacted_arguments->add(filters); + } _argument_filters = new StringArray(); return append_filters(_argument_filters, true, filters); } @@ -139,9 +147,8 @@ bool JfrRedactedEvents::append_filters(StringArray* target, bool argument, const } if (filters[0] == '\0') { LogMessage(jfr, redact) msg; - msg.warning("Default redaction filters are replaced. Specify:"); - msg.warning("-XX:FlightRecorderOptions:%s=none to disable filters without a warning.", option_name); - return true; + msg.error("Specify -XX:FlightRecorderOptions:%s=none to disable filters completely.", option_name); + return false; } if (strcmp(filters, "none") == 0) { return true; @@ -187,6 +194,67 @@ char* JfrRedactedEvents::new_redacted_text() { return result; } +void JfrRedactedEvents::redact(String* scratch_string, const char* target, const String* redaction) { + if (strchr(redaction->text(), REDACTED_MARKER)) { + return; + } + const char* position = target; + while (true) { + const char* sensitive = strstr(position, redaction->text()); + if (sensitive == nullptr) { + return; + } + size_t index = (size_t)(sensitive - target); + for (size_t i = 0; i < redaction->length(); i++) { + scratch_string->set(index + i, REDACTED_MARKER); + } + position = sensitive + 1; + } +} + +String* JfrRedactedEvents::redact_environment_variable_value(const char* value) { + if (strchr(value, REDACTED_MARKER)) { + return new String(REDACTED); + } + bool changed = false; + String* input = new String(value); + if (_redacted_flight_recorder_options_with_marker != nullptr) { + size_t length = strlen(FlightRecorderOptions); + while (const char* start = strstr(input->text(), FlightRecorderOptions)) { + changed = true; + const char* end = start + length; + stringStream s; + s.write(input->text(), start - input->text()); + s.write(_redacted_flight_recorder_options_with_marker->text(), _redacted_flight_recorder_options_with_marker->length()); + s.write(end, strlen(end)); + String* result = new String(s.base()); + delete input; + input = result; + } + } + String* scratch_string = new String(input->text()); + for (int i = 0; i < _redacted_arguments->length(); i++) { + redact(scratch_string, input->text(), _redacted_arguments->at(i)); + } + stringStream result; + bool inside_redaction = false; + for (size_t i = 0; i < scratch_string->length(); i++) { + if (scratch_string->at(i) == REDACTED_MARKER) { + changed = true; + if (!inside_redaction) { + result.print(REDACTED); + } + inside_redaction = true; + } else { + result.put(scratch_string->at(i)); + inside_redaction = false; + } + } + delete scratch_string; + delete input; + return changed ? new String(result.base()) : nullptr; +} + bool JfrRedactedEvents::emit_initial_environment_variables(bool log) { if (_initial_environment_variables == nullptr) { ensure_initialized(); @@ -207,6 +275,16 @@ bool JfrRedactedEvents::emit_initial_environment_variables(bool log) { if (log) { log_debug(jfr, redact)("Redacted initial environment variable named '%s'", key->text()); } + } else { + String* redacted_value = redact_environment_variable_value(value); + if (redacted_value != nullptr) { + if (log) { + log_debug(jfr, redact)("Redacted argument in initial environment variable value named '%s'", key->text()); + } + _initial_environment_variables->append(new StringKeyValue(key, redacted_value->text())); + delete redacted_value; + continue; + } } _initial_environment_variables->append(new StringKeyValue(key, value)); } @@ -262,6 +340,9 @@ bool JfrRedactedEvents::match_flag(const char* flag_name, const char* arg) { if (flag_name == nullptr || arg == nullptr) { return false; } + if (strncmp(arg, "-XX:", 4) == 0) { + arg += 4; + } while (*flag_name) { if (*arg != *flag_name) { return false; @@ -346,6 +427,34 @@ void JfrRedactedEvents::emit_jvm_information(bool log) { } } +// Method assumes that FlightRecorderOptions has been successfully parsed during startup +String* JfrRedactedEvents::redact_flight_recorder_options(const char* option, bool marker) { + JavaThread* THREAD = JavaThread::current(); + const size_t length = strlen(option); + DCmdArgIter iterator(option, length, ','); + while (iterator.next(THREAD)) { + if (strncmp(iterator.key_addr(), REDACT_ARGUMENT, REDACT_ARGUMENT_LENGTH) == 0) { + const char* start = iterator.value_addr(); + const char* end = start + iterator.value_length(); + stringStream result; + result.write(option, start - option); + if (marker) { + result.put(REDACTED_MARKER); + } else { + result.write(REDACTED, REDACTED_LENGTH); + } + result.write(end, option + length - end); + return new String(result.base()); + } + } + if (HAS_PENDING_EXCEPTION) { + DEBUG_ONLY(ShouldNotReachHere();) + CLEAR_PENDING_EXCEPTION; + return new String(REDACTED); + } + return nullptr; +} + void JfrRedactedEvents::ensure_initialized() { if (_initialized) { return; @@ -359,31 +468,12 @@ void JfrRedactedEvents::ensure_initialized() { add_default_filters(_argument_filters, true); } if (FlightRecorderOptions != nullptr) { - if (strstr(FlightRecorderOptions, REDACT_ARGUMENT_EQUAL) != nullptr) { - DCmdIter iterator(FlightRecorderOptions, ','); - stringStream result; - size_t pos = 0; - while(iterator.has_next()) { - CmdLine line = iterator.next(); - const char* start = line.cmd_addr(); - if (strncmp(start, REDACT_ARGUMENT_EQUAL, REDACT_ARGUMENT_EQUAL_LENGTH) == 0) { - result.print(REDACT_ARGUMENT_EQUAL); - result.print(REDACTED); - // Preserve ',' if there are more tokens - pos = iterator.has_next() ? iterator.cursor() - 1 : iterator.cursor(); - } - while (pos < iterator.cursor()) { - result.write(FlightRecorderOptions + pos, 1); - pos++; - } - } - _redacted_flight_recorder_options = new String(result.base()); - } else { - _redacted_flight_recorder_options = new String(FlightRecorderOptions); - } + _redacted_flight_recorder_options = redact_flight_recorder_options(FlightRecorderOptions, false); + _redacted_flight_recorder_options_with_marker = redact_flight_recorder_options(FlightRecorderOptions, true); + } + if (_redacted_arguments == nullptr) { + _redacted_arguments = new StringArray(); } - - _redacted_arguments = new StringArray(); StringArray* java_args = make_java_args_array(); _redacted_java_command_line = redact_command_line(java_args); @@ -396,7 +486,6 @@ void JfrRedactedEvents::ensure_initialized() { StringArray* flags_args = make_jvm_args_array(Arguments::jvm_flags_array(), Arguments::num_jvm_flags()); _redacted_flags_command_line = redact_command_line(flags_args); delete flags_args; - _initialized = true; } @@ -422,8 +511,8 @@ String* JfrRedactedEvents::redact_command_line(StringArray* arguments) { for (int j = arg_index; j < next_index; j++) { result->add(REDACTED); const char* arg = arguments->at(j)->text(); - if (arg != nullptr && strncmp(arg, "-XX:", 4) == 0) { - _redacted_arguments->add(arg + 4); + if (arg != nullptr) { + _redacted_arguments->add(arg); } } arg_index = next_index; @@ -504,15 +593,23 @@ StringArray* JfrRedactedEvents::make_jvm_args_array(char** jvm_args_array, int a return nullptr; } StringArray* result = new StringArray(array_length); - for(int i = 0; i < array_length; i++) { + for (int i = 0; i < array_length; i++) { char* argument = jvm_args_array[i]; - if (_redacted_flight_recorder_options != nullptr && - strncmp(argument, "-XX:FlightRecorderOptions", 25) == 0) { - const char* text = _redacted_flight_recorder_options->text(); - size_t length = _redacted_flight_recorder_options->length(); - // Length must be at least 26 or the JVM will not start. - result->add(new String(argument, 26, text, length)); - continue; + if (strncmp(argument, "-XX:FlightRecorderOptions", 25) == 0) { + size_t length = strlen(argument); + if (length > 25 && + _redacted_flight_recorder_options != nullptr && + strcmp(argument + 26, FlightRecorderOptions) == 0) { + const char* text = _redacted_flight_recorder_options->text(); + // Length must be at least 26 or the JVM will not start. + result->add(new String(argument, 26, text, _redacted_flight_recorder_options->length())); + continue; + } + if (strstr(argument, REDACT_ARGUMENT_EQUAL) != nullptr) { + _redacted_arguments->add(argument); + result->add("-XX:FlightRecorderOptions:[REDACTED]"); + continue; + } } if (strncmp(argument, "-D", 2) == 0) { const char* key_start = argument + 2; @@ -523,6 +620,7 @@ StringArray* JfrRedactedEvents::make_jvm_args_array(char** jvm_args_array, int a bool redact = match_key(_key_filters, key_tmp->text()); delete key_tmp; if (redact) { + _redacted_arguments->add(argument); size_t unsensitive_length = (size_t)(eq - argument) + 1; result->add(new String(argument, unsensitive_length, REDACTED, REDACTED_LENGTH)); continue; diff --git a/src/hotspot/share/jfr/periodic/jfrRedactedEvents.hpp b/src/hotspot/share/jfr/periodic/jfrRedactedEvents.hpp index dc972190b6c..c3d6fd32cbc 100644 --- a/src/hotspot/share/jfr/periodic/jfrRedactedEvents.hpp +++ b/src/hotspot/share/jfr/periodic/jfrRedactedEvents.hpp @@ -195,6 +195,7 @@ class JfrRedactedEvents: public AllStatic { static String* _redacted_jvm_command_line; static String* _redacted_flags_command_line; static String* _redacted_flight_recorder_options; + static String* _redacted_flight_recorder_options_with_marker; static GrowableArray* _initial_system_properties; static GrowableArray* _initial_environment_variables; static GrowableArray* _string_flags; @@ -217,7 +218,10 @@ class JfrRedactedEvents: public AllStatic { static int match_arguments(StringArray* filter_array, StringArray* arguments, int arg_index); static bool match_key(StringArray* array, const char* text); static bool read_file(StringArray* target, const char* filename); + static void redact(String* scratch_string, const char* target, const String* redaction); + static String* redact_flight_recorder_options(const char* option, bool marker); static String* redact_command_line(StringArray* arguments); + static String* redact_environment_variable_value(const char* value); static StringArray* split(const char* text, char separator); }; diff --git a/src/java.base/share/man/java.md b/src/java.base/share/man/java.md index 89166ae39e1..a0717055864 100644 --- a/src/java.base/share/man/java.md +++ b/src/java.base/share/man/java.md @@ -1215,9 +1215,11 @@ These `java` options control the runtime behavior of the Java HotSpot VM. be replaced with `[REDACTED]`. The option `redact-argument` is best-effort and applies only to command-line arguments in the `jdk.JVMInformation` event and to the `java.command` system property in the - `jdk.InitialSystemProperty` event. Other events, such as `jdk.ProcessStart` - (child processes), are not redacted. Use `-XX:FlightRecorderOptions:help` - to see the default filters used by the `redact-argument` option. + `jdk.InitialSystemProperty` event, and to matching command-line argument + text in the values of `jdk.InitialEnvironmentVariable` events. Other + events, such as `jdk.ProcessStart` (child processes), are not redacted. + Use `-XX:FlightRecorderOptions:help` to see the default filters used by + the `redact-argument` option. `redact-key=`key-filter : Replace the value of environment variables and system properties diff --git a/test/jdk/jdk/jfr/startupargs/TestRedact.java b/test/jdk/jdk/jfr/startupargs/TestRedact.java index 2d96408a3f5..f3f3a9e5fa6 100644 --- a/test/jdk/jdk/jfr/startupargs/TestRedact.java +++ b/test/jdk/jdk/jfr/startupargs/TestRedact.java @@ -42,6 +42,7 @@ import jdk.jfr.consumer.RecordingFile; import jdk.test.lib.Asserts; import jdk.test.lib.jfr.CommonHelper; +import jdk.test.lib.Platform; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; @@ -169,6 +170,7 @@ public static void main(String... args) throws Exception { testRedactKey(); testRedactArgument(); testRedactMultiple(); + testOptionVariable(); testWildcards(); testDefaults(); testRedactFile(); @@ -315,15 +317,6 @@ private static void testRedactFile() throws Exception { private static void testEmpty() throws Exception { var environment = Map.of("API_TOKEN", "Zebra1"); var properties = Map.of("API_KEY", "Zebra2"); - Execution e1 = run(environment, properties, - "-XX:FlightRecorderOptions:redact-key=,redact-argument=", "Zebra3" - ); - e1.output().shouldContain("Default redaction filters are replaced."); - e1.output().shouldContain("redact-key=none to disable filters without a warning"); - e1.output().shouldContain("redact-argument=none to disable filters without a warning"); - e1.assertUnredacted("Zebra1"); - e1.assertUnredacted("Zebra2"); - e1.assertUnredacted("Zebra3"); Execution e2 = run(environment, properties, "-XX:FlightRecorderOptions:redact-argument=none,redact-key=none", "Zebra3" @@ -377,6 +370,12 @@ private static void testRedactArgument() throws Exception { e.assertRedactedArgument("N4711"); e.assertRedactedArgument("Smith:abc123"); e.assertUnredacted("Banana"); + + String option = Platform.isWindows() ? + "-XX:FlightRecorderOptions:redact-argument='Foo,bar'" : + "-XX:FlightRecorderOptions:redact-argument=\"Foo,bar\""; + Execution e2 = run(option,"Foo,bar"); + e2.assertRedactedArgument("Foo,bar"); } private static void testRedactMultiple() throws Exception { @@ -390,6 +389,69 @@ private static void testRedactMultiple() throws Exception { e.assertRedactedArgument("Quz"); } + private static void testOptionVariable() throws Exception { + // Simulate shell expansion with the three options: + // SYSTEM_PROPS, JVM_OPTIONS and PROGRAM_OPTIONS + String systemProperty = "-Dsecret=apple"; + String jvmOption = "-XX:FlightRecorderOptions:stackdepth=32,redact-argument=+Aracuan"; + String programOption = "Aracuan"; + Execution e1 = run( + Map.of("SYSTEM_PROPS", systemProperty, + "JVM_OPTIONS", jvmOption, + "PROGRAM_OPTIONS", programOption), + Map.of("secret","apple"), + List.of(systemProperty, jvmOption), + programOption + ); + e1.assertRedactedKey("SYSTEM_PROPS"); + String redactedJVMOption = e1.environment.get("JVM_OPTIONS"); + if (!redactedJVMOption.equals("-XX:FlightRecorderOptions:stackdepth=32,redact-argument=[REDACTED]")) { + throw new Exception("Expected partial redaction for environment variable with -XX:FlightRecorderOptions:redact-argument="); + } + e1.assertRedactedKey("PROGRAM_OPTIONS"); + e1.assertRedactedKey("secret"); + e1.assertRedactedArgument("Aracuan"); + + Execution e2 = run( + Map.of("PROGRAM_OPTIONS", "BLUE RED GREEN GREDELINE"), + Map.of(), + "-XX:FlightRecorderOptions:redact-argument=+*red*", + "BLUE", "RED", "GREEN", "GREDELINE" + ); + String programOptions = e2.environment().get("PROGRAM_OPTIONS"); + if (!programOptions.equals("BLUE [REDACTED] GREEN [REDACTED]")) { + e2.print(); + throw new Exception("Missing redaction inside option variable"); + } + + Execution e3 = run( + Map.of("PROGRAM_OPTIONS", "ZEBRA FISH ZEBRACCOON FISH RACCOON"), + Map.of(), + "-XX:FlightRecorderOptions:redact-argument=+zebra;raccoon", + "ZEBRA", "FISH", "ZEBRACCOON", "FISH", "RACCOON" + ); + programOptions = e3.environment().get("PROGRAM_OPTIONS"); + if (!programOptions.equals("[REDACTED] FISH [REDACTED] FISH [REDACTED]")) { + e3.print(); + throw new Exception("Incorrect redaction when option arguments overlap"); + } + + String option1 = "-XX:FlightRecorderOptions:redact-argument=Zebra,gibberish=,,,"; + String option2 = "-XX:FlightRecorderOptions:redact-argument=Tiger"; + Execution e4 = run( + Map.of("MY_JVM_OPTIONS", option1 + " " + option2), + Map.of(), + List.of(option1, option2), + "TIGER" + ); + e4.assertRedactedArgument("TIGER"); + String redacted = e4.environment().get("MY_JVM_OPTIONS"); + if (!redacted.equals("[REDACTED] -XX:FlightRecorderOptions:redact-argument=[REDACTED]")) { + e4.print(); + throw new Exception("Incorrect redaction with multiple options in environment variables"); + } + } + private static void testRedactKey() throws Exception { Execution e = run( Map.of("cart", "wheel", "banana", "split", "rose", "bud"), @@ -407,14 +469,20 @@ private static Execution run(String options, String... args) throws Exception { return run(Map.of(), Map.of(), options, args); } - private static Execution run(Map environment, Map properties, String options, String... args) throws Exception { + private static Execution run(Map environment, Map properties, String option, String... args) throws Exception { + return run(environment, properties, List.of(option), args); + } + + private static Execution run(Map environment, Map properties, List options, String... args) throws Exception { List arguments = new ArrayList<>(); Path file = Path.of("file.jfr"); for (var entry : properties.entrySet()) { arguments.add("-D" + entry.getKey() + "=" + entry.getValue()); } arguments.add("-XX:StartFlightRecording:filename=" + file.toAbsolutePath().toString()); - arguments.add(options); + for (String option : options) { + arguments.add(option); + } arguments.add("jdk.jfr.startupargs.Application"); arguments.addAll(Arrays.asList(args)); From 376ecc5a9d89b5f8c1593d3e717eb2c748b17449 Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Wed, 15 Jul 2026 13:37:35 +0000 Subject: [PATCH 234/707] 8386546: Fix race when reserving memory with NUMA interleaving on Windows Reviewed-by: asmehra, stuefe --- src/hotspot/os/windows/os_windows.cpp | 175 ++++++++++++- src/hotspot/os/windows/os_windows.hpp | 51 ++++ .../hotspot/gtest/runtime/test_os_windows.cpp | 240 ++++++++++++++++++ 3 files changed, 456 insertions(+), 10 deletions(-) diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 0fc636483f5..f62e9c298e8 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -3507,6 +3507,152 @@ char* os::pd_reserve_memory(size_t bytes, bool exec) { return pd_attempt_reserve_memory_at(nullptr /* addr */, bytes, exec); } +// This allocates a placeholder via VirtualAlloc2(MEM_RESERVE_PLACEHOLDER). +os::win32::PlaceholderRegion os::win32::reserve_placeholder_memory(size_t bytes, char* addr) { + assert(bytes > 0, "Size must be a value greater than 0"); + assert(is_aligned(addr, os::vm_allocation_granularity()), "Requested address should be aligned to allocation granularity."); + assert(is_aligned(bytes, os::vm_page_size()), "Requested size, bytes, should be aligned to page size."); + + if (!is_VirtualAlloc2_supported()) { + return PlaceholderRegion(); + } + + char* res = (char*)os::win32::VirtualAlloc2( + GetCurrentProcess(), + addr, + bytes, + MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, + PAGE_NOACCESS, + nullptr, 0); + + if (res != nullptr) { + log_trace(os)("VirtualAlloc2 placeholder of size (%zu) returned " PTR_FORMAT ".", bytes, p2i(res)); + return PlaceholderRegion(res, bytes); + } else { + log_warning(os)("VirtualAlloc2 placeholder reservation of size (%zu) at " PTR_FORMAT ": error %lu.", bytes, p2i(addr), GetLastError()); + return PlaceholderRegion(); + } +} + +os::win32::PlaceholderRegionPair os::win32::split_memory(const PlaceholderRegion& orig, size_t offset) { + guarantee(is_VirtualAlloc2_supported(), "split_memory requires VirtualAlloc2."); + assert(!orig.is_empty(), "Region cannot be empty"); + assert(offset <= orig.size(), "Offset must be less than or equal to region size"); + + char* original_base = orig.base(); + size_t original_size = orig.size(); + + if (offset == 0) { + log_trace(os)("Split memory has offset 0: " RANGEFMT, RANGEFMTARGS(original_base, original_size)); + return { PlaceholderRegion(), orig }; + } else if (offset == original_size) { + log_trace(os)("Split memory consumed the whole region: " RANGEFMT, RANGEFMTARGS(original_base, original_size)); + return { orig, PlaceholderRegion() }; + } + + assert(is_aligned(offset, os::vm_allocation_granularity()), "If the split does not consume the entire original region, the offset should be aligned to allocation granularity since a new Placeholder is spawned the split point."); + + // VirtualFree with MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER splits the + // placeholder [original_base, original_base+original_size) in two: + // [original_base, original_base+offset) and [original_base+offset, original_base+original_size) + // + // With correct inputs, this should not fail. + // A failure indicates either a programming error (e.g., bad alignment, + // region not actually a placeholder) or a catastrophic system problem. + // Crashing with a diagnostic is more useful than attempting recovery. + BOOL result = virtualFree(original_base, offset, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); + guarantee(result != FALSE, + "Failed to split placeholder at " PTR_FORMAT " (offset %zu): error %lu.", + p2i(original_base), offset, GetLastError()); + + log_trace(os)("Split placeholder " RANGE_FORMAT " at offset %zu.", + RANGE_FORMAT_ARGS(original_base, original_size), offset); + + return {PlaceholderRegion(original_base, offset), PlaceholderRegion(original_base + offset, original_size - offset)}; +} + +char* os::win32::convert_to_reserved(PlaceholderRegion region, int numa_node) { + guarantee(is_VirtualAlloc2_supported(), "convert_to_reserved requires VirtualAlloc2"); + assert(!region.is_empty(), "Region cannot be empty"); + + char* base = region.base(); + size_t size = region.size(); + + assert(base != nullptr, "Region base cannot be null"); + assert(size > 0, "Region size must be positive"); + + MEM_EXTENDED_PARAMETER param = { 0 }; + MEM_EXTENDED_PARAMETER* param_ptr = nullptr; + ULONG param_count = 0; + + if (numa_node >= 0) { + param.Type = MemExtendedParameterNumaNode; + param.ULong = (DWORD)numa_node; + param_ptr = ¶m; + param_count = 1; + } + + // Similar to split_memory, with correct inputs, this should never fail. + char* reserved = (char*)os::win32::VirtualAlloc2( + GetCurrentProcess(), + base, + size, + MEM_RESERVE | MEM_REPLACE_PLACEHOLDER, + PAGE_READWRITE, + param_ptr, param_count); + guarantee(reserved != nullptr, + "Failed to convert placeholder to reservation at " PTR_FORMAT " (%zu, numa node %d): error %lu.", + p2i(base), size, numa_node, GetLastError()); + + if (numa_node >= 0) { + log_trace(os)("Converted placeholder " RANGE_FORMAT " to reservation on NUMA node %d.", RANGE_FORMAT_ARGS(reserved, size), numa_node); + } else { + log_trace(os)("Converted placeholder " RANGE_FORMAT " to reservation.", RANGE_FORMAT_ARGS(reserved, size)); + } + + return reserved; +} + +// Reserve a region split across NUMA nodes. +// Uses VirtualAlloc2 placeholders in order to avoid races when splitting up the initial reservation into +// chunks assigned to different nodes. Returns the base address of the reserved range, or nullptr on failure. +static char* reserve_with_numa_placeholder(char* addr, size_t bytes) { + assert(is_VirtualAlloc2_supported(), "requires VirtualAlloc2"); + + const size_t chunk_size = NUMAInterleaveGranularity; + + // Reserve the full range as a placeholder. + // If we requested an address, reserve_placeholder_memory will obtain it or fail. + os::win32::PlaceholderRegion whole_range = os::win32::reserve_placeholder_memory(bytes, addr); + if (whole_range.is_empty()) { + log_warning(os)("Failed to reserve placeholder for NUMA interleaving (" PTR_FORMAT ", %zu).", p2i(addr), bytes); + return nullptr; + } + + char* const whole_range_base = whole_range.base(); + log_trace(os)("Created VirtualAlloc2 NUMA placeholder at " RANGE_FORMAT " (%zu bytes).", RANGE_FORMAT_ARGS(whole_range_base, bytes), bytes); + + char* cur = whole_range_base; + size_t remaining_len = whole_range.size(); + + int count = 0; + const int node_count = numa_node_list_holder.get_count(); + + while (remaining_len > 0) { + const size_t bytes_to_rq = MIN2(remaining_len, chunk_size - ((uintptr_t)cur % chunk_size)); + os::win32::PlaceholderRegion remaining(cur, remaining_len); + os::win32::PlaceholderRegionPair split = os::win32::split_memory(remaining, bytes_to_rq); + // Assign 0 for testing on systems without NUMA interleaving + DWORD node = node_count > 0 ? numa_node_list_holder.get_node_list_entry(count % node_count) : 0; + os::win32::convert_to_reserved(split.left, (int)node); + cur = split.right.base(); + remaining_len = split.right.size(); + count++; + } + + return whole_range_base; +} + // Reserve memory at an arbitrary address, only if that area is // available (and not reserved for something else). char* os::pd_attempt_reserve_memory_at(char* addr, size_t bytes, bool exec) { @@ -3516,23 +3662,32 @@ char* os::pd_attempt_reserve_memory_at(char* addr, size_t bytes, bool exec) { char* res; // note that if UseLargePages is on, all the areas that require interleaving // will go thru reserve_memory_special rather than thru here. - bool use_individual = (UseNUMAInterleaving && !UseLargePages); - if (!use_individual) { - res = (char*)virtualAlloc(addr, bytes, MEM_RESERVE, PAGE_READWRITE); - } else { + bool use_numa_interleaving = (UseNUMAInterleaving && !UseLargePages); + if (use_numa_interleaving) { elapsedTimer reserveTimer; if (Verbose && PrintMiscellaneous) reserveTimer.start(); - // in numa interleaving, we have to allocate pages individually - // (well really chunks of NUMAInterleaveGranularity size) - res = allocate_pages_individually(bytes, addr, MEM_RESERVE, PAGE_READWRITE); - if (res == nullptr) { - warning("NUMA page allocation failed"); + if (is_VirtualAlloc2_supported()) { + // Splittable NUMA interleaving with VirtualAlloc2 placeholders. + res = reserve_with_numa_placeholder(addr, bytes); + if (res == nullptr) { + log_warning(os)("NUMA allocation using placeholders failed"); + } + } else { + // Non-splittable NUMA interleaving: allocate_pages_individually (possible races). + // (well really chunks of NUMAInterleaveGranularity size) + res = allocate_pages_individually(bytes, addr, MEM_RESERVE, PAGE_READWRITE); + if (res == nullptr) { + log_warning(os)("NUMA page allocation failed"); + } } if (Verbose && PrintMiscellaneous) { reserveTimer.stop(); tty->print_cr("reserve_memory of %zx bytes took " JLONG_FORMAT " ms (" JLONG_FORMAT " ticks)", bytes, - reserveTimer.milliseconds(), reserveTimer.ticks()); + reserveTimer.milliseconds(), reserveTimer.ticks()); } + } else { + // Standard reservation. + res = (char*)virtualAlloc(addr, bytes, MEM_RESERVE, PAGE_READWRITE); } assert(res == nullptr || addr == nullptr || addr == res, "Unexpected address from reserve."); diff --git a/src/hotspot/os/windows/os_windows.hpp b/src/hotspot/os/windows/os_windows.hpp index 5ebc80c817b..68e77c9957f 100644 --- a/src/hotspot/os/windows/os_windows.hpp +++ b/src/hotspot/os/windows/os_windows.hpp @@ -122,6 +122,57 @@ class os::win32 { typedef PVOID (WINAPI *MapViewOfFile3Fn)(HANDLE, HANDLE, PVOID, ULONG64, SIZE_T, ULONG, ULONG, MEM_EXTENDED_PARAMETER*, ULONG); static MapViewOfFile3Fn MapViewOfFile3; + // A "reserved" region of address space that can be split or converted to a + // normal reservation. Conceptually distinct from a reserved region: + // callers must NOT call commit_memory, map_memory, or other operations + // directly on the raw address. They must first convert it via + // convert_to_reserved(). + class PlaceholderRegion { + char* const _base; + size_t const _size; + public: + PlaceholderRegion() : _base(nullptr), _size(0) {} + PlaceholderRegion(char* base, size_t size) : _base(base), _size(size) { + if (base != nullptr) { + assert(size > 0, "Non-empty Placeholder must have positive size."); + assert(is_aligned(base, os::vm_allocation_granularity()), "New Placeholder base should be aligned to allocation granularity."); + assert(is_aligned(size, os::vm_page_size()), "New Placeholder size should be page-aligned"); + } else { + assert(size == 0, "Empty Placeholder must have zero size."); + } + } + PlaceholderRegion(const PlaceholderRegion& source) : PlaceholderRegion(source._base, source._size) {} + char* base() const { return _base; } + size_t size() const { return _size; } + bool is_empty() const { return _base == nullptr; } + }; + + struct PlaceholderRegionPair { + PlaceholderRegion left; + PlaceholderRegion right; + }; + + // Reserves a virtual memory region that can be split after allocation. + // The returned region must be converted via convert_to_reserved() before committing. + // If the returned PlaceholderRegion is empty, the reservation failed. + // This should only be called after os::init_2() has completed, otherwise the Windows API may not be initialized. + // Uses VirtualAlloc2, which requires the base address be null or aligned to allocation granularity. + static PlaceholderRegion reserve_placeholder_memory(size_t bytes, char* addr); + + // Split 'orig' at 'offset'. Returns left and right placeholder pieces as a PlaceholderRegionPair. + // The caller must not use 'orig' afterward. + // Offset must be aligned to allocation granularity. + // If offset == orig.size(), returns { orig, empty }. + // If offset == 0, returns { empty, orig }. + // This should not fail. If unsuccessful, this function fails fatally. + static PlaceholderRegionPair split_memory(const PlaceholderRegion& orig, size_t offset); + + // Convert a placeholder region into a regular reserved region via VirtualAlloc2(MEM_REPLACE_PLACEHOLDER). + // After conversion the Placeholder region should no longer be used. + // This should not fail. If unsuccessful, this function fails fatally. + // If numa_node >= 0, binds the reservation to that NUMA node. + static char* convert_to_reserved(PlaceholderRegion region, int numa_node = -1); + private: static void initialize_performance_counter(); diff --git a/test/hotspot/gtest/runtime/test_os_windows.cpp b/test/hotspot/gtest/runtime/test_os_windows.cpp index 6822e37b539..5efa0580eda 100644 --- a/test/hotspot/gtest/runtime/test_os_windows.cpp +++ b/test/hotspot/gtest/runtime/test_os_windows.cpp @@ -32,6 +32,8 @@ #include "concurrentTestRunner.inline.hpp" #include "unittest.hpp" +#include + namespace { class MemoryReleaser { char* const _ptr; @@ -873,4 +875,242 @@ TEST_VM(os_windows, SafeFetch32_with_page_guard_protection) { ::VirtualFree(p, 0, MEM_RELEASE); } +#define SKIP_IF_PLACEHOLDER_NOT_SUPPORTED \ + if (os::win32::VirtualAlloc2 == nullptr) GTEST_SKIP() << "VirtualAlloc2 not available"; + +TEST_VM(os, placeholder_reserve_and_convert) { + SKIP_IF_PLACEHOLDER_NOT_SUPPORTED; + + const size_t size = 4 * os::vm_allocation_granularity(); + + os::win32::PlaceholderRegion region = os::win32::reserve_placeholder_memory(size, nullptr); + ASSERT_FALSE(region.is_empty()); + ASSERT_EQ(region.size(), size); + ASSERT_NE(region.base(), (char*)nullptr); + + char* reserved = os::win32::convert_to_reserved(region); + ASSERT_EQ(reserved, region.base()); + + // Commit, but bypass NMT + ASSERT_NE(::VirtualAlloc(reserved, size, MEM_COMMIT, PAGE_READWRITE), nullptr); + // Touch the memory to confirm it's usable. + memset(reserved, 0xAB, size); + EXPECT_EQ((unsigned char)reserved[0], 0xAB); + EXPECT_EQ((unsigned char)reserved[size - 1], 0xAB); + + ASSERT_TRUE(::VirtualFree(reserved, 0, MEM_RELEASE)); +} + +TEST_VM(os, placeholder_split_two_way) { + SKIP_IF_PLACEHOLDER_NOT_SUPPORTED; + + const size_t granularity = os::vm_allocation_granularity(); + const size_t total = 4 * granularity; + const size_t split_offset = 3 * granularity; + + os::win32::PlaceholderRegion region = os::win32::reserve_placeholder_memory(total, nullptr); + ASSERT_FALSE(region.is_empty()); + + char* original_base = region.base(); + os::win32::PlaceholderRegionPair split = os::win32::split_memory(region, split_offset); + + // Leading piece: [base, base+split_offset) + ASSERT_EQ(split.left.base(), original_base); + ASSERT_EQ(split.left.size(), split_offset); + + // Trailing piece: [base+split_offset, base+total) + ASSERT_EQ(split.right.base(), original_base + split_offset); + ASSERT_EQ(split.right.size(), total - split_offset); + + // Convert both and commit. + char* addr1 = os::win32::convert_to_reserved(split.left); + char* addr2 = os::win32::convert_to_reserved(split.right); + ASSERT_EQ(addr1, original_base); + ASSERT_EQ(addr2, original_base + split_offset); + + // Commit, but bypass NMT + ASSERT_NE(::VirtualAlloc(addr1, split_offset, MEM_COMMIT, PAGE_READWRITE), nullptr); + ASSERT_NE(::VirtualAlloc(addr2, total - split_offset, MEM_COMMIT, PAGE_READWRITE), nullptr); + + // Touch the memory to confirm it's usable. + memset(addr1, 0x11, split_offset); + memset(addr2, 0x22, total - split_offset); + EXPECT_EQ((unsigned char)addr1[0], 0x11); + EXPECT_EQ((unsigned char)addr2[0], 0x22); + + // Verify we can release the parts separately. + ASSERT_TRUE(::VirtualFree(addr1, 0, MEM_RELEASE)); + ASSERT_TRUE(::VirtualFree(addr2, 0, MEM_RELEASE)); +} + +TEST_VM(os, placeholder_split_consumes_full_range) { + SKIP_IF_PLACEHOLDER_NOT_SUPPORTED; + + const size_t region_size = os::vm_allocation_granularity(); + os::win32::PlaceholderRegion region = os::win32::reserve_placeholder_memory(region_size, nullptr); + ASSERT_FALSE(region.is_empty()); + + char* original_base = region.base(); + os::win32::PlaceholderRegionPair split = os::win32::split_memory(region, region_size); + + // Leading piece + ASSERT_EQ(split.left.base(), original_base); + ASSERT_EQ(split.left.size(), region_size); + + // Trailing piece + ASSERT_TRUE(split.right.is_empty()); + + // Commit and touch to confirm it's usable. + char* addr = os::win32::convert_to_reserved(split.left); + ASSERT_NE(::VirtualAlloc(addr, region_size, MEM_COMMIT, PAGE_READWRITE), nullptr); + memset(addr, 0x11, region_size); + EXPECT_EQ((unsigned char)addr[0], 0x11); + + ASSERT_TRUE(::VirtualFree(addr, 0, MEM_RELEASE)); +} + +TEST_VM(os, placeholder_split_consumes_nothing) { + SKIP_IF_PLACEHOLDER_NOT_SUPPORTED; + + const size_t region_size = os::vm_allocation_granularity(); + os::win32::PlaceholderRegion region = os::win32::reserve_placeholder_memory(region_size, nullptr); + ASSERT_FALSE(region.is_empty()); + + char* original_base = region.base(); + os::win32::PlaceholderRegionPair split = os::win32::split_memory(region, 0); + + // Leading piece + ASSERT_TRUE(split.left.is_empty()); + + // Trailing piece + ASSERT_EQ(split.right.base(), original_base); + ASSERT_EQ(split.right.size(), region_size); + + // Commit and touch to confirm it's usable. + char* addr = os::win32::convert_to_reserved(split.right); + ASSERT_NE(::VirtualAlloc(addr, region_size, MEM_COMMIT, PAGE_READWRITE), nullptr); + memset(addr, 0x11, region_size); + EXPECT_EQ((unsigned char)addr[0], 0x11); + + ASSERT_TRUE(::VirtualFree(addr, 0, MEM_RELEASE)); +} + +TEST_VM_FATAL_ERROR_MSG(os, placeholder_double_convert, ".*Failed to convert placeholder.*") { + SKIP_IF_PLACEHOLDER_NOT_SUPPORTED; + const size_t size = 4 * os::vm_allocation_granularity(); + + os::win32::PlaceholderRegion region = os::win32::reserve_placeholder_memory(size, nullptr); + ASSERT_FALSE(region.is_empty()); + ASSERT_EQ(region.size(), size); + ASSERT_NE(region.base(), (char*)nullptr); + + // Double convert + char* reserved = os::win32::convert_to_reserved(region); + ASSERT_EQ(reserved, region.base()); + // This second conversion attempt should crash producing the error "...Failed to convert placeholder..." + reserved = os::win32::convert_to_reserved(region); +} + +TEST_VM(os, placeholder_commit_before_convert) { + SKIP_IF_PLACEHOLDER_NOT_SUPPORTED; + const size_t size = 4 * os::vm_allocation_granularity(); + + os::win32::PlaceholderRegion region = os::win32::reserve_placeholder_memory(size, nullptr); + ASSERT_FALSE(region.is_empty()); + ASSERT_EQ(region.size(), size); + ASSERT_NE(region.base(), (char*)nullptr); + + // Committing should fail here, but not crash. + ASSERT_FALSE(::VirtualAlloc(region.base(), size, MEM_COMMIT, PAGE_READWRITE)); + ASSERT_TRUE(::VirtualFree(region.base(), 0, MEM_RELEASE)); +} + +TEST_VM(os, placeholder_release_before_convert) { + SKIP_IF_PLACEHOLDER_NOT_SUPPORTED; + + const size_t size = 4 * os::vm_allocation_granularity(); + + os::win32::PlaceholderRegion region = os::win32::reserve_placeholder_memory(size, nullptr); + ASSERT_FALSE(region.is_empty()); + ASSERT_EQ(region.size(), size); + ASSERT_NE(region.base(), (char*)nullptr); + + ASSERT_TRUE(::VirtualFree(region.base(), 0, MEM_RELEASE)); +} + +// Test that reserve_with_numa_placeholder works correctly. +// On NUMA systems with a single NUMA node, there is no true interleaving +// (all chunks are put on node 0) but the placeholder split/replace path +// is still properly exercised. +TEST_VM(os_windows, placeholder_numa_reserve_commit) { + SKIP_IF_PLACEHOLDER_NOT_SUPPORTED; + + const size_t num_nodes = os::numa_get_groups_num(); + + // Enable NUMA interleaving for this test so the correct code path is taken. + AutoSaveRestore FLAG_GUARD(UseNUMAInterleaving); + AutoSaveRestore FLAG_GUARD(UseLargePages); + FLAG_SET_CMDLINE(UseNUMAInterleaving, true); + FLAG_SET_CMDLINE(UseLargePages, false); + + // Allocate a region large enough to span multiple NUMA interleave chunks. + // NUMAInterleaveGranularity defaults to 2MB + const size_t chunk_size = NUMAInterleaveGranularity; + const size_t num_chunks = 4; + const size_t size = num_chunks * chunk_size; + + char* result = os::attempt_reserve_memory_at(nullptr, size, mtTest); + ASSERT_TRUE(result != nullptr) << "Failed to reserve memory"; + ASSERT_TRUE(is_aligned(result, os::vm_allocation_granularity())); + ASSERT_TRUE(os::commit_memory(result, size, false)); + + // Walk (and touch) the chunks using the same alignment logic as reserve_with_numa_placeholder: + // the first chunk may be shorter (up to the next chunk_size boundary), + // then full chunk_size pieces, with a possible shorter trailing chunk. + PSAPI_WORKING_SET_EX_INFORMATION wsi[num_chunks + 1]; + memset(wsi, 0, sizeof(wsi)); + size_t bytes_remaining = size; + char* addr = result; + size_t actual_chunks = 0; + + while (bytes_remaining > 0) { + size_t this_chunk_size = MIN2(bytes_remaining, chunk_size - ((size_t)addr % chunk_size)); + + memset(addr, 0xDA, this_chunk_size); + + wsi[actual_chunks] = {0}; + wsi[actual_chunks].VirtualAddress = addr; + actual_chunks++; + + bytes_remaining -= this_chunk_size; + addr += this_chunk_size; + } + + BOOL query_ok = QueryWorkingSetEx(GetCurrentProcess(), wsi, sizeof(wsi)); + ASSERT_TRUE(query_ok) << "QueryWorkingSetEx failed: " << GetLastError(); + + // Verify all pages are valid (in the working set). + for (size_t i = 0; i < actual_chunks; i++) { + EXPECT_TRUE(wsi[i].VirtualAttributes.Valid) << "Chunk " << i << " page not valid in working set"; + } + + if (num_nodes > 1) { + // On a multi-NUMA system, verify that not all chunks are assigned to the same node. + ULONG first_node = (ULONG)wsi[0].VirtualAttributes.Node; + bool found_different_node = false; + for (size_t i = 1; i < actual_chunks; i++) { + if (wsi[i].VirtualAttributes.Valid && + (ULONG)wsi[i].VirtualAttributes.Node != first_node) { + found_different_node = true; + break; + } + } + EXPECT_TRUE(found_different_node) + << "All " << actual_chunks << " chunks assigned to NUMA node " << first_node + << "; expected interleaving across " << num_nodes << " nodes"; + } + + os::release_memory(result, size); +} + #endif From d1c87e5b4f3f17c473e2425983b3eb8c7ac15fa8 Mon Sep 17 00:00:00 2001 From: Derek White Date: Wed, 15 Jul 2026 13:42:10 +0000 Subject: [PATCH 235/707] 8388080: Increase static size of some stubs for APX code Reviewed-by: sviswanathan, kvn --- src/hotspot/cpu/x86/methodHandles_x86.hpp | 2 +- src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/cpu/x86/methodHandles_x86.hpp b/src/hotspot/cpu/x86/methodHandles_x86.hpp index c4dde903d29..8fdb6c1fb52 100644 --- a/src/hotspot/cpu/x86/methodHandles_x86.hpp +++ b/src/hotspot/cpu/x86/methodHandles_x86.hpp @@ -27,7 +27,7 @@ // Adapters enum /* platform_dependent_constants */ { - adapter_code_size = 6000 DEBUG_ONLY(+ 6000) + adapter_code_size = 8000 DEBUG_ONLY(+ 6000) }; // Additional helper methods for MethodHandles code generation: diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp index 8bb9982a820..7fc105046ff 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp @@ -3452,7 +3452,7 @@ RuntimeStub* SharedRuntime::generate_jfr_write_checkpoint() { }; const char* name = SharedRuntime::stub_name(StubId::shared_jfr_write_checkpoint_id); - CodeBuffer code(name, 1024, 64); + CodeBuffer code(name, 1024 + (UseAPX ? 1024 : 0), 64); MacroAssembler* masm = new MacroAssembler(&code); address start = __ pc(); From 82a2089959fb47ee0a6bd9f8836de6212de1f02f Mon Sep 17 00:00:00 2001 From: Matias Saavedra Silva Date: Wed, 15 Jul 2026 14:30:08 +0000 Subject: [PATCH 236/707] 8365575: AOT cache should include classes verified using "fail over" verification Reviewed-by: iklam, liach --- src/hotspot/share/classfile/verifier.cpp | 14 +++-- src/hotspot/share/oops/instanceKlass.cpp | 10 ++++ src/hotspot/share/oops/instanceKlass.hpp | 3 + src/hotspot/share/oops/instanceKlassFlags.hpp | 1 + .../cds/appcds/aotCache/VerifierFailOver.java | 59 +++++++++++++++++-- 5 files changed, 77 insertions(+), 10 deletions(-) diff --git a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp index 48be24c20dc..8422a39827a 100644 --- a/src/hotspot/share/classfile/verifier.cpp +++ b/src/hotspot/share/classfile/verifier.cpp @@ -222,9 +222,9 @@ bool Verifier::verify(InstanceKlass* klass, bool should_verify_class, TRAPS) { split_verifier.verify_class(THREAD); exception_name = split_verifier.result(); - // If dumping {classic, final} static archive, don't bother to run the old verifier, as + // If dumping classic static archive, don't bother to run the old verifier, as // the class will be excluded from the archive anyway. - bool can_failover = !(CDSConfig::is_dumping_classic_static_archive() || CDSConfig::is_dumping_final_static_archive()) && + bool can_failover = !(CDSConfig::is_dumping_classic_static_archive()) && klass->major_version() < NOFAILOVER_MAJOR_VERSION; if (can_failover && !HAS_PENDING_EXCEPTION && // Split verifier doesn't set PENDING_EXCEPTION for failure @@ -233,9 +233,9 @@ bool Verifier::verify(InstanceKlass* klass, bool should_verify_class, TRAPS) { log_info(verification)("Fail over class verification to old verifier for: %s", klass->external_name()); log_info(class, init)("Fail over class verification to old verifier for: %s", klass->external_name()); #if INCLUDE_CDS - // Exclude any classes that are verified with the old verifier, as the old verifier - // doesn't call SystemDictionaryShared::add_verification_constraint() - if (CDSConfig::is_dumping_archive()) { + // Exclude any classes that are verified with the old verifier when the verification constraints + // cannot be preserved. + if (CDSConfig::is_dumping_archive() && !CDSConfig::is_preserving_verification_constraints()) { SystemDictionaryShared::log_exclusion(klass, "Verified with old verifier"); SystemDictionaryShared::set_excluded(klass); } @@ -244,6 +244,10 @@ bool Verifier::verify(InstanceKlass* klass, bool should_verify_class, TRAPS) { exception_message = message_buffer; exception_name = inference_verify( klass, message_buffer, message_buffer_len, THREAD); + + if (exception_name == nullptr && !HAS_PENDING_EXCEPTION) { + klass->set_fail_over_verified(); + } } if (exception_name != nullptr) { exception_message = split_verifier.exception_message(); diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index fd1cf1b1457..8161516421e 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -2911,6 +2911,16 @@ bool InstanceKlass::can_be_verified_at_dumptime() const { // SystemDictionaryShared::check_verification_constraints() will not work for this class. return false; } + + if (CDSConfig::is_dumping_final_static_archive() && fail_over_verified()) { + // This is a class with version >50 but was verified with the old verifier in the training run, + // which had -XX:+AOTClassLinking. However, we are now in the assembly run with -XX:-AOTClassLinking. + // As SystemDictionaryShared::check_verification_constraints() does not support this case, + // we must exclude this class. + assert(!CDSConfig::is_dumping_aot_linked_classes(), "must be"); + return false; + } + if (super() != nullptr && !super()->can_be_verified_at_dumptime()) { return false; } diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp index 41f176330fa..721a50c73c6 100644 --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -339,6 +339,9 @@ class InstanceKlass: public Klass { bool has_localvariable_table() const { return _misc_flags.has_localvariable_table(); } void set_has_localvariable_table(bool b) { _misc_flags.set_has_localvariable_table(b); } + bool fail_over_verified() const { return _misc_flags.fail_over_verified(); } + void set_fail_over_verified() { _misc_flags.set_fail_over_verified(true); } + // field sizes int nonstatic_field_size() const { return _nonstatic_field_size; } void set_nonstatic_field_size(int size) { _nonstatic_field_size = size; } diff --git a/src/hotspot/share/oops/instanceKlassFlags.hpp b/src/hotspot/share/oops/instanceKlassFlags.hpp index 1709c02a171..84041a3a0e7 100644 --- a/src/hotspot/share/oops/instanceKlassFlags.hpp +++ b/src/hotspot/share/oops/instanceKlassFlags.hpp @@ -54,6 +54,7 @@ class InstanceKlassFlags { flag(has_miranda_methods , 1 << 12) /* True if this class has miranda methods in it's vtable */ \ flag(has_final_method , 1 << 13) /* True if klass has final method */ \ flag(trust_final_fields , 1 << 14) /* All instance final fields in this class should be trusted */ \ + flag(fail_over_verified , 1 << 15) /* class failed split verification but passed inference verification */ \ /* end of list */ #define IK_FLAGS_ENUM_NAME(name, value) _misc_##name = value, diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/VerifierFailOver.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/VerifierFailOver.java index 8107e3fe0a3..82a5ccb2142 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/VerifierFailOver.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/VerifierFailOver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 @@ -24,6 +24,7 @@ /* * @test + * @bug 8365575 * @summary Sanity test for AOTCache * @requires vm.cds.supports.aot.class.linking * @library /test/lib @@ -33,24 +34,72 @@ * @run driver VerifierFailOver */ +import jdk.test.lib.cds.CDSAppTester; import jdk.test.lib.cds.SimpleCDSAppTester; +import jdk.test.lib.helpers.ClassFileInstaller; import jdk.test.lib.process.OutputAnalyzer; public class VerifierFailOver { + + static final String mainClass = VerifierFailOverApp.class.getName(); + static final String appJar = ClassFileInstaller.getJarPath("app.jar"); + public static void main(String... args) throws Exception { SimpleCDSAppTester.of("VerifierFailOver") .addVmArgs("-Xlog:aot,aot+class=debug") .classpath("app.jar") .appCommandLine("VerifierFailOverApp") .setTrainingChecker((OutputAnalyzer out) -> { - out.shouldContain("Skipping VerifierFailOver_Helper: Verified with old verifier"); + out.shouldMatch("class.* klasses.* VerifierFailOver_Helper"); }) .setAssemblyChecker((OutputAnalyzer out) -> { - // classes verified with fail-over mode should not be cached. - out.shouldMatch("class.* klasses.* VerifierFailOverApp"); - out.shouldNotMatch("class.* klasses.* VerifierFailOver_Helper"); + // Classes verified with fail-over can be cached if AOTClassLinking is on + out.shouldMatch("class.* klasses.* VerifierFailOverApp aot-linked"); + out.shouldMatch("class.* klasses.* VerifierFailOver_Helper aot-linked"); }) .runAOTWorkflow(); + + + // When running an assembly run without AOTClassLinking, any classes verified with + // fail-over need to be excluded. + Tester t = new Tester(); + t.runAOTWorkflow(); + } + + static class Tester extends CDSAppTester { + public Tester() { + super(mainClass); + } + + @Override + public String classpath(RunMode runMode) { + return appJar; + } + + @Override + public String[] vmArgs(RunMode runMode) { + if (runMode == RunMode.ASSEMBLY) { + return new String[] {"-XX:-AOTClassLinking", "-Xlog:aot,aot+class=debug"}; + } else { + return new String[] { "-Xlog:aot,aot+class=debug" }; + } + } + + @Override + public String[] appCommandLine(RunMode runMode) { + return new String[] { mainClass }; + } + + @Override + public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception { + if (runMode == RunMode.TRAINING) { + out.shouldMatch("class.* klasses.* VerifierFailOver_Helper"); + } else if (runMode == RunMode.ASSEMBLY) { + out.shouldContain("Skipping VerifierFailOver_Helper: Old class has been linked"); + out.shouldMatch("class.* klasses.* VerifierFailOverApp"); + out.shouldNotMatch("class.* klasses.* VerifierFailOver_Helper"); + } + } } } From 79568c915ae8c348b0fed271b949d21247770d13 Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Wed, 15 Jul 2026 15:02:05 +0000 Subject: [PATCH 237/707] 8384557: Allow configuration of the JVM's temporary directory on Linux Co-authored-by: Kevin Walls Co-authored-by: David Holmes Reviewed-by: dholmes, sspitsyn, jsjolen, kevinw --- src/hotspot/os/linux/globals_linux.hpp | 6 +- src/hotspot/os/linux/os_linux.cpp | 45 ++- src/hotspot/os/posix/attachListener_posix.cpp | 9 +- src/hotspot/os/posix/perfMemory_posix.cpp | 13 +- src/hotspot/share/runtime/arguments.cpp | 1 + src/hotspot/share/runtime/os.hpp | 6 + src/java.base/share/man/java.md | 17 ++ .../sun/tools/attach/VirtualMachineImpl.java | 16 +- .../native/libattach/VirtualMachineImpl.c | 41 ++- .../sun/jvmstat/PlatformSupportImpl.java | 53 ++-- src/jdk.jcmd/share/man/jcmd.md | 10 +- src/jdk.jcmd/share/man/jinfo.md | 4 + src/jdk.jcmd/share/man/jmap.md | 7 +- src/jdk.jcmd/share/man/jps.md | 5 +- src/jdk.jcmd/share/man/jstack.md | 5 + src/jdk.jcmd/share/man/jstat.md | 7 +- .../com/sun/tools/attach/JvmTempDirTest.java | 258 ++++++++++++++++++ .../jdk/com/sun/tools/attach/TempDirTest.java | 6 +- test/jdk/sun/tools/jps/TestJpsTempDir.java | 72 +++++ 19 files changed, 533 insertions(+), 48 deletions(-) create mode 100644 test/jdk/com/sun/tools/attach/JvmTempDirTest.java create mode 100644 test/jdk/sun/tools/jps/TestJpsTempDir.java diff --git a/src/hotspot/os/linux/globals_linux.hpp b/src/hotspot/os/linux/globals_linux.hpp index 90e1e5e5f3f..fa7b5a63c6c 100644 --- a/src/hotspot/os/linux/globals_linux.hpp +++ b/src/hotspot/os/linux/globals_linux.hpp @@ -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 @@ -94,6 +94,10 @@ " 0 = no timeout (default)") \ range(0,1000000) \ \ + product(ccstr, AltTempDir, nullptr, \ + "Alternate temporary directory for JVM files.") \ + \ + // end of RUNTIME_OS_FLAGS // diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index aad18edf2a6..12a4ea2bda4 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -112,6 +112,7 @@ # include # include # include +# include # include # include # include @@ -1547,11 +1548,47 @@ int os::current_process_id() { return ::getpid(); } -// DLL functions +static bool is_writable_directory(const char* name) { + struct stat mystat; + int ret_val = stat(name, &mystat); + return (ret_val != -1 && S_ISDIR(mystat.st_mode) > 0 && access(name, R_OK|W_OK|X_OK) == 0); +} + +// Check that a given alternate temporary directory name specifies an absolute path and is an existing, writable +// directory. + +// If it is not an absolute path, revert back to hardcoded /tmp. If the directory is non existant or not +// writable give a warning but use AltTempDir. In the latter case, we may be connecting to a process that is +// inside a container. +// +// Since the attach mechanism uses the socket name length, this limits the length of the alternate +// temporary directory name. We don't check that here since the temporary directory is +// used for many things. The perfData and attach code will check it. + +void os::pd_check_temp_directory() { + if (AltTempDir != nullptr && AltTempDir[0] != '\0') { + if (AltTempDir[0] != '/') { + log_warning(os)("Warning: AltTempDir is ignored because it must be an absolute pathname"); + AltTempDir = nullptr; + } else { + if (!is_writable_directory(AltTempDir)) { + // This is only a warning and still uses AltTempDir, which is needed to attach to a + // containerized process from the host. + log_warning(os)("Warning: AltTempDir is not an existing or writable directory"); + } + } + } else { + if (!is_writable_directory("/tmp")) { + log_warning(os)("Warning: /tmp is not writable. Consider using -XX:AltTempDir=/
    to set a writable temp directory"); + } + AltTempDir = nullptr; // avoid checking AltTempDir[0] again. + } +} -// This must be hard coded because it's the system's temporary -// directory not the java application's temp directory, ala java.io.tmpdir. -const char* os::get_temp_directory() { return "/tmp"; } +const char* os::get_temp_directory() { + // AltTempDir is already checked. + return AltTempDir != nullptr ? AltTempDir : "/tmp"; +} // check if addr is inside libjvm.so bool os::address_is_in_vm(address addr) { diff --git a/src/hotspot/os/posix/attachListener_posix.cpp b/src/hotspot/os/posix/attachListener_posix.cpp index a7cf1703128..152fd6140e0 100644 --- a/src/hotspot/os/posix/attachListener_posix.cpp +++ b/src/hotspot/os/posix/attachListener_posix.cpp @@ -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 @@ -201,6 +201,8 @@ int PosixAttachListener::init() { n = os::snprintf(initial_path, UNIX_PATH_MAX, "%s.tmp", path); } if (n >= (int)UNIX_PATH_MAX) { + log_warning(attach)("Failed to create temporary file for attach %s/.java_pid%d: file name is too long", + os::get_temp_directory(), os::current_process_id()); return -1; } @@ -346,8 +348,11 @@ void AttachListener::vm_start() { struct stat st; int ret; - os::snprintf_checked(fn, UNIX_PATH_MAX, "%s/.java_pid%d", + int n = os::snprintf(fn, UNIX_PATH_MAX, "%s/.java_pid%d", os::get_temp_directory(), os::current_process_id()); + if (n >= (int)UNIX_PATH_MAX) { + return; + } RESTARTABLE(::stat(fn, &st), ret); if (ret == 0) { diff --git a/src/hotspot/os/posix/perfMemory_posix.cpp b/src/hotspot/os/posix/perfMemory_posix.cpp index 300c86ffc47..aaeb33b6d9b 100644 --- a/src/hotspot/os/posix/perfMemory_posix.cpp +++ b/src/hotspot/os/posix/perfMemory_posix.cpp @@ -135,23 +135,25 @@ static void save_memory_to_file(char* addr, size_t size) { // return the user specific temporary directory name. // the caller is expected to free the allocated memory. // -#define TMP_BUFFER_LEN (4+22) static char* get_user_tmp_dir(const char* user, int vmid, int nspid) { char* tmpdir = (char *)os::get_temp_directory(); + char buffer[PATH_MAX] = {0}; #if defined(LINUX) // On linux, if containerized process, get dirname of // /proc/{vmid}/root/tmp/{PERFDATA_NAME_user} // otherwise /tmp/{PERFDATA_NAME_user} - char buffer[TMP_BUFFER_LEN]; - assert(strlen(tmpdir) == 4, "No longer using /tmp - update buffer size"); + // The /tmp directory can be overridden with AltTempDir. if (nspid != -1) { - jio_snprintf(buffer, TMP_BUFFER_LEN, "/proc/%d/root%s", vmid, tmpdir); + int val = os::snprintf(buffer, PATH_MAX, "/proc/%d/root%s", vmid, tmpdir); + if (val >= (int)PATH_MAX) { + log_warning(perf)("The temporary directory for perf data /proc/%d/root%s name is truncated", + vmid, tmpdir); + } tmpdir = buffer; } #endif #ifdef __APPLE__ - char buffer[PATH_MAX] = {0}; // Check if the current user is root and the target VM is running as non-root. // Otherwise the output of os::get_temp_directory() is used. // @@ -524,7 +526,6 @@ static char* get_user_name_slow(int vmid, int nspid, TRAPS) { char* tmpdirname = (char *)os::get_temp_directory(); #if defined(LINUX) char buffer[MAXPATHLEN + 1]; - assert(strlen(tmpdirname) == 4, "No longer using /tmp - update buffer size"); // On Linux, if nspid != -1, look in /proc/{vmid}/root/tmp for directories // containing nspid, otherwise just look for vmid in /tmp. diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 269a8b39e6b..b08e71f559a 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -1703,6 +1703,7 @@ jint Arguments::parse_vm_init_args(GrowableArrayCHeap. and .attach_pid. It is important that this // location is the same for all processes, otherwise the tools // will not be able to find all Hotspot processes. - // Any changes to this needs to be synchronized with HotSpot. - private static final Path TMPDIR = Path.of("/tmp"); + // This calls a Hotspot native method to get a consistent temporary + // directory. + private static final String vmTemp = PlatformSupport.getTemporaryDirectory(); + private static final Path TMPDIR = Path.of(vmTemp); private static final Path PROC = Path.of("/proc"); private static final Path STATUS = Path.of("status"); - private static final Path ROOT_TMP = Path.of("root/tmp"); String socket_path; private OperationProperties props = new OperationProperties(VERSION_1); // updated in ctor @@ -86,6 +88,9 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine { // Then we attempt to find the socket file again. final File socket_file = findSocketFile(pid, ns_pid); socket_path = socket_file.getPath(); + if (!validateSocketFileLength(socket_file.getPath())) { + throw new AttachNotSupportedException("Socket file path too long: " + socket_path); + } if (!socket_file.exists()) { // Keep canonical version of File, to delete, in case target process ends and /proc link has gone: File f = createAttachFile(pid, ns_pid).getCanonicalFile(); @@ -255,7 +260,8 @@ private File createAttachFile(long pid, long ns_pid) throws AttachNotSupportedEx } private String findTargetProcessTmpDirectory(long pid) throws IOException { - final var tmpOnProcPidRoot = PROC.resolve(Long.toString(pid)).resolve(ROOT_TMP); + final var tmpOnProcPidRoot = PROC.resolve(Long.toString(pid)).resolve("root") + .resolve(vmTemp.startsWith("/") ? vmTemp.substring(1) : vmTemp); /* We need to handle at least 4 different cases: * 1. Caller and target processes share PID namespace and root @@ -429,6 +435,8 @@ private static boolean checkCatchesAndSendQuitTo(int pid, boolean throwIfNotRead static native void write(int fd, byte buf[], int off, int bufLen) throws IOException; + static native boolean validateSocketFileLength(String socketPath); + static { System.loadLibrary("attach"); } diff --git a/src/jdk.attach/linux/native/libattach/VirtualMachineImpl.c b/src/jdk.attach/linux/native/libattach/VirtualMachineImpl.c index fc9af901835..df4fc54cc96 100644 --- a/src/jdk.attach/linux/native/libattach/VirtualMachineImpl.c +++ b/src/jdk.attach/linux/native/libattach/VirtualMachineImpl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, 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 @@ -76,11 +76,15 @@ JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_connect memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; - /* strncpy is safe because addr.sun_path was zero-initialized before. */ - strncpy(addr.sun_path, p, sizeof(addr.sun_path) - 1); + if (strlen(p) >= sizeof(addr.sun_path)) { + JNU_ThrowIOException(env, "Socket file path too long"); + } else { + /* strncpy is safe because addr.sun_path was zero-initialized before. */ + strncpy(addr.sun_path, p, sizeof(addr.sun_path) - 1); - if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { - err = errno; + if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { + err = errno; + } } if (isCopy) { @@ -256,3 +260,30 @@ JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_write } while (remaining > 0); } + +/* + * Class: sun_tools_attach_VirtualMachineImpl + * Method: validateSocketFileLength + * Signature: (Ljava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_sun_tools_attach_VirtualMachineImpl_validateSocketFileLength + (JNIEnv *env, jclass cls, jstring path) +{ + jboolean isCopy; + const char* p = GetStringPlatformChars(env, path, &isCopy); + if (p == NULL) { + JNU_ThrowIOException(env, "Socket file path is null"); + return JNI_FALSE; + } + + size_t pathLength = strlen(p); + + if (isCopy) { + JNU_ReleaseStringPlatformChars(env, path, p); + } + + struct sockaddr_un addr; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + return pathLength < sizeof(addr.sun_path); +} diff --git a/src/jdk.internal.jvmstat/linux/classes/sun/jvmstat/PlatformSupportImpl.java b/src/jdk.internal.jvmstat/linux/classes/sun/jvmstat/PlatformSupportImpl.java index d2c0fa29877..c0733e65a74 100644 --- a/src/jdk.internal.jvmstat/linux/classes/sun/jvmstat/PlatformSupportImpl.java +++ b/src/jdk.internal.jvmstat/linux/classes/sun/jvmstat/PlatformSupportImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -75,6 +75,7 @@ private boolean tempDirectoryEquals(Path p) { * It is important that this directory is well-known and the * same for all VM instances. It cannot be affected by configuration * variables such as java.io.tmpdir. + * It can be affected by VM option -XX:AltTempDir, however. * * Implementation Details: * @@ -170,8 +171,8 @@ public boolean accept(File dir, String name) { /* - * Extract either the host PID or the NameSpace PID - * from a file path. + * Extract the VM ID (pid) from a file path, + * specifically the host pid for a container process. * * File path should be in 1 of these 2 forms: * @@ -179,6 +180,8 @@ public boolean accept(File dir, String name) { * or * /tmp/hsperfdata_{user}/{pid} * + * (where /tmp may be substituted due to -XX:AltTempDir) + * * In either case we want to return {pid} and NOT {nspid} * * This function filters out host pids which do not have @@ -189,24 +192,38 @@ public boolean accept(File dir, String name) { */ public int getLocalVmId(File file) throws NumberFormatException { String p = file.getAbsolutePath(); - String s[] = p.split("\\/"); - - // Determine if this file is from a container - if (s.length == 7 && s[1].equals("proc")) { - int hostpid = Integer.parseInt(s[2]); - int nspid = Integer.parseInt(s[6]); - if (nspid == hostpid || nspid == getNamespaceVmId(hostpid)) { - return hostpid; - } - else { - return -1; - } + String procParts[] = p.split("\\/"); // "/proc/hostpid/root//hsperfdata_user/nsid" + + int hostpid = -1; + int nspid = -1; + + // ["", "proc", "hostpid", "root", "tmpdir" .. "tmpdir", "hsperfdata_user", "nsid"] + if (procParts.length > 4 && procParts[1].equals("proc") && procParts[3].equals("root")) { + hostpid = Integer.parseInt(procParts[2]); } - else { - return Integer.parseInt(file.getName()); + + // Some invalid path. + if (procParts.length < 2) { + return -1; } - } + // Path at the end after tmp dir is: "hsperfdata_username/PID" + int end = procParts.length - 1; + if (!procParts[end-1].startsWith("hsperfdata_")) { + return -1; + } + if (hostpid == -1) { + hostpid = Integer.parseInt(procParts[end]); + } else { + nspid = Integer.parseInt(procParts[end]); + } + if (nspid == -1) { + return hostpid; + } else { + // We have both pids. + return nspid == getNamespaceVmId(hostpid) ? hostpid : -1; + } + } /* * Return the inner most namespaced PID if there is one, diff --git a/src/jdk.jcmd/share/man/jcmd.md b/src/jdk.jcmd/share/man/jcmd.md index 23dfa67d864..97e7385138b 100644 --- a/src/jdk.jcmd/share/man/jcmd.md +++ b/src/jdk.jcmd/share/man/jcmd.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2012, 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 @@ -77,8 +77,12 @@ jcmd - send diagnostic command requests to a running Java Virtual Machine The `jcmd` utility is used to send diagnostic command requests to the JVM. It must be used on the same machine on which the JVM is running, and have the same -effective user and group identifiers that were used to launch the JVM. Each -diagnostic command has its own set of options and arguments. To display the description, +effective user and group identifiers that were used to launch the JVM. Both must +use the same temporary file location for communication; this is true by default +but also see the [`-XX:AltTempDir`](./java.html#-XX_AltTempDir) option that can be +set for the JVM. + +Each diagnostic command has its own set of options and arguments. To display the description, syntax, and a list of available options and arguments for a diagnostic command, use the name of the command as the argument. For example: diff --git a/src/jdk.jcmd/share/man/jinfo.md b/src/jdk.jcmd/share/man/jinfo.md index 8365c5af8a5..cc582aa9d5c 100644 --- a/src/jdk.jcmd/share/man/jinfo.md +++ b/src/jdk.jcmd/share/man/jinfo.md @@ -59,6 +59,10 @@ environment variable should contain the location of the `jvm.dll` that's used by the target process or the location from which the core dump file was produced. +If the target JVM is started with an alternate temporary file location, `jinfo` must +use the same temporary file location for communication; this is true by default +but see the [`-XX:AltTempDir`](./java.html#-XX_AltTempDir) option. + ## Options for the jinfo Command **Note:** diff --git a/src/jdk.jcmd/share/man/jmap.md b/src/jdk.jcmd/share/man/jmap.md index dd0be1b24ef..2fe766c25b5 100644 --- a/src/jdk.jcmd/share/man/jmap.md +++ b/src/jdk.jcmd/share/man/jmap.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 2004, 2024, 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 @@ -60,6 +60,11 @@ Debugging Tools for Windows must be installed to make these tools work. The that's used by the target process or the location from which the core dump file was produced. +If the target JVM is started with an alternate temporary file location, `jmap` must +use the same temporary file location for communication; this is true by default +but see the [`-XX:AltTempDir`](./java.html#-XX_AltTempDir) option. + + ## Options for the jmap Command [`-clstats`]{#option-clstats} *pid* diff --git a/src/jdk.jcmd/share/man/jps.md b/src/jdk.jcmd/share/man/jps.md index 2db93878801..cdd5e8e8b9d 100644 --- a/src/jdk.jcmd/share/man/jps.md +++ b/src/jdk.jcmd/share/man/jps.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 2004, 2024, 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 @@ -99,6 +99,9 @@ permissions granted to the principal running the command. The command lists only the JVMs for which the principal has access rights as determined by operating system-specific access control mechanisms. +The list of JVMs is also limited to those that use the same temporary file location as the `jps` +command. That is normally the case but see the [`-XX:AltTempDir`](./java.html#-XX_AltTempDir) option. + ## Host Identifier The host identifier, or `hostid`, is a string that indicates the target system. diff --git a/src/jdk.jcmd/share/man/jstack.md b/src/jdk.jcmd/share/man/jstack.md index 2e95abf36c4..b2cf02c6eb5 100644 --- a/src/jdk.jcmd/share/man/jstack.md +++ b/src/jdk.jcmd/share/man/jstack.md @@ -63,6 +63,11 @@ Debugging Tools for Windows must be installed so that these tools work. The is used by the target process, or the location from which the core dump file was produced. +If the target JVM is started with an alternate temporary file location, `jstack` must +use the same temporary file location for communication; this is true by default +but see the [`-XX:AltTempDir`](./java.html#-XX_AltTempDir) option. + + ## Options for the jstack Command `-l` diff --git a/src/jdk.jcmd/share/man/jstat.md b/src/jdk.jcmd/share/man/jstat.md index 624b675de76..4b686f73810 100644 --- a/src/jdk.jcmd/share/man/jstat.md +++ b/src/jdk.jcmd/share/man/jstat.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 2004, 2024, 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 @@ -85,6 +85,11 @@ statistical output. All options and their functionality are subject to change or removal in future releases. +If the target JVM is started with an alternate temporary file location, `jstat` must +use the same temporary file location for communication; this is true by default +but see the [`-XX:AltTempDir`](./java.html#-XX_AltTempDir) option. + + ## General Options If you specify one of the general options, then you can't specify any other diff --git a/test/jdk/com/sun/tools/attach/JvmTempDirTest.java b/test/jdk/com/sun/tools/attach/JvmTempDirTest.java new file mode 100644 index 00000000000..6729de149a6 --- /dev/null +++ b/test/jdk/com/sun/tools/attach/JvmTempDirTest.java @@ -0,0 +1,258 @@ +/* + * 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. + */ + +import com.sun.tools.attach.*; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Properties; +import java.util.List; +import java.io.File; + +import jdk.test.lib.thread.ProcessThread; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +/* + * @test + * @bug 8384557 + * @summary Test to make sure attach and jvmstat work correctly when -XX:AltTempDir is set. + * + * @requires os.family == "linux" + * @library /test/lib + * @modules jdk.attach + * jdk.jartool/sun.tools.jar + * + * @run build Application RunnerUtil + * @run main/timeout=200 JvmTempDirTest + */ + +/* + * This test is similar to TempDirTest.java. The property java.io.tmpdir does not affect how + * jdk.attach works, but -XX:AltTempDir does. + * + * This test runs with an extra long timeout since it takes a really long time with -Xcomp + * when starting many processes. + */ + +import jdk.test.lib.util.FileUtils; + +public class JvmTempDirTest { + + private static long startTime; + + public static void main(String args[]) throws Throwable { + + startTime = System.currentTimeMillis(); + + Path clientTmpDir = Files.createTempDirectory(Path.of("/tmp"), "c"); + Path targetTmpDir = Files.createTempDirectory(Path.of("/tmp"), "t"); + + try { + // Run the test with all possible combinations of setting AltTempDir. + // Different setting will cause the attach mechanism to fail. + String notFound = "not found in VM list"; + runExperiment(null, null, true, null); + runExperiment(targetTmpDir, targetTmpDir, true, null); + runExperiment(clientTmpDir, clientTmpDir, true, null); + + runExperiment(clientTmpDir, null, false, notFound); + runExperiment(clientTmpDir, targetTmpDir, false, notFound); + runExperiment(null, targetTmpDir, false, notFound); + } finally { + FileUtils.deleteFileTreeWithRetry(clientTmpDir); + FileUtils.deleteFileTreeWithRetry(targetTmpDir); + } + + String name = String.valueOf('a').repeat(200); + Path veryLongDir = Files.createTempDirectory(Path.of("/tmp"), name); + try { + runExperiment(veryLongDir, veryLongDir, false, "Socket file path too long"); + } finally { + FileUtils.deleteFileTreeWithRetry(veryLongDir); + } + + // Test a directory with only proc in one part of the name. + Path procTempDir = Files.createTempDirectory(Path.of("/tmp"), "proc"); + Path procDir = Files.createDirectory(procTempDir.resolve("proc")); + try { + runExperiment(procDir, procDir, true, null); + } finally { + FileUtils.deleteFileTreeWithRetry(procDir); + FileUtils.deleteFileTreeWithRetry(procTempDir); + } + + Path hsperfDir = Files.createTempDirectory(Path.of("/tmp"), "hsperfdata_"); + try { + runExperiment(hsperfDir, hsperfDir, true, null); + } finally { + FileUtils.deleteFileTreeWithRetry(hsperfDir); + } + + // Create /tmp/tmp, and try to use /tmp/tmp/noexist + Path tmpDir = Files.createTempDirectory(Path.of("/tmp"), "tmp"); + try { + Path noExist = tmpDir.resolve("noexist"); + runNoExistTest(noExist); + } finally { + FileUtils.deleteFileTreeWithRetry(tmpDir); + } + + Path relativeDir = Files.createTempDirectory(Path.of("."), "a"); + try { + runRelativeTest(relativeDir); + } finally { + FileUtils.deleteFileTreeWithRetry(relativeDir); + } + } + + /* + * The actual test is in the nested class TestMain. + * The responsibility of this class is to: + * 1. Start the Application class in a separate process. + * 2. Find the pid and shutdown port of the running Application. + * 3. Launch the tests in nested class TestMain that will attach to the Application. + * 4. Shut down the Application. + */ + public static void runExperiment(Path clientTmpDir, Path targetTmpDir, boolean shouldPass, String message) throws Throwable { + + System.out.print("### Running tests with overridden tmpdir for"); + System.out.print(" client: " + (clientTmpDir == null ? "no" : "yes")); + System.out.print(" target: " + (targetTmpDir == null ? "no" : "yes")); + System.out.println(" ###"); + + long elapsedTime = (System.currentTimeMillis() - startTime) / 1000; + System.out.println("Started after " + elapsedTime + "s"); + + ProcessThread processThread = null; + try { + String[] tmpDirArg = null; + if (targetTmpDir != null) { + tmpDirArg = new String[] {"-XX:AltTempDir=" + targetTmpDir}; + } + processThread = RunnerUtil.startApplication(tmpDirArg); + launchTests(processThread.getPid(), clientTmpDir, shouldPass, message); + } catch (Throwable t) { + System.out.println("JvmTempDirTest got unexpected exception: " + t); + t.printStackTrace(); + throw t; + } finally { + // Make sure the Application process is stopped. + RunnerUtil.stopApplication(processThread); + } + + elapsedTime = (System.currentTimeMillis() - startTime) / 1000; + System.out.println("Completed after " + elapsedTime + "s"); + + } + + /** + * Runs the actual tests in nested class TestMain. + * The reason for running the tests in a separate process + * is that we need to modify the class path and + * the -XX:AltTempDir argument. + */ + private static void launchTests(long pid, Path clientTmpDir, boolean shouldPass, String message) throws Throwable { + + String classpath = + System.getProperty("test.class.path", ""); + + String[] tmpDirArg = null; + if (clientTmpDir != null) { + tmpDirArg = new String [] {"-XX:AltTempDir=" + clientTmpDir}; + } + + // Arguments : [-XX:AltTempDir=] -classpath cp JvmTempDirTest$TestMain pid + String[] args = RunnerUtil.concat( + tmpDirArg, + new String[] { + "-classpath", + classpath, + "JvmTempDirTest$TestMain", + Long.toString(pid) }); + OutputAnalyzer output = ProcessTools.executeTestJava(args); + if (shouldPass) { + output.shouldHaveExitValue(0); + } else { + output.shouldContain(message); + output.shouldNotHaveExitValue(0); + } + } + + /** + * This is the actual test. It will attach to the running Application + * and perform a number of basic attach tests. + */ + public static class TestMain { + public static void main(String args[]) throws Exception { + String pid = args[0]; + + // Test 1 - list method should list the target VM + System.out.println(" - Test: VirtualMachine.list"); + List l = VirtualMachine.list(); + boolean found = false; + for (VirtualMachineDescriptor vmd: l) { + if (vmd.id().equals(pid)) { + found = true; + break; + } + } + if (found) { + System.out.println(" - " + pid + " found."); + } else { + throw new RuntimeException(pid + " not found in VM list"); + } + + // Test 2 - try to attach and verify connection + + System.out.println(" - Attaching to application ..."); + VirtualMachine vm = VirtualMachine.attach(pid); + + System.out.println(" - Test: system properties in target VM"); + Properties props = vm.getSystemProperties(); + String value = props.getProperty("attach.test"); + if (value == null || !value.equals("true")) { + throw new RuntimeException("attach.test property not set"); + } + System.out.println(" - attach.test property set as expected"); + } + } + + private static void runNoExistTest(Path tmpDir) throws Throwable { + // Arguments : [-XX:AltTempDir=] -version + String[] args = new String[] { "-XX:AltTempDir=" + tmpDir, "-version" }; + OutputAnalyzer output = ProcessTools.executeTestJava(args); + output.shouldMatch("\\[warning\\]\\[os *\\] Warning: AltTempDir is not an existing or writable directory"); + // Still passes, it's just a warning. + output.shouldHaveExitValue(0); + } + + private static void runRelativeTest(Path tmpDir) throws Throwable { + // Arguments : [-XX:AltTempDir=] -version + String[] args = new String[] { "-XX:AltTempDir=" + tmpDir, "-version" }; + OutputAnalyzer output = ProcessTools.executeTestJava(args); + output.shouldMatch("\\[warning\\]\\[os *\\] Warning: AltTempDir is ignored because it must be an absolute pathname"); + // Still passes, it's just a warning. + output.shouldHaveExitValue(0); + } +} diff --git a/test/jdk/com/sun/tools/attach/TempDirTest.java b/test/jdk/com/sun/tools/attach/TempDirTest.java index e0552d15fce..14b65207da9 100644 --- a/test/jdk/com/sun/tools/attach/TempDirTest.java +++ b/test/jdk/com/sun/tools/attach/TempDirTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, 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 @@ -64,7 +64,9 @@ public static void main(String args[]) throws Throwable { Path targetTmpDir = Files.createTempDirectory("TempDirTest-target"); targetTmpDir.toFile().deleteOnExit(); - // run the test with all possible combinations of setting java.io.tmpdir + // Run the test with all possible combinations of setting java.io.tmpdir. + // Note that the attach mechanism doesn't really use java.io.tmpdir, but this test verifies + // that different java.io.tmpdir settings for client and target don't break the attach mechanism. runExperiment(null, null); runExperiment(clientTmpDir, null); runExperiment(clientTmpDir, targetTmpDir); diff --git a/test/jdk/sun/tools/jps/TestJpsTempDir.java b/test/jdk/sun/tools/jps/TestJpsTempDir.java new file mode 100644 index 00000000000..b54fedad2b8 --- /dev/null +++ b/test/jdk/sun/tools/jps/TestJpsTempDir.java @@ -0,0 +1,72 @@ +/* + * 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 8384557 + * @summary Test to make sure jps works correctly when -XX:AltTempDir is set. + * @library /test/lib + * @requires os.family == "linux" + * @modules jdk.jartool/sun.tools.jar + * @build jdk.test.lib.apps.LingeredApp + * @run main/othervm TestJpsTempDir + */ + +// Test that jps finds hsperfdata file in -XX:AltTempDir. + +import jdk.test.lib.apps.LingeredApp; +import java.util.ArrayList; +import java.util.List; +import java.nio.file.Path; +import java.nio.file.Files; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.util.FileUtils; + +public class TestJpsTempDir { + + public static void main(java.lang.String[] unused) throws Exception { + Path clientTmpDir = Files.createTempDirectory(Path.of("/tmp"), "c"); + String tmpdirString = "-XX:AltTempDir=" + clientTmpDir.toString(); + + LingeredAppForJps app = new LingeredAppForJps(); + + try { + // Start LingeredApp with AltTempDir + List vmArgs = new ArrayList<>(List.of(JpsHelper.getVmArgs())); + vmArgs.add(tmpdirString); + LingeredApp.startApp(app, vmArgs.toArray(String[]::new)); + + // Pass to jps (adds -J) + List jpsArgs = new ArrayList<>(); + jpsArgs.add(tmpdirString); + + OutputAnalyzer output = JpsHelper.jps(jpsArgs, null); + output.shouldContain(app.getProcessName()); + output.shouldContain(Long.toString(app.getPid())); + output.shouldHaveExitValue(0); + } finally { + LingeredApp.stopApp(app); + FileUtils.deleteFileTreeWithRetry(clientTmpDir); + } + } +} From c6a068a5ee0fcd6719b85103ead1e3fee2b8b42b Mon Sep 17 00:00:00 2001 From: Naoto Sato Date: Wed, 15 Jul 2026 16:15:12 +0000 Subject: [PATCH 238/707] 8388183: Wrong example in documentation for String.toLowerCase() Reviewed-by: jlu, iris --- src/java.base/share/classes/java/lang/String.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java index 9f56ceb445a..e3d120c23f6 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -4054,7 +4054,7 @@ public static String join(CharSequence delimiter, * (all) * * ΙΧΘΥΣ - * ιχθυσ + * ιχθυς * lowercased all chars in String * * From 96737cf5104d3ffc9a4c96e66e132d4b16a79c31 Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Wed, 15 Jul 2026 16:18:00 +0000 Subject: [PATCH 239/707] 8387261: Locale.LanguageRange weight validation issues Reviewed-by: naoto --- .../share/classes/java/util/Locale.java | 28 +++++++++++-------- .../sun/util/locale/LocaleMatcher.java | 17 +++++------ .../java/util/Locale/LocaleMatchingTest.java | 4 ++- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/src/java.base/share/classes/java/util/Locale.java b/src/java.base/share/classes/java/util/Locale.java index f727d301954..9cb1521ac62 100644 --- a/src/java.base/share/classes/java/util/Locale.java +++ b/src/java.base/share/classes/java/util/Locale.java @@ -3214,18 +3214,20 @@ public LanguageRange(String range) { * * @param range a language range * @param weight a weight value between {@code MIN_WEIGHT} and - * {@code MAX_WEIGHT} + * {@code MAX_WEIGHT}, inclusive * @throws NullPointerException if the given {@code range} is * {@code null} * @throws IllegalArgumentException if the given {@code range} does not - * comply with the syntax of the language range mentioned in RFC 4647 - * or if the given {@code weight} is less than {@code MIN_WEIGHT} - * or greater than {@code MAX_WEIGHT} + * comply with the syntax of the language range mentioned in RFC 4647, + * or if the given {@code weight} is {@code Double.NaN}, less than {@code + * MIN_WEIGHT} or greater than {@code MAX_WEIGHT} */ public LanguageRange(String range, double weight) { Objects.requireNonNull(range); - if (weight < MIN_WEIGHT || weight > MAX_WEIGHT) { - throw new IllegalArgumentException("weight=" + weight); + if (weight < MIN_WEIGHT || weight > MAX_WEIGHT || Double.isNaN(weight)) { + throw new IllegalArgumentException( + "The weight " + weight + " must be between " + + MIN_WEIGHT + " and " + MAX_WEIGHT + ", inclusive."); } range = range.toLowerCase(Locale.ROOT); @@ -3311,9 +3313,9 @@ public double getWeight() { * * * In a weighted list, each language range is given a weight value. - * The weight value is identical to the "quality value" in + * The weight value has the same numeric bounds as the "quality value" * RFC 2616, and it - * expresses how much the user prefers the language. A weight value is + * expresses how much the user prefers the language. A weight value is * specified after a corresponding language range followed by * {@code ";q="}, and the default weight value is {@code MAX_WEIGHT} * when it is omitted. @@ -3356,8 +3358,9 @@ public double getWeight() { * included in the given {@code ranges} and their equivalent * language ranges if available. The list is modifiable. * @throws NullPointerException if {@code ranges} is null - * @throws IllegalArgumentException if a language range or a weight - * found in the given {@code ranges} is ill-formed + * @throws IllegalArgumentException if, in the given {@code ranges}, a + * language range is ill-formed, or a weight is out of range after + * string to double conversion by {@link Double#parseDouble(String)} * @spec https://www.rfc-editor.org/info/rfc2616 RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1 */ public static List parse(String ranges) { @@ -3378,8 +3381,9 @@ public static List parse(String ranges) { * @return a Language Priority List with customization. The list is * modifiable. * @throws NullPointerException if {@code ranges} is null - * @throws IllegalArgumentException if a language range or a weight - * found in the given {@code ranges} is ill-formed + * @throws IllegalArgumentException if, in the given {@code ranges}, a + * language range is ill-formed, or a weight is out of range after + * string to double conversion by {@link Double#parseDouble(String)} * @spec https://www.rfc-editor.org/info/rfc2616 RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1 * @see #parse(String) * @see #mapEquivalents(List, Map) diff --git a/src/java.base/share/classes/sun/util/locale/LocaleMatcher.java b/src/java.base/share/classes/sun/util/locale/LocaleMatcher.java index bc5115e1ff1..5385a5598b6 100644 --- a/src/java.base/share/classes/sun/util/locale/LocaleMatcher.java +++ b/src/java.base/share/classes/sun/util/locale/LocaleMatcher.java @@ -467,17 +467,18 @@ public static List parse(String ranges) { try { w = Double.parseDouble(range.substring(index)); } - catch (Exception e) { - throw new IllegalArgumentException("weight=\"" + catch (NumberFormatException _) { + throw new IllegalArgumentException("The weight \"" + range.substring(index) - + "\" for language range \"" + r + "\""); + + "\" for language range \"" + r + "\"" + + " must be between " + MIN_WEIGHT + + " and " + MAX_WEIGHT + ", inclusive."); } - if (w < MIN_WEIGHT || w > MAX_WEIGHT) { - throw new IllegalArgumentException("weight=" + w - + " for language range \"" + r - + "\". It must be between " + MIN_WEIGHT - + " and " + MAX_WEIGHT + "."); + throw new IllegalArgumentException("The weight \"" + w + + "\" for language range \"" + r + "\"" + + " must be between " + MIN_WEIGHT + + " and " + MAX_WEIGHT + ", inclusive."); } } diff --git a/test/jdk/java/util/Locale/LocaleMatchingTest.java b/test/jdk/java/util/Locale/LocaleMatchingTest.java index c5d8a00d458..806229ca8eb 100644 --- a/test/jdk/java/util/Locale/LocaleMatchingTest.java +++ b/test/jdk/java/util/Locale/LocaleMatchingTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 7069824 8042360 8032842 8175539 8210443 8242010 8276302 8381644 + * @bug 7069824 8042360 8032842 8175539 8210443 8242010 8276302 8381644 8387261 * @summary Verify implementation for Locale matching. * @run junit/othervm LocaleMatchingTest */ @@ -93,6 +93,7 @@ static Object[][] LRConstructorIAEData() { {"1996-de-Latn", MAX_WEIGHT}, // Testcase for 8042360 {"en-Latn-1234567890", MAX_WEIGHT}, + {"en", Double.NaN}, }; } @@ -146,6 +147,7 @@ static Object[][] LRParseIAEData() { // Ranges {""}, {"ja;q=3"}, + {"en;q=NaN"} }; } From 41a188f1d128298cb155c40681395df83916c469 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Wed, 15 Jul 2026 16:39:12 +0000 Subject: [PATCH 240/707] 8387463: Shenandoah: Use direct oop_oop_iterate methods for known types in marking loops Reviewed-by: wkemper, xpeng, kdnilsen --- .../share/gc/shenandoah/shenandoahMark.cpp | 16 +++- .../share/gc/shenandoah/shenandoahMark.hpp | 10 +-- .../gc/shenandoah/shenandoahMark.inline.hpp | 80 ++++++++++++------- src/hotspot/share/oops/instanceRefKlass.hpp | 10 +++ 4 files changed, 80 insertions(+), 36 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp index fc508dddd84..9354ef25f3d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp @@ -73,11 +73,19 @@ void ShenandoahMark::mark_loop_prework(uint w, TaskTerminator *t, StringDedup::R if (update_refs) { using Closure = ShenandoahMarkUpdateRefsClosure; Closure cl(q, rp, old_q); - mark_loop_work(&cl, ld, w, t, req); + if (UseCompressedOops) { + mark_loop_work(&cl, ld, w, t, req); + } else { + mark_loop_work(&cl, ld, w, t, req); + } } else { using Closure = ShenandoahMarkRefsClosure; Closure cl(q, rp, old_q); - mark_loop_work(&cl, ld, w, t, req); + if (UseCompressedOops) { + mark_loop_work(&cl, ld, w, t, req); + } else { + mark_loop_work(&cl, ld, w, t, req); + } } heap->flush_liveness_cache(w); @@ -154,7 +162,7 @@ void ShenandoahMark::mark_drain_extra_queues(ShenandoahObjToScanQueueSet* queues } } -template +template void ShenandoahMark::mark_loop_work(T* cl, ShenandoahLiveData* live_data, uint worker_id, TaskTerminator *terminator, StringDedup::Requests* const req) { uintx stride = ShenandoahMarkLoopStride; @@ -182,7 +190,7 @@ void ShenandoahMark::mark_loop_work(T* cl, ShenandoahLiveData* live_data, uint w for (uint i = 0; i < stride; i++) { if (q->pop(t) || queues->steal(worker_id, t)) { - do_task(q, cl, live_data, req, &t, worker_id); + do_task(q, cl, live_data, req, &t, worker_id); work++; } else { break; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp index 69d792d0277..a2c363b2129 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp @@ -72,17 +72,17 @@ class ShenandoahMark: public StackObj { private: // ---------- Marking loop and tasks - template + template ALWAYSINLINE static void do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveData* live_data, StringDedup::Requests* const req, ShenandoahMarkTask* task, uint worker_id); - template + template ALWAYSINLINE static void do_chunked_array_start(ShenandoahObjToScanQueue* q, T* cl, oop array, Klass* klass, bool weak); - template + template ALWAYSINLINE - static void do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, oop array, int chunk, int pow, bool weak); + static void do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, oop array, Klass* klass, int chunk, int pow, bool weak); template ALWAYSINLINE @@ -105,7 +105,7 @@ class ShenandoahMark: public StackObj { template void mark_loop_prework(uint worker_id, TaskTerminator *terminator, StringDedup::Requests* const req, bool update_refs); - template + template NOINLINE // Main hot loop, start inlining from here void mark_loop_work(T* cl, ShenandoahLiveData* live_data, uint worker_id, TaskTerminator *t, StringDedup::Requests* const req); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp index 8a7ce7ea831..45cec71935b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp @@ -47,7 +47,7 @@ #include "utilities/devirtualizer.inline.hpp" #include "utilities/powerOfTwo.hpp" -template +template void ShenandoahMark::do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveData* live_data, StringDedup::Requests* const req, ShenandoahMarkTask* task, uint worker_id) { oop obj = task->obj(); @@ -55,32 +55,58 @@ void ShenandoahMark::do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveD shenandoah_assert_marked(nullptr, obj); shenandoah_assert_not_in_cset_except(nullptr, obj, ShenandoahHeap::heap()->cancelled_gc()); + Klass* klass = obj->klass(); + // Are we in weak subgraph scan? bool weak = task->is_weak(); cl->set_weak(weak); if (task->is_not_chunked()) { - Klass* klass = obj->klass(); - if (klass->is_instance_klass()) { - // Case 1: Normal oop, process as usual. - if (STRING_DEDUP && (klass == vmClasses::String_klass())) { - dedup_string(obj, req); + // Dispatch based on object type. The case order does not seem to affect performance, + // so it matches the enum order for consistency. + switch (klass->kind()) { + case Klass::InstanceKlassKind: { + // Regular instance. + if (STRING_DEDUP && (klass == vmClasses::String_klass())) { + dedup_string(obj, req); + } + InstanceKlass::cast(klass)->oop_oop_iterate(obj, cl); + break; + } + case Klass::InstanceRefKlassKind: { + // (Weak) reference instance. + InstanceRefKlass::cast(klass)->oop_oop_iterate(obj, cl); + break; } - if (klass->is_stack_chunk_instance_klass()) { - // Loom doesn't support mixing of weak marking and strong marking of stack chunks. + case Klass::InstanceMirrorKlassKind: + case Klass::InstanceClassLoaderKlassKind: { + // Remaining rare classes, dispatch generically. + obj->oop_iterate(cl); + break; + } + case Klass::InstanceStackChunkKlassKind: { + // Stack chunk. Loom doesn't support mixing of weak marking and strong marking + // of stack chunks, upgrade to strong right away. cl->set_weak(false); + InstanceStackChunkKlass::cast(klass)->oop_oop_iterate(obj, cl); + break; + } + case Klass::TypeArrayKlassKind: { + // Primitive array. Do nothing, no oops there. We use the same + // performance tweak TypeArrayKlass::oop_oop_iterate_impl is using: + // We skip iterating over the klass pointer since we know that + // Universe::TypeArrayKlass never moves. + break; + } + case Klass::ObjArrayKlassKind: { + // Object array and no chunk is set. Must be the first + // time we visit it, start the chunked processing. + do_chunked_array_start(q, cl, obj, klass, weak); + break; + } + default: { + fatal("Unknown klass kind: %d", klass->kind()); } - obj->oop_iterate(cl); - } else if (klass->is_objArray_klass()) { - // Case 2: Object array instance and no chunk is set. Must be the first - // time we visit it, start the chunked processing. - do_chunked_array_start(q, cl, obj, klass, weak); - } else { - // Case 3: Primitive array. Do nothing, no oops there. We use the same - // performance tweak TypeArrayKlass::oop_oop_iterate_impl is using: - // We skip iterating over the klass pointer since we know that - // Universe::TypeArrayKlass never moves. - assert(klass->is_typeArray_klass(), "should be type array"); } // Count liveness the last: push the outstanding work to the queues first // Avoid double-counting objects that are visited twice due to upgrade @@ -89,8 +115,8 @@ void ShenandoahMark::do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveD count_liveness(live_data, obj, klass, worker_id); } } else { - // Case 4: Array chunk, has sensible chunk id. Process it. - do_chunked_array(q, cl, obj, task->chunk(), task->pow(), weak); + // Object array chunk. Process it. + do_chunked_array(q, cl, obj, klass, task->chunk(), task->pow(), weak); } } @@ -154,7 +180,7 @@ void ShenandoahMark::count_liveness(ShenandoahLiveData* live_data, oop obj, Klas } } -template +template void ShenandoahMark::do_chunked_array_start(ShenandoahObjToScanQueue* q, T* cl, oop obj, Klass* klass, bool weak) { assert(obj->is_objArray(), "expect object array"); objArrayOop array = objArrayOop(obj); @@ -167,7 +193,7 @@ void ShenandoahMark::do_chunked_array_start(ShenandoahObjToScanQueue* q, T* cl, if (len <= (int) ObjArrayMarkingStride*2) { // A few slices only, process directly - array->oop_iterate_elements_range(cl, 0, len); + ObjArrayKlass::cast(klass)->oop_oop_iterate_elements_range(array, cl, 0, len); } else { int bits = log2i_graceful(len); // Compensate for non-power-of-two arrays, cover the array in excess: @@ -216,13 +242,13 @@ void ShenandoahMark::do_chunked_array_start(ShenandoahObjToScanQueue* q, T* cl, // Process the irregular tail, if present int from = last_idx; if (from < len) { - array->oop_iterate_elements_range(cl, from, len); + ObjArrayKlass::cast(klass)->oop_oop_iterate_elements_range(array, cl, from, len); } } } -template -void ShenandoahMark::do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, oop obj, int chunk, int pow, bool weak) { +template +void ShenandoahMark::do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, oop obj, Klass* klass, int chunk, int pow, bool weak) { assert(obj->is_objArray(), "expect object array"); objArrayOop array = objArrayOop(obj); @@ -246,7 +272,7 @@ void ShenandoahMark::do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, oop ob assert (0 < to && to <= len, "to is sane: %d/%d", to, len); #endif - array->oop_iterate_elements_range(cl, from, to); + ObjArrayKlass::cast(klass)->oop_oop_iterate_elements_range(array, cl, from, to); } template diff --git a/src/hotspot/share/oops/instanceRefKlass.hpp b/src/hotspot/share/oops/instanceRefKlass.hpp index fc219d06739..de7ec6fce9a 100644 --- a/src/hotspot/share/oops/instanceRefKlass.hpp +++ b/src/hotspot/share/oops/instanceRefKlass.hpp @@ -58,6 +58,16 @@ class InstanceRefKlass: public InstanceKlass { public: InstanceRefKlass(); + static InstanceRefKlass* cast(Klass* k) { + return const_cast(cast(const_cast(k))); + } + + static const InstanceRefKlass* cast(const Klass* k) { + assert(k != nullptr, "k should not be null"); + assert(k->is_reference_instance_klass(), "cast to InstanceRefKlass"); + return static_cast(k); + } + // Oop fields (and metadata) iterators // // The InstanceRefKlass iterators also support reference processing. From c4a8cb23496cc6851ca71de2e2b15ec09f5cb742 Mon Sep 17 00:00:00 2001 From: Chris Plummer Date: Wed, 15 Jul 2026 17:46:57 +0000 Subject: [PATCH 241/707] 8387640: vmTestbase/nsk/jdi/EventRequestManager/threadStartRequests/thrstartreq001/TestDescription.java fails intermittently Reviewed-by: sspitsyn, lmesnik --- .../threadStartRequests/thrstartreq001.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/EventRequestManager/threadStartRequests/thrstartreq001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/EventRequestManager/threadStartRequests/thrstartreq001.java index 7866547177f..39dc949c233 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/EventRequestManager/threadStartRequests/thrstartreq001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/EventRequestManager/threadStartRequests/thrstartreq001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -201,7 +201,7 @@ class EventListener extends Thread { public void run() { try { - do { + while (true) { EventSet eventSet = vm.eventQueue().remove(1000); if (eventSet != null) { // there is not a timeout EventIterator it = eventSet.eventIterator(); @@ -219,11 +219,15 @@ public void run() { log.display("EventListener: following JDI event occured: " + event.toString()); } - if (isConnected) { - eventSet.resume(); - } + eventSet.resume(); + // Even if isConnected has been set false, we need to continue consuming + // events until there are no more. So do a continue here rather than + // allowing continuing to be conditional on isConnected below. + continue; } - } while (isConnected); + if (!isConnected) + break; + } } catch (InterruptedException e) { tot_res = FAILED; log.complain("FAILURE in EventListener: caught unexpected " From 2659bfe35598296f9ba1b74b87e9e34c5f229ec7 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Wed, 15 Jul 2026 19:16:54 +0000 Subject: [PATCH 242/707] 8388347: Remove enablePreview from TestEnableNativeAccessJarManifest Reviewed-by: jpai, jvernee --- .../enablenativeaccess/TestEnableNativeAccessJarManifest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/jdk/java/foreign/enablenativeaccess/TestEnableNativeAccessJarManifest.java b/test/jdk/java/foreign/enablenativeaccess/TestEnableNativeAccessJarManifest.java index 0ac7ea474a4..3522921bdd3 100644 --- a/test/jdk/java/foreign/enablenativeaccess/TestEnableNativeAccessJarManifest.java +++ b/test/jdk/java/foreign/enablenativeaccess/TestEnableNativeAccessJarManifest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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 @@ -29,7 +29,6 @@ * @requires jdk.foreign.linker != "UNSUPPORTED" * @requires !vm.musl * - * @enablePreview * @build TestEnableNativeAccessJarManifest * panama_module/* * org.openjdk.foreigntest.unnamed.PanamaMainUnnamedModule From 399317d393869e9da54fccf55af71450c2aa36be Mon Sep 17 00:00:00 2001 From: Vladimir Ivanov Date: Wed, 15 Jul 2026 21:21:29 +0000 Subject: [PATCH 243/707] 8388357: ProblemList compiler/vectorapi/VectorStoreMaskIdentityTest.java Reviewed-by: liach, kvn --- test/hotspot/jtreg/ProblemList.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 0a98477be69..71e626e5b39 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -69,6 +69,8 @@ compiler/c2/aarch64/TestStaticCallStub.java 8359963 generic-aarch64 compiler/escapeAnalysis/TestBCEscapeAnalyzerOverflow.java 8387392 windows-aarch64 +compiler/vectorapi/VectorStoreMaskIdentityTest.java 8388281 generic-all + ############################################################################# # :hotspot_gc From bc03baed726bef0f7fde8d4de5f90b48a9cc26fa Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Thu, 16 Jul 2026 00:48:42 +0000 Subject: [PATCH 244/707] 8387745: [aot] Several tests fail because a SoftReferenceKey cannot be archived Reviewed-by: iklam, kvn --- src/hotspot/share/cds/aotReferenceObjSupport.cpp | 10 ++++++++++ src/hotspot/share/classfile/vmSymbols.hpp | 1 + .../share/classes/sun/util/locale/BaseLocale.java | 6 ++++++ 3 files changed, 17 insertions(+) diff --git a/src/hotspot/share/cds/aotReferenceObjSupport.cpp b/src/hotspot/share/cds/aotReferenceObjSupport.cpp index 2d5fc8c7f21..62ed3a56b62 100644 --- a/src/hotspot/share/cds/aotReferenceObjSupport.cpp +++ b/src/hotspot/share/cds/aotReferenceObjSupport.cpp @@ -142,6 +142,16 @@ void AOTReferenceObjSupport::stabilize_cached_reference_objects(TRAPS) { vmSymbols::void_method_signature(), CHECK); } + { + TempNewSymbol method_name = SymbolTable::new_symbol("assemblySetup"); + JavaValue result(T_VOID); + Symbol* baseLocale_name = vmSymbols::sun_util_locale_BaseLocale(); + Klass* baseLocale_klass = SystemDictionary::resolve_or_fail(baseLocale_name, true, CHECK); + JavaCalls::call_static(&result, baseLocale_klass, + method_name, + vmSymbols::void_method_signature(), + CHECK); + } { Symbol* cds_name = vmSymbols::jdk_internal_misc_CDS(); diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index 0348fae28b0..4337020846f 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -731,6 +731,7 @@ class SerializeClosure; template(runtimeSetup, "runtimeSetup") \ template(toFileURL_name, "toFileURL") \ template(toFileURL_signature, "(Ljava/lang/String;)Ljava/net/URL;") \ + template(sun_util_locale_BaseLocale, "sun/util/locale/BaseLocale") \ \ /* jcmd Thread.dump_to_file */ \ template(jdk_internal_vm_ThreadDumper, "jdk/internal/vm/ThreadDumper") \ diff --git a/src/java.base/share/classes/sun/util/locale/BaseLocale.java b/src/java.base/share/classes/sun/util/locale/BaseLocale.java index 31078720ddc..295952e7896 100644 --- a/src/java.base/share/classes/sun/util/locale/BaseLocale.java +++ b/src/java.base/share/classes/sun/util/locale/BaseLocale.java @@ -275,4 +275,10 @@ public int hashCode() { } return h; } + + // This is called from C code, at the very end of Java code execution + // during the AOT cache assembly phase. + private static void assemblySetup() { + CACHE.get().prepareForAOTCache(); + } } From 820d28feb5043fefe353b40dae9862b6868e4f1c Mon Sep 17 00:00:00 2001 From: Ozan Cetin Date: Thu, 16 Jul 2026 10:22:01 +0000 Subject: [PATCH 245/707] 8370870: IGV: add simple regression tests for graph dumping Reviewed-by: chagedorn, shade --- .../compiler/igv/TestIdealGraphDump.java | 334 ++++++++++++++++++ 1 file changed, 334 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/igv/TestIdealGraphDump.java diff --git a/test/hotspot/jtreg/compiler/igv/TestIdealGraphDump.java b/test/hotspot/jtreg/compiler/igv/TestIdealGraphDump.java new file mode 100644 index 00000000000..d950908eeaa --- /dev/null +++ b/test/hotspot/jtreg/compiler/igv/TestIdealGraphDump.java @@ -0,0 +1,334 @@ +/* + * 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 TestIdealGraphDump + * @bug 8370870 + * @summary Verify that IGV graph dumping produces well-structured XML at different print levels + * @library /test/lib + * @requires vm.debug == true & vm.compiler2.enabled & vm.flagless + * @run driver ${test.main.class} + */ + +package compiler.igv; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import jdk.test.lib.Asserts; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class TestIdealGraphDump { + + private static final String TEST_CLASS = TestMethods.class.getName(); + private static final String METHOD_COMPUTE = TEST_CLASS + "::compute"; + private static final String METHOD_BRANCH = TEST_CLASS + "::branchyMethod"; + + private static final Map dumpCache = new HashMap<>(); + + public static void main(String[] args) throws Exception { + testDisabled(); + testLevel0(); + testLevel1(); + testLevel2(); + testLevel3(); + testLevel4(); + testLevel5(); + testLevel6(); + testMonotonicallyIncreasingGraphCounts(); + testXmlWellFormedness(); + testMethodNameInGraph(); + testMultipleMethods(); + testIGVPrintLevelDirective(); + } + + private static void testDisabled() throws Exception { + Path xmlFile = getCachedDump(-1); + Asserts.assertTrue(Files.size(xmlFile) == 0, + "Level -1 (disabled) must produce an empty file"); + } + + private static void testLevel0() throws Exception { + Path xmlFile = getCachedDump(0); + Asserts.assertTrue(Files.size(xmlFile) == 0, + "Level 0 must produce an empty file (no system-wide dumps)"); + } + + private static void testLevel1() throws Exception { + String content = getCachedContent(1); + assertContainsPhase(content, "After Parsing", 1); + assertContainsPhase(content, "Before Matching", 1); + assertContainsPhase(content, "Final Code", 1); + assertNotContainsPhase(content, "PhaseCCP 1", 1); + } + + private static void testLevel2() throws Exception { + String content = getCachedContent(2); + assertContainsPhase(content, "After Parsing", 2); + assertContainsPhase(content, "Final Code", 2); + assertContainsPhase(content, "Iter GVN 1", 2); + assertContainsPhase(content, "PhaseCCP 1", 2); + assertNotContainsPhase(content, "Before Macro Expansion", 2); + } + + private static void testLevel3() throws Exception { + String content = getCachedContent(3); + assertContainsPhase(content, "Before Macro Expansion", 3); + assertNotContainsPhase(content, "Initial Liveness", 3); + } + + private static void testLevel4() throws Exception { + String content = getCachedContent(4); + assertContainsPhase(content, "Initial Liveness", 4); + assertNotContainsPhase(content, "After Iter GVN Step", 4); + } + + private static void testLevel5() throws Exception { + String content = getCachedContent(5); + assertContainsPhase(content, "After Iter GVN Step", 5); + assertNotContainsPhase(content, "Bytecode", 5); + } + + private static void testLevel6() throws Exception { + String content = getCachedContent(6); + Asserts.assertTrue(containsPhase(content, "Bytecode"), + "Level 6 must contain per-bytecode graphs (e.g., 'Bytecode 0: ...')"); + } + + private static void testMonotonicallyIncreasingGraphCounts() throws Exception { + int prevCount = 0; + for (int level = 1; level <= 6; level++) { + String content = getCachedContent(level); + int count = countGraphs(content); + Asserts.assertTrue(count >= prevCount, + "Level " + level + " (" + count + " graphs) must have at least as many as level " + + (level - 1) + " (" + prevCount + " graphs)"); + prevCount = count; + } + } + + private static void testXmlWellFormedness() throws Exception { + Path xmlFile = getCachedDump(2); + + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + try { + builder.parse(xmlFile.toFile()); + } catch (Exception e) { + Asserts.fail("IGV XML at level 2 is not well-formed: " + e.getMessage()); + } + + String content = getCachedContent(2); + Asserts.assertTrue(content.contains(""), "Must contain "); + Asserts.assertTrue(content.contains(""), "Must contain closing "); + Asserts.assertTrue(content.contains(""), "Must contain "); + Asserts.assertTrue(content.contains(""), "Must contain "); + Asserts.assertTrue(content.contains(""), "Must contain "); + Asserts.assertTrue(content.contains(" elements"); + Asserts.assertTrue(content.contains(""); + Asserts.assertTrue(content.contains(""), "Must contain "); + Asserts.assertTrue(content.contains(""), "Must contain "); + } + + private static void testMethodNameInGraph() throws Exception { + String content = getCachedContent(1); + Asserts.assertTrue(content.contains("TestMethods.compute"), + "Graph output must contain the compiled method name 'TestMethods.compute'"); + } + + private static void testMultipleMethods() throws Exception { + Path xmlFile = dumpMultipleMethods(1); + String content = Files.readString(xmlFile); + + Asserts.assertTrue(content.contains("TestMethods.compute"), + "Must contain graphs for 'compute' method"); + Asserts.assertTrue(content.contains("TestMethods.branchyMethod"), + "Must contain graphs for 'branchyMethod' method"); + + int computeFinalCode = countMethodPhase(content, "TestMethods.compute", "Final Code"); + int branchFinalCode = countMethodPhase(content, "TestMethods.branchyMethod", "Final Code"); + Asserts.assertEquals(computeFinalCode, 1, + "compute must emit exactly one 'Final Code' graph, got " + computeFinalCode); + Asserts.assertEquals(branchFinalCode, 1, + "branchyMethod must emit exactly one 'Final Code' graph, got " + branchFinalCode); + } + + private static void testIGVPrintLevelDirective() throws Exception { + Path xmlFile = Files.createTempFile("igv_directive_", ".xml"); + xmlFile.toFile().deleteOnExit(); + + List options = new ArrayList<>(); + options.add("-Xbatch"); + options.add("-XX:PrintIdealGraphLevel=0"); + options.add("-XX:PrintIdealGraphFile=" + xmlFile.toAbsolutePath()); + options.add("-XX:CompileCommand=IGVPrintLevel," + METHOD_COMPUTE + ",2"); + options.add(TEST_CLASS); + + OutputAnalyzer oa = ProcessTools.executeTestJava(options); + oa.shouldHaveExitValue(0); + oa.shouldNotContain("# A fatal error has been detected by the Java Runtime Environment"); + + String content = Files.readString(xmlFile); + Asserts.assertTrue(Files.size(xmlFile) > 0, + "Per-method IGVPrintLevel directive must produce output even with system level 0"); + Asserts.assertTrue(content.contains("TestMethods.compute"), + "Directive-based dump must contain the target method"); + Asserts.assertFalse(content.contains("TestMethods.branchyMethod"), + "Directive-based dump must NOT contain non-targeted method"); + assertContainsPhase(content, "After Parsing", 2); + } + + private static Path getCachedDump(int level) throws Exception { + if (!dumpCache.containsKey(level)) { + dumpCache.put(level, dumpAtLevel(level)); + } + return dumpCache.get(level); + } + + private static String getCachedContent(int level) throws Exception { + return Files.readString(getCachedDump(level)); + } + + private static Path dumpAtLevel(int level) throws Exception { + Path xmlFile = Files.createTempFile("igv_level" + level + "_", ".xml"); + xmlFile.toFile().deleteOnExit(); + + List options = new ArrayList<>(); + options.add("-Xbatch"); + options.add("-XX:PrintIdealGraphLevel=" + level); + options.add("-XX:PrintIdealGraphFile=" + xmlFile.toAbsolutePath()); + options.add("-XX:CompileCommand=compileonly," + METHOD_COMPUTE); + options.add(TEST_CLASS); + + OutputAnalyzer oa = ProcessTools.executeTestJava(options); + oa.shouldHaveExitValue(0); + oa.shouldNotContain("# A fatal error has been detected by the Java Runtime Environment"); + + return xmlFile; + } + + private static Path dumpMultipleMethods(int level) throws Exception { + Path xmlFile = Files.createTempFile("igv_multi_", ".xml"); + xmlFile.toFile().deleteOnExit(); + + List options = new ArrayList<>(); + options.add("-Xbatch"); + options.add("-XX:PrintIdealGraphLevel=" + level); + options.add("-XX:PrintIdealGraphFile=" + xmlFile.toAbsolutePath()); + options.add("-XX:CompileCommand=compileonly," + METHOD_COMPUTE); + options.add("-XX:CompileCommand=compileonly," + METHOD_BRANCH); + options.add(TEST_CLASS); + + OutputAnalyzer oa = ProcessTools.executeTestJava(options); + oa.shouldHaveExitValue(0); + oa.shouldNotContain("# A fatal error has been detected by the Java Runtime Environment"); + + return xmlFile; + } + + private static int countGraphs(String content) { + return countOccurrences(content, "" + phaseName + "<") || + content.contains("'" + phaseName); + } + + private static void assertContainsPhase(String content, String phaseName, int level) { + Asserts.assertTrue(containsPhase(content, phaseName), + "Level " + level + " must contain phase '" + phaseName + "'"); + } + + private static void assertNotContainsPhase(String content, String phaseName, int level) { + Asserts.assertFalse(containsPhase(content, phaseName), + "Level " + level + " must NOT contain phase '" + phaseName + "'"); + } + + private static int countMethodPhase(String content, String methodName, String phaseName) { + int count = 0; + int groupStart = 0; + while ((groupStart = content.indexOf("", groupStart)) != -1) { + int groupEnd = content.indexOf("", groupStart); + if (groupEnd == -1) { + break; + } + String group = content.substring(groupStart, groupEnd); + if (group.contains(methodName)) { + count += countOccurrences(group, ""); + } + groupStart = groupEnd; + } + return count; + } + + private static int countOccurrences(String str, String sub) { + int count = 0; + int idx = 0; + while ((idx = str.indexOf(sub, idx)) != -1) { + count++; + idx += sub.length(); + } + return count; + } + + public static class TestMethods { + public static void main(String[] args) { + int sum = 0; + for (int i = 0; i < 20_000; i++) { + sum += compute(i, i + 1); + sum += branchyMethod(i, i % 7); + } + System.out.println(sum); + } + + static int compute(int a, int b) { + int result = 0; + for (int i = 0; i < a % 10; i++) { + result += b * i; + } + return result; + } + + static int branchyMethod(int x, int y) { + if (x > y) { + return x * y + 1; + } else if (x == y) { + return x + y; + } else { + return y - x; + } + } + } +} From 80476532c0c5d4339128bd45dd85dc5cc70a6fe6 Mon Sep 17 00:00:00 2001 From: Daisuke Yamazaki Date: Thu, 16 Jul 2026 14:10:24 +0000 Subject: [PATCH 246/707] 8382269: keytool man page references to "JKS" need to be cleaned up Reviewed-by: mullan, hchao --- src/java.base/share/man/keytool.md | 70 ++++++++++++-------------- src/jdk.jartool/share/man/jarsigner.md | 18 ++----- 2 files changed, 36 insertions(+), 52 deletions(-) diff --git a/src/java.base/share/man/keytool.md b/src/java.base/share/man/keytool.md index 1d70bd2f5f8..faa2ff563a1 100644 --- a/src/java.base/share/man/keytool.md +++ b/src/java.base/share/man/keytool.md @@ -1191,14 +1191,14 @@ These options can appear for all commands operating on a keystore: [`-keystore`]{#option-keystore} *keystore* : The keystore location. - If the JKS `storetype` is used and a keystore file doesn't yet exist, then - certain `keytool` commands can result in a new keystore file being created. - For example, if `keytool -genkeypair` is called and the `-keystore` option - isn't specified, the default keystore file named `.keystore` is created in - the user's home directory if it doesn't already exist. Similarly, if the - `-keystore ks_file` option is specified but `ks_file` doesn't exist, then - it is created. For more information on the JKS `storetype`, see the - **KeyStore Implementation** section in **KeyStore aliases**. + If a keystore file doesn't yet exist, then certain `keytool` commands can + result in a new keystore file being created. For example, if + `keytool -genkeypair` is called and the `-keystore` option isn't specified, + the default keystore file named `.keystore` is created in the user's home + directory if it doesn't already exist. Similarly, if the `-keystore ks_file` + option is specified but `ks_file` doesn't exist, then it is created. For + more information on keystore types and implementations, see the + **KeyStore implementation** section in [Terms]. Note that the input stream from the `-keystore` option is passed to the `KeyStore.load` method. If `NONE` is specified as the URL, then a null @@ -1766,11 +1766,11 @@ keystore, then it prompts you for a password. If it detects alias duplication, then it asks you for a new alias, and you can specify a new alias or simply allow the `keytool` command to overwrite the existing one. -For example, import entries from a typical JKS type keystore `key.jks` into a -PKCS \#11 type hardware-based keystore, by entering the following command: +For example, import entries from a typical PKCS12 type keystore `key.p12` into +a PKCS \#11 type hardware-based keystore, by entering the following command: -> `keytool -importkeystore -srckeystore key.jks -destkeystore NONE - -srcstoretype JKS -deststoretype PKCS11 -srcstorepass` *password* +> `keytool -importkeystore -srckeystore key.p12 -destkeystore NONE + -srcstoretype PKCS12 -deststoretype PKCS11 -srcstorepass` *password* `-deststorepass` *password* The `importkeystore` command can also be used to import a single entry from a @@ -1780,8 +1780,8 @@ import. With the `-srcalias` option specified, you can also specify the destination alias name, protection password for a secret or private key, and the destination protection password you want as follows: -> `keytool -importkeystore -srckeystore key.jks -destkeystore NONE - -srcstoretype JKS -deststoretype PKCS11 -srcstorepass` *password* +> `keytool -importkeystore -srckeystore key.p12 -destkeystore NONE + -srcstoretype PKCS12 -deststoretype PKCS11 -srcstorepass` *password* `-deststorepass` *password* `-srcalias myprivatekey -destalias myoldprivatekey -srckeypass` *password* `-destkeypass` *password* `-noprompt` @@ -1800,22 +1800,22 @@ certificates for three entities: Ensure that you store all the certificates in the same keystore. ``` -keytool -genkeypair -keystore root.jks -alias root -ext bc:c -keyalg rsa -keytool -genkeypair -keystore ca.jks -alias ca -ext bc:c -keyalg rsa -keytool -genkeypair -keystore server.jks -alias server -keyalg rsa +keytool -genkeypair -keystore root.p12 -alias root -ext bc:c -keyalg rsa +keytool -genkeypair -keystore ca.p12 -alias ca -ext bc:c -keyalg rsa +keytool -genkeypair -keystore server.p12 -alias server -keyalg rsa -keytool -keystore root.jks -alias root -exportcert -rfc > root.pem +keytool -keystore root.p12 -alias root -exportcert -rfc > root.pem -keytool -storepass password -keystore ca.jks -certreq -alias ca | - keytool -storepass password -keystore root.jks +keytool -storepass password -keystore ca.p12 -certreq -alias ca | + keytool -storepass password -keystore root.p12 -gencert -alias root -ext BC=0 -rfc > ca.pem -keytool -keystore ca.jks -importcert -alias ca -file ca.pem +keytool -keystore ca.p12 -importcert -alias ca -file ca.pem -keytool -storepass password -keystore server.jks -certreq -alias server | - keytool -storepass password -keystore ca.jks -gencert -alias ca +keytool -storepass password -keystore server.p12 -certreq -alias server | + keytool -storepass password -keystore ca.p12 -gencert -alias ca -ext ku:c=dig,kE -rfc > server.pem cat root.pem ca.pem server.pem | - keytool -keystore server.jks -importcert -alias server + keytool -keystore server.p12 -importcert -alias server ``` @@ -1886,11 +1886,7 @@ Keystore implementation is a cross platform keystore based on the RSA PKCS12 Personal Information Exchange Syntax Standard. This standard is primarily meant for storing or transporting a user's private keys, certificates, and miscellaneous - secrets. There is another built-in implementation, provided by Oracle. It - implements the keystore as a file with a proprietary keystore type (format) - named `JKS`. It protects each private key with its individual password, and - also protects the integrity of the entire keystore with a (possibly - different) password. + secrets. Keystore implementations are provider-based. More specifically, the application interfaces supplied by `KeyStore` are implemented in terms of a @@ -1946,16 +1942,12 @@ Keystore implementation > `keystore.type=pkcs12` To have the tools utilize a keystore implementation other than the default, - you can change that line to specify a different keystore type. For example, - if you want to use the Oracle's `jks` keystore implementation, then change - the line to the following: - - > `keystore.type=jks` + you can change that line to specify a different keystore type. **Note:** - Case doesn't matter in keystore type designations. For example, `JKS` would - be considered the same as `jks`. + Case doesn't matter in keystore type designations. For example, `PKCS12` + would be considered the same as `pkcs12`. Certificate : A certificate (or public-key certificate) is a digitally signed statement @@ -2157,9 +2149,9 @@ cacerts Certificates File The `cacerts` file represents a system-wide keystore with CA certificates. System administrators can configure and manage that file with the `keytool` - command by specifying `jks` as the keystore type. The `cacerts` keystore - file ships with a default set of root CA certificates. For Linux, macOS, and - Windows, you can list the default certificates with the following command: + command. The `cacerts` keystore file ships with a default set of root CA + certificates. For Linux, macOS, and Windows, you can list the default + certificates with the following command: > `keytool -list -cacerts` diff --git a/src/jdk.jartool/share/man/jarsigner.md b/src/jdk.jartool/share/man/jarsigner.md index d128b9c11ff..b24382fdda5 100644 --- a/src/jdk.jartool/share/man/jarsigner.md +++ b/src/jdk.jartool/share/man/jarsigner.md @@ -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. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -181,11 +181,7 @@ Currently, there are two command-line tools that use keystore implementations The default keystore implementation is `PKCS12`. This is a cross platform keystore based on the RSA PKCS12 Personal Information Exchange Syntax Standard. This standard is primarily meant for storing or transporting a user's private -keys, certificates, and miscellaneous secrets. There is another built-in -implementation, provided by Oracle. It implements the keystore as a file with a -proprietary keystore type (format) named `JKS`. It protects each private key -with its individual password, and also protects the integrity of the entire -keystore with a (possibly different) password. +keys, certificates, and miscellaneous secrets. Keystore implementations are provider-based, which means the application interfaces supplied by the `KeyStore` class are implemented in terms of a @@ -237,15 +233,11 @@ specified by the following line in the security properties file: > `keystore.type=pkcs12` -Case doesn't matter in keystore type designations. For example, `JKS` is the -same as `jks`. +Case doesn't matter in keystore type designations. For example, `PKCS12` is the +same as `pkcs12`. To have the tools utilize a keystore implementation other than the default, you -can change that line to specify a different keystore type. For example, if you -want to use the Oracle's `jks` keystore implementation, then change the line to -the following: - -> `keystore.type=jks` +can change that line to specify a different keystore type. ## Supported Algorithms From 816fe33ea6136b1563cdbe0cffbd09da1f62f1c7 Mon Sep 17 00:00:00 2001 From: April Ivy Date: Thu, 16 Jul 2026 18:55:38 +0000 Subject: [PATCH 247/707] 8387991: Optimize execution of runtime/Thread/TestSpinPause.java Reviewed-by: dholmes, lmesnik --- .../jtreg/runtime/Thread/TestSpinPause.java | 93 +++++++++++++++---- 1 file changed, 73 insertions(+), 20 deletions(-) diff --git a/test/hotspot/jtreg/runtime/Thread/TestSpinPause.java b/test/hotspot/jtreg/runtime/Thread/TestSpinPause.java index 7226c8ed058..b939aceee95 100644 --- a/test/hotspot/jtreg/runtime/Thread/TestSpinPause.java +++ b/test/hotspot/jtreg/runtime/Thread/TestSpinPause.java @@ -21,38 +21,91 @@ * questions. */ -/** - * @test TestSpinPause - * @summary JVM runtime can use SpinPause function for synchronized statements. - * Check different implementations of JVM SpinPause don't crash JVM. +/* + * @test id=default + * @summary Check the default SpinPause implementation for synchronized statements. * @bug 8278241 * @library /test/lib - * * @requires os.arch=="aarch64" - * * @run main/othervm TestSpinPause - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=none TestSpinPause - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop TestSpinPause - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb TestSpinPause - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield TestSpinPause - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop -XX:OnSpinWaitInstCount=10 TestSpinPause - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb -XX:OnSpinWaitInstCount=3 TestSpinPause - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield -XX:OnSpinWaitInstCount=3 TestSpinPause * @run main/othervm -Xint TestSpinPause - * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=none TestSpinPause - * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop TestSpinPause - * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb TestSpinPause - * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield TestSpinPause - * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop -XX:OnSpinWaitInstCount=10 TestSpinPause - * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb -XX:OnSpinWaitInstCount=3 TestSpinPause - * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield -XX:OnSpinWaitInstCount=3 TestSpinPause * @run main/othervm -Xcomp TestSpinPause + */ + +/* + * @test id=none + * @summary Check SpinPause for synchronized statements with OnSpinWaitInst=none. + * @bug 8278241 + * @library /test/lib + * @requires os.arch=="aarch64" + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=none TestSpinPause + * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=none TestSpinPause * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=none TestSpinPause + */ + +/* + * @test id=nop + * @summary Check SpinPause for synchronized statements with OnSpinWaitInst=nop. + * @bug 8278241 + * @library /test/lib + * @requires os.arch=="aarch64" + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop TestSpinPause + * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop TestSpinPause * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop TestSpinPause + */ + +/* + * @test id=isb + * @summary Check SpinPause for synchronized statements with OnSpinWaitInst=isb. + * @bug 8278241 + * @library /test/lib + * @requires os.arch=="aarch64" + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb TestSpinPause + * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb TestSpinPause * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb TestSpinPause + */ + +/* + * @test id=yield + * @summary Check SpinPause for synchronized statements with OnSpinWaitInst=yield. + * @bug 8278241 + * @library /test/lib + * @requires os.arch=="aarch64" + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield TestSpinPause + * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield TestSpinPause * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield TestSpinPause + */ + +/* + * @test id=nop-count-10 + * @summary Check SpinPause for synchronized statements with OnSpinWaitInst=nop and OnSpinWaitInstCount=10. + * @bug 8278241 + * @library /test/lib + * @requires os.arch=="aarch64" + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop -XX:OnSpinWaitInstCount=10 TestSpinPause + * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop -XX:OnSpinWaitInstCount=10 TestSpinPause * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop -XX:OnSpinWaitInstCount=10 TestSpinPause + */ + +/* + * @test id=isb-count-3 + * @summary Check SpinPause for synchronized statements with OnSpinWaitInst=isb and OnSpinWaitInstCount=3. + * @bug 8278241 + * @library /test/lib + * @requires os.arch=="aarch64" + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb -XX:OnSpinWaitInstCount=3 TestSpinPause + * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb -XX:OnSpinWaitInstCount=3 TestSpinPause * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb -XX:OnSpinWaitInstCount=3 TestSpinPause + */ + +/* + * @test id=yield-count-3 + * @summary Check SpinPause for synchronized statements with OnSpinWaitInst=yield and OnSpinWaitInstCount=3. + * @bug 8278241 + * @library /test/lib + * @requires os.arch=="aarch64" + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield -XX:OnSpinWaitInstCount=3 TestSpinPause + * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield -XX:OnSpinWaitInstCount=3 TestSpinPause * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield -XX:OnSpinWaitInstCount=3 TestSpinPause */ From 24a1532719f389f7adc7d54da8c15f2f48254296 Mon Sep 17 00:00:00 2001 From: Mark Powers Date: Thu, 16 Jul 2026 23:48:46 +0000 Subject: [PATCH 248/707] =?UTF-8?q?8359758:=20O(n=C2=B2)=20time=20complexi?= =?UTF-8?q?ty=20in=20sun.security.util.LocalizedMessage.getNonlocalized?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: djelinski, weijun, abarashev --- .../sun/security/util/LocalizedMessage.java | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/java.base/share/classes/sun/security/util/LocalizedMessage.java b/src/java.base/share/classes/sun/security/util/LocalizedMessage.java index 61062bf6e1a..8d79e85c8a5 100644 --- a/src/java.base/share/classes/sun/security/util/LocalizedMessage.java +++ b/src/java.base/share/classes/sun/security/util/LocalizedMessage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, 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 @@ -106,32 +106,33 @@ public static String getNonlocalized(String key, // Classes like StringTokenizer may not be loaded, so parsing // is performed with String methods StringBuilder sb = new StringBuilder(); - int nextBraceIndex; - while ((nextBraceIndex = value.indexOf('{')) >= 0) { + int pos = 0; + int leftBraceIndex; + while ((leftBraceIndex = value.indexOf('{', pos)) >= 0) { - String firstPart = value.substring(0, nextBraceIndex); - sb.append(firstPart); - value = value.substring(nextBraceIndex + 1); + sb.append(value, pos, leftBraceIndex); // look for closing brace and argument index - nextBraceIndex = value.indexOf('}'); - if (nextBraceIndex < 0) { + int rightBraceIndex = value.indexOf('}', leftBraceIndex + 1); + if (rightBraceIndex < 0) { // no closing brace // MessageFormat would throw IllegalArgumentException, but // that exception class may not be loaded yet throw new RuntimeException("Unmatched braces"); } - String indexStr = value.substring(0, nextBraceIndex); try { - int index = Integer.parseInt(indexStr); + int index = Integer.parseInt(value, leftBraceIndex + 1, + rightBraceIndex, 10); sb.append(arguments[index]); } catch (NumberFormatException e) { // argument index is not an integer - throw new RuntimeException("not an integer: " + indexStr); + throw new RuntimeException("not an integer: " + + value.substring(leftBraceIndex + 1, rightBraceIndex)); } - value = value.substring(nextBraceIndex + 1); + + pos = rightBraceIndex + 1; } - sb.append(value); + sb.append(value, pos, value.length()); return sb.toString(); } From b7b29c7082db536ed03abb44ffc6e9e76960a309 Mon Sep 17 00:00:00 2001 From: Guanqiang Han Date: Fri, 17 Jul 2026 02:14:36 +0000 Subject: [PATCH 249/707] 8388186: java -XX:UseSSE=2 -XX:+EnableX86ECoreOpts -version crashes with assert(UseAVX > 0) failed: requires some form of AVX Reviewed-by: asmehra, kvn --- src/hotspot/cpu/x86/vm_version_x86.cpp | 2 +- ...estEnableX86ECoreOptsWithAVX2Disabled.java | 57 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 test/hotspot/jtreg/compiler/cpuflags/TestEnableX86ECoreOptsWithAVX2Disabled.java diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index 6112c280a1d..e395dd301f4 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -1343,7 +1343,7 @@ void VM_Version::get_processor_features() { } if (UseSHA && ((supports_evex() && supports_avx512vlbw()) || - (EnableX86ECoreOpts && !supports_hybrid()))) { + (supports_avx2() && EnableX86ECoreOpts && !supports_hybrid()))) { if (FLAG_IS_DEFAULT(UseSHA3Intrinsics)) { FLAG_SET_DEFAULT(UseSHA3Intrinsics, true); } diff --git a/test/hotspot/jtreg/compiler/cpuflags/TestEnableX86ECoreOptsWithAVX2Disabled.java b/test/hotspot/jtreg/compiler/cpuflags/TestEnableX86ECoreOptsWithAVX2Disabled.java new file mode 100644 index 00000000000..c31514bbba1 --- /dev/null +++ b/test/hotspot/jtreg/compiler/cpuflags/TestEnableX86ECoreOptsWithAVX2Disabled.java @@ -0,0 +1,57 @@ +/* + * 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 8388186 + * @summary Test for VM crash with -XX:+EnableX86ECoreOpts and UseAVX < 2. + * @requires vm.flagless + * @requires os.arch == "amd64" | os.arch == "x86_64" + * @library /test/lib + * @run driver ${test.main.class} + */ + +package compiler.cpuflags; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class TestEnableX86ECoreOptsWithAVX2Disabled { + static final String[] OPTIONS = { + "-XX:UseSSE=2", + "-XX:UseSSE=3", + "-XX:UseAVX=0", + "-XX:UseAVX=1" + }; + + public static void main(String[] args) throws Exception { + for (String option : OPTIONS) { + OutputAnalyzer output = ProcessTools.executeLimitedTestJava( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+EnableX86ECoreOpts", + option, + "-version"); + output.shouldHaveExitValue(0); + } + } +} From bb3baa85b8f62924230926320ca5a782f51d85e6 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 17 Jul 2026 02:19:00 +0000 Subject: [PATCH 250/707] 8388360: Dead sharedRuntime.cpp stub name code Reviewed-by: dholmes --- src/hotspot/share/runtime/sharedRuntime.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index bcb7f5488f5..919161dde2f 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -108,14 +108,6 @@ nmethod* SharedRuntime::_cont_doYield_stub; -#if 0 -// TODO tweak global stub name generation to match this -#define SHARED_STUB_NAME_DECLARE(name, type) "Shared Runtime " # name "_blob", -const char *SharedRuntime::_stub_names[] = { - SHARED_STUBS_DO(SHARED_STUB_NAME_DECLARE) -}; -#endif - //----------------------------generate_stubs----------------------------------- void SharedRuntime::generate_initial_stubs() { // Build this early so it's available for the interpreter. From c5c366ad0cfe33361b0c30597bc71beb4770db09 Mon Sep 17 00:00:00 2001 From: Harshit Dhiman Date: Fri, 17 Jul 2026 04:14:32 +0000 Subject: [PATCH 251/707] 8388284: [s390] resolve_jobject uses wrong branch condition after tmll Reviewed-by: amitkumar, aph --- src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp index 9a401766200..d0f92cc129a 100644 --- a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp @@ -116,7 +116,7 @@ void BarrierSetAssembler::resolve_jobject(MacroAssembler* masm, Register value, __ z_bre(done); // Use null result as-is. __ z_tmll(value, JNIHandles::tag_mask); - __ z_btrue(tagged); // not zero + __ branch_optimized(Assembler::bcondNotAllZero, tagged); // not zero // Resolve Local handle __ access_load_at(T_OBJECT, IN_NATIVE | AS_RAW, Address(value, 0), value, tmp1, tmp2); @@ -124,7 +124,7 @@ void BarrierSetAssembler::resolve_jobject(MacroAssembler* masm, Register value, __ bind(tagged); __ testbit(value, exact_log2(JNIHandles::TypeTag::weak_global)); // test for weak tag - __ z_btrue(weak_tag); + __ branch_optimized(Assembler::bcondNotAllZero, weak_tag); // resolve global handle __ access_load_at(T_OBJECT, IN_NATIVE, Address(value, -JNIHandles::TypeTag::global), value, tmp1, tmp2); From 6987a3593fc7581f04992b034d3dbb0469d09f1f Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Fri, 17 Jul 2026 05:02:17 +0000 Subject: [PATCH 252/707] 8387718: JVMTI GetLocal/SetLocal: slot bounds check overflows for long/double slots Reviewed-by: dholmes, sspitsyn --- src/hotspot/share/prims/jvmtiImpl.cpp | 4 +- .../GetSetLocalSlotOverflow.java | 77 ++++++++++++ .../libGetSetLocalSlotOverflow.cpp | 113 ++++++++++++++++++ 3 files changed, 192 insertions(+), 2 deletions(-) create mode 100644 test/hotspot/jtreg/serviceability/jvmti/GetLocalVariable/GetSetLocalSlotOverflow.java create mode 100644 test/hotspot/jtreg/serviceability/jvmti/GetLocalVariable/libGetSetLocalSlotOverflow.cpp diff --git a/src/hotspot/share/prims/jvmtiImpl.cpp b/src/hotspot/share/prims/jvmtiImpl.cpp index c0a4ca949c9..96366cceaff 100644 --- a/src/hotspot/share/prims/jvmtiImpl.cpp +++ b/src/hotspot/share/prims/jvmtiImpl.cpp @@ -379,7 +379,7 @@ bool VM_BaseGetOrSetLocal::check_slot_type_lvt(javaVFrame* jvf) { if (!method->has_localvariable_table()) { // Just to check index boundaries. jint extra_slot = (_type == T_LONG || _type == T_DOUBLE) ? 1 : 0; - if (_index < 0 || _index + extra_slot >= method->max_locals()) { + if (_index < 0 || _index >= method->max_locals() - extra_slot) { _result = JVMTI_ERROR_INVALID_SLOT; return false; } @@ -451,7 +451,7 @@ bool VM_BaseGetOrSetLocal::check_slot_type_no_lvt(javaVFrame* jvf) { Method* method = jvf->method(); jint extra_slot = (_type == T_LONG || _type == T_DOUBLE) ? 1 : 0; - if (_index < 0 || _index + extra_slot >= method->max_locals()) { + if (_index < 0 || _index >= method->max_locals() - extra_slot) { _result = JVMTI_ERROR_INVALID_SLOT; return false; } diff --git a/test/hotspot/jtreg/serviceability/jvmti/GetLocalVariable/GetSetLocalSlotOverflow.java b/test/hotspot/jtreg/serviceability/jvmti/GetLocalVariable/GetSetLocalSlotOverflow.java new file mode 100644 index 00000000000..09073d99bfb --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/GetLocalVariable/GetSetLocalSlotOverflow.java @@ -0,0 +1,77 @@ +/* + * 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 8387718 + * @summary VM_GetOrSetLocal slot bounds check overflows for long/double slots, + * allowing an out-of-bounds StackValueCollection access when slot == INT_MAX. + * @requires vm.jvmti + * @compile GetSetLocalSlotOverflow.java + * @run main/othervm/native -agentlib:GetSetLocalSlotOverflow GetSetLocalSlotOverflow + */ + +/* + * Regression test / reproducer for the signed-overflow in + * VM_BaseGetOrSetLocal::check_slot_type_no_lvt (jvmtiImpl.cpp). + * + * For a T_LONG/T_DOUBLE access, the bounds check is + * if (_index < 0 || _index + extra_slot >= method->max_locals()) + * with extra_slot == 1. When the agent passes slot == INT_MAX, the + * sub-expression _index + extra_slot overflows to INT_MIN, which is < max_locals(), + * so the guard passes and the code goes on to index locals->at(INT_MAX). + * + * Expected (fixed) behavior: GetLocalLong/Double and SetLocalLong/Double with + * slot == INT_MAX return JVMTI_ERROR_INVALID_SLOT. + * + * On an unfixed VM this test does not merely fail: the out-of-bounds access + * crashes the VM (assertion failure in fastdebug, SIGSEGV / silent corruption + * in product). A clean PASS is only possible once the bounds check is fixed. + */ + +public class GetSetLocalSlotOverflow { + + // Invoked from runner(); the agent inspects the runner() frame at depth 1. + // Returns false if any accessor did not return JVMTI_ERROR_INVALID_SLOT. + static native boolean testOverflow(Thread thread); + + public static void main(String[] args) throws Exception { + if (!runner()) { + throw new RuntimeException("Test GetSetLocalSlotOverflow failed"); + } + } + + // A Java frame holding a few locals. The agent targets this frame (depth 1) + // with slot == INT_MAX. The actual local contents are irrelevant: the + // overflow happens in the slot bounds check, before any local is read. + public static boolean runner() { + long l = 0xCAFEBABEL; + double d = 3.14d; + boolean ok = testOverflow(Thread.currentThread()); + // Keep locals live across the native call. + if (l == 0 && d == 0) { + throw new AssertionError("unreachable"); + } + return ok; + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/GetLocalVariable/libGetSetLocalSlotOverflow.cpp b/test/hotspot/jtreg/serviceability/jvmti/GetLocalVariable/libGetSetLocalSlotOverflow.cpp new file mode 100644 index 00000000000..98872ddf0de --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/GetLocalVariable/libGetSetLocalSlotOverflow.cpp @@ -0,0 +1,113 @@ +/* + * 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. + */ + +#include +#include +#include "jvmti.h" +#include "jvmti_common.hpp" + +#ifdef __cplusplus +extern "C" { +#endif + +// The runner() frame at depth 1; INT_MAX makes (slot + extra_slot) overflow +// for the long/double accessors. +static const jint Depth = 1; +static const jint OverflowSlot = INT_MAX; // 0x7fffffff + +static jvmtiEnv *jvmti = nullptr; + +// Each access below MUST come back as JVMTI_ERROR_INVALID_SLOT. On an unfixed +// VM the overflowing bounds check is bypassed and the subsequent +// locals->at(INT_MAX) access crashes the VM before we ever see a return code. +static bool expect_invalid_slot(const char* what, jvmtiError err) { + if (err == JVMTI_ERROR_INVALID_SLOT) { + LOG(" PASS: %s returned JVMTI_ERROR_INVALID_SLOT (%d) for slot=INT_MAX\n", what, err); + return true; + } + LOG(" FAIL: %s returned %d for slot=INT_MAX, expected JVMTI_ERROR_INVALID_SLOT (%d)\n", + what, err, JVMTI_ERROR_INVALID_SLOT); + return false; +} + +JNIEXPORT jboolean JNICALL +Java_GetSetLocalSlotOverflow_testOverflow(JNIEnv *env, jclass cls, jobject thread) { + if (jvmti == nullptr) { + LOG("JVMTI client was not properly loaded!\n"); + return JNI_FALSE; + } + + jlong lval = 0; + jdouble dval = 0; + + // T_LONG / T_DOUBLE => extra_slot == 1 => INT_MAX + 1 overflows to INT_MIN. + bool ok = true; + ok &= expect_invalid_slot("GetLocalLong", jvmti->GetLocalLong(thread, Depth, OverflowSlot, &lval)); + ok &= expect_invalid_slot("GetLocalDouble", jvmti->GetLocalDouble(thread, Depth, OverflowSlot, &dval)); + ok &= expect_invalid_slot("SetLocalLong", jvmti->SetLocalLong(thread, Depth, OverflowSlot, (jlong)0)); + ok &= expect_invalid_slot("SetLocalDouble", jvmti->SetLocalDouble(thread, Depth, OverflowSlot, (jdouble)0)); + return ok ? JNI_TRUE : JNI_FALSE; +} + +static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { + jint res; + jvmtiError err; + static jvmtiCapabilities caps; + + res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_9); + if (res != JNI_OK || jvmti == nullptr) { + LOG("Wrong result of a valid call to GetEnv!\n"); + return JNI_ERR; + } + caps.can_access_local_variables = 1; + + err = jvmti->AddCapabilities(&caps); + if (err != JVMTI_ERROR_NONE) { + LOG("AddCapabilities: unexpected error: %d\n", err); + return JNI_ERR; + } + err = jvmti->GetCapabilities(&caps); + if (err != JVMTI_ERROR_NONE) { + LOG("GetCapabilities: unexpected error: %d\n", err); + return JNI_ERR; + } + if (!caps.can_access_local_variables) { + LOG("Warning: Access to local variables is not implemented\n"); + return JNI_ERR; + } + return JNI_OK; +} + +JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { + return Agent_Initialize(jvm, options, reserved); +} + +JNIEXPORT jint JNICALL +Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) { + return Agent_Initialize(jvm, options, reserved); +} + +#ifdef __cplusplus +} +#endif From 0ad1166ada90fe39e2855f21fd91db9fccfffe0b Mon Sep 17 00:00:00 2001 From: Volkan Yazici Date: Fri, 17 Jul 2026 05:55:39 +0000 Subject: [PATCH 253/707] 8387753: Improve SimpleDateFormat.set2DigitYearStart() documentation Reviewed-by: naoto --- .../classes/java/text/SimpleDateFormat.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/java.base/share/classes/java/text/SimpleDateFormat.java b/src/java.base/share/classes/java/text/SimpleDateFormat.java index 4c57214dbba..ba1ae827776 100644 --- a/src/java.base/share/classes/java/text/SimpleDateFormat.java +++ b/src/java.base/share/classes/java/text/SimpleDateFormat.java @@ -920,10 +920,15 @@ private void parseAmbiguousDatesAsAfter(Date startDate) { } /** - * Sets the 100-year period 2-digit years will be interpreted as being in - * to begin on the date the user specifies. + * Sets the start date of the 100-year period used to interpret 2-digit years. + *

    + * For example, given a {@code SimpleDateFormat} with a {@code GregorianCalendar}, + * if the start date is set to January 1, 1950, 2-digit years are + * interpreted as falling within the 100-year range from 1950 through 2049. + * In that case, 50 is interpreted as 1950, 99 as 1999, 00 as 2000, and 49 + * as 2049. * - * @param startDate During parsing, two digit years will be placed in the range + * @param startDate During parsing, 2-digit years will be placed in the range * {@code startDate} to {@code startDate + 100 years}. * @see #get2DigitYearStart * @throws NullPointerException if {@code startDate} is {@code null}. @@ -934,11 +939,8 @@ public void set2DigitYearStart(Date startDate) { } /** - * Returns the beginning date of the 100-year period 2-digit years are interpreted - * as being within. + * {@return the start date of the 100-year period used to interpret 2-digit years} * - * @return the start of the 100-year period into which two digit years are - * parsed * @see #set2DigitYearStart * @since 1.2 */ From 3e67ebff0719e5d8e09532bf91fcc4c80bb53ad5 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Fri, 17 Jul 2026 06:35:20 +0000 Subject: [PATCH 254/707] 8387701: TestAVXRegisterDump: guarantee(how == 0) failed: test guarantee Reviewed-by: missa, sviswanathan, kvn --- .../hotspot/jtreg/runtime/ErrorHandling/TestAVXRegisterDump.java | 1 + 1 file changed, 1 insertion(+) diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/TestAVXRegisterDump.java b/test/hotspot/jtreg/runtime/ErrorHandling/TestAVXRegisterDump.java index 1f2fec74fee..59692d94d2f 100644 --- a/test/hotspot/jtreg/runtime/ErrorHandling/TestAVXRegisterDump.java +++ b/test/hotspot/jtreg/runtime/ErrorHandling/TestAVXRegisterDump.java @@ -26,6 +26,7 @@ * @summary Test that YMM and ZMM registers are correctly dumped in hs_err for different UseAVX settings * @library /test/lib * @requires os.family == "linux" & os.arch == "amd64" + * @requires vm.cpu.features ~= ".*avx.*" * @requires vm.debug == true * @modules java.base/jdk.internal.misc * @build jdk.test.whitebox.WhiteBox From 160382009f121502bee469afcae8236676502e16 Mon Sep 17 00:00:00 2001 From: Volkan Yazici Date: Fri, 17 Jul 2026 06:38:30 +0000 Subject: [PATCH 255/707] 8387598: sun.net.httpserver.maxReqTime and maxRspTime properties expect values in seconds Reviewed-by: jpai --- src/jdk.httpserver/share/classes/module-info.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jdk.httpserver/share/classes/module-info.java b/src/jdk.httpserver/share/classes/module-info.java index 0a0e77c628f..842a7ec1c9a 100644 --- a/src/jdk.httpserver/share/classes/module-info.java +++ b/src/jdk.httpserver/share/classes/module-info.java @@ -83,7 +83,7 @@ * If the value is less than or equal to zero, there is no limit. * *

  37. {@systemProperty sun.net.httpserver.maxReqTime} (default: -1)
    - * The maximum time in milliseconds allowed to receive a request headers and body. + * The maximum time in seconds allowed to receive a request headers and body. * In practice, the actual time is a function of request size, network speed, and handler * processing delays. A value less than or equal to zero means the time is not limited. * If the limit is exceeded then the connection is terminated and the handler will receive a @@ -91,7 +91,7 @@ * that may mean requests are aborted later than the specified interval. *

  38. *
  39. {@systemProperty sun.net.httpserver.maxRspTime} (default: -1)
    - * The maximum time in milliseconds allowed to receive a response headers and body. + * The maximum time in seconds allowed to receive a response headers and body. * In practice, the actual time is a function of response size, network speed, and handler * processing delays. A value less than or equal to zero means the time is not limited. * If the limit is exceeded then the connection is terminated and the handler will receive a From 2278ade4e8714ee167b2267b7cdde2d5859dfdaf Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Fri, 17 Jul 2026 07:05:51 +0000 Subject: [PATCH 256/707] 8387674: Remove isXP() function from jabswitch.cpp Reviewed-by: prr, clanger --- .../windows/native/jabswitch/jabswitch.cpp | 25 +++---------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/src/jdk.accessibility/windows/native/jabswitch/jabswitch.cpp b/src/jdk.accessibility/windows/native/jabswitch/jabswitch.cpp index fdd7ff524da..7e4f63d2363 100644 --- a/src/jdk.accessibility/windows/native/jabswitch/jabswitch.cpp +++ b/src/jdk.accessibility/windows/native/jabswitch/jabswitch.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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,23 +53,6 @@ static LPCTSTR STR_ACCESSBRIDGE = FILE* origFile; FILE* tempFile; -bool isXP() -{ - static bool isXPFlag = false; - OSVERSIONINFO osvi; - - // Initialize the OSVERSIONINFO structure. - ZeroMemory( &osvi, sizeof( osvi ) ); - osvi.dwOSVersionInfoSize = sizeof( osvi ); - - GetVersionEx( &osvi ); - - if ( osvi.dwMajorVersion == 5 ) // For Windows XP and Windows 2000 - isXPFlag = true; - - return isXPFlag ; -} - void enableJAB() { // Copy lines from orig to temp modifying the line containing // assistive_technologies= @@ -458,16 +441,14 @@ int main(int argc, char* argv[]) { enableWasRequested = true; error = modify(true); if (error == 0) { - if( !isXP() ) - regEnable(); + regEnable(); } } else if (_stricmp(argv[1], "-disable") == 0 || _stricmp(argv[1], "/disable") == 0) { badParams = false; disableWasRequested = true; error = modify(false); if (error == 0) { - if( !isXP() ) - regDisable(); + regDisable(); } } } From 5bea309caa1e5747feca4957786e5595957316b9 Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Fri, 17 Jul 2026 07:07:52 +0000 Subject: [PATCH 257/707] 8387698: C2 VectorAPI: Float16Vector::indexInRange hits: fatal error: Not monotonic Reviewed-by: chagedorn, epeter --- src/hotspot/share/opto/castnode.cpp | 5 ++ .../TestFloat16VectorConvergence.java | 60 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/vectorapi/TestFloat16VectorConvergence.java diff --git a/src/hotspot/share/opto/castnode.cpp b/src/hotspot/share/opto/castnode.cpp index 076a95acfd8..ef7b4d5aef3 100644 --- a/src/hotspot/share/opto/castnode.cpp +++ b/src/hotspot/share/opto/castnode.cpp @@ -465,6 +465,11 @@ const Type* CheckCastPPNode::Value(PhaseGVN* phase) const { if (in_type != nullptr && my_type != nullptr) { TypePtr::PTR in_ptr = in_type->ptr(); if (in_ptr == TypePtr::Null) { + // A null input cast to a type that cannot be null (e.g. NotNull) describes + // an impossible value: the join is empty, so the result must be TOP. + if (my_type->join_ptr(TypePtr::Null) == TypePtr::TopPTR) { + return Type::TOP; + } result = in_type; } else if (in_ptr != TypePtr::Constant) { result = my_type->cast_to_ptr_type(my_type->join_ptr(in_ptr)); diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestFloat16VectorConvergence.java b/test/hotspot/jtreg/compiler/vectorapi/TestFloat16VectorConvergence.java new file mode 100644 index 00000000000..9cf92af2644 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestFloat16VectorConvergence.java @@ -0,0 +1,60 @@ +/* + * 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.vectorapi; + +import jdk.incubator.vector.Float16Vector; +import jdk.incubator.vector.VectorMask; + +import java.util.Random; + +/* + * @test + * @bug 8387698 + * @summary C2 VectorAPI: Float16Vector::indexInRange hits fatal error: Not monotonic. + * @modules jdk.incubator.vector + * @requires vm.debug == true + * @run main ${test.main.class} + * @run main/othervm -XX:-UncommonNullCast -Xbatch + * -XX:CompileCommand=compileonly,${test.main.class}::test + * ${test.main.class} + */ +public class TestFloat16VectorConvergence { + + static Object test(long offset, long limit) { + boolean[] array = new boolean[16]; + var v = VectorMask.fromArray(Float16Vector.SPECIES_256, array, 0); + return v.indexInRange(offset, limit); + } + + public static void main(String[] args) { + Random random = new Random(0); + for (int i = 0; i < 10_000; i++) { + // A negative offset drives indexInRange into indexPartiallyInRange, + // which is where the un-foldable Float16 mask unbox is produced. + Object mask = test(-70368744177664L, random.nextLong()); + if (mask == null) { + throw new AssertionError("Unexpected null result from indexInRange"); + } + } + } +} From a15f693cb860e0b2208b3755cee222a03956067a Mon Sep 17 00:00:00 2001 From: April Ivy Date: Fri, 17 Jul 2026 08:44:52 +0000 Subject: [PATCH 258/707] 8387467: ZGC: Use shared thread-local _nmethod_disarmed_guard_value Reviewed-by: aboldtch, ayang, eosterlund --- src/hotspot/share/gc/shared/barrierSet.cpp | 4 ++-- src/hotspot/share/gc/shared/barrierSetNMethod.cpp | 4 ++++ src/hotspot/share/gc/shared/barrierSetNMethod.hpp | 4 +++- src/hotspot/share/gc/shared/gcThreadLocalData.hpp | 4 ++-- src/hotspot/share/gc/z/zBarrierSet.cpp | 3 ++- src/hotspot/share/gc/z/zBarrierSetNMethod.cpp | 7 +------ src/hotspot/share/gc/z/zBarrierSetNMethod.hpp | 3 +-- src/hotspot/share/gc/z/zStackWatermark.cpp | 7 +++++-- src/hotspot/share/gc/z/zThreadLocalData.hpp | 12 +----------- 9 files changed, 21 insertions(+), 27 deletions(-) diff --git a/src/hotspot/share/gc/shared/barrierSet.cpp b/src/hotspot/share/gc/shared/barrierSet.cpp index a30b23ce2d9..1fd0317e8f1 100644 --- a/src/hotspot/share/gc/shared/barrierSet.cpp +++ b/src/hotspot/share/gc/shared/barrierSet.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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -86,7 +86,7 @@ BarrierSet::BarrierSet(BarrierSetAssembler* barrier_set_assembler, void BarrierSet::on_thread_attach(Thread* thread) { BarrierSetNMethod* bs_nm = barrier_set_nmethod(); - thread->set_nmethod_disarmed_guard_value(bs_nm->disarmed_guard_value()); + bs_nm->set_thread_disarmed_guard_value(thread); } // Called from init.cpp diff --git a/src/hotspot/share/gc/shared/barrierSetNMethod.cpp b/src/hotspot/share/gc/shared/barrierSetNMethod.cpp index 2f7b79beab0..c36deb3446a 100644 --- a/src/hotspot/share/gc/shared/barrierSetNMethod.cpp +++ b/src/hotspot/share/gc/shared/barrierSetNMethod.cpp @@ -135,6 +135,10 @@ ByteSize BarrierSetNMethod::thread_disarmed_guard_value_offset() const { return Thread::nmethod_disarmed_guard_value_offset(); } +void BarrierSetNMethod::set_thread_disarmed_guard_value(Thread* thread) { + thread->set_nmethod_disarmed_guard_value(disarmed_guard_value()); +} + class BarrierSetNMethodArmClosure : public ThreadClosure { private: int _disarmed_guard_value; diff --git a/src/hotspot/share/gc/shared/barrierSetNMethod.hpp b/src/hotspot/share/gc/shared/barrierSetNMethod.hpp index 812763e429d..cd01ddda09c 100644 --- a/src/hotspot/share/gc/shared/barrierSetNMethod.hpp +++ b/src/hotspot/share/gc/shared/barrierSetNMethod.hpp @@ -54,9 +54,11 @@ class BarrierSetNMethod: public CHeapObj { bool supports_entry_barrier(nmethod* nm); virtual bool nmethod_entry_barrier(nmethod* nm); - virtual ByteSize thread_disarmed_guard_value_offset() const; virtual int* disarmed_guard_value_address() const; + ByteSize thread_disarmed_guard_value_offset() const; + void set_thread_disarmed_guard_value(Thread* thread); + int disarmed_guard_value() const; static int nmethod_stub_entry_barrier(address* return_address_ptr); diff --git a/src/hotspot/share/gc/shared/gcThreadLocalData.hpp b/src/hotspot/share/gc/shared/gcThreadLocalData.hpp index 2847cd8bf33..b0659c58390 100644 --- a/src/hotspot/share/gc/shared/gcThreadLocalData.hpp +++ b/src/hotspot/share/gc/shared/gcThreadLocalData.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -40,6 +40,6 @@ // should consider placing frequently accessed fields first in // T, so that field offsets relative to Thread are small, which // often allows for a more compact instruction encoding. -typedef uint64_t GCThreadLocalData[40]; // 320 bytes +typedef uint64_t GCThreadLocalData[39]; // 312 bytes #endif // SHARE_GC_SHARED_GCTHREADLOCALDATA_HPP diff --git a/src/hotspot/share/gc/z/zBarrierSet.cpp b/src/hotspot/share/gc/z/zBarrierSet.cpp index f6f99672886..c5d6fc5a9b1 100644 --- a/src/hotspot/share/gc/z/zBarrierSet.cpp +++ b/src/hotspot/share/gc/z/zBarrierSet.cpp @@ -251,13 +251,14 @@ void ZBarrierSet::on_thread_destroy(Thread* thread) { } void ZBarrierSet::on_thread_attach(Thread* thread) { + BarrierSet::on_thread_attach(thread); + // Set thread local masks ZThreadLocalData::set_load_bad_mask(thread, ZPointerLoadBadMask); ZThreadLocalData::set_load_good_mask(thread, ZPointerLoadGoodMask); ZThreadLocalData::set_mark_bad_mask(thread, ZPointerMarkBadMask); ZThreadLocalData::set_store_bad_mask(thread, ZPointerStoreBadMask); ZThreadLocalData::set_store_good_mask(thread, ZPointerStoreGoodMask); - ZThreadLocalData::set_nmethod_disarmed(thread, ZPointerStoreGoodMask); if (thread->is_Java_thread()) { JavaThread* const jt = JavaThread::cast(thread); StackWatermark* const watermark = new ZStackWatermark(jt); diff --git a/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp b/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp index a439b3a167b..6e89c5a1032 100644 --- a/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp +++ b/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -30,7 +30,6 @@ #include "gc/z/zLock.inline.hpp" #include "gc/z/zNMethod.hpp" #include "gc/z/zResurrection.inline.hpp" -#include "gc/z/zThreadLocalData.hpp" #include "gc/z/zUncoloredRoot.inline.hpp" #include "logging/log.hpp" #include "runtime/icache.hpp" @@ -98,10 +97,6 @@ int* ZBarrierSetNMethod::disarmed_guard_value_address() const { return (int*)ZPointerStoreGoodMaskLowOrderBitsAddr; } -ByteSize ZBarrierSetNMethod::thread_disarmed_guard_value_offset() const { - return ZThreadLocalData::nmethod_disarmed_offset(); -} - oop ZBarrierSetNMethod::oop_load_no_keepalive(const nmethod* nm, int index) { return ZNMethod::oop_load_no_keepalive(nm, index); } diff --git a/src/hotspot/share/gc/z/zBarrierSetNMethod.hpp b/src/hotspot/share/gc/z/zBarrierSetNMethod.hpp index c7bbe35e17d..304be1f0a88 100644 --- a/src/hotspot/share/gc/z/zBarrierSetNMethod.hpp +++ b/src/hotspot/share/gc/z/zBarrierSetNMethod.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -36,7 +36,6 @@ class ZBarrierSetNMethod : public BarrierSetNMethod { public: uintptr_t color(nmethod* nm); - virtual ByteSize thread_disarmed_guard_value_offset() const; virtual int* disarmed_guard_value_address() const; virtual oop oop_load_no_keepalive(const nmethod* nm, int index); diff --git a/src/hotspot/share/gc/z/zStackWatermark.cpp b/src/hotspot/share/gc/z/zStackWatermark.cpp index 4a50dea0cec..de57ea974f3 100644 --- a/src/hotspot/share/gc/z/zStackWatermark.cpp +++ b/src/hotspot/share/gc/z/zStackWatermark.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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,6 +23,7 @@ #include "gc/z/zAddress.hpp" #include "gc/z/zBarrier.inline.hpp" +#include "gc/z/zBarrierSet.hpp" #include "gc/z/zGeneration.inline.hpp" #include "gc/z/zStackWatermark.hpp" #include "gc/z/zStoreBarrierBuffer.hpp" @@ -189,7 +190,9 @@ void ZStackWatermark::start_processing_impl(void* context) { ZThreadLocalData::set_mark_bad_mask(_jt, ZPointerMarkBadMask); ZThreadLocalData::set_store_bad_mask(_jt, ZPointerStoreBadMask); ZThreadLocalData::set_store_good_mask(_jt, ZPointerStoreGoodMask); - ZThreadLocalData::set_nmethod_disarmed(_jt, ZPointerStoreGoodMask); + + // Update thread-local nmethod disarmed guard value + BarrierSet::barrier_set()->barrier_set_nmethod()->set_thread_disarmed_guard_value(_jt); // Retire TLAB if (ZGeneration::young()->is_phase_mark() || ZGeneration::old()->is_phase_mark()) { diff --git a/src/hotspot/share/gc/z/zThreadLocalData.hpp b/src/hotspot/share/gc/z/zThreadLocalData.hpp index a141fd8f83a..297d57c2cfe 100644 --- a/src/hotspot/share/gc/z/zThreadLocalData.hpp +++ b/src/hotspot/share/gc/z/zThreadLocalData.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ class ZThreadLocalData { uintptr_t _mark_bad_mask; uintptr_t _store_good_mask; uintptr_t _store_bad_mask; - uintptr_t _nmethod_disarmed; ZStoreBarrierBuffer* _store_barrier_buffer; ZMarkThreadLocalStacks _mark_stacks[2]; zaddress_unsafe* _invisible_root; @@ -50,7 +49,6 @@ class ZThreadLocalData { _mark_bad_mask(0), _store_good_mask(0), _store_bad_mask(0), - _nmethod_disarmed(0), _store_barrier_buffer(new ZStoreBarrierBuffer()), _mark_stacks(), _invisible_root(nullptr) {} @@ -92,10 +90,6 @@ class ZThreadLocalData { data(thread)->_store_good_mask = mask; } - static void set_nmethod_disarmed(Thread* thread, uintptr_t value) { - data(thread)->_nmethod_disarmed = value; - } - static ZMarkThreadLocalStacks* mark_stacks(Thread* thread, ZGenerationId id) { return &data(thread)->_mark_stacks[(int)id]; } @@ -134,10 +128,6 @@ class ZThreadLocalData { return Thread::gc_data_offset() + byte_offset_of(ZThreadLocalData, _store_good_mask); } - static ByteSize nmethod_disarmed_offset() { - return Thread::gc_data_offset() + byte_offset_of(ZThreadLocalData, _nmethod_disarmed); - } - static ByteSize store_barrier_buffer_offset() { return Thread::gc_data_offset() + byte_offset_of(ZThreadLocalData, _store_barrier_buffer); } From 026a63d0d941abc50b4ecbecca1668a93fa6963a Mon Sep 17 00:00:00 2001 From: Harshit Dhiman Date: Fri, 17 Jul 2026 11:08:58 +0000 Subject: [PATCH 259/707] 8388285: [s390] compP_reg_mem is missing the barrier_data() == 0 predicate Reviewed-by: amitkumar, aph --- src/hotspot/cpu/s390/s390.ad | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index 256e39b03c2..5f33cf4fa77 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -8786,6 +8786,7 @@ instruct compP_decode_reg_imm0(flagsReg cr, iRegN op1, immP0 op2) %{ instruct compP_reg_mem(iRegP dst, memory src, flagsReg cr)%{ match(Set cr (CmpP dst (LoadP src))); + predicate(n->in(2)->as_Load()->barrier_data() == 0); ins_cost(MEMORY_REF_COST); size(Z_DISP3_SIZE); format %{ "CLG $dst, $src\t # ptr" %} From 9601cfb31b7b489db10fc3523de0e5d86cd2faed Mon Sep 17 00:00:00 2001 From: Dingli Zhang Date: Fri, 17 Jul 2026 13:18:05 +0000 Subject: [PATCH 260/707] 8388399: RISC-V: Enable vector FP16 conversions with Zvfhmin Reviewed-by: fyang, wenanjian --- src/hotspot/cpu/riscv/globals_riscv.hpp | 1 + src/hotspot/cpu/riscv/riscv_v.ad | 1 + src/hotspot/cpu/riscv/vm_version_riscv.hpp | 147 +++++++++--------- .../os_cpu/linux_riscv/riscv_hwprobe.cpp | 3 + 4 files changed, 80 insertions(+), 72 deletions(-) diff --git a/src/hotspot/cpu/riscv/globals_riscv.hpp b/src/hotspot/cpu/riscv/globals_riscv.hpp index d399bc13082..a7f0da42f4e 100644 --- a/src/hotspot/cpu/riscv/globals_riscv.hpp +++ b/src/hotspot/cpu/riscv/globals_riscv.hpp @@ -120,6 +120,7 @@ define_pd_global(intx, InlineSmallCode, 1000); product(bool, UseZvbb, false, DIAGNOSTIC, "Use Zvbb instructions") \ product(bool, UseZvbc, false, EXPERIMENTAL, "Use Zvbc instructions") \ product(bool, UseZvfh, false, DIAGNOSTIC, "Use Zvfh instructions") \ + product(bool, UseZvfhmin, false, DIAGNOSTIC, "Use Zvfhmin instructions") \ product(bool, UseZvkg, false, DIAGNOSTIC, "Use Zvkg instructions") \ product(bool, UseZvkn, false, DIAGNOSTIC, \ "Use Zvkn group extension, Zvkned, Zvknhb, Zvkb, Zvkt") \ diff --git a/src/hotspot/cpu/riscv/riscv_v.ad b/src/hotspot/cpu/riscv/riscv_v.ad index a0af43364cb..2a63221de04 100644 --- a/src/hotspot/cpu/riscv/riscv_v.ad +++ b/src/hotspot/cpu/riscv/riscv_v.ad @@ -113,6 +113,7 @@ source %{ break; case Op_VectorCastHF2F: case Op_VectorCastF2HF: + return UseZvfh || UseZvfhmin; case Op_AddVHF: case Op_SubVHF: case Op_MulVHF: diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.hpp b/src/hotspot/cpu/riscv/vm_version_riscv.hpp index 11a88dfedd7..e5d925d1bea 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.hpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.hpp @@ -219,78 +219,80 @@ class VM_Version : public Abstract_VM_Version { // // Fields description in `decl`: // declaration name, extension name, bit value from linux, feature string?, mapped flag) - #define RV_EXT_FEATURE_FLAGS(decl) \ - /* A Atomic Instructions */ \ - decl(a , ('A' - 'A'), true , NO_UPDATE_DEFAULT) \ - /* C Compressed Instructions */ \ - decl(c , ('C' - 'A'), true , UPDATE_DEFAULT(UseRVC)) \ - /* D Single-Precision Floating-Point */ \ - decl(d , ('D' - 'A'), true , NO_UPDATE_DEFAULT) \ - /* F Single-Precision Floating-Point */ \ - decl(f , ('F' - 'A'), true , NO_UPDATE_DEFAULT) \ - /* H Hypervisor */ \ - decl(h , ('H' - 'A'), true , NO_UPDATE_DEFAULT) \ - /* I RV64I */ \ - decl(i , ('I' - 'A'), true , NO_UPDATE_DEFAULT) \ - /* M Integer Multiplication and Division */ \ - decl(m , ('M' - 'A'), true , NO_UPDATE_DEFAULT) \ - /* Q Quad-Precision Floating-Point */ \ - decl(q , ('Q' - 'A'), true , NO_UPDATE_DEFAULT) \ - /* V Vector */ \ - decl(v , ('V' - 'A'), true , UPDATE_DEFAULT(UseRVV)) \ - \ - /* ----------------------- Other extensions ----------------------- */ \ - \ - /* Atomic compare-and-swap (CAS) instructions */ \ - decl(Zacas , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZacas)) \ - /* Zba Address generation instructions */ \ - decl(Zba , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZba)) \ - /* Zbb Basic bit-manipulation */ \ - decl(Zbb , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZbb)) \ - /* Zbc Carry-less multiplication */ \ - decl(Zbc , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \ - /* Bitmanip instructions for Cryptography */ \ - decl(Zbkb , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZbkb)) \ - /* Zbs Single-bit instructions */ \ - decl(Zbs , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZbs)) \ - /* Zcb Simple code-size saving instructions */ \ - decl(Zcb , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZcb)) \ - /* Additional Floating-Point instructions */ \ - decl(Zfa , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfa)) \ - /* Zfh Half-Precision Floating-Point instructions */ \ - decl(Zfh , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfh)) \ - /* Zfhmin Minimal Half-Precision Floating-Point instructions */ \ - decl(Zfhmin , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfhmin)) \ - /* Zicbom Cache Block Management Operations */ \ - decl(Zicbom , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicbom)) \ - /* Zicbop Cache Block Prefetch Operations */ \ - 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)) \ - /* Base Counters and Timers */ \ - decl(Zicntr , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \ - /* Zicond Conditional operations */ \ - decl(Zicond , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicond)) \ - /* Zicsr Control and Status Register (CSR) Instructions */ \ - decl(Zicsr , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \ - /* Zic64b Cache blocks must be 64 bytes in size, naturally aligned in the address space. */ \ - decl(Zic64b , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZic64b)) \ - /* Zifencei Instruction-Fetch Fence */ \ - decl(Zifencei , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \ - /* Zihintpause Pause instruction HINT */ \ - decl(Zihintpause , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZihintpause)) \ - /* Total Store Ordering */ \ - decl(Ztso , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZtso)) \ - /* Vector Basic Bit-manipulation */ \ - decl(Zvbb , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvbb, &ext_v, nullptr)) \ - /* Vector Carryless Multiplication */ \ - decl(Zvbc , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvbc, &ext_v, nullptr)) \ - /* Vector Extension for Half-Precision Floating-Point */ \ - decl(Zvfh , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvfh, &ext_v, &ext_Zfh, nullptr)) \ - /* Shorthand for Zvkned + Zvknhb + Zvkb + Zvkt */ \ - decl(Zvkn , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvkn, &ext_v, nullptr)) \ - /* Zvkg crypto extension for ghash and gcm */ \ - decl(Zvkg , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvkg, &ext_v, nullptr)) \ + #define RV_EXT_FEATURE_FLAGS(decl) \ + /* A Atomic Instructions */ \ + decl(a , ('A' - 'A'), true , NO_UPDATE_DEFAULT) \ + /* C Compressed Instructions */ \ + decl(c , ('C' - 'A'), true , UPDATE_DEFAULT(UseRVC)) \ + /* D Single-Precision Floating-Point */ \ + decl(d , ('D' - 'A'), true , NO_UPDATE_DEFAULT) \ + /* F Single-Precision Floating-Point */ \ + decl(f , ('F' - 'A'), true , NO_UPDATE_DEFAULT) \ + /* H Hypervisor */ \ + decl(h , ('H' - 'A'), true , NO_UPDATE_DEFAULT) \ + /* I RV64I */ \ + decl(i , ('I' - 'A'), true , NO_UPDATE_DEFAULT) \ + /* M Integer Multiplication and Division */ \ + decl(m , ('M' - 'A'), true , NO_UPDATE_DEFAULT) \ + /* Q Quad-Precision Floating-Point */ \ + decl(q , ('Q' - 'A'), true , NO_UPDATE_DEFAULT) \ + /* V Vector */ \ + decl(v , ('V' - 'A'), true , UPDATE_DEFAULT(UseRVV)) \ + \ + /* ----------------------- Other extensions ----------------------- */ \ + \ + /* Atomic compare-and-swap (CAS) instructions */ \ + decl(Zacas , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZacas)) \ + /* Zba Address generation instructions */ \ + decl(Zba , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZba)) \ + /* Zbb Basic bit-manipulation */ \ + decl(Zbb , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZbb)) \ + /* Zbc Carry-less multiplication */ \ + decl(Zbc , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \ + /* Bitmanip instructions for Cryptography */ \ + decl(Zbkb , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZbkb)) \ + /* Zbs Single-bit instructions */ \ + decl(Zbs , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZbs)) \ + /* Zcb Simple code-size saving instructions */ \ + decl(Zcb , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZcb)) \ + /* Additional Floating-Point instructions */ \ + decl(Zfa , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfa)) \ + /* Zfh Half-Precision Floating-Point instructions */ \ + decl(Zfh , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfh)) \ + /* Zfhmin Minimal Half-Precision Floating-Point instructions */ \ + decl(Zfhmin , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfhmin)) \ + /* Zicbom Cache Block Management Operations */ \ + decl(Zicbom , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicbom)) \ + /* Zicbop Cache Block Prefetch Operations */ \ + 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)) \ + /* Base Counters and Timers */ \ + decl(Zicntr , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \ + /* Zicond Conditional operations */ \ + decl(Zicond , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicond)) \ + /* Zicsr Control and Status Register (CSR) Instructions */ \ + decl(Zicsr , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \ + /* Zic64b Cache blocks must be 64 bytes in size, naturally aligned in the address space. */ \ + decl(Zic64b , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZic64b)) \ + /* Zifencei Instruction-Fetch Fence */ \ + decl(Zifencei , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \ + /* Zihintpause Pause instruction HINT */ \ + decl(Zihintpause , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZihintpause)) \ + /* Total Store Ordering */ \ + decl(Ztso , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZtso)) \ + /* Vector Basic Bit-manipulation */ \ + decl(Zvbb , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvbb, &ext_v, nullptr)) \ + /* Vector Carryless Multiplication */ \ + decl(Zvbc , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvbc, &ext_v, nullptr)) \ + /* Vector Extension for Half-Precision Floating-Point */ \ + decl(Zvfh , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvfh, &ext_v, &ext_Zfhmin, nullptr)) \ + /* Vector Extension for Minimal Half-Precision Floating-Point */ \ + decl(Zvfhmin , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvfhmin, &ext_v, nullptr)) \ + /* Shorthand for Zvkned + Zvknhb + Zvkb + Zvkt */ \ + decl(Zvkn , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvkn, &ext_v, nullptr)) \ + /* Zvkg crypto extension for ghash and gcm */ \ + decl(Zvkg , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvkg, &ext_v, nullptr)) \ #define DECLARE_RV_EXT_FEATURE(PRETTY, LINUX_BIT, FSTRING, FLAGF) \ struct ext_##PRETTY##RVExtFeatureValue : public RVExtFeatureValue { \ @@ -442,6 +444,7 @@ class VM_Version : public Abstract_VM_Version { RV_ENABLE_EXTENSION(UseZicboz) \ RV_ENABLE_EXTENSION(UseZicond) \ RV_ENABLE_EXTENSION(UseZihintpause) \ + RV_ENABLE_EXTENSION(UseZvfhmin) \ static void useRVA23U64Profile(); diff --git a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp index fe555ec5ffb..bbefd4ba50d 100644 --- a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp +++ b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp @@ -247,6 +247,9 @@ void RiscvHwprobe::add_features_from_query_result() { if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVFH)) { VM_Version::ext_Zvfh.enable_feature(); } + if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVFHMIN)) { + VM_Version::ext_Zvfhmin.enable_feature(); + } if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVKNED) && is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVKNHB) && is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVKB) && From 9e9fae6584c8865c20542303e9349a0799888330 Mon Sep 17 00:00:00 2001 From: Thomas Stuefe Date: Fri, 17 Jul 2026 13:44:59 +0000 Subject: [PATCH 261/707] 8387652: [aarch64] Fallback mode for narrow klass decoding Reviewed-by: aph, adinn, rkennke, galder --- src/hotspot/cpu/aarch64/aarch64.ad | 8 +- .../cpu/aarch64/c1_LIRAssembler_aarch64.cpp | 42 ++-- .../cpu/aarch64/c1_MacroAssembler_aarch64.cpp | 2 +- .../cpu/aarch64/c1_Runtime1_aarch64.cpp | 6 +- .../cpu/aarch64/c2_MacroAssembler_aarch64.cpp | 2 +- .../cpu/aarch64/compressedKlass_aarch64.cpp | 8 +- .../cpu/aarch64/interp_masm_aarch64.cpp | 2 +- .../cpu/aarch64/macroAssembler_aarch64.cpp | 195 ++++++++---------- .../cpu/aarch64/macroAssembler_aarch64.hpp | 55 ++--- .../cpu/aarch64/methodHandles_aarch64.cpp | 6 +- .../cpu/aarch64/stubGenerator_aarch64.cpp | 12 +- .../cpu/aarch64/templateTable_aarch64.cpp | 26 +-- .../cpu/aarch64/vtableStubs_aarch64.cpp | 4 +- src/hotspot/share/cds/aotMetaspace.cpp | 26 +-- src/hotspot/share/memory/metaspace.cpp | 3 +- src/hotspot/share/oops/compressedKlass.cpp | 21 +- src/hotspot/share/oops/compressedKlass.hpp | 11 +- .../gtest/aarch64/test_assembler_aarch64.cpp | 139 +++++++++++++ test/hotspot/jtreg/gtest/AssemblerGtests.java | 51 +++++ ...CompressedClassPointersEncodingScheme.java | 115 ++++------- .../AccessZeroNKlassHitsProtectionZone.java | 2 +- 21 files changed, 420 insertions(+), 316 deletions(-) create mode 100644 test/hotspot/jtreg/gtest/AssemblerGtests.java diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index be9d79d03c7..37c8e0ae011 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -8228,7 +8228,7 @@ instruct encodeKlass_not_null(iRegNNoSp dst, iRegP src) %{ ins_encode %{ Register src_reg = as_Register($src$$reg); Register dst_reg = as_Register($dst$$reg); - __ encode_klass_not_null(dst_reg, src_reg); + __ encode_klass_not_null(dst_reg, src_reg, rscratch1); %} ins_pipe(ialu_reg); @@ -8243,11 +8243,7 @@ instruct decodeKlass_not_null(iRegPNoSp dst, iRegN src) %{ ins_encode %{ Register src_reg = as_Register($src$$reg); Register dst_reg = as_Register($dst$$reg); - if (dst_reg != src_reg) { - __ decode_klass_not_null(dst_reg, src_reg); - } else { - __ decode_klass_not_null(dst_reg); - } + __ decode_klass_not_null(dst_reg, src_reg, rscratch1); %} ins_pipe(ialu_reg); diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index 0290a200366..5b77d15457f 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -1324,7 +1324,7 @@ void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, L __ bind(not_null); Register recv = k_RInfo; - __ load_klass(recv, obj); + __ load_klass(recv, obj, rscratch1); type_profile_helper(mdo, md, data, recv); } else { __ cbz(obj, *obj_is_null); @@ -1340,15 +1340,15 @@ void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, L if (op->fast_check()) { // get object class // not a safepoint as obj null check happens earlier - __ load_klass(rscratch1, obj); - __ cmp( rscratch1, k_RInfo); + __ load_klass(rscratch2, obj, rscratch1); + __ cmp( rscratch2, k_RInfo); __ br(Assembler::NE, *failure_target); // successful cast, fall through to profile or jump } else { // get object class // not a safepoint as obj null check happens earlier - __ load_klass(klass_RInfo, obj); + __ load_klass(klass_RInfo, obj, rscratch1); if (k->is_loaded()) { // See if we get an immediate positive hit __ ldr(rscratch1, Address(klass_RInfo, int64_t(k->super_check_offset()))); @@ -1433,15 +1433,15 @@ void LIR_Assembler::emit_opTypeCheck(LIR_OpTypeCheck* op) { __ bind(not_null); Register recv = k_RInfo; - __ load_klass(recv, value); + __ load_klass(recv, value, rscratch1); type_profile_helper(mdo, md, data, recv); } else { __ cbz(value, done); } add_debug_info_for_null_check_here(op->info_for_exception()); - __ load_klass(k_RInfo, array); - __ load_klass(klass_RInfo, value); + __ load_klass(k_RInfo, array, rscratch1); + __ load_klass(klass_RInfo, value, rscratch1); // get instance klass (it's already uncompressed) __ ldr(k_RInfo, Address(k_RInfo, ObjArrayKlass::element_klass_offset())); @@ -2258,14 +2258,14 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { // an instance type. if (flags & LIR_OpArrayCopy::type_check) { if (!(flags & LIR_OpArrayCopy::LIR_OpArrayCopy::dst_objarray)) { - __ load_klass(tmp, dst); + __ load_klass(tmp, dst, rscratch1); __ ldrw(rscratch1, Address(tmp, in_bytes(Klass::layout_helper_offset()))); __ cmpw(rscratch1, Klass::_lh_neutral_value); __ br(Assembler::GE, *stub->entry()); } if (!(flags & LIR_OpArrayCopy::LIR_OpArrayCopy::src_objarray)) { - __ load_klass(tmp, src); + __ load_klass(tmp, src, rscratch1); __ ldrw(rscratch1, Address(tmp, in_bytes(Klass::layout_helper_offset()))); __ cmpw(rscratch1, Klass::_lh_neutral_value); __ br(Assembler::GE, *stub->entry()); @@ -2319,8 +2319,8 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { __ PUSH(src, dst); - __ load_klass(src, src); - __ load_klass(dst, dst); + __ load_klass(src, src, rscratch1); + __ load_klass(dst, dst, rscratch1); __ check_klass_subtype_fast_path(src, dst, tmp, &cont, &slow, nullptr); @@ -2344,9 +2344,9 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { assert(flags & mask, "one of the two should be known to be an object array"); if (!(flags & LIR_OpArrayCopy::src_objarray)) { - __ load_klass(tmp, src); + __ load_klass(tmp, src, rscratch1); } else if (!(flags & LIR_OpArrayCopy::dst_objarray)) { - __ load_klass(tmp, dst); + __ load_klass(tmp, dst, rscratch1); } int lh_offset = in_bytes(Klass::layout_helper_offset()); Address klass_lh_addr(tmp, lh_offset); @@ -2372,7 +2372,7 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { __ uxtw(c_rarg2, length); assert_different_registers(c_rarg2, dst); - __ load_klass(c_rarg4, dst); + __ load_klass(c_rarg4, dst, rscratch1); __ ldr(c_rarg4, Address(c_rarg4, ObjArrayKlass::element_klass_offset())); __ ldrw(c_rarg3, Address(c_rarg4, Klass::super_check_offset_offset())); __ far_call(RuntimeAddress(copyfunc_addr)); @@ -2428,12 +2428,12 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { __ mov_metadata(tmp, default_type->constant_encoding()); if (basic_type != T_OBJECT) { - __ cmp_klass(dst, tmp, rscratch1); + __ cmp_klass(dst, tmp, rscratch1, rscratch2); __ br(Assembler::NE, halt); - __ cmp_klass(src, tmp, rscratch1); + __ cmp_klass(src, tmp, rscratch1, rscratch2); __ br(Assembler::EQ, known_ok); } else { - __ cmp_klass(dst, tmp, rscratch1); + __ cmp_klass(dst, tmp, rscratch1, rscratch2); __ br(Assembler::EQ, known_ok); __ cmp(src, dst); __ br(Assembler::EQ, known_ok); @@ -2508,7 +2508,7 @@ void LIR_Assembler::emit_load_klass(LIR_OpLoadKlass* op) { add_debug_info_for_null_check_here(info); } - __ load_klass(result, obj); + __ load_klass(result, obj, rscratch1); } void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) { @@ -2550,7 +2550,7 @@ void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) { // Fall back to runtime helper to handle the rest at runtime. __ mov_metadata(recv, known_klass->constant_encoding()); } else { - __ load_klass(recv, recv); + __ load_klass(recv, recv, rscratch1); } type_profile_helper(mdo, md, data, recv); } else { @@ -2636,7 +2636,7 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) { #ifdef ASSERT if (exact_klass != nullptr) { Label ok; - __ load_klass(tmp, tmp); + __ load_klass(tmp, tmp, rscratch1); __ mov_metadata(rscratch1, exact_klass->constant_encoding()); __ eor(rscratch1, tmp, rscratch1); __ cbz(rscratch1, ok); @@ -2649,7 +2649,7 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) { if (exact_klass != nullptr) { __ mov_metadata(tmp, exact_klass->constant_encoding()); } else { - __ load_klass(tmp, tmp); + __ load_klass(tmp, tmp, rscratch1); } __ ldr(rscratch2, mdo_addr); diff --git a/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp index 89a9422ea48..f81c976d291 100644 --- a/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp @@ -105,7 +105,7 @@ void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register } else { mov(t1, checked_cast(markWord::prototype().value())); str(t1, Address(obj, oopDesc::mark_offset_in_bytes())); - encode_klass_not_null(t1, klass); // Take care not to kill klass + encode_klass_not_null(t1, klass, t1); // Take care not to kill klass strw(t1, Address(obj, oopDesc::klass_offset_in_bytes())); } diff --git a/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp index 449ad4f8a4c..1745bb8aae9 100644 --- a/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp @@ -824,7 +824,7 @@ OopMapSet* Runtime1::generate_code_for(StubId id, StubAssembler* sasm) { // load the klass and check the has finalizer flag Label register_finalizer; Register t = r5; - __ load_klass(t, r0); + __ load_klass(t, r0, rscratch1); __ ldrb(t, Address(t, Klass::misc_flags_offset())); __ tbnz(t, exact_log2(KlassFlags::_misc_has_finalizer), register_finalizer); __ ret(lr); @@ -947,7 +947,7 @@ OopMapSet* Runtime1::generate_code_for(StubId id, StubAssembler* sasm) { __ br(Assembler::EQ, is_secondary); // Klass is a secondary superclass // Klass is a concrete class - __ load_klass(r5, obj); + __ load_klass(r5, obj, rscratch1); __ ldr(rscratch1, Address(r5, r3)); __ cmp(klass, rscratch1); __ cset(result, Assembler::EQ); @@ -955,7 +955,7 @@ OopMapSet* Runtime1::generate_code_for(StubId id, StubAssembler* sasm) { __ bind(is_secondary); - __ load_klass(obj, obj); + __ load_klass(obj, obj, rscratch1); // This is necessary because I am never in my own secondary_super list. __ cmp(obj, klass); diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index fe9180bda5c..3321fcc4edb 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -167,7 +167,7 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register t1, } if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(t1, obj); + load_klass(t1, obj, rscratch2); ldrb(t1, Address(t1, Klass::misc_flags_offset())); tst(t1, KlassFlags::_misc_is_value_based_class); br(Assembler::NE, slow_path); diff --git a/src/hotspot/cpu/aarch64/compressedKlass_aarch64.cpp b/src/hotspot/cpu/aarch64/compressedKlass_aarch64.cpp index 3874c8cd54e..7cc2a004c40 100644 --- a/src/hotspot/cpu/aarch64/compressedKlass_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/compressedKlass_aarch64.cpp @@ -120,11 +120,7 @@ char* CompressedKlassPointers::reserve_address_space_for_compressed_classes(size return result; } -bool CompressedKlassPointers::check_klass_decode_mode(address base, int shift, const size_t range) { - return MacroAssembler::check_klass_decode_mode(base, shift, range); -} - -bool CompressedKlassPointers::set_klass_decode_mode() { +void CompressedKlassPointers::initialize_pd() { const size_t range = klass_range_end() - base(); - return MacroAssembler::set_klass_decode_mode(_base, _shift, range); + MacroAssembler::initialize_klass_decode_mode(_base, _shift, range); } diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp index 22c2383816c..0da237c133f 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp @@ -1403,7 +1403,7 @@ void InterpreterMacroAssembler::profile_obj_type(Register obj, const Address& md b(next); bind(update); - load_klass(obj, obj); + load_klass(obj, obj, rscratch1); ldr(rscratch1, mdo_addr); eor(obj, obj, rscratch1); diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index f2208aa0ad6..527e79459ec 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -25,6 +25,7 @@ #include "asm/assembler.hpp" #include "asm/assembler.inline.hpp" +#include "cds/archiveBuilder.hpp" #include "ci/ciEnv.hpp" #include "code/compiledIC.hpp" #include "compiler/compileTask.hpp" @@ -5115,9 +5116,9 @@ void MacroAssembler::load_narrow_klass(Register dst, Register src) { } } -void MacroAssembler::load_klass(Register dst, Register src) { +void MacroAssembler::load_klass(Register dst, Register src, Register tmp) { load_narrow_klass(dst, src); - decode_klass_not_null(dst); + decode_klass_not_null(dst, dst, tmp); } void MacroAssembler::restore_cpu_control_state_after_jni(Register tmp1, Register tmp2) { @@ -5167,8 +5168,8 @@ void MacroAssembler::load_mirror(Register dst, Register method, Register tmp1, R resolve_oop_handle(dst, tmp1, tmp2); } -void MacroAssembler::cmp_klass(Register obj, Register klass, Register tmp) { - assert_different_registers(obj, klass, tmp); +void MacroAssembler::cmp_klass(Register obj, Register klass, Register tmp, Register tmp2) { + assert_different_registers(obj, klass, tmp, tmp2); if (UseCompactObjectHeaders) { load_narrow_klass_compact(tmp, obj); } else { @@ -5184,7 +5185,7 @@ void MacroAssembler::cmp_klass(Register obj, Register klass, Register tmp) { cmpw(klass, tmp); return; } - decode_klass_not_null(tmp); + decode_klass_not_null(tmp, tmp, tmp2); cmp(klass, tmp); } @@ -5199,11 +5200,11 @@ void MacroAssembler::cmp_klasses_from_objects(Register obj1, Register obj2, Regi cmpw(tmp1, tmp2); } -void MacroAssembler::store_klass(Register dst, Register src) { +void MacroAssembler::store_klass(Register dst, Register src, Register tmp) { // FIXME: Should this be a store release? concurrent gcs assumes // klass length is valid if klass field is not null. assert(!UseCompactObjectHeaders, "not with compact headers"); - encode_klass_not_null(src); + encode_klass_not_null(src, src, tmp); strw(src, Address(dst, oopDesc::klass_offset_in_bytes())); } @@ -5356,8 +5357,6 @@ MacroAssembler::KlassDecodeMode MacroAssembler::klass_decode_mode() { } MacroAssembler::KlassDecodeMode MacroAssembler::klass_decode_mode(address base, int shift, const size_t range) { - // KlassDecodeMode shouldn't be set already. - assert(_klass_decode_mode == KlassDecodeNone, "set once"); if (base == nullptr) { return KlassDecodeZero; @@ -5377,148 +5376,128 @@ MacroAssembler::KlassDecodeMode MacroAssembler::klass_decode_mode(address base, return KlassDecodeMovk; } - // No valid encoding. - return KlassDecodeNone; -} - -// Check if one of the above decoding modes will work for given base, shift and range. -bool MacroAssembler::check_klass_decode_mode(address base, int shift, const size_t range) { - return klass_decode_mode(base, shift, range) != KlassDecodeNone; + return KlassDecodeFallback; } -bool MacroAssembler::set_klass_decode_mode(address base, int shift, const size_t range) { +void MacroAssembler::initialize_klass_decode_mode(address base, int shift, const size_t range) { + // KlassDecodeMode shouldn't be set already. + assert(_klass_decode_mode == KlassDecodeNone, "set once"); _klass_decode_mode = klass_decode_mode(base, shift, range); - return _klass_decode_mode != KlassDecodeNone; + log_info(metaspace)("Klass Decode Mode: %d", (int)_klass_decode_mode); } -static Register pick_different_tmp(Register dst, Register src) { - auto tmps = RegSet::of(r0, r1, r2) - RegSet::of(src, dst); - return *tmps.begin(); +void MacroAssembler::encode_klass_not_null(Register dst, Register src, Register tmp) { + emit_encode_klass_not_null(dst, src, tmp, CompressedKlassPointers::base(), + CompressedKlassPointers::shift(), klass_decode_mode()); } -void MacroAssembler::encode_klass_not_null_for_aot(Register dst, Register src) { - // we have to load the klass base from the AOT constants area but - // not the shift because it is not allowed to change - int shift = CompressedKlassPointers::shift(); - assert(shift >= 0 && shift <= CompressedKlassPointers::max_shift(), "unexpected compressed klass shift!"); - if (dst != src) { - // we can load the base into dst, subtract it formthe src and shift down - lea(dst, ExternalAddress(CompressedKlassPointers::base_addr())); - ldr(dst, dst); - sub(dst, src, dst); - lsr(dst, dst, shift); - } else { - // we need an extra register in order to load the coop base - Register tmp = pick_different_tmp(dst, src); - RegSet regs = RegSet::of(tmp); - push(regs, sp); +void MacroAssembler::emit_encode_klass_not_null(Register dst, Register src, Register tmp, + address base, int shift, KlassDecodeMode decode_mode) { + + assert_different_registers(tmp, src); + assert(tmp != noreg, "valid tmp required"); + + if (AOTCodeCache::is_on_for_dump()) { + // We are generating code during AOT buildup that will run in *future* processes + // with likely different encoding settings. Therefore, we have to load the + // encoding base dynamically, we cannot just bake it in as immediate. + // Note that we only need to do this for base. The encoding shift would be the + // same between build time and runtime: the standard precomputed shift. + assert(shift == ArchiveBuilder::precomputed_narrow_klass_shift(), "unexpected compressed klass shift!"); lea(tmp, ExternalAddress(CompressedKlassPointers::base_addr())); ldr(tmp, tmp); sub(dst, src, tmp); lsr(dst, dst, shift); - pop(regs, sp); - } -} - -void MacroAssembler::encode_klass_not_null(Register dst, Register src) { - if (CompressedKlassPointers::base() != nullptr && AOTCodeCache::is_on_for_dump()) { - encode_klass_not_null_for_aot(dst, src); return; } - switch (klass_decode_mode()) { + switch (decode_mode) { case KlassDecodeZero: - if (CompressedKlassPointers::shift() != 0) { - lsr(dst, src, CompressedKlassPointers::shift()); - } else { - if (dst != src) mov(dst, src); - } + lsr(dst, src, shift); break; case KlassDecodeXor: - if (CompressedKlassPointers::shift() != 0) { - eor(dst, src, (uint64_t)CompressedKlassPointers::base()); - lsr(dst, dst, CompressedKlassPointers::shift()); - } else { - eor(dst, src, (uint64_t)CompressedKlassPointers::base()); - } + eor(dst, src, (uint64_t)base); + lsr(dst, dst, shift); break; case KlassDecodeMovk: - if (CompressedKlassPointers::shift() != 0) { - ubfx(dst, src, CompressedKlassPointers::shift(), 32); + if (shift != 0) { + ubfx(dst, src, shift, 32); } else { movw(dst, src); } break; + case KlassDecodeFallback: { + mov(tmp, base); + sub(dst, src, tmp); + lsr(dst, dst, shift); + break; + } + case KlassDecodeNone: ShouldNotReachHere(); break; } + +#ifdef ASSERT + if (tmp != dst) { + mov(tmp, 0xdead); + } +#endif // ASSERT + } -void MacroAssembler::encode_klass_not_null(Register r) { - encode_klass_not_null(r, r); +void MacroAssembler::decode_klass_not_null(Register dst, Register src, Register tmp) { + emit_decode_klass_not_null(dst, src, tmp, + CompressedKlassPointers::base(), + CompressedKlassPointers::shift(), + klass_decode_mode()); } -void MacroAssembler::decode_klass_not_null_for_aot(Register dst, Register src) { - // we have to load the klass base from the AOT constants area but - // not the shift because it is not allowed to change - int shift = CompressedKlassPointers::shift(); - assert(shift >= 0 && shift <= CompressedKlassPointers::max_shift(), "unexpected compressed klass shift!"); - if (dst != src) { - // we can load the base into dst then add the offset with a suitable shift - lea(dst, ExternalAddress(CompressedKlassPointers::base_addr())); - ldr(dst, dst); - add(dst, dst, src, LSL, shift); - } else { - // we need an extra register in order to load the coop base - Register tmp = pick_different_tmp(dst, src); - RegSet regs = RegSet::of(tmp); - push(regs, sp); +void MacroAssembler::emit_decode_klass_not_null(Register dst, Register src, Register tmp, + address base, int shift, KlassDecodeMode decode_mode) { + + assert_different_registers(tmp, src); + assert(tmp != noreg, "valid tmp required"); + + if (AOTCodeCache::is_on_for_dump()) { + // We are generating code during AOT buildup that will run in *future* processes + // with likely different encoding settings. Therefore, we have to load the + // encoding base dynamically, we cannot just bake it in as immediate. + // Note that we only need to do this for base. The encoding shift would be the + // same between build time and runtime: the standard precomputed shift. + assert(shift == ArchiveBuilder::precomputed_narrow_klass_shift(), "unexpected compressed klass shift!"); lea(tmp, ExternalAddress(CompressedKlassPointers::base_addr())); ldr(tmp, tmp); add(dst, tmp, src, LSL, shift); - pop(regs, sp); - } -} - -void MacroAssembler::decode_klass_not_null(Register dst, Register src) { - if (AOTCodeCache::is_on_for_dump()) { - decode_klass_not_null_for_aot(dst, src); return; } - switch (klass_decode_mode()) { - case KlassDecodeZero: - if (CompressedKlassPointers::shift() != 0) { - lsl(dst, src, CompressedKlassPointers::shift()); - } else { - if (dst != src) mov(dst, src); - } + switch (decode_mode) { + case KlassDecodeZero: // 0-1 instructions + lsl(dst, src, shift); break; - case KlassDecodeXor: - if (CompressedKlassPointers::shift() != 0) { - lsl(dst, src, CompressedKlassPointers::shift()); - eor(dst, dst, (uint64_t)CompressedKlassPointers::base()); - } else { - eor(dst, src, (uint64_t)CompressedKlassPointers::base()); - } + case KlassDecodeXor: // 1-2 instructions + lsl(dst, src, shift); + eor(dst, dst, (uint64_t)base); break; - case KlassDecodeMovk: { + case KlassDecodeMovk: { // 1-3 instructions const uint64_t shifted_base = - (uint64_t)CompressedKlassPointers::base() >> CompressedKlassPointers::shift(); + (uint64_t)base >> shift; if (dst != src) movw(dst, src); movk(dst, shifted_base >> 32, 32); + lsl(dst, dst, shift); + break; + } - if (CompressedKlassPointers::shift() != 0) { - lsl(dst, dst, CompressedKlassPointers::shift()); - } - + case KlassDecodeFallback: { // 3-4 instructions + mov(tmp, base); + add(dst, tmp, src, LSL, shift); break; } @@ -5526,10 +5505,14 @@ void MacroAssembler::decode_klass_not_null(Register dst, Register src) { ShouldNotReachHere(); break; } -} -void MacroAssembler::decode_klass_not_null(Register r) { - decode_klass_not_null(r, r); +#ifdef ASSERT + // Always clobber tmp + if (tmp != dst) { + mov(tmp, 0xdead); + } +#endif // ASSERT + } void MacroAssembler::set_narrow_oop(Register dst, jobject obj) { @@ -7181,7 +7164,7 @@ void MacroAssembler::fast_lock(Register basic_lock, Register obj, Register t1, R } if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(t1, obj); + load_klass(t1, obj, rscratch1); ldrb(t1, Address(t1, Klass::misc_flags_offset())); tst(t1, KlassFlags::_misc_is_value_based_class); br(Assembler::NE, slow); diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index b39596aab53..6dfdde51ac5 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -38,6 +38,7 @@ #include "utilities/powerOfTwo.hpp" class OopMap; +struct GtestFriendToMacroAssembler; // MacroAssembler extends Assembler by frequently used macros. // @@ -46,6 +47,7 @@ class OopMap; class MacroAssembler: public Assembler { friend class LIR_Assembler; + friend struct GtestFriendToMacroAssembler; public: using Assembler::mov; @@ -91,28 +93,31 @@ class MacroAssembler: public Assembler { void call_VM_helper(Register oop_result, address entry_point, int number_of_arguments, bool check_exceptions = true); + private: + enum KlassDecodeMode { KlassDecodeNone, KlassDecodeZero, KlassDecodeXor, - KlassDecodeMovk + KlassDecodeMovk, + KlassDecodeFallback }; - // Calculate decoding mode based on given parameters, used for checking then ultimately setting. - static KlassDecodeMode klass_decode_mode(address base, int shift, const size_t range); - - private: static KlassDecodeMode _klass_decode_mode; // Returns above setting with asserts static KlassDecodeMode klass_decode_mode(); - public: - // Checks the decode mode and returns false if not compatible with preferred decoding mode. - static bool check_klass_decode_mode(address base, int shift, const size_t range); + // Calculate decoding mode based on given parameters, used for checking then ultimately setting. + static KlassDecodeMode klass_decode_mode(address base, int shift, const size_t range); - // Sets the decode mode and returns false if cannot be set. - static bool set_klass_decode_mode(address base, int shift, const size_t range); + void emit_encode_klass_not_null(Register dst, Register src, Register tmp, + address base, int shift, KlassDecodeMode decode_mode); + void emit_decode_klass_not_null(Register dst, Register src, Register tmp, + address base, int shift, KlassDecodeMode decode_mode); + public: + // Determines the decode mode best suited for the given encoding parameters. + static void initialize_klass_decode_mode(address base, int shift, const size_t range); public: MacroAssembler(CodeBuffer* code) : Assembler(code) {} @@ -308,19 +313,27 @@ class MacroAssembler: public Assembler { } inline void lslw(Register Rd, Register Rn, unsigned imm) { - ubfmw(Rd, Rn, ((32 - imm) & 31), (31 - imm)); + if (imm > 0 || Rd != Rn) { + ubfmw(Rd, Rn, ((32 - imm) & 31), (31 - imm)); + } } inline void lsl(Register Rd, Register Rn, unsigned imm) { - ubfm(Rd, Rn, ((64 - imm) & 63), (63 - imm)); + if (imm > 0 || Rd != Rn) { + ubfm(Rd, Rn, ((64 - imm) & 63), (63 - imm)); + } } inline void lsrw(Register Rd, Register Rn, unsigned imm) { - ubfmw(Rd, Rn, imm, 31); + if (imm > 0 || Rd != Rn) { + ubfmw(Rd, Rn, imm, 31); + } } inline void lsr(Register Rd, Register Rn, unsigned imm) { - ubfm(Rd, Rn, imm, 63); + if (imm > 0 || Rd != Rn) { + ubfm(Rd, Rn, imm, 63); + } } inline void rorw(Register Rd, Register Rn, unsigned imm) { @@ -925,9 +938,9 @@ class MacroAssembler: public Assembler { // oop manipulations void load_narrow_klass_compact(Register dst, Register src); void load_narrow_klass(Register dst, Register src); - void load_klass(Register dst, Register src); - void store_klass(Register dst, Register src); - void cmp_klass(Register obj, Register klass, Register tmp); + void load_klass(Register dst, Register src, Register tmp); + void store_klass(Register dst, Register src, Register tmp); + void cmp_klass(Register obj, Register klass, Register tmp, Register tmp2); void cmp_klasses_from_objects(Register obj1, Register obj2, Register tmp1, Register tmp2); void resolve_weak_handle(Register result, Register tmp1, Register tmp2); @@ -972,12 +985,8 @@ class MacroAssembler: public Assembler { void set_narrow_oop(Register dst, jobject obj); - void decode_klass_not_null_for_aot(Register dst, Register src); - void encode_klass_not_null_for_aot(Register dst, Register src); - void encode_klass_not_null(Register r); - void decode_klass_not_null(Register r); - void encode_klass_not_null(Register dst, Register src); - void decode_klass_not_null(Register dst, Register src); + void encode_klass_not_null(Register dst, Register src, Register tmp); + void decode_klass_not_null(Register dst, Register src, Register tmp); void set_narrow_klass(Register dst, Klass* k); diff --git a/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp b/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp index cdf67e3423f..7dc74f44cdc 100644 --- a/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp @@ -76,7 +76,7 @@ void MethodHandles::verify_klass(MacroAssembler* _masm, __ verify_oop(obj); __ cbz(obj, L_bad); __ push(RegSet::of(temp, temp2), sp); - __ load_klass(temp, obj); + __ load_klass(temp, obj, temp2); __ cmpptr(temp, ExternalAddress((address) klass_addr)); __ br(Assembler::EQ, L_ok); intptr_t super_check_offset = klass->super_check_offset(); @@ -368,7 +368,7 @@ void MethodHandles::generate_method_handle_dispatch(MacroAssembler* _masm, __ null_check(receiver_reg); } else { // load receiver klass itself - __ load_klass(temp1_recv_klass, receiver_reg); + __ load_klass(temp1_recv_klass, receiver_reg, temp2); __ verify_klass_ptr(temp1_recv_klass); } BLOCK_COMMENT("check_receiver {"); @@ -376,7 +376,7 @@ void MethodHandles::generate_method_handle_dispatch(MacroAssembler* _masm, // Check the receiver against the MemberName.clazz if (VerifyMethodHandles && iid == vmIntrinsics::_linkToSpecial) { // Did not load it above... - __ load_klass(temp1_recv_klass, receiver_reg); + __ load_klass(temp1_recv_klass, receiver_reg, temp2); __ verify_klass_ptr(temp1_recv_klass); } if (VerifyMethodHandles && iid != vmIntrinsics::_linkToInterface) { diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index 5dfd41293fd..03eb5084eb4 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -2258,7 +2258,7 @@ class StubGenerator: public StubCodeGenerator { // checked. assert_different_registers(from, to, count, ckoff, ckval, start_to, - copied_oop, r19_klass, count_save); + copied_oop, r19_klass, count_save, rscratch1); __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); @@ -2342,7 +2342,7 @@ class StubGenerator: public StubCodeGenerator { gct1); __ cbz(copied_oop, L_store_element); - __ load_klass(r19_klass, copied_oop);// query the object klass + __ load_klass(r19_klass, copied_oop, rscratch1);// query the object klass BLOCK_COMMENT("type_check:"); generate_type_check(/*sub_klass*/r19_klass, @@ -2583,7 +2583,7 @@ class StubGenerator: public StubCodeGenerator { BLOCK_COMMENT("} assert klasses not null done"); } #endif - __ decode_klass_not_null(scratch_src_klass, scratch_src_klass); + __ decode_klass_not_null(scratch_src_klass, scratch_src_klass, rscratch1); // Load layout helper (32-bits) // @@ -2603,7 +2603,7 @@ class StubGenerator: public StubCodeGenerator { __ cbzw(rscratch2, L_objArray); // if (src->klass() != dst->klass()) return -1; - __ load_klass(rscratch2, dst); + __ load_klass(rscratch2, dst, rscratch1); __ eor(rscratch2, rscratch2, scratch_src_klass); __ cbnz(rscratch2, L_failed); @@ -2699,7 +2699,7 @@ class StubGenerator: public StubCodeGenerator { Label L_plain_copy, L_checkcast_copy; // test array classes for subtyping - __ load_klass(r15, dst); + __ load_klass(r15, dst, rscratch1); __ cmp(scratch_src_klass, r15); // usual case is exact equality __ br(Assembler::NE, L_checkcast_copy); @@ -2728,7 +2728,7 @@ class StubGenerator: public StubCodeGenerator { arraycopy_range_checks(src, src_pos, dst, dst_pos, scratch_length, r15, L_failed); - __ load_klass(dst_klass, dst); // reload + __ load_klass(dst_klass, dst, rscratch1); // reload // Marshal the base address arguments now, freeing registers. __ lea(from, Address(src, src_pos, Address::lsl(LogBytesPerHeapOop))); diff --git a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp index b6cf58d6062..a0ce1d04317 100644 --- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp @@ -1123,9 +1123,9 @@ void TemplateTable::aastore() { __ cbz(r0, is_null); // Move subklass into r1 - __ load_klass(r1, r0); + __ load_klass(r1, r0, rscratch1); // Move superklass into r0 - __ load_klass(r0, r3); + __ load_klass(r0, r3, rscratch1); __ ldr(r0, Address(r0, ObjArrayKlass::element_klass_offset())); // Compress array + index*oopSize + 12 into a single register. Frees r2. @@ -1173,7 +1173,7 @@ void TemplateTable::bastore() // Need to check whether array is boolean or byte // since both types share the bastore bytecode. - __ load_klass(r2, r3); + __ load_klass(r2, r3, rscratch1); __ ldrw(r2, Address(r2, Klass::layout_helper_offset())); int diffbit_index = exact_log2(Klass::layout_helper_boolean_diffbit()); Label L_skip; @@ -2194,7 +2194,7 @@ void TemplateTable::_return(TosState state) assert(state == vtos, "only valid state"); __ ldr(c_rarg1, aaddress(0)); - __ load_klass(r3, c_rarg1); + __ load_klass(r3, c_rarg1, rscratch1); __ ldrb(r3, Address(r3, Klass::misc_flags_offset())); Label skip_register_finalizer; __ tbz(r3, exact_log2(KlassFlags::_misc_has_finalizer), skip_register_finalizer); @@ -3338,8 +3338,8 @@ void TemplateTable::invokevirtual_helper(Register index, Register recv, Register flags) { - // Uses temporary registers r0, r3 - assert_different_registers(index, recv, r0, r3); + // Uses temporary registers r0, r3, rscratch1 + assert_different_registers(index, recv, r0, r3, rscratch1); // Test for an invoke of a final method Label notFinal; __ tbz(flags, ResolvedMethodEntry::is_vfinal_shift, notFinal); @@ -3363,7 +3363,7 @@ void TemplateTable::invokevirtual_helper(Register index, __ bind(notFinal); // get receiver klass - __ load_klass(r0, recv); + __ load_klass(r0, recv, rscratch1); // profile this call __ profile_virtual_call(r0, rlocals); @@ -3464,7 +3464,7 @@ void TemplateTable::invokeinterface(int byte_no) { __ tbz(r3, ResolvedMethodEntry::is_vfinal_shift, notVFinal); // Get receiver klass into r3 - __ load_klass(r3, r2); + __ load_klass(r3, r2, rscratch1); Label subtype; __ check_klass_subtype(r3, r0, r4, subtype); @@ -3479,7 +3479,7 @@ void TemplateTable::invokeinterface(int byte_no) { __ bind(notVFinal); // Get receiver klass into r3 - __ load_klass(r3, r2); + __ load_klass(r3, r2, rscratch1); Label no_such_method; @@ -3678,7 +3678,7 @@ void TemplateTable::_new() { __ mov(rscratch1, (intptr_t)markWord::prototype().value()); __ str(rscratch1, Address(r0, oopDesc::mark_offset_in_bytes())); __ store_klass_gap(r0, zr); // zero klass gap for compressed oops - __ store_klass(r0, r4); // store klass last + __ store_klass(r0, r4, rscratch1); // store klass last } if (DTraceAllocProbes) { @@ -3759,7 +3759,7 @@ void TemplateTable::checkcast() __ load_resolved_klass_at_offset(r2, r19, r0, rscratch1); // r0 = klass __ bind(resolved); - __ load_klass(r19, r3); + __ load_klass(r19, r3, rscratch1); // Generate subtype check. Blows r2, r5. Object in r3. // Superklass in r0. Subklass in r19. @@ -3805,12 +3805,12 @@ void TemplateTable::instanceof() { __ get_vm_result_metadata(r0, rthread); __ pop(r3); // restore receiver __ verify_oop(r3); - __ load_klass(r3, r3); + __ load_klass(r3, r3, rscratch1); __ b(resolved); // Get superklass in r0 and subklass in r3 __ bind(quicked); - __ load_klass(r3, r0); + __ load_klass(r3, r0, rscratch1); __ load_resolved_klass_at_offset(r2, r19, r0, rscratch1); __ bind(resolved); diff --git a/src/hotspot/cpu/aarch64/vtableStubs_aarch64.cpp b/src/hotspot/cpu/aarch64/vtableStubs_aarch64.cpp index 714904ab3df..1b7820fc337 100644 --- a/src/hotspot/cpu/aarch64/vtableStubs_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vtableStubs_aarch64.cpp @@ -79,7 +79,7 @@ VtableStub* VtableStubs::create_vtable_stub(int vtable_index) { // get receiver klass address npe_addr = __ pc(); - __ load_klass(r16, j_rarg0); + __ load_klass(r16, j_rarg0, rscratch1); #ifndef PRODUCT if (DebugVtables) { @@ -189,7 +189,7 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) { // get receiver klass (also an implicit null-check) address npe_addr = __ pc(); - __ load_klass(recv_klass_reg, j_rarg0); + __ load_klass(recv_klass_reg, j_rarg0, rscratch1); // Receiver subtype check against REFC. // Get selected method from declaring class and itable index diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index fbd12038c94..8106258c331 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -165,22 +165,15 @@ size_t AOTMetaspace::protection_zone_size() { } bool AOTMetaspace::shared_base_valid(char* shared_base) { - // We check user input for SharedBaseAddress at dump time. - // At CDS runtime, "shared_base" will be the (attempted) mapping start. It will also // be the encoding base, since the headers of archived base objects (and with Lilliput, // the prototype mark words) carry pre-computed narrow Klass IDs that refer to the mapping // start as base. - // - // The "shared_base" may not be later usable as encoding base, depending on the - // total size of the reserved area and the precomputed_narrow_klass_shift. This is checked - // before reserving memory. Here we weed out values already known to be invalid later. - // Since we cannot predict the range, we use the full maximum encoding range - // (4G). - constexpr size_t range = 4 * G; - address addr = (address)shared_base; - const int shift = ArchiveBuilder::precomputed_narrow_klass_shift(); - return CompressedKlassPointers::check_klass_decode_mode(addr, shift, range); + // Note that all narrowKlass inside CDS/AOT archives will be precomputed with the + // shift that, at build time, will afford us the maximum encoding range of 4GB. We do this + // since we don't know how large the class space at runtime will actually be. + return CLASS_SPACE_ONLY(is_aligned(shared_base, Metaspace::reserve_alignment())) + NOT_CLASS_SPACE(true); } class DumpClassListCLDClosure : public CLDClosure { @@ -1976,16 +1969,11 @@ char* AOTMetaspace::reserve_address_space_for_archives(FileMapInfo* static_mapin const size_t total_range_size = archive_space_size + gap_size + class_space_size; - // The code for dumping the archive ensures that the base address is valid. - // Here we validate that the base address plus shift can be decoded when - // restored. - assert(shared_base_valid((char*)base_address), - "Cannot use SharedBaseAddress " PTR_FORMAT " with precomputed shift %d.", - p2i(base_address), ArchiveBuilder::precomputed_narrow_klass_shift()); - assert(total_range_size > ccs_begin_offset, "must be"); if (use_windows_memory_mapping() && use_archive_base_addr) { if (base_address != nullptr) { + // Note: We already checked the base address for validity at dump time. + // On Windows, we cannot safely split a reserved memory space into two (see JDK-8255917). // Hence, we optimistically reserve archive space and class space side-by-side. We only // do this for use_archive_base_addr=true since for use_archive_base_addr=false case diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp index 8b8b80cd893..43bd5e452c8 100644 --- a/src/hotspot/share/memory/metaspace.cpp +++ b/src/hotspot/share/memory/metaspace.cpp @@ -593,7 +593,8 @@ ReservedSpace Metaspace::reserve_address_space_for_compressed_classes(size_t siz optimize_for_zero_base)); if (result == nullptr) { - // Fallback: reserve anywhere + // Fallback: we let the OS decide where to place the area, but align (overallocation-and-cut) + // to metaspace reserve alignment (16MB). log_debug(metaspace, map)("Trying anywhere..."); result = os::reserve_memory_aligned(size, Metaspace::reserve_alignment(), mtClass); } diff --git a/src/hotspot/share/oops/compressedKlass.cpp b/src/hotspot/share/oops/compressedKlass.cpp index ca1c46d4095..134f5a93365 100644 --- a/src/hotspot/share/oops/compressedKlass.cpp +++ b/src/hotspot/share/oops/compressedKlass.cpp @@ -188,11 +188,7 @@ void CompressedKlassPointers::initialize_for_given_encoding(address addr, size_t calc_lowest_highest_narrow_klass_id(); - // This has already been checked for SharedBaseAddress and if this fails, it's a bug in the allocation code. - if (!set_klass_decode_mode()) { - fatal("base=" PTR_FORMAT " given with shift %d, cannot be used to encode class pointers", - p2i(_base), _shift); - } + initialize_pd(); DEBUG_ONLY(sanity_check_after_initialization();) } @@ -299,20 +295,7 @@ void CompressedKlassPointers::initialize(address addr, size_t len) { calc_lowest_highest_narrow_klass_id(); - // Initialize JIT-specific decoding settings - if (!set_klass_decode_mode()) { - - // Give fatal error if this is a specified address - if (CompressedClassSpaceBaseAddress == (size_t)_base) { - vm_exit_during_initialization( - err_msg("CompressedClassSpaceBaseAddress=" PTR_FORMAT " given with shift %d, cannot be used to encode class pointers", - CompressedClassSpaceBaseAddress, _shift)); - } else { - // If this fails, it's a bug in the allocation code. - fatal("CompressedClassSpaceBaseAddress=" PTR_FORMAT " given with shift %d, cannot be used to encode class pointers", - p2i(_base), _shift); - } - } + initialize_pd(); DEBUG_ONLY(sanity_check_after_initialization();) } diff --git a/src/hotspot/share/oops/compressedKlass.hpp b/src/hotspot/share/oops/compressedKlass.hpp index fe1ce9e07ae..ff2dd15eb75 100644 --- a/src/hotspot/share/oops/compressedKlass.hpp +++ b/src/hotspot/share/oops/compressedKlass.hpp @@ -270,15 +270,8 @@ class CompressedKlassPointers : public AllStatic { // Returns true if address points into protection zone (for error reporting) static bool is_in_protection_zone(address addr); -#if defined(AARCH64) && !defined(ZERO) - // Check that with the given base, shift and range, aarch64 code can encode and decode the klass pointer. - static bool check_klass_decode_mode(address base, int shift, const size_t range); - // Called after initialization. - static bool set_klass_decode_mode(); -#else - static bool check_klass_decode_mode(address base, int shift, const size_t range) { return true; } - static bool set_klass_decode_mode() { return true; } -#endif + // platform-specific initializations + static void initialize_pd() NOT_AARCH64({}); }; #endif // SHARE_OOPS_COMPRESSEDKLASS_HPP diff --git a/test/hotspot/gtest/aarch64/test_assembler_aarch64.cpp b/test/hotspot/gtest/aarch64/test_assembler_aarch64.cpp index 63b8ddd865d..db240aeee90 100644 --- a/test/hotspot/gtest/aarch64/test_assembler_aarch64.cpp +++ b/test/hotspot/gtest/aarch64/test_assembler_aarch64.cpp @@ -30,9 +30,15 @@ #include "asm/macroAssembler.hpp" #include "compiler/disassembler.hpp" #include "memory/resourceArea.hpp" +#include "runtime/threadWXSetters.inline.hpp" +#include "utilities/powerOfTwo.hpp" #include "nativeInst_aarch64.hpp" #include "unittest.hpp" +// remove comment for debug log +//#define LOG_PLEASE +#include "testutils.hpp" + #define __ _masm. static void asm_check(const unsigned int *insns, const unsigned int *insns1, size_t len) { @@ -511,4 +517,137 @@ TEST_VM(AssemblerAArch64, native_instruction_load_predicates) { EXPECT_FALSE(ni_ldrs->is_ldrw_gpr_literal()); } +struct GtestFriendToMacroAssembler { + + typedef MacroAssembler::KlassDecodeMode Mode; + + typedef address (*decode_function)(narrowKlass encoded); + typedef narrowKlass (*encode_function)(address decoded); + + using CKP = CompressedKlassPointers; + using MA = MacroAssembler; + + static void build_and_run_encode_decode_klass(address base, int shift, + Mode expected_mode) { + + if ((shift + CKP::narrow_klass_pointer_bits()) > 32) { + return; // unsupported + } + + LOG_HERE("base " PTR_FORMAT " shift %d => mode %d: ", + p2u(base), shift, (int)expected_mode); + + // Test if the given base+shift value (with an assumed maximum Klass* range) + // yields the expected decode mode + const Mode real_mode = MA::klass_decode_mode(base, shift, CKP::max_klass_range_size()); + + ASSERT_EQ(real_mode, expected_mode) << " different mode?"; + + // Now generate encode and decode functions for this base and shift ... + BufferBlob* bb = BufferBlob::create("test_decode_klass", 512); + CodeBuffer code(bb); + address entry_encode = nullptr; + address entry_decode = nullptr; + + { + MA masm(&code); + + entry_encode = masm.pc(); + masm.emit_encode_klass_not_null(c_rarg0, // x0: dst+return + c_rarg0, // x0: src + rscratch1, // x8: tmp + base, shift, + real_mode); + masm.ret(lr); + + entry_decode = masm.pc(); + masm.emit_decode_klass_not_null(c_rarg0, // x0: dst+return + c_rarg0, // x0: src + rscratch1, // x8: tmp + base, shift, + real_mode); + masm.ret(lr); + + masm.flush(); // icache invalidate + } + + { + MACOS_AARCH64_ONLY(ThreadWXEnable wx(WXExec, Thread::current())); + + // ... and call it with some values spread over the full width of the narrowKlass range. + const narrowKlass highest = right_n_bits(CKP::narrow_klass_pointer_bits()); + + const struct { narrowKlass encoded; address decoded; } testvalues [] = { + { 0, base }, + // The highest value we can express with the current narrowKlass width + { highest, (address)(p2u(base) + ((uint64_t)highest << shift)) }, + // midpoint + { highest / 2, (address)(p2u(base) + (((uint64_t)highest / 2) << shift)) } + }; + constexpr int num_testvalues = sizeof(testvalues) / sizeof(testvalues[0]); + + for (int i = 0; i < num_testvalues; i++) { + const narrowKlass encoded = testvalues[i].encoded; + const address decoded = testvalues[i].decoded; + + const narrowKlass encoded_real = ((encode_function)entry_encode)(decoded); + LOG_HERE(" encode: " PTR_FORMAT " => " UINT32_FORMAT_X, p2u(decoded), encoded_real); + EXPECT_EQ(encoded_real, encoded) << " bad encode?"; + + const address decoded_real = ((decode_function)entry_decode)(encoded); + LOG_HERE(" decode: " UINT32_FORMAT_X " => " PTR_FORMAT, encoded, p2u(decoded_real)); + EXPECT_EQ(decoded_real, decoded) << " bad decode?"; + } + } + BufferBlob::free(bb); + } + + static void test_decode_encode_klass() { + + for (int shift = 0; shift < CKP::max_shift(); shift++) { + + // test zero-based + build_and_run_encode_decode_klass((address)nullptr, shift, MA::KlassDecodeZero); + + // test XOR-based encoding + // Base must be a valid immediate that does not intersect the highest left-shifted nKlass + const int lowest_xor_base_bit = 32; + const int highest_xor_base_bit = 51; // highest user address space bit on all our platforms + + // Highest base bit set + build_and_run_encode_decode_klass((address)nth_bit(highest_xor_base_bit), shift, MA::KlassDecodeXor); + // lowest base bit set + build_and_run_encode_decode_klass((address)nth_bit(lowest_xor_base_bit), shift, MA::KlassDecodeXor); + // all base bits set + build_and_run_encode_decode_klass((address)(right_n_bits(highest_xor_base_bit - lowest_xor_base_bit) << lowest_xor_base_bit), + shift, MA::KlassDecodeXor); + + // test movk-based + // Only bits in the third quadrant and not a valid immediate + build_and_run_encode_decode_klass((address)0x0000'A000'0000'0000ULL, 0, MA::KlassDecodeMovk); + + // test Fallback mode. + // base has low bits that intersect with nKlass, no other mode would work + build_and_run_encode_decode_klass((address)(0x5'0000'0000ULL + os::vm_page_size()), + shift, MA::KlassDecodeFallback); + build_and_run_encode_decode_klass((address)(0x5'0000'0000ULL - os::vm_page_size()), + shift, MA::KlassDecodeFallback); + + // a base that has ones in all four quadrants to trigger the full movz+3*movk path + // when loading the immediate + build_and_run_encode_decode_klass((address)right_n_bits(52), + shift, MA::KlassDecodeFallback); + + // spread over multiple 16-bit quadrants and not encodable as immediate, + // no other mode would work + build_and_run_encode_decode_klass((address)0x00AA'AAA0'0000'0000ULL, + shift, MA::KlassDecodeFallback); + } + } +}; + +// Run this with and without UseCompactObjectHeaders +TEST_VM(AssemblerAArch64, decode_encode_klass_not_null) { + GtestFriendToMacroAssembler::test_decode_encode_klass(); +} #endif // AARCH64 diff --git a/test/hotspot/jtreg/gtest/AssemblerGtests.java b/test/hotspot/jtreg/gtest/AssemblerGtests.java new file mode 100644 index 00000000000..19fb3398267 --- /dev/null +++ b/test/hotspot/jtreg/gtest/AssemblerGtests.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, IBM Corp. 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. + * + */ + +/* + * This runs the MacroAssembler gtests related to Klass de- and encoding + * (for now, only on aarch64) with and without COH. + */ + +/* @test id=coh + * @summary Run Assembler-related gtests + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.xml + * @requires vm.flagless + * @requires os.arch=="aarch64" + * @run main/native GTestWrapper --gtest_filter=AssemblerAArch64::decode_encode_klass* -XX:+UseCompactObjectHeaders + */ + +/* @test id=noncoh + * @summary Run Assembler-related gtests + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.xml + * @requires vm.flagless + * @requires os.arch=="aarch64" + * @run main/native GTestWrapper --gtest_filter=AssemblerAArch64::decode_encode_klass* -XX:-UseCompactObjectHeaders + */ + diff --git a/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointersEncodingScheme.java b/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointersEncodingScheme.java index d14dbc93245..f4ef0800a73 100644 --- a/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointersEncodingScheme.java +++ b/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointersEncodingScheme.java @@ -54,7 +54,7 @@ private static void test(long forceAddress, boolean COH, long classSpaceSize, lo "-XX:" + (COH ? "+" : "-") + "UseObjectMonitorTable", "-XX:CompressedClassSpaceBaseAddress=" + forceAddress, "-XX:CompressedClassSpaceSize=" + classSpaceSize, - "-Xmx128m", + "-Xmx64m", "-Xlog:metaspace*", "-version"); OutputAnalyzer output = new OutputAnalyzer(pb.start()); @@ -71,35 +71,6 @@ private static void test(long forceAddress, boolean COH, long classSpaceSize, lo output.shouldContain("Narrow klass base: " + expectedEncodingBaseString + ", Narrow klass shift: " + expectedEncodingShift); } - private static void testFailure(String forceAddressString) throws IOException { - ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( - "-Xshare:off", // to make CompressedClassSpaceBaseAddress work - "-XX:+UnlockExperimentalVMOptions", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:-UseCompactObjectHeaders", - "-XX:CompressedClassSpaceBaseAddress=" + forceAddressString, - "-Xmx128m", - "-Xlog:metaspace*", - "-version"); - OutputAnalyzer output = new OutputAnalyzer(pb.start()); - - output.reportDiagnosticSummary(); - - // We ignore cases where we were not able to map at the force address - if (!output.contains("Successfully forced class space address to " + forceAddressString)) { - throw new SkippedException("Skipping because we cannot force ccs to " + forceAddressString); - } - - if (Platform.isAArch64()) { - output.shouldHaveExitValue(1); - output.shouldContain("Error occurred during initialization of VM"); - output.shouldContain("CompressedClassSpaceBaseAddress=" + forceAddressString + - " given with shift 0, cannot be used to encode class pointers"); - } else { - output.shouldHaveExitValue(0); - } - } - final static long K = 1024; final static long M = K * 1024; final static long G = M * 1024; @@ -108,53 +79,47 @@ public static void main(String[] args) throws Exception { // Expecting base=0, shift=0 test(4 * G - 128 * M, false, 128 * M, 0, 0); - // Test ccs nestling right at the end of the 32G range - // Expecting: - // - non-aarch64: base=0, shift=3 - // - aarch64: base to start of class range, shift 0 - if (Platform.isAArch64()) { - // The best we can do on aarch64 is to be *near* the end of the 32g range, since a valid encoding base - // on aarch64 must be 4G aligned, and the max. class space size is 3G. - long forceAddress = 0x7_0000_0000L; // 28g, and also a valid EOR immediate - test(forceAddress, false, 3 * G, forceAddress, 0); - } else { - test(32 * G - 128 * M, false, 128 * M, 0, 3); - } - - // Test ccs starting *below* 4G, but extending upwards beyond 4G. All platforms except aarch64 should pick - // zero based encoding. On aarch64, this test is excluded since the only valid mode would be XOR, but bit - // pattern for base and bit pattern would overlap. - if (!Platform.isAArch64()) { - test(4 * G - 128 * M, false, 2 * 128 * M, 0, 3); - } - // add more... + // aarch64 does not do extended zero based encoding (shift>0) + boolean expectExtendedZeroBasedEncoding = !Platform.isAArch64(); + + // Test ccs nestling right at the end of the 32G range. + // Expect all platforms but aarch64 to do shift-extended zero-based encoding; + long forceAddress = 32 * G - 128 * M; + test(forceAddress, false, 128 * M, + expectExtendedZeroBasedEncoding ? 0 : forceAddress, // expected base + expectExtendedZeroBasedEncoding ? 3 : 0 // expected shift + ); + + // Test ccs starting *below* 4G, but extending upwards beyond 4G. + // Expect all platforms but aarch64 to do shift-extended zero-based encoding; aarch64 does not do that but + // drops right to non-zero-based with shift = 0 + forceAddress = 4 * G - 128 * M; + test(forceAddress, false, 2 * 128 * M, + expectExtendedZeroBasedEncoding ? 0 : forceAddress, // expected base + expectExtendedZeroBasedEncoding ? 3 : 0 // expected shift + ); // Compact Object Header Mode: - // On aarch64 and x64 we expect the VM to chose the smallest possible shift value needed to cover - // the encoding range. We expect the encoding Base to start at the class space start - but to enforce that, - // we choose a high address. - if (Platform.isAArch64() || Platform.isX64() || Platform.isRISCV64()) { - long forceAddress = 32 * G; - - long ccsSize = 128 * M; - int expectedShift = 6; - test(forceAddress, true, ccsSize, forceAddress, expectedShift); - - ccsSize = 512 * M; - expectedShift = 8; - test(forceAddress, true, ccsSize, forceAddress, expectedShift); - - ccsSize = G; - expectedShift = 9; - test(forceAddress, true, ccsSize, forceAddress, expectedShift); - - ccsSize = 3 * G; - expectedShift = 10; - test(forceAddress, true, ccsSize, forceAddress, expectedShift); - } - - // Test failure for -XX:CompressedClassBaseAddress and -Xshare:off - testFailure("0x0000040001000000"); + // We expect the VM to chose the smallest possible shift value needed to cover the encoding range. + // We expect the encoding Base to start at the class space start - but to enforce that, + // we choose unsuited to even shift-extended zero-based mode. + forceAddress = 32 * G; + + test(forceAddress, true, 128 * M, forceAddress, 6); + test(forceAddress, true, 256 * M, forceAddress, 7); + test(forceAddress, true, 512 * M, forceAddress, 8); + test(forceAddress, true, G, forceAddress, 9); + test(forceAddress, true, 3 * G, forceAddress, 10); + + // Test a "crooked" base address: + // - just aligned enough to pass metaspace reserve alignment test of 16MB. + // - not encodable on aarch64 as logical immediate + // - sufficiently complex enough to need multiple moves on risc platforms to materialize as immediate + // - small enough to not cause test errors on small devices (e.g. arm64 39bit address space) + // - large enough to not end up with zero-based encoding + forceAddress = 0x0000000d55000000L; + test(forceAddress, true, 32 * M, forceAddress, 6); + test(forceAddress, false, 32 * M, forceAddress, 0); } } diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/AccessZeroNKlassHitsProtectionZone.java b/test/hotspot/jtreg/runtime/ErrorHandling/AccessZeroNKlassHitsProtectionZone.java index 61d017d2264..4e177a6fe1d 100644 --- a/test/hotspot/jtreg/runtime/ErrorHandling/AccessZeroNKlassHitsProtectionZone.java +++ b/test/hotspot/jtreg/runtime/ErrorHandling/AccessZeroNKlassHitsProtectionZone.java @@ -126,7 +126,7 @@ private static OutputAnalyzer run_test(boolean COH, boolean CDS, String forceBas private static void run_test(boolean COH, boolean CDS) throws IOException, SkippedException { // Notes: - // We want to enforce zero-based encoding, to test the protection page in that case. For zero-based encoding, + // We want to enforce non-zero-based encoding, to test the protection page in that case. For zero-based encoding, // protection page is at address zero, no need to test that. // If CDS is on, we never use zero-based, forceBase is ignored. // If CDS is off, we use forceBase to (somewhat) reliably force the encoding base to beyond 32G, From 5b3456ce70dc4fb0bc28ff8bacb00a0ec504400b Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Fri, 17 Jul 2026 15:02:34 +0000 Subject: [PATCH 262/707] 8388406: [BACKOUT] C2: crash in compiled code due to zero division because of widened CastII Reviewed-by: thartmann, chagedorn --- src/hotspot/share/opto/c2_globals.hpp | 4 +- src/hotspot/share/opto/castnode.cpp | 3 + src/hotspot/share/opto/cfgnode.cpp | 14 +- src/hotspot/share/opto/classes.hpp | 1 - src/hotspot/share/opto/compile.cpp | 25 +- src/hotspot/share/opto/compile.hpp | 9 +- src/hotspot/share/opto/convertnode.cpp | 14 + src/hotspot/share/opto/divnode.cpp | 14 - src/hotspot/share/opto/divnode.hpp | 13 +- src/hotspot/share/opto/loopopts.cpp | 2 +- src/hotspot/share/opto/movenode.cpp | 5 + src/hotspot/share/opto/node.cpp | 37 +- src/hotspot/share/opto/node.hpp | 13 +- src/hotspot/share/opto/parse2.cpp | 2 +- src/hotspot/share/opto/phaseX.cpp | 123 +- src/hotspot/share/opto/phaseX.hpp | 6 +- src/hotspot/share/opto/rootnode.cpp | 45 - src/hotspot/share/opto/rootnode.hpp | 31 - src/hotspot/share/opto/vectornode.cpp | 2 +- .../c2/TestDeadPathManyDeadDataNodes.java | 1301 ----------------- .../TestDivByZeroInLiveCFGPath.java | 64 - .../TestZeroDivModWidenedCastII.java | 1122 -------------- ...yAccessAboveRCAfterRCCastIIEliminated.java | 24 +- 23 files changed, 81 insertions(+), 2793 deletions(-) delete mode 100644 test/hotspot/jtreg/compiler/c2/TestDeadPathManyDeadDataNodes.java delete mode 100644 test/hotspot/jtreg/compiler/integerArithmetic/TestDivByZeroInLiveCFGPath.java delete mode 100644 test/hotspot/jtreg/compiler/integerArithmetic/TestZeroDivModWidenedCastII.java diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index dd9288f7617..9ff88e8c310 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -922,8 +922,8 @@ "Use StoreStore barrier instead of Release barrier at the end " \ "of constructors") \ \ - develop(bool, KillPathsReachableByDeadDataNode, true, \ - "When a data node becomes top, make paths where the node is " \ + 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 " \ "could corrupt the graph in rare cases and should be used with " \ "care.") \ diff --git a/src/hotspot/share/opto/castnode.cpp b/src/hotspot/share/opto/castnode.cpp index ef7b4d5aef3..befa208a5e2 100644 --- a/src/hotspot/share/opto/castnode.cpp +++ b/src/hotspot/share/opto/castnode.cpp @@ -111,6 +111,9 @@ Node* ConstraintCastNode::Ideal(PhaseGVN* phase, bool can_reshape) { if (in(0) != nullptr && remove_dead_region(phase, can_reshape)) { return this; } + if (in(1) != nullptr && phase->type(in(1)) != Type::TOP) { + return TypeNode::Ideal(phase, can_reshape); + } return nullptr; } diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index ed5da046608..828e5bf299f 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -693,13 +693,14 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) { if (add_to_worklist) { igvn->add_users_to_worklist(this); // Check for further allowed opts } - uint edges_removed; - for (DUIterator_Last imin, i = last_outs(imin); i >= imin; i -= edges_removed) { - edges_removed = 1; + for (DUIterator_Last imin, i = last_outs(imin); i >= imin; --i) { Node* n = last_out(i); igvn->hash_delete(n); // Remove from worklist before modifying edges if (n->outcnt() == 0) { - edges_removed = n->replace_edge(this, phase->C->top(), igvn); + int uses_found = n->replace_edge(this, phase->C->top(), igvn); + if (uses_found > 1) { // (--i) done at the end of the loop. + i -= (uses_found - 1); + } continue; } if( n->is_Phi() ) { // Collapse all Phis @@ -718,7 +719,10 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) { } else if( n->is_Region() ) { // Update all incoming edges assert(n != this, "Must be removed from DefUse edges"); - edges_removed = n->replace_edge(this, parent_ctrl, igvn); + int uses_found = n->replace_edge(this, parent_ctrl, igvn); + if (uses_found > 1) { // (--i) done at the end of the loop. + i -= (uses_found - 1); + } } else { assert(n->in(0) == this, "Expect RegionNode to be control parent"); diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index c296237de37..53a72f979db 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -121,7 +121,6 @@ macro(CompareAndExchangeI) macro(CompareAndExchangeL) macro(CompareAndExchangeP) macro(CompareAndExchangeN) -macro(DeadPath) macro(GetAndAddB) macro(GetAndAddS) macro(GetAndAddI) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index db43c6fb1c4..93d8e4c425d 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -312,8 +312,6 @@ void Compile::identify_useful_nodes(Unique_Node_List &useful) { // If 'top' is cached, declare it useful to preserve cached node if (cached_top_node()) { useful.push(cached_top_node()); } - if (dead_path()) { useful.push(dead_path()); } - // Push all useful nodes onto the list, breadthfirst for( uint next = 0; next < useful.size(); ++next ) { assert( next < unique(), "Unique useful nodes < total nodes"); @@ -390,7 +388,7 @@ void Compile::remove_useless_node(Node* dead) { // it reachable by adding use edges. So, we will NOT count Con nodes // as dead to be conservative about the dead node count at any // given time. - if (!dead->is_Con() && dead != dead_path()) { + if (!dead->is_Con()) { record_dead_node(dead->_idx); } if (dead->is_macro()) { @@ -686,7 +684,6 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, _node_arena_one(mtCompiler, Arena::Tag::tag_node), _node_arena_two(mtCompiler, Arena::Tag::tag_node), _node_arena(&_node_arena_one), - _dead_path(nullptr), _mach_constant_base_node(nullptr), _Compile_types(mtCompiler, Arena::Tag::tag_type), _initial_gvn(nullptr), @@ -757,7 +754,6 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, } Init(/*do_aliasing=*/ true); - set_dead_path(new DeadPathNode()); print_compile_messages(); @@ -967,7 +963,6 @@ Compile::Compile(ciEnv* ci_env, _node_arena_one(mtCompiler, Arena::Tag::tag_node), _node_arena_two(mtCompiler, Arena::Tag::tag_node), _node_arena(&_node_arena_one), - _dead_path(nullptr), _mach_constant_base_node(nullptr), _Compile_types(mtCompiler, Arena::Tag::tag_type), _initial_gvn(nullptr), @@ -2635,9 +2630,6 @@ void Compile::Optimize() { } } - // Unique DeadPath node should not be used anymore - _dead_path = nullptr; - print_method(PHASE_OPTIMIZE_FINISHED, 2); DEBUG_ONLY(set_phase_optimize_finished();) } @@ -3946,21 +3938,6 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f break; } #endif - case Op_DeadPath: { - // The CFG inputs are dead paths. Replace the DeadPath with a Region and insert a Halt node. - assert(n->req() > 1, "why not removed if no input other than itself?"); - RegionNode* r = new RegionNode(n->req()); - for (uint i = 1; i < n->req(); ++i) { - r->set_req(i, n->in(i)); - } - n->disconnect_inputs(this); - Node* frame = start()->proj_out(TypeFunc::FramePtr); - stringStream ss; - ss.print("dead path discovered by data nodes during igvn"); - Node* halt = new HaltNode(r, frame, ss.as_string(comp_arena())); - root()->set_req(root()->find_edge(n), halt); - break; - } default: assert(!n->is_Call(), ""); assert(!n->is_Mem(), ""); diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 73e136787f8..ab36f59a28f 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -57,7 +57,6 @@ class CallStaticJavaNode; class CloneMap; class CompilationFailureInfo; class ConnectionGraph; -class DeadPathNode; class IdealGraphPrinter; class InlineTree; class Matcher; @@ -428,7 +427,7 @@ class Compile : public Phase { private: RootNode* _root; // Unique root of compilation, or null after bail-out. Node* _top; // Unique top node. (Reset by various phases.) - DeadPathNode* _dead_path; // Unique DeadPath node + Node* _immutable_memory; // Initial memory state Node* _recent_alloc_obj; @@ -898,12 +897,6 @@ class Compile : public Phase { Arena* old_arena() { return (&_node_arena_one == _node_arena) ? &_node_arena_two : &_node_arena_one; } RootNode* root() const { return _root; } void set_root(RootNode* r) { _root = r; } - DeadPathNode* dead_path() const { return _dead_path; } - - void set_dead_path(DeadPathNode* dead_path) { - assert(_dead_path == nullptr, "can only set once"); - _dead_path = dead_path; - } StartNode* start() const; // (Derived from root.) void verify_start(StartNode* s) const NOT_DEBUG_RETURN; Node* immutable_memory(); diff --git a/src/hotspot/share/opto/convertnode.cpp b/src/hotspot/share/opto/convertnode.cpp index d706a13feb3..a495814da61 100644 --- a/src/hotspot/share/opto/convertnode.cpp +++ b/src/hotspot/share/opto/convertnode.cpp @@ -755,6 +755,13 @@ bool Compile::push_thru_add(PhaseGVN* phase, Node* z, const TypeInteger* tz, con //------------------------------Ideal------------------------------------------ Node* ConvI2LNode::Ideal(PhaseGVN* phase, bool can_reshape) { + if (in(1) != nullptr && phase->type(in(1)) != Type::TOP) { + Node* progress = TypeNode::Ideal(phase, can_reshape); + if (progress != nullptr) { + return progress; + } + } + const TypeLong* this_type = this->type()->is_long(); if (can_reshape && !phase->C->post_loop_opts_phase()) { // makes sure we run ::Value to potentially remove type assertion after loop opts @@ -857,6 +864,13 @@ const Type* ConvL2INode::Value(PhaseGVN* phase) const { // Return a node which is more "ideal" than the current node. // Blow off prior masking to int Node* ConvL2INode::Ideal(PhaseGVN* phase, bool can_reshape) { + if (in(1) != nullptr && phase->type(in(1)) != Type::TOP) { + Node* progress = TypeNode::Ideal(phase, can_reshape); + if (progress != nullptr) { + return progress; + } + } + Node *andl = in(1); uint andl_op = andl->Opcode(); if( andl_op == Op_AndL ) { diff --git a/src/hotspot/share/opto/divnode.cpp b/src/hotspot/share/opto/divnode.cpp index 3b51491294e..1687ff2cade 100644 --- a/src/hotspot/share/opto/divnode.cpp +++ b/src/hotspot/share/opto/divnode.cpp @@ -1031,10 +1031,6 @@ const Type* UDivINode::Value(PhaseGVN* phase) const { if( t1 == Type::TOP ) return Type::TOP; if( t2 == Type::TOP ) return Type::TOP; - if (t2 == TypeInt::ZERO) { - return Type::TOP; - } - // x/x == 1 since we always generate the dynamic divisor check for 0. if (in(1) == in(2)) { return TypeInt::ONE; @@ -1071,10 +1067,6 @@ const Type* UDivLNode::Value(PhaseGVN* phase) const { if( t1 == Type::TOP ) return Type::TOP; if( t2 == Type::TOP ) return Type::TOP; - if (t2 == TypeLong::ZERO) { - return Type::TOP; - } - // x/x == 1 since we always generate the dynamic divisor check for 0. if (in(1) == in(2)) { return TypeLong::ONE; @@ -1388,9 +1380,6 @@ Node* UModINode::Ideal(PhaseGVN* phase, bool can_reshape) { } const Type* UModINode::Value(PhaseGVN* phase) const { - if (phase->type(in(2)) == TypeInt::ZERO) { - return Type::TOP; - } return unsigned_mod_value(phase, this); } @@ -1531,9 +1520,6 @@ Node *UModLNode::Ideal(PhaseGVN *phase, bool can_reshape) { } const Type* UModLNode::Value(PhaseGVN* phase) const { - if (phase->type(in(2)) == TypeLong::ZERO) { - return Type::TOP; - } return unsigned_mod_value(phase, this); } diff --git a/src/hotspot/share/opto/divnode.hpp b/src/hotspot/share/opto/divnode.hpp index de89dcaad06..366e3fb882d 100644 --- a/src/hotspot/share/opto/divnode.hpp +++ b/src/hotspot/share/opto/divnode.hpp @@ -40,9 +40,7 @@ class DivModIntegerNode : public Node { bool _pinned; protected: - DivModIntegerNode(Node* c, Node* dividend, Node* divisor) : Node(c, dividend, divisor), _pinned(false) { - init_class_id(Class_DivModInteger); - } + DivModIntegerNode(Node* c, Node* dividend, Node* divisor) : Node(c, dividend, divisor), _pinned(false) {} private: virtual uint size_of() const override { return sizeof(DivModIntegerNode); } @@ -54,15 +52,6 @@ class DivModIntegerNode : public Node { res->_pinned = true; return res; } - -public: - const TypeInteger* zero() const { - if (bottom_type() == TypeInt::INT) { - return TypeInt::ZERO; - } - assert(bottom_type() == TypeLong::LONG, "should be int or long"); - return TypeLong::ZERO; - } }; //------------------------------DivINode--------------------------------------- diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index d525c274ef6..ccd53129a87 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -1725,7 +1725,7 @@ void PhaseIdealLoop::try_sink_out_of_loop(Node* n) { !n->is_OpaqueTemplateAssertionPredicate() && !is_raw_to_oop_cast && // don't extend live ranges of raw oops n->Opcode() != Op_CreateEx && - (KillPathsReachableByDeadDataNode || !n->is_Type()) + (KillPathsReachableByDeadTypeNode || !n->is_Type()) ) { Node *n_ctrl = get_ctrl(n); IdealLoopTree *n_loop = get_loop(n_ctrl); diff --git a/src/hotspot/share/opto/movenode.cpp b/src/hotspot/share/opto/movenode.cpp index 7d38238da2f..6b6becb434f 100644 --- a/src/hotspot/share/opto/movenode.cpp +++ b/src/hotspot/share/opto/movenode.cpp @@ -90,6 +90,11 @@ Node *CMoveNode::Ideal(PhaseGVN *phase, bool can_reshape) { phase->type(in(IfTrue)) == Type::TOP) { return nullptr; } + Node* progress = TypeNode::Ideal(phase, can_reshape); + if (progress != nullptr) { + return progress; + } + // Check for Min/Max patterns. This is called before constants are pushed to the right input, as that transform can // make BoolTests non-canonical. Node* minmax = Ideal_minmax(phase, this); diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index 264216ddc6d..726a3ea1b55 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -597,7 +597,6 @@ void Node::setup_is_top() { //------------------------------~Node------------------------------------------ // Fancy destructor; eagerly attempt to reclaim Node numberings and storage void Node::destruct(PhaseValues* phase) { - assert(this != Compile::current()->dead_path(), "we want to keep the unique DeadPath node around"); Compile* compile = (phase != nullptr) ? phase->C : Compile::current(); if (phase != nullptr && phase->is_IterGVN()) { phase->is_IterGVN()->_worklist.remove(this); @@ -736,14 +735,11 @@ void Node::out_grow(uint len) { //------------------------------is_dead---------------------------------------- bool Node::is_dead() const { // Mach and pinch point nodes may look like dead. - if (is_top() || is_Mach() || (Opcode() == Op_Node && _outcnt > 0) || this == Compile::current()->dead_path()) { + if( is_top() || is_Mach() || (Opcode() == Op_Node && _outcnt > 0) ) return false; - } - for (uint i = 0; i < _max; i++) { - if (_in[i] != nullptr) { + for( uint i = 0; i < _max; i++ ) + if( _in[i] != nullptr ) return false; - } - } return true; } @@ -3182,11 +3178,10 @@ uint TypeNode::ideal_reg() const { return _type->ideal_reg(); } -void Node::make_path_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, Node* ctrl_use, uint j, const char* phase_str) { +void TypeNode::make_path_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, Node* ctrl_use, uint j, const char* phase_str) { Node* c = ctrl_use->in(j); - Node* top = igvn->C->top(); - if (c != top) { - igvn->replace_input_of(ctrl_use, j, top); + if (igvn->type(c) != Type::TOP) { + igvn->replace_input_of(ctrl_use, j, igvn->C->top()); create_halt_path(igvn, c, loop, phase_str); } } @@ -3198,18 +3193,14 @@ void Node::make_path_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, Node* ctrl_u // constant folds and the control flow that leads to the Type node becomes unreachable. There are cases where that // doesn't happen, however. They are handled here by following uses of the Type node until a CFG or a Phi to find dead // paths. The dead paths are then replaced by a Halt node. -void Node::make_paths_from_here_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, const char* phase_str) { +void TypeNode::make_paths_from_here_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, const char* phase_str) { Unique_Node_List wq; wq.push(this); for (uint i = 0; i < wq.size(); ++i) { Node* n = wq.at(i); - if (n->is_CFG()) { - n->remove_dead_region(igvn, true); - } for (DUIterator_Fast kmax, k = n->fast_outs(kmax); k < kmax; k++) { Node* u = n->fast_out(k); if (u->is_CFG()) { - wq.push(u); assert(!u->is_Region(), "Can't reach a Region without going through a Phi"); make_path_dead(igvn, loop, u, 0, phase_str); } else if (u->is_Phi()) { @@ -3229,7 +3220,7 @@ void Node::make_paths_from_here_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, c } } -void Node::create_halt_path(PhaseIterGVN* igvn, Node* c, PhaseIdealLoop* loop, const char* phase_str) { +void TypeNode::create_halt_path(PhaseIterGVN* igvn, Node* c, PhaseIdealLoop* loop, const char* phase_str) const { Node* frame = new ParmNode(igvn->C->start(), TypeFunc::FramePtr); if (loop == nullptr) { igvn->register_new_node_with_optimizer(frame); @@ -3248,3 +3239,15 @@ void Node::create_halt_path(PhaseIterGVN* igvn, Node* c, PhaseIdealLoop* loop, c } igvn->add_input_to(igvn->C->root(), halt); } + +Node* TypeNode::Ideal(PhaseGVN* phase, bool can_reshape) { + if (KillPathsReachableByDeadTypeNode && can_reshape && Value(phase) == Type::TOP) { + PhaseIterGVN* igvn = phase->is_IterGVN(); + Node* top = igvn->C->top(); + ResourceMark rm; + make_paths_from_here_dead(igvn, nullptr, "igvn"); + return top; + } + + return Node::Ideal(phase, can_reshape); +} diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index e593822c313..b3de7498e50 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -82,7 +82,6 @@ class CountedLoopEndNode; class DecodeNarrowPtrNode; class DecodeNNode; class DecodeNKlassNode; -class DivModIntegerNode; class EncodeNarrowPtrNode; class EncodePNode; class EncodePKlassNode; @@ -830,9 +829,8 @@ class Node { DEFINE_CLASS_ID(LShift, Node, 21) DEFINE_CLASS_ID(Neg, Node, 22) DEFINE_CLASS_ID(ReachabilityFence, Node, 23) - DEFINE_CLASS_ID(DivModInteger, Node, 24) - _max_classes = ClassMask_DivModInteger + _max_classes = ClassMask_Neg }; #undef DEFINE_CLASS_ID @@ -949,7 +947,6 @@ class Node { DEFINE_CLASS_QUERY(DecodeNarrowPtr) DEFINE_CLASS_QUERY(DecodeN) DEFINE_CLASS_QUERY(DecodeNKlass) - DEFINE_CLASS_QUERY(DivModInteger) DEFINE_CLASS_QUERY(EncodeNarrowPtr) DEFINE_CLASS_QUERY(EncodeP) DEFINE_CLASS_QUERY(EncodePKlass) @@ -1504,10 +1501,6 @@ class Node { uint _del_tick; // Bumped when a deletion happens.. #endif #endif - void make_paths_from_here_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, const char* phase_str); - - static void create_halt_path(PhaseIterGVN* igvn, Node* c, PhaseIdealLoop* loop, const char* phase_str); - void make_path_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, Node* ctrl_use, uint j, const char* phase_str); }; inline bool not_a_node(const Node* n) { @@ -2205,13 +2198,17 @@ class TypeNode : public Node { init_class_id(Class_Type); } virtual const Type* Value(PhaseGVN* phase) const; + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); virtual const Type *bottom_type() const; virtual uint ideal_reg() const; + void make_path_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, Node* ctrl_use, uint j, const char* phase_str); #ifndef PRODUCT virtual void dump_spec(outputStream *st) const; virtual void dump_compact_spec(outputStream *st) const; #endif + void make_paths_from_here_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, const char* phase_str); + void create_halt_path(PhaseIterGVN* igvn, Node* c, PhaseIdealLoop* loop, const char* phase_str) const; }; #include "opto/opcodes.hpp" diff --git a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp index 6e58fae51e1..9cb20cfcd00 100644 --- a/src/hotspot/share/opto/parse2.cpp +++ b/src/hotspot/share/opto/parse2.cpp @@ -1843,7 +1843,7 @@ void Parse::sharpen_type_after_if(BoolTest::mask btest, const Type* obj_type = _gvn.type(obj); const Type* tboth = obj_type->filter_speculative(cast_type); assert(tboth->higher_equal(obj_type) && tboth->higher_equal(cast_type), "sanity"); - if (tboth == Type::TOP && KillPathsReachableByDeadDataNode) { + if (tboth == Type::TOP && KillPathsReachableByDeadTypeNode) { // Let dead type node cleaning logic prune effectively dead path for us. // CheckCastPP::Value() == TOP and it will trigger the cleanup during GVN. // Don't materialize the cast when cleanup is disabled, because diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index c124f940a27..a4d6a6c33d0 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -32,7 +32,6 @@ #include "opto/castnode.hpp" #include "opto/cfgnode.hpp" #include "opto/convertnode.hpp" -#include "opto/divnode.hpp" #include "opto/idealGraphPrinter.hpp" #include "opto/loopnode.hpp" #include "opto/machnode.hpp" @@ -2198,107 +2197,6 @@ Node *PhaseIterGVN::transform( Node *n ) { return transform_old(n); } -DeadPathNode* PhaseIterGVN::dead_path() { - DeadPathNode* dead_path_node = C->dead_path(); - if (!dead_path_node->is_active()) { - dead_path_node->activate(this); - } - assert(C->root()->find_edge(dead_path_node) > 0, "should be reachable from root"); - return dead_path_node; -} - - -// If dead_node is a data node, all CFG nodes reachable from dead_node are dead cfg paths. This method follows uses from -// dead_node until it encounters a cfg node or a phi and eagerly kills these dead cfg paths. This is needed because, in -// some corner cases, a data node dies but some data paths that use it (and are unreachable at runtime) are not proven -// dead by igvn, possibly leading to incorrect IR graphs. -// Also see comment at DeadPathNode declaration. -void PhaseIterGVN::make_dependent_paths_dead_if_top(Node* dead_node, const Type* t) { - if (t != Type::TOP) { - return; - } - if (!KillPathsReachableByDeadDataNode) { - return; - } - // dead_node is going dead, follow uses - ResourceMark rm; - Unique_Node_List wq; - wq.push(dead_node); - for (uint i = 0; i < wq.size(); i++) { - Node* n = wq.at(i); - if (n != dead_node && (n->is_Phi() || n->is_CFG())) { - continue; - } - for (DUIterator_Fast kmax, k = n->fast_outs(kmax); k < kmax; k++) { - Node* u = n->fast_out(k); - wq.push(u); - } - } - for (uint i = 0; i < wq.size(); i++) { - Node* n = wq.at(i); - if (n->is_Phi()) { - Node* region = n->in(0); - // Find out through which of the Phi's input, we reached that Phi and mark the corresponding CFG path dead - for (uint j = 1; j < n->req(); j++) { - Node* in = n->in(j); - // We don't follow uses beyond Phis so if 'in' is a Phi (unless it's dead_node), we couldn't reach this Phi through it - if (in == dead_node || (in != nullptr && !in->is_Phi() && wq.member(in))) { - if (!region->is_top() && region->in(j) != nullptr && !region->in(j)->is_top()) { - // We reached this CFG path through data nodes, record it in dead path to later insert a Halt node, if it - // doesn't die in the meantime - dead_path()->add_req(region->in(j)); - _worklist.push(dead_path()); - replace_input_of(region, j, C->top()); - } - replace_input_of(n, j, C->top()); - if (in->outcnt() == 0) { - remove_dead_node(in, NodeOrigin::Graph); - } - } - } - continue; - } - if (n == dead_node) { - continue; - } - // We don't want to follow CFG nodes but is_CFG() can return false for a cfg projection if its input is top. So - // there's no foolproof way of telling if dead_node is a cfg or not and as a consequence we can reach a Region. - if (n->is_Region()) { - // Find out through which of the Region's input, we reached that Region and mark it dead - for (uint j = 1; j < n->req(); j++) { - Node* in = n->in(j); - // We don't follow uses beyond Regions so if 'in' is a Region, we couldn't reach this Region through it - if (in != nullptr && !in->is_Region() && wq.member(in)) { - replace_input_of(n, j, C->top()); - in->remove_dead_region(this, true); - } - } - continue; - } - // If we reached this CFG node through a data input... - if (n->is_CFG()) { - Node* control_input = n->in(0); - if (control_input != nullptr && !control_input->is_top()) { - // record it in dead path to later insert a Halt node, if it doesn't die in the meantime - dead_path()->add_req(control_input); - _worklist.push(dead_path()); - replace_input_of(n, 0, C->top()); - } - n->remove_dead_region(this, true); - continue; - } - if (n->outcnt() == 0) { - remove_dead_node(n, NodeOrigin::Graph); - } - } -#ifdef ASSERT - for (uint i = 0; i < wq.size(); i++) { - Node* n = wq.at(i); - assert(n->is_Region() || n->is_Phi() || n->is_CFG() || n->outcnt() == 0, "node should be dead now"); - } -#endif -} - Node *PhaseIterGVN::transform_old(Node* n) { NOT_PRODUCT(set_transforms()); // Remove 'n' from hash table in case it gets modified @@ -2390,7 +2288,6 @@ Node *PhaseIterGVN::transform_old(Node* n) { } // If 'k' computes a constant, replace it with a constant if (t->singleton() && !k->is_Con()) { - make_dependent_paths_dead_if_top(k, t); set_progress(); Node* con = makecon(t); // Make a constant add_users_to_worklist(k); @@ -3060,14 +2957,10 @@ void PhaseCCP::analyze_step(Unique_Node_List& worklist, Node* n) { set_type(n, new_type); push_child_nodes_to_worklist(worklist, n); } - if (KillPathsReachableByDeadDataNode && n->is_Type() && new_type == Type::TOP) { + if (KillPathsReachableByDeadTypeNode && n->is_Type() && new_type == Type::TOP) { // Keep track of Type nodes to kill CFG paths that use Type // nodes that become dead. - _maybe_top_type_or_div_mod_nodes.push(n); - } - if (KillPathsReachableByDeadDataNode && new_type == Type::TOP && n->is_DivModInteger() && - type(n->in(2)) == n->as_DivModInteger()->zero()) { - _maybe_top_type_or_div_mod_nodes.push(n); + _maybe_top_type_nodes.push(n); } } @@ -3363,16 +3256,16 @@ Node *PhaseCCP::transform( Node *n ) { // track all visited nodes, so that we can remove the complement Unique_Node_List useful; - if (KillPathsReachableByDeadDataNode) { - for (uint i = 0; i < _maybe_top_type_or_div_mod_nodes.size(); ++i) { - Node* data_node = _maybe_top_type_or_div_mod_nodes.at(i); - if (type(data_node) == Type::TOP) { + if (KillPathsReachableByDeadTypeNode) { + for (uint i = 0; i < _maybe_top_type_nodes.size(); ++i) { + Node* type_node = _maybe_top_type_nodes.at(i); + if (type(type_node) == Type::TOP) { ResourceMark rm; - data_node->make_paths_from_here_dead(this, nullptr, "ccp"); + type_node->as_Type()->make_paths_from_here_dead(this, nullptr, "ccp"); } } } else { - assert(_maybe_top_type_or_div_mod_nodes.size() == 0, "we don't need type nodes"); + assert(_maybe_top_type_nodes.size() == 0, "we don't need type nodes"); } // Initialize the traversal. diff --git a/src/hotspot/share/opto/phaseX.hpp b/src/hotspot/share/opto/phaseX.hpp index 7ea7aa99142..014d16f92f6 100644 --- a/src/hotspot/share/opto/phaseX.hpp +++ b/src/hotspot/share/opto/phaseX.hpp @@ -501,10 +501,6 @@ class PhaseIterGVN : public PhaseGVN { // Usually returns new_type. Returns old_type if new_type is only a slight // improvement, such that it would take many (>>10) steps to reach 2**32. - DeadPathNode* dead_path(); - - void make_dependent_paths_dead_if_top(Node* dead_node, const Type* t); - public: PhaseIterGVN(PhaseIterGVN* igvn); // Used by CCP constructor @@ -699,7 +695,7 @@ class PhaseIterGVN : public PhaseGVN { // Should be replaced with combined CCP & GVN someday. class PhaseCCP : public PhaseIterGVN { Unique_Node_List _root_and_safepoints; - Unique_Node_List _maybe_top_type_or_div_mod_nodes; + Unique_Node_List _maybe_top_type_nodes; // Non-recursive. Use analysis to transform single Node. virtual Node* transform_once(Node* n); diff --git a/src/hotspot/share/opto/rootnode.cpp b/src/hotspot/share/opto/rootnode.cpp index 1e5ef29e79c..60167c5436a 100644 --- a/src/hotspot/share/opto/rootnode.cpp +++ b/src/hotspot/share/opto/rootnode.cpp @@ -90,48 +90,3 @@ const Type* HaltNode::Value(PhaseGVN* phase) const { const RegMask &HaltNode::out_RegMask() const { return RegMask::EMPTY; } - -Node* DeadPathNode::Ideal(PhaseGVN* phase, bool can_reshape) { - assert(unique_ctrl_out() == phase->C->root(), "only referenced from root"); - assert(can_reshape, "only used once igvn executes"); - bool modified = false; - for (uint i = 1; i < req(); i++) { // For all inputs - // Check for and remove dead inputs - if (phase->type(in(i)) == Type::TOP) { - del_req(i--); // Delete TOP inputs - modified = true; - } - } - if (req() == 1 && is_active()) { - assert(modified, "only if some inputs were removed"); - deactivate(); - } - return modified ? this : nullptr; -} - -const Type* DeadPathNode::Value(PhaseGVN* phase) const { - if (req() == 1) { - return Type::TOP; - } - return bottom_type(); -} - -void DeadPathNode::activate(PhaseIterGVN* igvn) { - assert(Compile::current()->root()->find_edge(this) < 0, "should be disconnected from root"); - set_req(0, this); - // If an entire subgraph died such as with Node::remove_dead_region(), some dead inputs to the DeadPath node will have - // been left behind - while (req() > 1) { - uint last = req() - 1; - assert(in(last) == nullptr || in(last)->is_top(), "only dead inputs should remain"); - del_req(last); - } - Node* root_node = Compile::current()->root(); - root_node->add_req(this); - igvn->_worklist.push(root_node); - igvn->set_type(this, bottom_type()); -} - -void DeadPathNode::deactivate() { - set_req(0, nullptr); -} diff --git a/src/hotspot/share/opto/rootnode.hpp b/src/hotspot/share/opto/rootnode.hpp index 61ad317d455..76f0ec440a9 100644 --- a/src/hotspot/share/opto/rootnode.hpp +++ b/src/hotspot/share/opto/rootnode.hpp @@ -69,35 +69,4 @@ class HaltNode : public Node { virtual uint match_edge(uint idx) const { return 0; } }; - -// This node collects paths that are found dead by PhaseIterGVN::make_dependent_paths_dead_if_top() - -// There is a single DeadPath node for the lifetime of optimizations. It's initially not active (i.e. unreachable from -// the IR graph). When a cfg path becomes dead it's added as an input to the unique DeadPath node. If after some -// optimizations run, the DeadPath node gets disconnected, it's not destroyed. It becomes inactive and can possibly be -// activated again on a subsequent igvn. When optimizations are over, the DeadPath node, if it is active, is expanded to -// a Region and Halt node in Compile::final_graph_reshaping(). - -// Rather than having this dedicated node, igvn could add a Halt node everytime it finds a dead cfg path from a data -// node. What's likely, however, is that as igvn progresses, that same cfg path is found dead by following cfg edges. -// The Halt node then becomes dead. To avoid this unnecessary cycle of creation of a Halt node only to have it be found -// dead shortly after, dead cfg paths are added to the unique DeadPath node. -class DeadPathNode : public RegionNode { -public: - DeadPathNode() : RegionNode(1) { - deactivate(); - assert(Compile::current()->dead_path() == nullptr, "only one"); - } - virtual int Opcode() const; - virtual const Type* bottom_type() const { return Type::BOTTOM; } - virtual Node* Identity(PhaseGVN* phase) { return this; } - virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); - virtual const Type* Value(PhaseGVN* phase) const; - bool is_active() const { - return in(0) == this; - } - void activate(PhaseIterGVN* igvn); - void deactivate(); -}; - #endif // SHARE_OPTO_ROOTNODE_HPP diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index 60eda1204b7..20857eed35c 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -2391,7 +2391,7 @@ Node* VectorMaskOpNode::Ideal(PhaseGVN* phase, bool can_reshape) { if (n != nullptr) { return n; } - return nullptr; + return TypeNode::Ideal(phase, can_reshape); } Node* VectorMaskCastNode::Identity(PhaseGVN* phase) { diff --git a/test/hotspot/jtreg/compiler/c2/TestDeadPathManyDeadDataNodes.java b/test/hotspot/jtreg/compiler/c2/TestDeadPathManyDeadDataNodes.java deleted file mode 100644 index e9c5a8f7529..00000000000 --- a/test/hotspot/jtreg/compiler/c2/TestDeadPathManyDeadDataNodes.java +++ /dev/null @@ -1,1301 +0,0 @@ -/* - * Copyright (c) 2026 IBM Corporation. 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 8380166 - * @summary C2: crash in compiled code due to zero division because of widened CastII - * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions - * -Xcomp -XX:CompileOnly=TestDeadPathManyDeadDataNodes::test1 - * -XX:CompileCommand=quiet - * -XX:CompileCommand=inline,TestDeadPathManyDeadDataNodes::inlined1 - * -XX:MaxRecursiveInlineLevel=1000 -XX:MaxInlineLevel=1000 - * -XX:-TieredCompilation -XX:+AlwaysIncrementalInline - * -XX:+DelayAfterInliningCutoff -XX:+IncrementalInlineForceCleanup - * -XX:NodeCountInliningCutoff=100000 -XX:+StressIGVN - * ${test.main.class} - * @run main ${test.main.class} - */ - -package compiler.c2; - -public class TestDeadPathManyDeadDataNodes { - private static int field; - private static boolean boolField2; - private static int arrayLengthField; - - public static void main(String[] args) { - Object o = new Object(); - try { - test1(false, 0); - } catch (NegativeArraySizeException nase) { - } - } - - private static int test1(boolean boolParam, int intParam) { - int length; - int res = 0; - length = -1; - for (int i = 0; i < 2; i++) { - if (boolParam) { - field = 42; - } - int[] array = new int[length]; - arrayLengthField = array.length; - while(true) { - Object o = new Object(); - int arrayLength = arrayLengthField; - arrayLengthField = 0; - switch (intParam) { - case 0: - if (boolField2) { - break; - } - field = 42; - continue; - case 1: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 2: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 3: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 4: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 5: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 6: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 7: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 8: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 9: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 10: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 11: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 12: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 13: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 14: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 15: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 16: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 17: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 18: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 19: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 20: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 21: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 22: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 23: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 24: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 25: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 26: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 27: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 28: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 29: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 30: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 31: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 32: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 33: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 34: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 35: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 36: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 37: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 38: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 39: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 40: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 41: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 42: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 43: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 44: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 45: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 46: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 47: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 48: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 49: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 50: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 51: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 52: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 53: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 54: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 55: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 56: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 57: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 58: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 59: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 60: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 61: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 62: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 63: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 64: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 65: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 66: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 67: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 68: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 69: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 70: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 71: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 72: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 73: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 74: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 75: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 76: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 77: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 78: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 79: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 80: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 81: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 82: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 83: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 84: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 85: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 86: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 87: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 88: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 89: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 90: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 91: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 92: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 93: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 94: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 95: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 96: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 97: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 98: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 99: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - default: - res += inlined1(boolParam, intParam/100, arrayLength, 92); - continue; - } - field = 42; - break; - } - length = lastInlined(); - } - return res; - } - - static int lastInlined() { - return -1; - } - - static int inlined1(boolean boolParam, int intParam, int arrayLength, int count) { - int res = 0; - switch (intParam) { - case 0: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 1: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 2: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 3: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 4: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 5: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 6: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 7: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 8: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 9: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 10: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 11: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 12: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 13: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 14: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 15: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 16: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 17: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 18: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 19: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 20: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 21: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 22: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 23: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 24: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 25: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 26: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 27: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 28: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 29: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 30: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 31: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 32: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 33: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 34: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 35: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 36: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 37: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 38: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 39: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 40: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 41: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 42: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 43: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 44: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 45: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 46: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 47: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 48: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 49: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 50: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 51: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 52: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 53: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 54: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 55: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 56: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 57: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 58: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 59: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 60: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 61: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 62: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 63: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 64: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 65: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 66: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 67: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 68: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 69: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 70: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 71: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 72: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 73: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 74: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 75: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 76: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 77: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 78: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 79: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 80: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 81: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 82: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 83: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 84: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 85: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 86: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 87: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 88: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 89: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 90: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 91: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 92: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 93: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 94: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 95: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 96: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 97: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 98: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 99: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - default: - if (count == 0) { - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - } else { - return inlined1(boolParam, intParam / 100, arrayLength, count-1); - } - } - } -} diff --git a/test/hotspot/jtreg/compiler/integerArithmetic/TestDivByZeroInLiveCFGPath.java b/test/hotspot/jtreg/compiler/integerArithmetic/TestDivByZeroInLiveCFGPath.java deleted file mode 100644 index 6eaf3d86e71..00000000000 --- a/test/hotspot/jtreg/compiler/integerArithmetic/TestDivByZeroInLiveCFGPath.java +++ /dev/null @@ -1,64 +0,0 @@ - -/* - * 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 8383815 - * @summary C2: assert(false) failed: malformed IfNode with 1 outputs - * @run main/othervm -XX:CompileCommand=compileonly,${test.main.class}*::* -XX:-TieredCompilation -Xbatch -XX:PerMethodTrapLimit=0 ${test.main.class} - * @run main ${test.main.class} - */ - -package compiler.integerArithmetic; - -public class TestDivByZeroInLiveCFGPath { - static long lFld; - static int iArr[] = new int[400]; - - public static void main(String[] strArr) { - for (int i = 0; i < 10; i++) { - test(); - } - } - - static void test() { - int x; - for (int i = 9; i < 100; ++i) { - int j = 100; - while (--j > 0) { - iArr[1] = (int) lFld; - } - try { - iArr[1] = (5 / j); - x = (i / iArr[8]); - } catch (ArithmeticException a_e) { - } - } - - for (int i = 18; i < 50; i++) { - iArr[2] += lFld; - } - } -} - diff --git a/test/hotspot/jtreg/compiler/integerArithmetic/TestZeroDivModWidenedCastII.java b/test/hotspot/jtreg/compiler/integerArithmetic/TestZeroDivModWidenedCastII.java deleted file mode 100644 index a5bc8fc9287..00000000000 --- a/test/hotspot/jtreg/compiler/integerArithmetic/TestZeroDivModWidenedCastII.java +++ /dev/null @@ -1,1122 +0,0 @@ -/* - * Copyright (c) 2026 IBM Corporation. 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 8380166 - * @summary C2: crash in compiled code due to zero division because of widened CastII - * - * @run main/othervm -XX:-TieredCompilation -XX:-UseOnStackReplacement -XX:-BackgroundCompilation - * ${test.main.class} - * @run main ${test.main.class} - * - */ - -package compiler.integerArithmetic; - -public class TestZeroDivModWidenedCastII { - private static int intField; - private static long longField; - private static volatile int volatileField; - - public static void main(String[] args) { - for (int i = 0; i < 20_000; i++) { - test1(0, 9, 1, true, false); - test1(0, 9, 1, false, false); - inlined1_2(9, 1, 1, true, 0); - inlined1_3(0, 0); - test2(0, 9, 1, true, false); - test2(0, 9, 1, false, false); - inlined2_2(9, 1, 1, true, 0); - inlined2_3(0, 0); - test3(0, 9, 1, true, false); - test3(0, 9, 1, false, false); - inlined3_2(9, 1, 1, true, 0); - inlined3_3(0, 0); - test4(0, 9, 1, true, false); - test4(0, 9, 1, false, false); - inlined4_2(9, 1, 1, true, 0); - inlined4_3(0, 0); - test5(0, 9, 1, true, false); - test5(0, 9, 1, false, false); - inlined5_2(9, 1, 1, true, 0); - inlined5_3(0, 0); - test6(0, 9, 1, true, false); - test6(0, 9, 1, false, false); - inlined6_2(9, 1, 1, true, 0); - inlined6_3(0, 0); - test7(0, 9, 1, true, false); - test7(0, 9, 1, false, false); - inlined7_2(9, 1, 1, true, 0); - inlined7_3(0, 0); - test8(0, 9, 1, true, false); - test8(0, 9, 1, false, false); - inlined8_2(9, 1, 1, true, 0); - inlined8_3(0, 0); - test9(0, 9, 1, true, false); - test9(0, 9, 1, false, false); - inlined9_2(9, 1, 1, true, 0); - inlined9_3(0, 0); - test10(0, 9, 1, false); - inlined10_2(9, 1, 1, true, 0); - inlined10_3(0, 0); - test11(0, 9, 1, false); - inlined11_2(9, 1, 1, true, 0); - inlined11_3(0, 0); - test12(0, 9, 1, false); - inlined12_2(9, 1, 1, true, 0); - inlined12_3(0, 0); - test13(0, 9, 1, false); - inlined13_2(9, 1, 1, true, 0); - inlined13_3(0, 0); - test14(0, 9, 1, false); - inlined14_2(9, 1, 1, true, 0); - inlined14_3(0, 0); - test15(0, 9, 1, false); - inlined15_2(9, 1, 1, true, 0); - inlined15_3(0, 0); - test16(0, 9, 1, false); - inlined16_2(9, 1, 1, true, 0); - inlined16_3(0, 0); - test17(0, 9, 1, false); - inlined17_2(9, 1, 1, true, 0); - inlined17_3(0, 0); - } - } - - private static void test1(int k, int j, int flag, boolean flag2, boolean flag3) { - int l = 0; - for (; l < 10; l++); - int m = inlined1_3(j, l); - - int i = inlined1(k, flag2); - j = Integer.min(j, 9); - int[] array = new int[10]; - if (flag == 0) { - throw new RuntimeException("never taken"); - } - if (flag2) { - inlined1_2(j, flag, i, flag3, m); - } else { - inlined1_2(j, flag, i, flag3, m); - } - } - - private static int inlined1_3(int j, int l) { - if (l == 10) { - j = 1; - } - return j; - } - - private static void inlined1_2(int j, int flag, int i, boolean flag3, int m) { - if (flag3) { - float[] newArray = new float[j + 1]; // j + 1 in [0..10] - // RC i Date: Fri, 17 Jul 2026 15:51:06 +0000 Subject: [PATCH 263/707] 8388358: HotCodeHeap should throw warning when enabled without C2 Reviewed-by: kvn, eastigeevich --- src/hotspot/share/compiler/compilerDefinitions.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/hotspot/share/compiler/compilerDefinitions.cpp b/src/hotspot/share/compiler/compilerDefinitions.cpp index 5bb96f8d031..1ad667e51f1 100644 --- a/src/hotspot/share/compiler/compilerDefinitions.cpp +++ b/src/hotspot/share/compiler/compilerDefinitions.cpp @@ -272,7 +272,11 @@ void CompilerConfig::set_compilation_policy_flags() { } #ifdef COMPILER2 - if (HotCodeHeap) { + if (HotCodeHeap && !is_c2_enabled()) { + warning("HotCodeHeap disabled because C2 is disabled."); + FLAG_SET_ERGO(HotCodeHeap, false); + FLAG_SET_ERGO(HotCodeHeapSize, 0); + } else if (HotCodeHeap) { if (FLAG_IS_DEFAULT(SegmentedCodeCache)) { FLAG_SET_ERGO(SegmentedCodeCache, true); } else if (!SegmentedCodeCache) { @@ -285,10 +289,6 @@ void CompilerConfig::set_compilation_policy_flags() { vm_exit_during_initialization("HotCodeHeap requires NMethodRelocation enabled"); } - if (!is_c2_enabled()) { - vm_exit_during_initialization("HotCodeHeap requires C2 enabled"); - } - if (HotCodeMinSamplingMs > HotCodeMaxSamplingMs) { vm_exit_during_initialization("HotCodeMinSamplingMs cannot be larger than HotCodeMaxSamplingMs"); } From 2a83c509772d1645eb4ca1ad52a112d1ac57daa3 Mon Sep 17 00:00:00 2001 From: Artur Barashev Date: Thu, 8 Jan 2026 19:08:20 +0000 Subject: [PATCH 264/707] 8373275: Improve DTLS handshaking Reviewed-by: rhalade, pkumaraswamy, ahgross, jnibedita, jnimeh, mullan --- .../sun/security/ssl/HelloCookieManager.java | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/java.base/share/classes/sun/security/ssl/HelloCookieManager.java b/src/java.base/share/classes/sun/security/ssl/HelloCookieManager.java index b3155f5170a..4268b9779bd 100644 --- a/src/java.base/share/classes/sun/security/ssl/HelloCookieManager.java +++ b/src/java.base/share/classes/sun/security/ssl/HelloCookieManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ package sun.security.ssl; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; @@ -121,6 +122,7 @@ abstract boolean isCookieValid(ServerHandshakeContext context, private static final class D10HelloCookieManager extends HelloCookieManager { + private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; final SecureRandom secureRandom; private int cookieVersion; // allow to wrap, version + sequence private final byte[] cookieSecret; @@ -170,6 +172,7 @@ byte[] createCookie(ServerHandshakeContext context, } byte[] helloBytes = clientHello.getHelloCookieBytes(); md.update(helloBytes); + md.update(getHostPortBytes(context)); byte[] cookie = md.digest(secret); // 32 bytes cookie[0] = (byte)((version >> 24) & 0xFF); @@ -205,11 +208,30 @@ boolean isCookieValid(ServerHandshakeContext context, } byte[] helloBytes = clientHello.getHelloCookieBytes(); md.update(helloBytes); + md.update(getHostPortBytes(context)); byte[] target = md.digest(secret); // 32 bytes target[0] = cookie[0]; return MessageDigest.isEqual(target, cookie); } + + /** + * Returns host and port bytes if those are set. + * Using ASCII unit separator character to separate host and port so we + * can differentiate between otherwise identical host and port string + * concatenations, for example host 172.0.0.1 with port 25 and host + * 172.0.0.12 with port 5. + */ + private static byte[] getHostPortBytes(ServerHandshakeContext context) { + final String host = context.conContext.transport.getPeerHost(); + final int port = context.conContext.transport.getPeerPort(); + final String hostStr = host != null ? host : ""; + final String portStr = port > -1 ? Integer.toString(port) : ""; + return hostStr.isEmpty() && portStr.isEmpty() ? + EMPTY_BYTE_ARRAY : + (hostStr + '\u001F' + portStr).getBytes( + StandardCharsets.UTF_8); + } } private static final From b84cef8064de569d6f728f5291662bc9bb4cd8a9 Mon Sep 17 00:00:00 2001 From: Bradford Wetmore Date: Wed, 14 Jan 2026 20:54:56 +0000 Subject: [PATCH 265/707] 8368041: Enhance TLS certificate handling Reviewed-by: jnimeh, abarashev, hchao, djelinski, ksreenath, ahgross, rhalade --- .../share/classes/sun/security/ssl/Alert.java | 60 ++++++++++++------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/src/java.base/share/classes/sun/security/ssl/Alert.java b/src/java.base/share/classes/sun/security/ssl/Alert.java index e9588a09b3d..c081ec5f747 100644 --- a/src/java.base/share/classes/sun/security/ssl/Alert.java +++ b/src/java.base/share/classes/sun/security/ssl/Alert.java @@ -181,11 +181,13 @@ private static final class AlertMessage { AlertMessage(TransportContext context, ByteBuffer m) throws IOException { - // From RFC 8446 "Implementations - // MUST NOT send Handshake and Alert records that have a zero-length - // TLSInnerPlaintext.content; if such a message is received, the - // receiving implementation MUST terminate the connection with an - // "unexpected_message" alert." + + // From RFC 8446: TLSv1.3 + // + // Implementations MUST NOT send Handshake and Alert records that + // have a zero-length TLSInnerPlaintext.content; if such a message + // is received, the receiving implementation MUST terminate the + // connection with an "unexpected_message" alert. if (m.remaining() == 0) { throw context.fatal(Alert.UNEXPECTED_MESSAGE, "Alert fragments must not be zero length."); @@ -264,27 +266,39 @@ public void consume(ConnectionContext context, } else if ((level == Level.WARNING) && (alert != null)) { // Terminate the connection if an alert with a level of warning // is received during handshaking, except the no_certificate - // warning. - if (alert.handshakeOnly && (tc.handshakeContext != null)) { - // It's OK to get a no_certificate alert from a client of - // which we requested client authentication. However, - // if we required it, then this is not acceptable. - if (tc.sslConfig.isClientMode || - alert != Alert.NO_CERTIFICATE || - (tc.sslConfig.clientAuthType != + // warning for SSLv3. + HandshakeContext hc = tc.handshakeContext; + if (alert.handshakeOnly && (hc != null)) { + // In SSLv3, it's OK to get a no_certificate alert from a + // client where we requested (want) client authentication. + // If we required it (need), this is not acceptable + // and must fail. + // + // no_certificate alerts are not acceptable in TLSv1.*. + // + if (!tc.sslConfig.isClientMode && + (hc.negotiatedProtocol == ProtocolVersion.SSL30) && + (alert == Alert.NO_CERTIFICATE) && + (tc.sslConfig.clientAuthType == ClientAuthType.CLIENT_AUTH_REQUESTED)) { - throw tc.fatal(Alert.HANDSHAKE_FAILURE, - "received handshake warning: " + alert.description); - } else { - // Otherwise, ignore the warning but remove the - // Certificate and CertificateVerify handshake - // consumer so the state machine doesn't expect it. - tc.handshakeContext.handshakeConsumers.remove( - SSLHandshake.CERTIFICATE.id); - tc.handshakeContext.handshakeConsumers.remove( + + // We'll ignore the warning and remove the Certificate, + // CompressedCertificate and CertificateVerify handshake + // consumers so the state machine isn't expecting them. + if (hc.handshakeConsumers.remove( + SSLHandshake.CERTIFICATE.id) != null) { + hc.handshakeConsumers.remove( SSLHandshake.COMPRESSED_CERTIFICATE.id); - tc.handshakeContext.handshakeConsumers.remove( + hc.handshakeConsumers.remove( SSLHandshake.CERTIFICATE_VERIFY.id); + } else { + throw tc.fatal(Alert.HANDSHAKE_FAILURE, + "NO_CERTIFICATE alert received when certs" + + " were not expected or already received"); + } + } else { + throw tc.fatal(Alert.HANDSHAKE_FAILURE, + "Received handshake warning: " + alert.description); } } // Otherwise, ignore the warning } else { // fatal or unknown From 1b0aeb09407fd9ff3d1c0864c812b479b228609c Mon Sep 17 00:00:00 2001 From: Volkan Yazici Date: Mon, 2 Mar 2026 10:00:34 +0000 Subject: [PATCH 266/707] 8377498: Improve HttpServer handling Reviewed-by: dfuchs --- .../sun/net/httpserver/ServerImpl.java | 11 +++++++++- .../simpleserver/FileServerHandler.java | 22 ++++++++++--------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java index 3d77a61c0be..f0a8efe1a6b 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java @@ -770,15 +770,24 @@ public void run() { requestLine, "Bad request line"); return; } + + // Read the request URI String uriStr = requestLine.substring(start, space); + // Reject ambiguous URIs + if (uriStr.startsWith("//")) { + reject(Code.HTTP_BAD_REQUEST, + requestLine, "Bad request URI"); + return; + } URI uri; try { uri = new URI(uriStr); } catch (URISyntaxException e3) { reject(Code.HTTP_BAD_REQUEST, - requestLine, "URISyntaxException thrown"); + requestLine, "Bad request URI"); return; } + start = space+1; String version = requestLine.substring(start); Headers headers = req.headers(); diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/simpleserver/FileServerHandler.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/simpleserver/FileServerHandler.java index cbf032e8398..08ea357b7f4 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/simpleserver/FileServerHandler.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/simpleserver/FileServerHandler.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 @@ -106,16 +106,22 @@ private void handleGET(HttpExchange exchange, Path path) throws IOException { private void handleSupportedMethod(HttpExchange exchange, Path path, boolean writeBody) throws IOException { + boolean requestURIEndsWithSlash = pathEndsWithSlash(exchange); if (Files.isDirectory(path)) { - if (missingSlash(exchange)) { + if (!requestURIEndsWithSlash) { handleMovedPermanently(exchange); return; } - if (indexFile(path) != null) { - serveFile(exchange, indexFile(path), writeBody); + Path indexFile = indexFile(path); + if (indexFile != null) { + serveFile(exchange, indexFile, writeBody); } else { listFiles(exchange, path, writeBody); } + } + // Disallow non-directory paths ending with slash + else if (requestURIEndsWithSlash) { + handleNotFound(exchange); } else { serveFile(exchange, path, writeBody); } @@ -126,10 +132,6 @@ private void handleMovedPermanently(HttpExchange exchange) throws IOException { exchange.sendResponseHeaders(301, RSPBODY_EMPTY); } - private void handleForbidden(HttpExchange exchange) throws IOException { - exchange.sendResponseHeaders(403, RSPBODY_EMPTY); - } - private void handleNotFound(HttpExchange exchange) throws IOException { String fileNotFound = ResourceBundleHelper.getMessage("html.not.found"); var bytes = (openHTML @@ -161,8 +163,8 @@ private String getRedirectURI(URI uri) { return query == null ? redirectPath : redirectPath + "?" + query; } - private static boolean missingSlash(HttpExchange exchange) { - return !exchange.getRequestURI().getPath().endsWith("/"); + private static boolean pathEndsWithSlash(HttpExchange exchange) { + return exchange.getRequestURI().getPath().endsWith("/"); } private static String contextPath(HttpExchange exchange) { From d72538f9a59944ca977d3196e019bcede1195ff8 Mon Sep 17 00:00:00 2001 From: Jayathirth D V Date: Wed, 4 Mar 2026 17:01:12 +0000 Subject: [PATCH 267/707] 8374058: Enhance JPEG handling Reviewed-by: mschoene, rhalade, psadhukhan, prr --- .../share/native/libjavajpeg/imageioJPEG.c | 395 ++++++++---------- .../jpeg/LargeJpegReadWithProgressBench.java | 166 ++++++++ .../plugins/jpeg/LargeJpegReadWriteBench.java | 141 +++++++ 3 files changed, 472 insertions(+), 230 deletions(-) create mode 100644 test/micro/org/openjdk/bench/javax/imageio/plugins/jpeg/LargeJpegReadWithProgressBench.java create mode 100644 test/micro/org/openjdk/bench/javax/imageio/plugins/jpeg/LargeJpegReadWriteBench.java diff --git a/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c b/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c index a764eb1ae3b..ac37ad8eab6 100644 --- a/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c +++ b/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -120,6 +120,7 @@ typedef struct streamBufferStruct { size_t bufferLength; // Allocated, nut just used int suspendable; // Set to true to suspend input long remaining_skip; // Used only on input + jboolean isCopy; // GetByteArrayElements copied/pinned the Java array } streamBuffer, *streamBufferPtr; /* @@ -200,7 +201,8 @@ static void destroyStreamBuffer(JNIEnv *env, streamBufferPtr sb) { // Forward reference static void unpinStreamBuffer(JNIEnv *env, streamBufferPtr sb, - const JOCTET *next_byte); + const JOCTET *next_byte, + int streamReleaseMode); /* * Resets the state of a streamBuffer object that has been in use. * The global reference to the stream is released, but the reference @@ -212,15 +214,16 @@ static void resetStreamBuffer(JNIEnv *env, streamBufferPtr sb) { (*env)->DeleteWeakGlobalRef(env, sb->ioRef); sb->ioRef = NULL; } - unpinStreamBuffer(env, sb, NULL); + unpinStreamBuffer(env, sb, NULL, JNI_ABORT); sb->bufferOffset = NO_DATA; sb->suspendable = FALSE; sb->remaining_skip = 0; } /* - * Pins the data buffer associated with this stream. Returns OK on - * success, NOT_OK on failure, as GetPrimitiveArrayCritical may fail. + * Pins/copies the data buffer associated with this stream. Returns OK on + * success, NOT_OK on failure, as GetByteArrayElements + * may fail. */ static int pinStreamBuffer(JNIEnv *env, streamBufferPtr sb, @@ -228,9 +231,9 @@ static int pinStreamBuffer(JNIEnv *env, if (sb->hstreamBuffer != NULL) { assert(sb->buf == NULL); sb->buf = - (JOCTET *)(*env)->GetPrimitiveArrayCritical(env, - sb->hstreamBuffer, - NULL); + (JOCTET *)(*env)->GetByteArrayElements(env, + sb->hstreamBuffer, + &sb->isCopy); if (sb->buf == NULL) { return NOT_OK; } @@ -242,11 +245,12 @@ static int pinStreamBuffer(JNIEnv *env, } /* - * Unpins the data buffer associated with this stream. + * Unpins/releases the data buffer associated with this stream. */ static void unpinStreamBuffer(JNIEnv *env, streamBufferPtr sb, - const JOCTET *next_byte) { + const JOCTET *next_byte, + int streamReleaseMode) { if (sb->buf != NULL) { assert(sb->hstreamBuffer != NULL); if (next_byte == NULL) { @@ -254,11 +258,13 @@ static void unpinStreamBuffer(JNIEnv *env, } else { sb->bufferOffset = next_byte - sb->buf; } - (*env)->ReleasePrimitiveArrayCritical(env, - sb->hstreamBuffer, - sb->buf, - 0); - sb->buf = NULL; + (*env)->ReleaseByteArrayElements(env, + sb->hstreamBuffer, + (jbyte *)sb->buf, + streamReleaseMode); + if (streamReleaseMode != JNI_COMMIT) { + sb->buf = NULL; + } } } @@ -276,6 +282,7 @@ static void clearStreamBuffer(streamBufferPtr sb) { typedef struct pixelBufferStruct { jobject hpixelObject; // Usually a DataBuffer bank as a byte array unsigned int byteBufferLength; + jboolean isCopy; // GetByteArrayElements copied/pinned the Java array union pixptr { INT32 *ip; // Pinned buffer pointer, as 32-bit ints unsigned char *bp; // Pinned buffer pointer, as bytes @@ -309,7 +316,7 @@ static int setPixelBuffer(JNIEnv *env, pixelBufferPtr pb, jobject obj) { } // Forward reference -static void unpinPixelBuffer(JNIEnv *env, pixelBufferPtr pb); +static void unpinPixelBuffer(JNIEnv *env, pixelBufferPtr pb, int pixelReleaseMode); /* * Resets a pixel buffer to its initial state. Unpins any pixel buffer, @@ -318,7 +325,7 @@ static void unpinPixelBuffer(JNIEnv *env, pixelBufferPtr pb); */ static void resetPixelBuffer(JNIEnv *env, pixelBufferPtr pb) { if (pb->hpixelObject != NULL) { - unpinPixelBuffer(env, pb); + unpinPixelBuffer(env, pb, JNI_ABORT); (*env)->DeleteGlobalRef(env, pb->hpixelObject); pb->hpixelObject = NULL; pb->byteBufferLength = 0; @@ -326,13 +333,13 @@ static void resetPixelBuffer(JNIEnv *env, pixelBufferPtr pb) { } /* - * Pins the data buffer. Returns OK on success, NOT_OK on failure. + * Pins/copies the data buffer. Returns OK on success, NOT_OK on failure. */ static int pinPixelBuffer(JNIEnv *env, pixelBufferPtr pb) { if (pb->hpixelObject != NULL) { assert(pb->buf.ip == NULL); - pb->buf.bp = (unsigned char *)(*env)->GetPrimitiveArrayCritical - (env, pb->hpixelObject, NULL); + pb->buf.bp = (unsigned char *)(*env)->GetByteArrayElements + (env, pb->hpixelObject, &pb->isCopy); if (pb->buf.bp == NULL) { return NOT_OK; } @@ -341,17 +348,19 @@ static int pinPixelBuffer(JNIEnv *env, pixelBufferPtr pb) { } /* - * Unpins the data buffer. + * Unpins/releases the pixel buffer. */ -static void unpinPixelBuffer(JNIEnv *env, pixelBufferPtr pb) { +static void unpinPixelBuffer(JNIEnv *env, pixelBufferPtr pb, int pixelReleaseMode) { if (pb->buf.ip != NULL) { assert(pb->hpixelObject != NULL); - (*env)->ReleasePrimitiveArrayCritical(env, - pb->hpixelObject, - pb->buf.ip, - 0); - pb->buf.ip = NULL; + (*env)->ReleaseByteArrayElements(env, + pb->hpixelObject, + (jbyte *)pb->buf.ip, + pixelReleaseMode); + if (pixelReleaseMode != JNI_COMMIT) { + pb->buf.ip = NULL; + } } } @@ -468,34 +477,28 @@ static j_common_ptr destroyImageioData(JNIEnv *env, imageIODataPtr data) { /******************** Java array pinning and unpinning *****************/ -/* We use Get/ReleasePrimitiveArrayCritical functions to avoid - * the need to copy array elements for the above two objects. - * - * MAKE SURE TO: - * - * - carefully insert pairs of RELEASE_ARRAYS and GET_ARRAYS around - * callbacks to Java. - * - call RELEASE_ARRAYS before returning to Java. - * - * Otherwise things will go horribly wrong. There may be memory leaks, - * excessive pinning, or even VM crashes! - * - * Note that GetPrimitiveArrayCritical may fail! +/* + * We use Get/ReleaseByteArrayElements functions for access stream + * and pixel information from Java level arrays. + * If we receive reference to copy of Java array make sure you update + * Java array also when the latest information is needed at Java level. + * Also we use specific release modes for performance optimizations. */ /* - * Release (unpin) all the arrays in use during a read. + * Release (unpin) both stream and pixel arrays. */ -static void RELEASE_ARRAYS(JNIEnv *env, imageIODataPtr data, const JOCTET *next_byte) +static void RELEASE_ARRAYS(JNIEnv *env, imageIODataPtr data, const JOCTET *next_byte, + int streamReleaseMode, int pixelReleaseMode) { - unpinStreamBuffer(env, &data->streamBuf, next_byte); + unpinStreamBuffer(env, &data->streamBuf, next_byte, streamReleaseMode); - unpinPixelBuffer(env, &data->pixelBuf); + unpinPixelBuffer(env, &data->pixelBuf, pixelReleaseMode); } /* - * Get (pin) all the arrays in use during a read. + * Get (pin) both stream and pixel arrays. */ static int GET_ARRAYS(JNIEnv *env, imageIODataPtr data, const JOCTET **next_byte) { if (pinStreamBuffer(env, &data->streamBuf, next_byte) == NOT_OK) { @@ -503,7 +506,7 @@ static int GET_ARRAYS(JNIEnv *env, imageIODataPtr data, const JOCTET **next_byte } if (pinPixelBuffer(env, &data->pixelBuf) == NOT_OK) { - RELEASE_ARRAYS(env, data, *next_byte); + RELEASE_ARRAYS(env, data, *next_byte, JNI_ABORT, JNI_ABORT); return NOT_OK; } return OK; @@ -570,26 +573,16 @@ sun_jpeg_output_message (j_common_ptr cinfo) theObject = data->imageIOobj; if (cinfo->is_decompressor) { - struct jpeg_source_mgr *src = ((j_decompress_ptr)cinfo)->src; - RELEASE_ARRAYS(env, data, src->next_input_byte); (*env)->CallVoidMethod(env, theObject, JPEGImageReader_warningWithMessageID, string); - if ((*env)->ExceptionCheck(env) - || !GET_ARRAYS(env, data, &(src->next_input_byte))) { - cinfo->err->error_exit(cinfo); - } } else { - struct jpeg_destination_mgr *dest = ((j_compress_ptr)cinfo)->dest; - RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte)); (*env)->CallVoidMethod(env, theObject, JPEGImageWriter_warningWithMessageID, string); - if ((*env)->ExceptionCheck(env) - || !GET_ARRAYS(env, data, - (const JOCTET **)(&dest->next_output_byte))) { - cinfo->err->error_exit(cinfo); - } + } + if ((*env)->ExceptionCheck(env)) { + cinfo->err->error_exit(cinfo); } } @@ -941,7 +934,7 @@ imageio_fill_input_buffer(j_decompress_ptr cinfo) #ifdef DEBUG_IIO_JPEG printf("Filling input buffer, remaining skip is %ld, ", sb->remaining_skip); - printf("Buffer length is %d\n", sb->bufferLength); + printf("Buffer length is %zu\n", sb->bufferLength); #endif /* @@ -956,8 +949,15 @@ imageio_fill_input_buffer(j_decompress_ptr cinfo) /* * Now fill a complete buffer, or as much of one as the stream * will give us if we are near the end. + * + * The native copy of java array is not valid anymore so we just + * release it and get new copy, if we don't have native copy we rely + * on JVM to maintain the pinned handle of java array. */ - RELEASE_ARRAYS(env, data, src->next_input_byte); + jboolean isCopy = sb->isCopy; + if (isCopy) { + unpinStreamBuffer(env, &data->streamBuf, src->next_input_byte, JNI_ABORT); + } GET_IO_REF(input); @@ -969,9 +969,12 @@ imageio_fill_input_buffer(j_decompress_ptr cinfo) if ((ret > 0) && ((unsigned int)ret > sb->bufferLength)) { ret = (int)sb->bufferLength; } - if ((*env)->ExceptionCheck(env) - || !GET_ARRAYS(env, data, &(src->next_input_byte))) { - cinfo->err->error_exit((j_common_ptr) cinfo); + + if ((*env)->ExceptionCheck(env) || + (isCopy && (pinStreamBuffer(env, + &data->streamBuf, + &(src->next_input_byte)) == NOT_OK))) { + cinfo->err->error_exit((j_common_ptr) cinfo); } #ifdef DEBUG_IIO_JPEG @@ -988,12 +991,10 @@ imageio_fill_input_buffer(j_decompress_ptr cinfo) #ifdef DEBUG_IIO_JPEG printf("YO! Early EOI! ret = %d\n", ret); #endif - RELEASE_ARRAYS(env, data, src->next_input_byte); (*env)->CallVoidMethod(env, reader, JPEGImageReader_warningOccurredID, READ_NO_EOI); - if ((*env)->ExceptionCheck(env) - || !GET_ARRAYS(env, data, &(src->next_input_byte))) { + if ((*env)->ExceptionCheck(env)) { cinfo->err->error_exit((j_common_ptr) cinfo); } @@ -1008,97 +1009,6 @@ imageio_fill_input_buffer(j_decompress_ptr cinfo) return TRUE; } -/* - * With I/O suspension turned on, the JPEG library requires that all - * buffer filling be done at the top application level, using this - * function. Due to the way that backtracking works, this procedure - * saves all of the data that was left in the buffer when suspension - * occurred and read new data only at the end. - */ - -GLOBAL(void) -imageio_fill_suspended_buffer(j_decompress_ptr cinfo) -{ - struct jpeg_source_mgr *src = cinfo->src; - imageIODataPtr data = (imageIODataPtr) cinfo->client_data; - streamBufferPtr sb = &data->streamBuf; - JNIEnv *env = (JNIEnv *)JNU_GetEnv(the_jvm, JNI_VERSION_1_2); - jint ret; - size_t offset, buflen; - jobject input = NULL; - - /* - * The original (jpegdecoder.c) had code here that called - * InputStream.available and just returned if the number of bytes - * available was less than any remaining skip. Presumably this was - * to avoid blocking, although the benefit was unclear, as no more - * decompression can take place until more data is available, so - * the code would block on input a little further along anyway. - * ImageInputStreams don't have an available method, so we'll just - * block in the skip if we have to. - */ - - if (sb->remaining_skip) { - src->skip_input_data(cinfo, 0); - } - - /* Save the data currently in the buffer */ - offset = src->bytes_in_buffer; - if (src->next_input_byte > sb->buf) { - memcpy(sb->buf, src->next_input_byte, offset); - } - - - RELEASE_ARRAYS(env, data, src->next_input_byte); - - GET_IO_REF(input); - - buflen = sb->bufferLength - offset; - if (buflen <= 0) { - if (!GET_ARRAYS(env, data, &(src->next_input_byte))) { - cinfo->err->error_exit((j_common_ptr) cinfo); - } - RELEASE_ARRAYS(env, data, src->next_input_byte); - return; - } - - ret = (*env)->CallIntMethod(env, input, - JPEGImageReader_readInputDataID, - sb->hstreamBuffer, - offset, buflen); - if ((ret > 0) && ((unsigned int)ret > buflen)) ret = (int)buflen; - if ((*env)->ExceptionCheck(env) - || !GET_ARRAYS(env, data, &(src->next_input_byte))) { - cinfo->err->error_exit((j_common_ptr) cinfo); - } - /* - * If we have reached the end of the stream, then the EOI marker - * is missing. We accept such streams but generate a warning. - * The image is likely to be corrupted, though everything through - * the end of the last complete MCU should be usable. - */ - if (ret <= 0) { - jobject reader = data->imageIOobj; - RELEASE_ARRAYS(env, data, src->next_input_byte); - (*env)->CallVoidMethod(env, reader, - JPEGImageReader_warningOccurredID, - READ_NO_EOI); - if ((*env)->ExceptionCheck(env) - || !GET_ARRAYS(env, data, &(src->next_input_byte))) { - cinfo->err->error_exit((j_common_ptr) cinfo); - } - - sb->buf[offset] = (JOCTET) 0xFF; - sb->buf[offset + 1] = (JOCTET) JPEG_EOI; - ret = 2; - } - - src->next_input_byte = sb->buf; - src->bytes_in_buffer = ret + offset; - - return; -} - /* * Skip num_bytes worth of data. The buffer pointer and count are * advanced over num_bytes input bytes, using the input stream @@ -1160,16 +1070,13 @@ imageio_skip_input_data(j_decompress_ptr cinfo, long num_bytes) return; } - RELEASE_ARRAYS(env, data, src->next_input_byte); - GET_IO_REF(input); ret = (*env)->CallLongMethod(env, input, JPEGImageReader_skipInputBytesID, (jlong) num_bytes); - if ((*env)->ExceptionCheck(env) - || !GET_ARRAYS(env, data, &(src->next_input_byte))) { + if ((*env)->ExceptionCheck(env)) { cinfo->err->error_exit((j_common_ptr) cinfo); } @@ -1181,15 +1088,12 @@ imageio_skip_input_data(j_decompress_ptr cinfo, long num_bytes) */ if (ret <= 0) { reader = data->imageIOobj; - RELEASE_ARRAYS(env, data, src->next_input_byte); (*env)->CallVoidMethod(env, reader, JPEGImageReader_warningOccurredID, READ_NO_EOI); - - if ((*env)->ExceptionCheck(env) - || !GET_ARRAYS(env, data, &(src->next_input_byte))) { - cinfo->err->error_exit((j_common_ptr) cinfo); + if ((*env)->ExceptionCheck(env)) { + cinfo->err->error_exit((j_common_ptr) cinfo); } sb->buf[0] = (JOCTET) 0xFF; sb->buf[1] = (JOCTET) JPEG_EOI; @@ -1215,7 +1119,7 @@ imageio_term_source(j_decompress_ptr cinfo) JNIEnv *env = (JNIEnv *)JNU_GetEnv(the_jvm, JNI_VERSION_1_2); jobject reader = data->imageIOobj; if (src->bytes_in_buffer > 0) { - RELEASE_ARRAYS(env, data, src->next_input_byte); + RELEASE_ARRAYS(env, data, src->next_input_byte, JNI_ABORT, 0); (*env)->CallVoidMethod(env, reader, JPEGImageReader_pushBackID, @@ -1659,7 +1563,7 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImageHeader if (setjmp(jerr->setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error while reading the header. */ - RELEASE_ARRAYS(env, data, src->next_input_byte); + RELEASE_ARRAYS(env, data, src->next_input_byte, JNI_ABORT, JNI_ABORT); if (!(*env)->ExceptionCheck(env)) { char buffer[JMSG_LENGTH_MAX]; (*cinfo->err->format_message) ((struct jpeg_common_struct *) cinfo, @@ -1678,7 +1582,7 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImageHeader (*env)->ExceptionClear(env); JNU_ThrowByName(env, "javax/imageio/IIOException", - "Array pin failed"); + "Get array elements failed"); return retval; } @@ -1701,7 +1605,11 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImageHeader printf("just read tables-only image; q table 0 at %p\n", cinfo->quant_tbl_ptrs[0]); #endif - RELEASE_ARRAYS(env, data, src->next_input_byte); + /* + * readImageHeader can be called independently, so + * we release the arrays when we return back. + */ + RELEASE_ARRAYS(env, data, src->next_input_byte, JNI_ABORT, 0); } else { /* * Now adjust the jpeg_color_space variable, which was set in @@ -1802,7 +1710,6 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImageHeader /* Leave the output space as CMYK */ } } - RELEASE_ARRAYS(env, data, src->next_input_byte); /* read icc profile data */ profileData = read_icc_profile(env, cinfo); @@ -1819,14 +1726,17 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImageHeader cinfo->out_color_space, cinfo->num_components, profileData); - if ((*env)->ExceptionCheck(env) - || !GET_ARRAYS(env, data, &(src->next_input_byte))) { + if ((*env)->ExceptionCheck(env)) { cinfo->err->error_exit((j_common_ptr) cinfo); } if (reset) { jpeg_abort_decompress(cinfo); } - RELEASE_ARRAYS(env, data, src->next_input_byte); + /* + * readImageHeader can be called independently, so + * we release the arrays when we return back. + */ + RELEASE_ARRAYS(env, data, src->next_input_byte, JNI_ABORT, 0); } return retval; @@ -1987,7 +1897,7 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImage if (setjmp(jerr->setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error while reading. */ - RELEASE_ARRAYS(env, data, src->next_input_byte); + RELEASE_ARRAYS(env, data, src->next_input_byte, JNI_ABORT, JNI_ABORT); if (!(*env)->ExceptionCheck(env)) { char buffer[JMSG_LENGTH_MAX]; (*cinfo->err->format_message) ((struct jpeg_common_struct *) cinfo, @@ -2005,7 +1915,7 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImage (*env)->ExceptionClear(env); JNU_ThrowByName(env, "javax/imageio/IIOException", - "Array pin failed"); + "Get array elements failed"); return data->abortFlag; } @@ -2037,7 +1947,7 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImage jpeg_start_decompress(cinfo); if (numBands != cinfo->output_components) { - RELEASE_ARRAYS(env, data, src->next_input_byte); + RELEASE_ARRAYS(env, data, src->next_input_byte, JNI_ABORT, JNI_ABORT); JNU_ThrowByName(env, "javax/imageio/IIOException", "Invalid argument to native readImage"); return data->abortFlag; @@ -2046,7 +1956,7 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImage if (cinfo->output_components <= 0 || cinfo->image_width > (0xffffffffu / (unsigned int)cinfo->output_components)) { - RELEASE_ARRAYS(env, data, src->next_input_byte); + RELEASE_ARRAYS(env, data, src->next_input_byte, JNI_ABORT, JNI_ABORT); JNU_ThrowByName(env, "javax/imageio/IIOException", "Invalid number of output components"); return data->abortFlag; @@ -2055,7 +1965,7 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImage // Allocate a 1-scanline buffer scanLinePtr = (JSAMPROW)malloc(cinfo->image_width*cinfo->output_components); if (scanLinePtr == NULL) { - RELEASE_ARRAYS(env, data, src->next_input_byte); + RELEASE_ARRAYS(env, data, src->next_input_byte, JNI_ABORT, JNI_ABORT); JNU_ThrowByName( env, "java/lang/OutOfMemoryError", "Reading JPEG Stream"); @@ -2070,22 +1980,18 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImage // the first interesting pass. jpeg_start_output(cinfo, cinfo->input_scan_number); if (wantUpdates) { - RELEASE_ARRAYS(env, data, src->next_input_byte); (*env)->CallVoidMethod(env, this, JPEGImageReader_passStartedID, cinfo->input_scan_number-1); - if ((*env)->ExceptionCheck(env) - || !GET_ARRAYS(env, data, &(src->next_input_byte))) { + if ((*env)->ExceptionCheck(env)) { cinfo->err->error_exit((j_common_ptr) cinfo); } } } else if (wantUpdates) { - RELEASE_ARRAYS(env, data, src->next_input_byte); (*env)->CallVoidMethod(env, this, JPEGImageReader_passStartedID, 0); - if ((*env)->ExceptionCheck(env) - || !GET_ARRAYS(env, data, &(src->next_input_byte))) { + if ((*env)->ExceptionCheck(env)) { cinfo->err->error_exit((j_common_ptr) cinfo); } } @@ -2136,16 +2042,20 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImage } } - // And call it back to Java - RELEASE_ARRAYS(env, data, src->next_input_byte); + /* + * Optimisation to just commit the native pixel buffer + * content back to java array without releasing the + * native buffer. + */ + if (pb->isCopy) { + unpinPixelBuffer(env, pb, JNI_COMMIT); + } (*env)->CallVoidMethod(env, this, JPEGImageReader_acceptPixelsID, targetLine++, progressive); - - if ((*env)->ExceptionCheck(env) - || !GET_ARRAYS(env, data, &(src->next_input_byte))) { + if ((*env)->ExceptionCheck(env)) { cinfo->err->error_exit((j_common_ptr) cinfo); } @@ -2175,11 +2085,9 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImage done = TRUE; } if (wantUpdates) { - RELEASE_ARRAYS(env, data, src->next_input_byte); (*env)->CallVoidMethod(env, this, JPEGImageReader_passCompleteID); - if ((*env)->ExceptionCheck(env) - || !GET_ARRAYS(env, data, &(src->next_input_byte))) { + if ((*env)->ExceptionCheck(env)) { cinfo->err->error_exit((j_common_ptr) cinfo); } } @@ -2204,13 +2112,16 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImage this, JPEGImageReader_skipPastImageID, imageIndex); + if ((*env)->ExceptionCheck(env)) { + cinfo->err->error_exit((j_common_ptr) cinfo); + } } else { jpeg_finish_decompress(cinfo); } free(scanLinePtr); - RELEASE_ARRAYS(env, data, src->next_input_byte); + RELEASE_ARRAYS(env, data, src->next_input_byte, JNI_ABORT, 0); return data->abortFlag; } @@ -2405,8 +2316,16 @@ imageio_empty_output_buffer (j_compress_ptr cinfo) JNIEnv *env = (JNIEnv *)JNU_GetEnv(the_jvm, JNI_VERSION_1_2); jobject output = NULL; - RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte)); - + /* + * Optimization to not delete the native copy of stream buffer, + * but just commit the content back to the Java array. + * In case where we don't have a copy, we rely on JVM to maintain + * the native reference of Java array. + */ + jboolean isCopy = sb->isCopy; + if (isCopy) { + unpinStreamBuffer(env, sb, dest->next_output_byte, JNI_COMMIT); + } GET_IO_REF(output); (*env)->CallVoidMethod(env, @@ -2415,10 +2334,8 @@ imageio_empty_output_buffer (j_compress_ptr cinfo) sb->hstreamBuffer, 0, sb->bufferLength); - if ((*env)->ExceptionCheck(env) - || !GET_ARRAYS(env, data, - (const JOCTET **)(&dest->next_output_byte))) { - cinfo->err->error_exit((j_common_ptr) cinfo); + if ((*env)->ExceptionCheck(env)) { + cinfo->err->error_exit((j_common_ptr) cinfo); } dest->next_output_byte = sb->buf; @@ -2447,7 +2364,16 @@ imageio_term_destination (j_compress_ptr cinfo) if (datacount != 0) { jobject output = NULL; - RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte)); + /* + * Optimization to not delete the native copy of stream buffer, + * but just commit the content back to the Java array. + * In case where we don't have a copy, we rely on JVM to maintain + * the native reference of Java array. + */ + jboolean isCopy = sb->isCopy; + if (isCopy) { + unpinStreamBuffer(env, sb, dest->next_output_byte, JNI_COMMIT); + } GET_IO_REF(output); @@ -2457,17 +2383,13 @@ imageio_term_destination (j_compress_ptr cinfo) sb->hstreamBuffer, 0, datacount); - - if ((*env)->ExceptionCheck(env) - || !GET_ARRAYS(env, data, - (const JOCTET **)(&dest->next_output_byte))) { + if ((*env)->ExceptionCheck(env)) { cinfo->err->error_exit((j_common_ptr) cinfo); } } dest->next_output_byte = NULL; dest->free_in_buffer = 0; - } /* @@ -2668,7 +2590,7 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_writeTables if (setjmp(jerr->setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error while writing. */ - RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte)); + RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte), JNI_ABORT, JNI_ABORT); if (!(*env)->ExceptionCheck(env)) { char buffer[JMSG_LENGTH_MAX]; (*cinfo->err->format_message) ((j_common_ptr) cinfo, @@ -2683,7 +2605,7 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_writeTables (*env)->ExceptionClear(env); JNU_ThrowByName(env, "javax/imageio/IIOException", - "Array pin failed"); + "Get array elements failed"); return; } @@ -2703,7 +2625,15 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_writeTables } jpeg_write_tables(cinfo); // Flushes the buffer for you - RELEASE_ARRAYS(env, data, NULL); + /* + * writeTables can be called independently, so + * we release the arrays when we return back. + * Also the table content in output_buffer is + * already flushed, so no need to commit the + * native copy of stream content back to the + * Java array. + */ + RELEASE_ARRAYS(env, data, NULL, JNI_ABORT, 0); } static void freeArray(UINT8** arr, jint size) { @@ -2766,7 +2696,6 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_writeImage UINT8** scale = NULL; boolean success = TRUE; - /* verify the inputs */ if (data == NULL) { @@ -2891,7 +2820,7 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_writeImage if (setjmp(jerr->setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error while writing. */ - RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte)); + RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte), JNI_ABORT, JNI_ABORT); if (!(*env)->ExceptionCheck(env)) { char buffer[JMSG_LENGTH_MAX]; (*cinfo->err->format_message) ((j_common_ptr) cinfo, @@ -2973,7 +2902,7 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_writeImage free(scanLinePtr); JNU_ThrowByName(env, "javax/imageio/IIOException", - "Array pin failed"); + "Get array elements failed"); return data->abortFlag; } @@ -3006,7 +2935,7 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_writeImage scanptr = (int *) cinfo->script_space; scanData = (*env)->GetIntArrayElements(env, scanInfo, NULL); if (scanData == NULL) { - RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte)); + RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte), JNI_ABORT, JNI_ABORT); freeArray(scale, numBands); free(scanLinePtr); return data->abortFlag; @@ -3034,16 +2963,13 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_writeImage if (haveMetadata) { // Flush the buffer imageio_flush_destination(cinfo); - // Call Java to write the metadata - RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte)); + // Call Java to write the metadata. (*env)->CallVoidMethod(env, this, JPEGImageWriter_writeMetadataID); - if ((*env)->ExceptionCheck(env) - || !GET_ARRAYS(env, data, - (const JOCTET **)(&dest->next_output_byte))) { - cinfo->err->error_exit((j_common_ptr) cinfo); - } + if ((*env)->ExceptionCheck(env)) { + cinfo->err->error_exit((j_common_ptr) cinfo); + } } targetLine = 0; @@ -3053,20 +2979,29 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_writeImage // for each line in destHeight while ((data->abortFlag == JNI_FALSE) && (cinfo->next_scanline < cinfo->image_height)) { - // get the line from Java - RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte)); + /* + * Get a line of pixel data from Java. + * In case where we have native copy of Java pixel array, + * we need to just use JNI_ABORT to exclude any copy operation + * and then get new copy for next scanline. + * + * If we have direct reference to Java array, we rely on + * JVM to maintain the reference appropriately. + */ + jboolean isCopy = pb->isCopy; + if (isCopy) { + unpinPixelBuffer(env, pb, JNI_ABORT); + } (*env)->CallVoidMethod(env, this, JPEGImageWriter_grabPixelsID, targetLine); - if ((*env)->ExceptionCheck(env) - || !GET_ARRAYS(env, data, - (const JOCTET **)(&dest->next_output_byte))) { - cinfo->err->error_exit((j_common_ptr) cinfo); - } + if ((*env)->ExceptionCheck(env) || + (isCopy && (pinPixelBuffer(env, pb) == NOT_OK))) { + cinfo->err->error_exit((j_common_ptr) cinfo); + } // subsample it into our buffer - in = data->pixelBuf.buf.bp; out = scanLinePtr; pixelLimit = in + ((pixelBufferSize > data->pixelBuf.byteBufferLength) ? @@ -3108,7 +3043,7 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_writeImage freeArray(scale, numBands); free(scanLinePtr); - RELEASE_ARRAYS(env, data, NULL); + RELEASE_ARRAYS(env, data, NULL, 0, JNI_ABORT); return data->abortFlag; } diff --git a/test/micro/org/openjdk/bench/javax/imageio/plugins/jpeg/LargeJpegReadWithProgressBench.java b/test/micro/org/openjdk/bench/javax/imageio/plugins/jpeg/LargeJpegReadWithProgressBench.java new file mode 100644 index 00000000000..70f8020f358 --- /dev/null +++ b/test/micro/org/openjdk/bench/javax/imageio/plugins/jpeg/LargeJpegReadWithProgressBench.java @@ -0,0 +1,166 @@ +/* + * 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 org.openjdk.bench.javax.imageio.plugins.jpeg; + +import java.awt.Color; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.Iterator; + +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +import javax.imageio.ImageIO; +import javax.imageio.ImageReader; +import javax.imageio.event.IIOReadProgressListener; +import javax.imageio.stream.ImageInputStream; + +/** + * Measure time taken to read large jpeg image + * make test TEST="micro:javax.imageio.plugins.jpeg.LargeJpegReadWithProgressBench" + */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(3) +@State(Scope.Benchmark) +public class LargeJpegReadWithProgressBench { + + private static final File pwd = new File("."); + private static ImageReader reader; + + @Setup + public void setup() throws IOException { + BufferedImage src = createSource(); + ImageInputStream iis = prepareInput(src); + reader = null; + Iterator it = ImageIO.getImageReadersByFormatName("jpeg"); + if (it.hasNext()) { + reader = (ImageReader)it.next(); + } else { + throw new RuntimeException("Could not find JPEG reader"); + } + reader.setInput(iis); + ImageReadProgressListener listener = new ImageReadProgressListener(); + reader.addIIOReadProgressListener(listener); + } + + @Benchmark + public void readLargeJpegImage(Blackhole bh) throws IOException { + reader.read(0); + } + + private static BufferedImage createSource() { + int width = 2000; + int height = 2000; + int squareSize = 20; + + Color red = Color.RED; + Color green = Color.GREEN; + BufferedImage image = new BufferedImage(width, height, + BufferedImage.TYPE_INT_RGB); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + if (((x / squareSize) + (y / squareSize)) % 2 == 0) { + image.setRGB(x, y, red.getRGB()); + } else { + image.setRGB(x, y, green.getRGB()); + } + } + } + return image; + } + + private static ImageInputStream prepareInput(BufferedImage src) + throws IOException { + File f = File.createTempFile("src_", ".jpeg", pwd); + if (ImageIO.write(src, "jpeg", f)) { + ImageInputStream iis = ImageIO.createImageInputStream(f); + f.deleteOnExit(); + return iis; + } else { + throw new RuntimeException("Unable to write jpeg image"); + } + } +} + +class ImageReadProgressListener implements IIOReadProgressListener { + // This class is a no-op, it is added just to have a progress listener + @Override + public void sequenceStarted(ImageReader source, int minIndex) { + + } + + @Override + public void sequenceComplete(ImageReader source) { + + } + + @Override + public void imageStarted(ImageReader source, int imageIndex) { + + } + + @Override + public void imageProgress(ImageReader source, float percentageDone) { + + } + + @Override + public void imageComplete(ImageReader source) { + + } + + @Override + public void thumbnailStarted(ImageReader source, int imageIndex, int thumbnailIndex) { + + } + + @Override + public void thumbnailProgress(ImageReader source, float percentageDone) { + + } + + @Override + public void thumbnailComplete(ImageReader source) { + + } + + @Override + public void readAborted(ImageReader source) { + + } +} diff --git a/test/micro/org/openjdk/bench/javax/imageio/plugins/jpeg/LargeJpegReadWriteBench.java b/test/micro/org/openjdk/bench/javax/imageio/plugins/jpeg/LargeJpegReadWriteBench.java new file mode 100644 index 00000000000..8a84eab4da7 --- /dev/null +++ b/test/micro/org/openjdk/bench/javax/imageio/plugins/jpeg/LargeJpegReadWriteBench.java @@ -0,0 +1,141 @@ +/* + * 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 org.openjdk.bench.javax.imageio.plugins.jpeg; + +import java.awt.Color; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.Iterator; + +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +import javax.imageio.ImageIO; +import javax.imageio.ImageReader; +import javax.imageio.ImageWriter; +import javax.imageio.stream.ImageInputStream; +import javax.imageio.stream.ImageOutputStream; + +/** + * Measure time taken to read large jpeg image + * make test TEST="micro:javax.imageio.plugins.jpeg.LargeJpegReadWriteBench" + */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(3) +@State(Scope.Benchmark) +public class LargeJpegReadWriteBench { + + private static final File pwd = new File("."); + private static ImageReader reader; + private static ImageWriter writer; + private static BufferedImage src; + + @Setup + public void setup() throws IOException { + src = createSource(); + ImageInputStream iis = prepareInput(src); + reader = null; + Iterator readerIterator = ImageIO.getImageReadersByFormatName("jpeg"); + if (readerIterator.hasNext()) { + reader = readerIterator.next(); + } else { + throw new RuntimeException("Could not find JPEG reader"); + } + reader.setInput(iis); + + ImageOutputStream ios = prepareOutput(src); + writer = null; + Iterator writerIterator = ImageIO.getImageWritersByFormatName("jpeg"); + if (writerIterator.hasNext()) { + writer = writerIterator.next(); + } else { + throw new RuntimeException("Could not find JPEG writer"); + } + writer.setOutput(ios); + } + + @Benchmark + public void readLargeJpegImage(Blackhole bh) throws IOException { + reader.read(0); + } + + @Benchmark + public void writeLargeJpegImage(Blackhole bh) throws IOException { + writer.write(src); + } + + private static BufferedImage createSource() { + int width = 2000; + int height = 2000; + int squareSize = 20; + + Color red = Color.RED; + Color green = Color.GREEN; + BufferedImage image = new BufferedImage(width, height, + BufferedImage.TYPE_INT_RGB); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + if (((x / squareSize) + (y / squareSize)) % 2 == 0) { + image.setRGB(x, y, red.getRGB()); + } else { + image.setRGB(x, y, green.getRGB()); + } + } + } + return image; + } + + private static ImageInputStream prepareInput(BufferedImage src) + throws IOException { + File f = File.createTempFile("src_", ".jpeg", pwd); + if (ImageIO.write(src, "jpeg", f)) { + ImageInputStream iis = ImageIO.createImageInputStream(f); + f.deleteOnExit(); + return iis; + } else { + throw new RuntimeException("Unable to write jpeg image"); + } + } + + private static ImageOutputStream prepareOutput(BufferedImage src) throws IOException { + File f = File.createTempFile("dest_", ".jpeg", pwd); + ImageOutputStream ios = ImageIO.createImageOutputStream(f); + f.deleteOnExit(); + return ios; + } +} From a8834d6dd076880202f553414d4fb1cc468e7891 Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Fri, 6 Mar 2026 09:28:51 +0000 Subject: [PATCH 268/707] 8378687: Improve delegation of HttpURLConnection Reviewed-by: rhalade, jpai, michaelm, skoivu --- .../classes/sun/net/www/protocol/http/HttpURLConnection.java | 4 ++-- .../protocol/https/AbstractDelegateHttpsURLConnection.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java index 480553e9a62..45e641f11ee 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java @@ -571,8 +571,8 @@ public void setRequestMethod(String method) throws ProtocolException { lock(); try { - if (connecting) { - throw new IllegalStateException("connect in progress"); + if (connected || connecting) { + throw new IllegalStateException("Already connected"); } super.setRequestMethod(method); } finally { diff --git a/src/java.base/share/classes/sun/net/www/protocol/https/AbstractDelegateHttpsURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/https/AbstractDelegateHttpsURLConnection.java index 1415658e34d..88449caaf09 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/https/AbstractDelegateHttpsURLConnection.java +++ b/src/java.base/share/classes/sun/net/www/protocol/https/AbstractDelegateHttpsURLConnection.java @@ -178,7 +178,7 @@ public void setConnected(boolean conn) { public void connect() throws IOException { if (connected) return; - plainConnect(); + super.connect(); if (cachedResponse != null) { // using cached response return; From 0203dcff4b14675df9f4f9b8dc31b33b9c4097d2 Mon Sep 17 00:00:00 2001 From: Weijun Wang Date: Tue, 17 Mar 2026 21:03:49 +0000 Subject: [PATCH 269/707] 8377833: Enhance Jar file processing Reviewed-by: ahgross, rhalade, hchao, mullan --- .../share/classes/java/util/jar/JarVerifier.java | 6 +++--- .../sun/security/util/SignatureFileVerifier.java | 15 +++++++++++---- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/java.base/share/classes/java/util/jar/JarVerifier.java b/src/java.base/share/classes/java/util/jar/JarVerifier.java index d73231a4c61..e3bdb0307b9 100644 --- a/src/java.base/share/classes/java/util/jar/JarVerifier.java +++ b/src/java.base/share/classes/java/util/jar/JarVerifier.java @@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -68,7 +68,7 @@ class JarVerifier { private ArrayList pendingBlocks; /* cache of CodeSigner objects */ - private ArrayList signerCache; + private List signerCache; /* Are we parsing a block? */ private boolean parsingBlockOrSF = false; @@ -288,7 +288,7 @@ private void processEntry(ManifestEntryVerifier mev) String key = uname.substring(0, uname.lastIndexOf('.')); if (signerCache == null) - signerCache = new ArrayList<>(); + signerCache = new LinkedList<>(); if (manDig == null) { synchronized(manifestRawBytes) { diff --git a/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java b/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java index d7e65b6aef0..0b21ccbd294 100644 --- a/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java +++ b/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java @@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,7 +46,12 @@ public class SignatureFileVerifier { /* Are we debugging ? */ private static final Debug debug = Debug.getInstance("jar"); - private final ArrayList signerCache; + private final List signerCache; + + // The maximum size of the signerCache. This is for debug only + // and not intended to be adjusted by users. + private static int SIGNER_CACHE_SIZE + = Integer.getInteger("sun.security.util.jar.signer.cache.size", 5); private static final String ATTR_DIGEST = "-DIGEST-" + ManifestDigester.MF_MAIN_ATTRS.toUpperCase(Locale.ENGLISH); @@ -97,7 +102,7 @@ public class SignatureFileVerifier { * * @param rawBytes the raw bytes of the signature block file */ - public SignatureFileVerifier(ArrayList signerCache, + public SignatureFileVerifier(List signerCache, ManifestDigester md, String name, byte[] rawBytes) @@ -282,7 +287,6 @@ public void process(Hashtable signers, } finally { Providers.stopJarVerification(obj); } - } private void processImpl(Hashtable signers, @@ -850,6 +854,9 @@ void updateSigners(CodeSigner[] newSigners, newSigners.length); } signerCache.add(cachedSigners); + if (signerCache.size() > SIGNER_CACHE_SIZE) { + signerCache.remove(0); + } signers.put(name, cachedSigners); } From 404a4dd17762c34ea4a5084acd6f07d9a8f21701 Mon Sep 17 00:00:00 2001 From: Artur Barashev Date: Wed, 8 Apr 2026 12:08:39 +0000 Subject: [PATCH 270/707] 8380672: Improve certification checking Reviewed-by: ahgross, jnibedita, pkumaraswamy, rhalade, weijun, mullan --- .../sun/security/util/HostnameChecker.java | 4 ++-- .../classes/sun/security/x509/DNSName.java | 8 +++++++- .../test/lib/security/CertificateBuilder.java | 20 ++++++++++++++++--- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/java.base/share/classes/sun/security/util/HostnameChecker.java b/src/java.base/share/classes/sun/security/util/HostnameChecker.java index 65115c9aeaf..b5a6e48e570 100644 --- a/src/java.base/share/classes/sun/security/util/HostnameChecker.java +++ b/src/java.base/share/classes/sun/security/util/HostnameChecker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -263,7 +263,7 @@ public static X500Name getSubjectX500Name(X509Certificate cert) * The name parameter should represent a DNS name. The * template parameter may contain the wildcard character '*'. */ - private boolean isMatched(String name, String template, + public boolean isMatched(String name, String template, boolean chainsToPublicCA) { // Normalize to Unicode, because PSL is in Unicode. diff --git a/src/java.base/share/classes/sun/security/x509/DNSName.java b/src/java.base/share/classes/sun/security/x509/DNSName.java index ce903a3d16c..17820d279a5 100644 --- a/src/java.base/share/classes/sun/security/x509/DNSName.java +++ b/src/java.base/share/classes/sun/security/x509/DNSName.java @@ -52,6 +52,8 @@ public class DNSName implements GeneralNameInterface { private final String name; + private static final HostnameChecker HOSTNAME_CHECKER = + HostnameChecker.getInstance(HostnameChecker.TYPE_TLS); private static final String DNS_ALLOWED = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-"; @@ -218,6 +220,9 @@ public int hashCode() { * For example, www.host.example.com would satisfy the constraint but * host1.example.com would not. *

    + * RFC6125: Match wildcard pattern in the input name being constrained, + * any wildcard in this name will be matched as a literal character. + *

    * RFC1034: By convention, domain names can be stored with arbitrary case, but * domain name comparisons for all present domain functions are done in a * case-insensitive manner, assuming an ASCII character set, and a high @@ -238,7 +243,8 @@ else if (inputName.getType() != NAME_DNS) String inName = (((DNSName)inputName).getName()).toLowerCase(Locale.ENGLISH); String thisName = name.toLowerCase(Locale.ENGLISH); - if (inName.equals(thisName)) + + if (HOSTNAME_CHECKER.isMatched(thisName, inName, false)) constraintType = NAME_MATCH; else if (thisName.endsWith(inName)) { int inNdx = thisName.lastIndexOf(inName); diff --git a/test/lib/jdk/test/lib/security/CertificateBuilder.java b/test/lib/jdk/test/lib/security/CertificateBuilder.java index 6bf554c3517..a2d2a7d9eb1 100644 --- a/test/lib/jdk/test/lib/security/CertificateBuilder.java +++ b/test/lib/jdk/test/lib/security/CertificateBuilder.java @@ -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 @@ -24,6 +24,7 @@ package jdk.test.lib.security; import java.io.*; +import java.net.IDN; import java.security.cert.*; import java.security.cert.Extension; import java.util.*; @@ -41,7 +42,9 @@ import sun.security.x509.AlgorithmId; import sun.security.x509.AuthorityInfoAccessExtension; import sun.security.x509.AuthorityKeyIdentifierExtension; +import sun.security.x509.GeneralSubtrees; import sun.security.x509.IPAddressName; +import sun.security.x509.NameConstraintsExtension; import sun.security.x509.SubjectKeyIdentifierExtension; import sun.security.x509.BasicConstraintsExtension; import sun.security.x509.CertificateSerialNumber; @@ -326,7 +329,6 @@ public CertificateBuilder addExtensions(List extList) { * Helper method to add DNSName types for the SAN extension * * @param dnsNames A {@code List} of names to add as DNSName types - * * @throws IOException if an encoding error occurs. */ public CertificateBuilder addSubjectAltNameDNSExt(List dnsNames) @@ -334,7 +336,8 @@ public CertificateBuilder addSubjectAltNameDNSExt(List dnsNames) if (!dnsNames.isEmpty()) { GeneralNames gNames = new GeneralNames(); for (String name : dnsNames) { - gNames.add(new GeneralName(new DNSName(name))); + gNames.add(new GeneralName(new DNSName(new DerValue( + DerValue.tag_IA5String, IDN.toASCII(name))))); } addExtension(new SubjectAlternativeNameExtension(false, gNames)); @@ -437,6 +440,17 @@ public CertificateBuilder addBasicConstraintsExt(boolean crit, boolean isCA, maxPathLen)); } + /** + * Set the Name Constraints Extension for a certificate. + * + * @param permitted permitted names + * @param excluded excluded names + */ + public CertificateBuilder addNameConstraintsExt( + GeneralSubtrees permitted, GeneralSubtrees excluded) { + return addExtension(new NameConstraintsExtension(permitted, excluded)); + } + /** * Add the Authority Key Identifier extension. * From 33e220059bace41b02017c035505fe6fd81844da Mon Sep 17 00:00:00 2001 From: Jayathirth D V Date: Fri, 10 Apr 2026 12:14:14 +0000 Subject: [PATCH 271/707] 8381039: Enhance AWT ImagingLib Reviewed-by: mschoene, rhalade, azvegint, prr --- .../libawt/awt/medialib/awt_ImagingLib.c | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/src/java.desktop/share/native/libawt/awt/medialib/awt_ImagingLib.c b/src/java.desktop/share/native/libawt/awt/medialib/awt_ImagingLib.c index bb93108f111..b6e10617cc3 100644 --- a/src/java.desktop/share/native/libawt/awt/medialib/awt_ImagingLib.c +++ b/src/java.desktop/share/native/libawt/awt/medialib/awt_ImagingLib.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -2218,7 +2218,11 @@ allocateArray(JNIEnv *env, BufImageS_t *imageP, /* Means we need to fill in alpha */ if (!cvtToDefault && addAlpha) { *mlibImagePP = (*sMlibSysFns.createFP)(MLIB_BYTE, 4, width, height); - if (*mlibImagePP != NULL) { + if (*mlibImagePP == NULL) { + (*env)->ReleasePrimitiveArrayCritical(env, rasterP->jdata, dataP, + JNI_ABORT); + return -1; + } else { unsigned int *dstP = (unsigned int *) mlib_ImageGetData(*mlibImagePP); int dstride = (*mlibImagePP)->stride>>2; @@ -2234,10 +2238,10 @@ allocateArray(JNIEnv *env, BufImageS_t *imageP, dP[x] = sP[x] | 0xff000000; } } + (*env)->ReleasePrimitiveArrayCritical(env, rasterP->jdata, dataP, + JNI_ABORT); + return 0; } - (*env)->ReleasePrimitiveArrayCritical(env, rasterP->jdata, dataP, - JNI_ABORT); - return 0; } else if ((hintP->packing & BYTE_INTERLEAVED) == BYTE_INTERLEAVED) { int nChans = (cmP->isDefaultCompatCM ? 4 : hintP->numChans); @@ -2252,6 +2256,11 @@ allocateArray(JNIEnv *env, BufImageS_t *imageP, hintP->sStride, (unsigned char *)dataP + hintP->dataOffset); + if (*mlibImagePP == NULL) { + (*env)->ReleasePrimitiveArrayCritical(env, rasterP->jdata, dataP, + JNI_ABORT); + return -1; + } } else if ((hintP->packing & SHORT_INTERLEAVED) == SHORT_INTERLEAVED) { *mlibImagePP = (*sMlibSysFns.createStructFP)(MLIB_SHORT, @@ -2261,6 +2270,11 @@ allocateArray(JNIEnv *env, BufImageS_t *imageP, imageP->raster.scanlineStride*2, (unsigned short *)dataP + hintP->channelOffset); + if (*mlibImagePP == NULL) { + (*env)->ReleasePrimitiveArrayCritical(env, rasterP->jdata, dataP, + JNI_ABORT); + return -1; + } } else { /* Release the data array */ @@ -2360,6 +2374,11 @@ allocateRasterArray(JNIEnv *env, RasterS_t *rasterP, width, height, rasterP->scanlineStride*4, (unsigned char *)dataP + offset); + if (*mlibImagePP == NULL) { + (*env)->ReleasePrimitiveArrayCritical(env, rasterP->jdata, dataP, + JNI_ABORT); + return -1; + } *dataPP = dataP; return 0; case sun_awt_image_IntegerComponentRaster_TYPE_BYTE_SAMPLES: @@ -2388,6 +2407,11 @@ allocateRasterArray(JNIEnv *env, RasterS_t *rasterP, width, height, rasterP->scanlineStride, (unsigned char *)dataP + offset); + if (*mlibImagePP == NULL) { + (*env)->ReleasePrimitiveArrayCritical(env, rasterP->jdata, dataP, + JNI_ABORT); + return -1; + } *dataPP = dataP; return 0; case sun_awt_image_IntegerComponentRaster_TYPE_USHORT_SAMPLES: @@ -2418,6 +2442,11 @@ allocateRasterArray(JNIEnv *env, RasterS_t *rasterP, width, height, rasterP->scanlineStride*2, (unsigned char *)dataP + offset); + if (*mlibImagePP == NULL) { + (*env)->ReleasePrimitiveArrayCritical(env, rasterP->jdata, dataP, + JNI_ABORT); + return -1; + } *dataPP = dataP; return 0; From 48d32601cde66fb211f89885bee8a0bd182cbea0 Mon Sep 17 00:00:00 2001 From: Anthony Scarpino Date: Thu, 16 Apr 2026 17:15:33 +0000 Subject: [PATCH 272/707] 8381519: Enhance Der Value Handling Reviewed-by: mschoene, jnimeh, valeriep --- .../share/classes/sun/security/util/DerValue.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/sun/security/util/DerValue.java b/src/java.base/share/classes/sun/security/util/DerValue.java index ec8b482b07d..8d86c8dd143 100644 --- a/src/java.base/share/classes/sun/security/util/DerValue.java +++ b/src/java.base/share/classes/sun/security/util/DerValue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -157,6 +157,9 @@ public class DerValue { */ public static final byte tag_SetOf = 0x31; + // Max nested depth for constructed data + private static final int MAX_CONSTRUCTED_NEST = 30; + // This class is mostly immutable except that: // // 1. resetTag() modifies the tag @@ -564,6 +567,14 @@ public ObjectIdentifier getOID() throws IOException { * @return the octet string held in this DER value */ public byte[] getOctetString() throws IOException { + return getOctetString(0); + } + + private byte[] getOctetString(int limit) throws IOException { + if (++limit > MAX_CONSTRUCTED_NEST) { + throw new IOException("Nested OctetString limit reached (" + + MAX_CONSTRUCTED_NEST + ")."); + } if (tag != tag_OctetString && !isConstructed(tag_OctetString)) { throw new IOException( @@ -582,7 +593,7 @@ public byte[] getOctetString() throws IOException { ByteArrayOutputStream bout = new ByteArrayOutputStream(); DerInputStream dis = data(); while (dis.available() > 0) { - bout.write(dis.getDerValue().getOctetString()); + bout.write(dis.getDerValue().getOctetString(limit)); } return bout.toByteArray(); } From 7e17c402e4fb0a3cffabef43352d91a62f0f94b8 Mon Sep 17 00:00:00 2001 From: Jamil Nimeh Date: Thu, 23 Apr 2026 00:55:06 +0000 Subject: [PATCH 273/707] 8381796: Enhance Certificate parsing Reviewed-by: ascarpino, abarashev, rhalade, mdonovan --- .../provider/certpath/URICertStore.java | 89 +++++++++++++- .../sun/security/util/SecurityProperties.java | 32 ++++- .../share/conf/security/java.security | 16 +++ .../certpath/ldap/LDAPCertStoreImpl.java | 45 ++++++- .../test/lib/security/CertificateBuilder.java | 114 +++++++++++++----- 5 files changed, 261 insertions(+), 35 deletions(-) diff --git a/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java b/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java index 3e1fc8db164..6eb95f92246 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java @@ -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. * 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,6 +25,7 @@ package sun.security.provider.certpath; +import java.io.FilterInputStream; import java.io.InputStream; import java.io.IOException; import java.net.HttpURLConnection; @@ -188,6 +189,16 @@ private static int initializeTimeout(String prop, int def) { return timeoutVal; } + /** + * Maximum size for a CRL downloaded through a URICertStore + * in bytes. This can be controlled by the com.sun.security.crl.maxSize + * Security or System property. The System property, if set, overrides + * the Security property. The default size is 20MiB. + */ + private static final long MAX_CRL_DOWNLOAD_SIZE = + SecurityProperties.getOverridableLongProp( + "com.sun.security.crl.maxSize", 20971520, debug); + /** * Enumeration for the allowed schemes we support when following a * URI from an authorityInfoAccess extension on a certificate. @@ -228,6 +239,13 @@ static AllowedScheme nameOf(String name) { private static final boolean CA_ISS_ALLOW_ANY; static { + // Add a debug message for the configured CRL download limit + if (debug != null) { + debug.println("Maximum downloadable CRL size: " + + MAX_CRL_DOWNLOAD_SIZE + + ((MAX_CRL_DOWNLOAD_SIZE < 0) ? " (DISABLED)" : "")); + } + boolean allowAny = false; try { if (Builder.USE_AIA) { @@ -623,7 +641,19 @@ public synchronized Collection engineGetCRLs(CRLSelector selector) if (debug != null) { debug.println("Downloading new CRL..."); } - crl = (X509CRL) factory.generateCRL(in); + InputStream crlIn = (MAX_CRL_DOWNLOAD_SIZE > -1) ? + new SizeLimitedInputStream(in, MAX_CRL_DOWNLOAD_SIZE) : + in; + try { + crl = (X509CRL) factory.generateCRL(crlIn); + } catch (IllegalArgumentException iae) { + // IAE should only be thrown when the CRL exceeds a + // configured maximum length. + if (debug != null) { + debug.println("Discarding CRL: " + iae.getMessage()); + crl = null; + } + } } return getMatchingCRLs(crl, selector); } catch (IOException | CRLException e) { @@ -816,4 +846,59 @@ boolean matchRule(URI filterRule, URI caIssuer) { return true; } } + + /** + * Stream wrapper used when an InputStream passed into a CertificateFactory + * needs to be size limited. It will throw IllegalArgumentException when + * the downloaded resource via the underlying stream exceeds the maximum + * limit. + */ + private static class SizeLimitedInputStream extends FilterInputStream { + + private final long maxBytes; + private long bytesRead = 0; + + private SizeLimitedInputStream(InputStream in, long maxBytes) { + super(in); + this.maxBytes = maxBytes; + } + + @Override + public int read() throws IOException { + if (bytesRead >= maxBytes) { + // We will use IAE here to differentiate this special case + // from other IOEs that the underlying input stream might + // legitimately throw. + throw new IllegalArgumentException("InputStream exceeded max " + + "size of " + maxBytes); + } + + int b = super.read(); + if (b != -1) { + bytesRead++; + } + return b; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + + if (bytesRead >= maxBytes) { + // We will use IAE here to differentiate this special case + // from other IOEs that the underlying input stream might + // legitimately throw. + throw new IllegalArgumentException("InputStream exceeded max " + + "size of " + maxBytes); + } + + long remaining = maxBytes - bytesRead; + int toRead = (int) Math.min(len, remaining); + + int n = super.read(b, off, toRead); + if (n != -1) { + bytesRead += n; + } + return n; + } + } } diff --git a/src/java.base/share/classes/sun/security/util/SecurityProperties.java b/src/java.base/share/classes/sun/security/util/SecurityProperties.java index 98bc71d829b..da69ecbf5d6 100644 --- a/src/java.base/share/classes/sun/security/util/SecurityProperties.java +++ b/src/java.base/share/classes/sun/security/util/SecurityProperties.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -139,6 +139,36 @@ public static int getTimeoutSystemProp(String prop, int def, Debug dbg) { } } + /** + * A convenience routine for fetching a numeric value from a Security + * or System property and returning it as a long. The value from the + * property is obtained according to the logic in + * {@link SecurityProperties#getOverridableProperty(String)} + * + * @param prop the property to query + * @param defaultValue the default value + * @param dbg a Debug object, if null no debug messages will be sent + * @return the value of the property as a {@code long}. If a non-numeric + * value is supplied, the default value will be returned. + */ + public static long getOverridableLongProp(String prop, long defaultValue, + Debug dbg) { + long longVal = defaultValue; + try { + String propVal = SecurityProperties.getOverridableProperty(prop); + if (propVal != null) { + longVal = Long.parseLong(propVal); + } + } catch (NumberFormatException nfe) { + // We will use the default, but add a warning debug message + if (dbg != null) { + dbg.println("Warning: Non-numeric value found in property " + + prop + ", using default value of " + defaultValue); + } + } + return longVal; + } + /** * Convenience method for fetching System property values that are booleans. * diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security index 26842d0c845..2fc908c6bf9 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -1714,6 +1714,22 @@ jdk.epkcs8.defaultAlgorithm=PBEWithHmacSHA256AndAES_128 # ldap://ldap.company.com/dc=company,dc=com?caCertificate;binary com.sun.security.allowedAIALocations= +# +# Certificate Revocation List (CRL) Download Size Limitation +# +# This property sets a size limit for CRLs downloaded via URIs provided +# in the CRL Distribution Points certificate extension. This property +# must be a numeric value that is the size in bytes of the DER-encoded CRL. +# For protocols that can return multi-value responses, such as LDAP, the +# size threshold is the sum of all CRLs downloaded from a single search +# query. CRLs that exceed this length will not be processed during certificate +# path validation. This size limit does not apply to CRLs that are imported +# through non-network-based means. A negative value will disable this size +# limitation. A non-numeric value will be ignored and the default size will +# be used instead. The default size limit is 20MiB. +# This property may be overridden by a System property of the same name. +com.sun.security.crl.maxSize = 20971520 + # # PKCS #8 encoding format for newly created ML-KEM and ML-DSA private keys # diff --git a/src/java.naming/share/classes/sun/security/provider/certpath/ldap/LDAPCertStoreImpl.java b/src/java.naming/share/classes/sun/security/provider/certpath/ldap/LDAPCertStoreImpl.java index 8f18e04760a..ebf09e57bd1 100644 --- a/src/java.naming/share/classes/sun/security/provider/certpath/ldap/LDAPCertStoreImpl.java +++ b/src/java.naming/share/classes/sun/security/provider/certpath/ldap/LDAPCertStoreImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, 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 @@ -52,6 +52,7 @@ import sun.security.provider.certpath.X509CertificatePair; import sun.security.util.Cache; import sun.security.util.Debug; +import sun.security.util.SecurityProperties; /** * Core implementation of a LDAP Cert Store. @@ -96,6 +97,16 @@ final class LDAPCertStoreImpl { private static final String PROP_DISABLE_APP_RESOURCE_FILES = "sun.security.certpath.ldap.disable.app.resource.files"; + /** + * Maximum size for a CRL downloaded through an LDAPCertStoreImpl + * in bytes. This can be controlled by the com.sun.security.crl.maxSize + * Security or System property. The System property, if set, overrides + * the Security property. The default size is 20MiB. + */ + private static final long MAX_CRL_DOWNLOAD_SIZE = + SecurityProperties.getOverridableLongProp( + "com.sun.security.crl.maxSize", 20971520, debug); + static { String s = System.getProperty(PROP_LIFETIME); if (s != null) { @@ -103,6 +114,13 @@ final class LDAPCertStoreImpl { } else { LIFETIME = DEFAULT_CACHE_LIFETIME; } + + // Add a debug message for the configured CRL download limit + if (debug != null) { + debug.println("Maximum downloadable CRL size: " + + MAX_CRL_DOWNLOAD_SIZE + + ((MAX_CRL_DOWNLOAD_SIZE < 0) ? " (DISABLED)" : "")); + } } /** @@ -672,12 +690,12 @@ private Collection getMatchingCrossCerts( return certs; } - /* + /** * Gets CRLs from an attribute id and location in the LDAP directory. * Returns a Collection containing only the CRLs that match the * specified X509CRLSelector. * - * @param name the location holding the attribute + * @param request the LDAP request used for this CRL fetch operation * @param id the attribute identifier * @param sel a X509CRLSelector that the CRLs must match * @return a Collection of CRLs found @@ -689,7 +707,26 @@ private Collection getCRLs(LDAPRequest request, String id, /* fetch the encoded crls from storage */ byte[][] encodedCRL; try { - encodedCRL = request.getValues(id); + byte[][] tmpCrls = request.getValues(id); + if (MAX_CRL_DOWNLOAD_SIZE > -1) { + int totalSize = 0; + for (byte[] tCrl : tmpCrls) { + totalSize += tCrl.length; + } + if (totalSize <= MAX_CRL_DOWNLOAD_SIZE) { + encodedCRL = tmpCrls; + } else { + if (debug != null) { + debug.println("Received " + tmpCrls.length + + " CRL(s). Combined length of " + totalSize + + " exceeds configured maximum. Discarding."); + } + encodedCRL = new byte[0][]; + } + } else { + // Download limits disabled + encodedCRL = tmpCrls; + } } catch (NamingException namingEx) { throw new CertStoreException(namingEx); } diff --git a/test/lib/jdk/test/lib/security/CertificateBuilder.java b/test/lib/jdk/test/lib/security/CertificateBuilder.java index a2d2a7d9eb1..86aaba2a0b1 100644 --- a/test/lib/jdk/test/lib/security/CertificateBuilder.java +++ b/test/lib/jdk/test/lib/security/CertificateBuilder.java @@ -42,6 +42,7 @@ import sun.security.x509.AlgorithmId; import sun.security.x509.AuthorityInfoAccessExtension; import sun.security.x509.AuthorityKeyIdentifierExtension; +import sun.security.x509.CRLDistributionPointsExtension; import sun.security.x509.GeneralSubtrees; import sun.security.x509.IPAddressName; import sun.security.x509.NameConstraintsExtension; @@ -49,6 +50,7 @@ import sun.security.x509.BasicConstraintsExtension; import sun.security.x509.CertificateSerialNumber; import sun.security.x509.ExtendedKeyUsageExtension; +import sun.security.x509.DistributionPoint; import sun.security.x509.DNSName; import sun.security.x509.GeneralName; import sun.security.x509.GeneralNames; @@ -61,13 +63,13 @@ /** * Helper class that builds and signs X.509 certificates. - * + *

    * A CertificateBuilder is created with a default constructor, and then * uses additional public methods to set the public key, desired validity * dates, serial number and extensions. It is expected that the caller will * have generated the necessary key pairs prior to using a CertificateBuilder * to generate certificates. - * + *

    * The following methods are mandatory before calling build(): *