8380903: [BACKOUT] Mitigate Neoverse-N1 erratum 1542419 negative impact on GCs and JIT performance

Reviewed-by: aboldtch
This commit is contained in:
Joel Sikström 2026-03-25 14:01:26 +00:00
parent 6466d9809c
commit 4dca6e4ca8
24 changed files with 49 additions and 1244 deletions

View File

@ -879,9 +879,7 @@ void ZBarrierSetAssembler::patch_barrier_relocation(address addr, int format) {
ShouldNotReachHere();
}
if (!UseSingleICacheInvalidation) {
ICache::invalidate_word((address)patch_addr);
}
ICache::invalidate_word((address)patch_addr);
}
#ifdef COMPILER1

View File

@ -127,8 +127,6 @@ define_pd_global(intx, InlineSmallCode, 1000);
"Branch Protection to use: none, standard, pac-ret") \
product(bool, AlwaysMergeDMB, true, DIAGNOSTIC, \
"Always merge DMB instructions in code emission") \
product(bool, NeoverseN1ICacheErratumMitigation, false, DIAGNOSTIC, \
"Enable workaround for Neoverse N1 erratum 1542419") \
// end of ARCH_FLAGS

View File

@ -54,12 +54,7 @@ void Relocation::pd_set_data_value(address x, bool verify_only) {
bytes = MacroAssembler::pd_patch_instruction_size(addr(), x);
break;
}
if (UseSingleICacheInvalidation) {
assert(_binding != nullptr, "expect to be called with RelocIterator in use");
} else {
ICache::invalidate_range(addr(), bytes);
}
ICache::invalidate_range(addr(), bytes);
}
address Relocation::pd_call_destination(address orig_addr) {

View File

@ -52,9 +52,6 @@ uintptr_t VM_Version::_pac_mask;
SpinWait VM_Version::_spin_wait;
bool VM_Version::_cache_dic_enabled;
bool VM_Version::_cache_idc_enabled;
const char* VM_Version::_features_names[MAX_CPU_FEATURES] = { nullptr };
static SpinWait get_spin_wait_desc() {
@ -66,19 +63,6 @@ static SpinWait get_spin_wait_desc() {
return spin_wait;
}
static bool has_neoverse_n1_errata_1542419() {
const int major_rev_num = VM_Version::cpu_variant();
const int minor_rev_num = VM_Version::cpu_revision();
// Neoverse N1: 0xd0c
// Erratum 1542419 affects r3p0, r3p1 and r4p0.
// It is fixed in r4p1 and later revisions, which are not affected.
return (VM_Version::cpu_family() == VM_Version::CPU_ARM &&
VM_Version::model_is(0xd0c) &&
((major_rev_num == 3 && minor_rev_num == 0) ||
(major_rev_num == 3 && minor_rev_num == 1) ||
(major_rev_num == 4 && minor_rev_num == 0)));
}
void VM_Version::initialize() {
#define SET_CPU_FEATURE_NAME(id, name, bit) \
_features_names[bit] = XSTR(name);
@ -90,9 +74,6 @@ void VM_Version::initialize() {
_supports_atomic_getset8 = true;
_supports_atomic_getadd8 = true;
_cache_dic_enabled = false;
_cache_idc_enabled = false;
get_os_cpu_info();
int dcache_line = VM_Version::dcache_line_size();
@ -680,32 +661,6 @@ void VM_Version::initialize() {
clear_feature(CPU_SVE);
}
if (FLAG_IS_DEFAULT(UseSingleICacheInvalidation) && is_cache_idc_enabled() && is_cache_dic_enabled()) {
FLAG_SET_DEFAULT(UseSingleICacheInvalidation, true);
}
if (FLAG_IS_DEFAULT(NeoverseN1ICacheErratumMitigation) && has_neoverse_n1_errata_1542419()) {
FLAG_SET_DEFAULT(NeoverseN1ICacheErratumMitigation, true);
}
if (NeoverseN1ICacheErratumMitigation) {
if (!has_neoverse_n1_errata_1542419()) {
vm_exit_during_initialization("NeoverseN1ICacheErratumMitigation is set for the CPU not having Neoverse N1 errata 1542419");
}
if (FLAG_IS_DEFAULT(UseSingleICacheInvalidation)) {
FLAG_SET_DEFAULT(UseSingleICacheInvalidation, true);
}
if (!UseSingleICacheInvalidation) {
vm_exit_during_initialization("NeoverseN1ICacheErratumMitigation is set but UseSingleICacheInvalidation is not enabled");
}
}
if (UseSingleICacheInvalidation
&& (!is_cache_idc_enabled() || (!is_cache_dic_enabled() && !NeoverseN1ICacheErratumMitigation))) {
vm_exit_during_initialization("UseSingleICacheInvalidation is set but neither IDC nor DIC nor NeoverseN1ICacheErratumMitigation is enabled");
}
// Construct the "features" string
stringStream ss(512);
ss.print("0x%02x:0x%x:0x%03x:%d", _cpu, _variant, _model, _revision);

View File

@ -58,8 +58,6 @@ protected:
// When _prefer_sve_merging_mode_cpy is true, `cpy (imm, zeroing)` is
// implemented as `movi; cpy(imm, merging)`.
static constexpr bool _prefer_sve_merging_mode_cpy = true;
static bool _cache_dic_enabled;
static bool _cache_idc_enabled;
static SpinWait _spin_wait;
@ -255,9 +253,6 @@ public:
return vector_length_in_bytes <= 16;
}
static bool is_cache_dic_enabled() { return _cache_dic_enabled; }
static bool is_cache_idc_enabled() { return _cache_idc_enabled; }
static void get_cpu_features_name(void* features_buffer, stringStream& ss);
// Returns names of features present in features_set1 but not in features_set2

View File

@ -1,28 +0,0 @@
/*
* 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 "runtime/icache.hpp"
#include "utilities/globalDefinitions.hpp"
NOT_PRODUCT(THREAD_LOCAL AArch64ICacheInvalidationContext* AArch64ICacheInvalidationContext::_current_context = nullptr;)

View File

@ -26,10 +26,6 @@
#ifndef OS_CPU_LINUX_AARCH64_ICACHE_AARCH64_HPP
#define OS_CPU_LINUX_AARCH64_ICACHE_AARCH64_HPP
#include "memory/allocation.hpp"
#include "runtime/vm_version.hpp"
#include "utilities/globalDefinitions.hpp"
// Interface for updating the instruction cache. Whenever the VM
// modifies code, part of the processor instruction cache potentially
// has to be flushed.
@ -41,103 +37,8 @@ class ICache : public AbstractICache {
__builtin___clear_cache((char *)addr, (char *)(addr + 4));
}
static void invalidate_range(address start, int nbytes) {
if (NeoverseN1ICacheErratumMitigation) {
assert(VM_Version::is_cache_idc_enabled(),
"Expect CTR_EL0.IDC to be enabled for Neoverse N1 with erratum "
"1542419");
assert(!VM_Version::is_cache_dic_enabled(),
"Expect CTR_EL0.DIC to be disabled for Neoverse N1 with erratum "
"1542419");
asm volatile("dsb ish \n"
"ic ivau, xzr \n"
"dsb ish \n"
"isb \n"
: : : "memory");
} else {
__builtin___clear_cache((char *)start, (char *)(start + nbytes));
}
__builtin___clear_cache((char *)start, (char *)(start + nbytes));
}
};
class AArch64ICacheInvalidationContext : StackObj {
private:
#ifdef ASSERT
static THREAD_LOCAL AArch64ICacheInvalidationContext* _current_context;
#endif
bool _has_modified_code;
public:
NONCOPYABLE(AArch64ICacheInvalidationContext);
AArch64ICacheInvalidationContext()
: _has_modified_code(false) {
assert(_current_context == nullptr, "nested ICacheInvalidationContext not supported");
#ifdef ASSERT
_current_context = this;
#endif
}
~AArch64ICacheInvalidationContext() {
NOT_PRODUCT(_current_context = nullptr);
if (!_has_modified_code || !UseSingleICacheInvalidation) {
return;
}
assert(VM_Version::is_cache_idc_enabled(), "Expect CTR_EL0.IDC to be enabled");
asm volatile("dsb ish" : : : "memory");
if (NeoverseN1ICacheErratumMitigation) {
assert(!VM_Version::is_cache_dic_enabled(),
"Expect CTR_EL0.DIC to be disabled for Neoverse N1 with erratum "
"1542419");
// Errata 1542419: Neoverse N1 cores with the 'COHERENT_ICACHE' feature
// may fetch stale instructions when software depends on
// prefetch-speculation-protection instead of explicit synchronization.
//
// Neoverse-N1 implementation mitigates the errata 1542419 with a
// workaround:
// - Disable coherent icache.
// - Trap IC IVAU instructions.
// - Execute:
// - tlbi vae3is, xzr
// - dsb sy
// - Ignore trapped IC IVAU instructions.
//
// `tlbi vae3is, xzr` invalidates all translation entries (all VAs, all
// possible levels). It waits for all memory accesses using in-scope old
// translation information to complete before it is considered complete.
//
// As this workaround has significant overhead, Arm Neoverse N1 (MP050)
// Software Developer Errata Notice version 29.0 suggests:
//
// "Since one TLB inner-shareable invalidation is enough to avoid this
// erratum, the number of injected TLB invalidations should be minimized
// in the trap handler to mitigate the performance impact due to this
// workaround."
// As the address for icache invalidation is not relevant and
// IC IVAU instruction is ignored, we use XZR in it.
asm volatile(
"ic ivau, xzr \n"
"dsb ish \n"
:
:
: "memory");
} else {
assert(VM_Version::is_cache_dic_enabled(), "Expect CTR_EL0.DIC to be enabled");
}
asm volatile("isb" : : : "memory");
}
void set_has_modified_code() {
_has_modified_code = true;
}
};
#define PD_ICACHE_INVALIDATION_CONTEXT AArch64ICacheInvalidationContext
#endif // OS_CPU_LINUX_AARCH64_ICACHE_AARCH64_HPP

View File

@ -169,8 +169,6 @@ void VM_Version::get_os_cpu_info() {
_icache_line_size = (1 << (ctr_el0 & 0x0f)) * 4;
_dcache_line_size = (1 << ((ctr_el0 >> 16) & 0x0f)) * 4;
_cache_idc_enabled = ((ctr_el0 >> 28) & 0x1) != 0;
_cache_dic_enabled = ((ctr_el0 >> 29) & 0x1) != 0;
if (!(dczid_el0 & 0x10)) {
_zva_length = 4 << (dczid_el0 & 0xf);

View File

@ -745,6 +745,9 @@ void CodeBuffer::copy_code_to(CodeBlob* dest_blob) {
// Done moving code bytes; were they the right size?
assert((int)align_up(dest.total_content_size(), oopSize) == dest_blob->content_size(), "sanity");
// Flush generated code
ICache::invalidate_range(dest_blob->code_begin(), dest_blob->code_size());
}
// Move all my code into another code buffer. Consult applicable

View File

@ -331,13 +331,7 @@ RuntimeBlob::RuntimeBlob(
: CodeBlob(name, kind, cb, size, header_size, frame_complete, frame_size, oop_maps, caller_must_gc_arguments,
align_up(cb->total_relocation_size(), oopSize))
{
if (code_size() == 0) {
// Nothing to copy
return;
}
cb->copy_code_and_locs_to(this);
ICache::invalidate_range(code_begin(), code_size());
}
void RuntimeBlob::free(RuntimeBlob* blob) {

View File

@ -1328,7 +1328,6 @@ nmethod::nmethod(
code_buffer->copy_values_to(this);
post_init();
ICache::invalidate_range(code_begin(), code_size());
}
if (PrintNativeNMethods || PrintDebugInfo || PrintRelocations || PrintDependencies) {
@ -1810,7 +1809,6 @@ nmethod::nmethod(
init_immutable_data_ref_count();
post_init();
ICache::invalidate_range(code_begin(), code_size());
// we use the information of entry points to find out if a method is
// static or non static
@ -2038,7 +2036,7 @@ void nmethod::copy_values(GrowableArray<jobject>* array) {
// The code and relocations have already been initialized by the
// CodeBlob constructor, so it is valid even at this early point to
// iterate over relocations and patch the code.
fix_oop_relocations(/*initialize_immediates=*/ true);
fix_oop_relocations(nullptr, nullptr, /*initialize_immediates=*/ true);
}
void nmethod::copy_values(GrowableArray<Metadata*>* array) {
@ -2050,42 +2048,24 @@ void nmethod::copy_values(GrowableArray<Metadata*>* array) {
}
}
bool nmethod::fix_oop_relocations(bool initialize_immediates) {
void nmethod::fix_oop_relocations(address begin, address end, bool initialize_immediates) {
// re-patch all oop-bearing instructions, just in case some oops moved
RelocIterator iter(this);
bool modified_code = false;
RelocIterator iter(this, begin, end);
while (iter.next()) {
if (iter.type() == relocInfo::oop_type) {
oop_Relocation* reloc = iter.oop_reloc();
if (!reloc->oop_is_immediate()) {
// Refresh the oop-related bits of this instruction.
reloc->set_value(reloc->value());
modified_code = true;
} else if (initialize_immediates) {
if (initialize_immediates && reloc->oop_is_immediate()) {
oop* dest = reloc->oop_addr();
jobject obj = *reinterpret_cast<jobject*>(dest);
initialize_immediate_oop(dest, obj);
}
// Refresh the oop-related bits of this instruction.
reloc->fix_oop_relocation();
} else if (iter.type() == relocInfo::metadata_type) {
metadata_Relocation* reloc = iter.metadata_reloc();
reloc->fix_metadata_relocation();
modified_code |= !reloc->metadata_is_immediate();
}
}
return modified_code;
}
void nmethod::fix_oop_relocations() {
ICacheInvalidationContext icic;
fix_oop_relocations(&icic);
}
void nmethod::fix_oop_relocations(ICacheInvalidationContext* icic) {
assert(icic != nullptr, "must provide context to track if code was modified");
bool modified_code = fix_oop_relocations(/*initialize_immediates=*/ false);
if (modified_code) {
icic->set_has_modified_code();
}
}
static void install_post_call_nop_displacement(nmethod* nm, address pc) {

View File

@ -41,7 +41,6 @@ class Dependencies;
class DirectiveSet;
class DebugInformationRecorder;
class ExceptionHandlerTable;
class ICacheInvalidationContext;
class ImplicitExceptionTable;
class JvmtiThreadState;
class MetadataClosure;
@ -802,15 +801,15 @@ public:
// Relocation support
private:
bool fix_oop_relocations(bool initialize_immediates);
void fix_oop_relocations(address begin, address end, bool initialize_immediates);
inline void initialize_immediate_oop(oop* dest, jobject handle);
protected:
address oops_reloc_begin() const;
public:
void fix_oop_relocations(ICacheInvalidationContext* icic);
void fix_oop_relocations();
void fix_oop_relocations(address begin, address end) { fix_oop_relocations(begin, end, false); }
void fix_oop_relocations() { fix_oop_relocations(nullptr, nullptr, false); }
bool is_at_poll_return(address pc);
bool is_at_poll_or_poll_return(address pc);

View File

@ -590,6 +590,15 @@ oop oop_Relocation::oop_value() {
return *oop_addr();
}
void oop_Relocation::fix_oop_relocation() {
if (!oop_is_immediate()) {
// get the oop from the pool, and re-insert it into the instruction:
set_value(value());
}
}
void oop_Relocation::verify_oop_relocation() {
if (!oop_is_immediate()) {
// get the oop from the pool, and re-insert it into the instruction:

View File

@ -988,6 +988,8 @@ class oop_Relocation : public DataRelocation {
void pack_data_to(CodeSection* dest) override;
void unpack_data() override;
void fix_oop_relocation(); // reasserts oop value
void verify_oop_relocation();
address value() override { return *reinterpret_cast<address*>(oop_addr()); }

View File

@ -33,7 +33,6 @@
#include "gc/z/zThreadLocalData.hpp"
#include "gc/z/zUncoloredRoot.inline.hpp"
#include "logging/log.hpp"
#include "runtime/icache.hpp"
#include "runtime/threadWXSetters.inline.hpp"
bool ZBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) {
@ -71,15 +70,12 @@ bool ZBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) {
return false;
}
{
ICacheInvalidationContext icic;
// Heal barriers
ZNMethod::nmethod_patch_barriers(nm, &icic);
// Heal barriers
ZNMethod::nmethod_patch_barriers(nm);
// Heal oops
ZUncoloredRootProcessWeakOopClosure cl(ZNMethod::color(nm));
ZNMethod::nmethod_oops_do_inner(nm, &cl, &icic);
}
// Heal oops
ZUncoloredRootProcessWeakOopClosure cl(ZNMethod::color(nm));
ZNMethod::nmethod_oops_do_inner(nm, &cl);
const uintptr_t prev_color = ZNMethod::color(nm);
const uintptr_t new_color = *ZPointerStoreGoodMaskLowOrderBitsAddr;

View File

@ -58,7 +58,6 @@
#include "prims/jvmtiTagMap.hpp"
#include "runtime/continuation.hpp"
#include "runtime/handshake.hpp"
#include "runtime/icache.hpp"
#include "runtime/safepoint.hpp"
#include "runtime/threads.hpp"
#include "runtime/vmOperations.hpp"
@ -1435,15 +1434,12 @@ public:
virtual void do_nmethod(nmethod* nm) {
ZLocker<ZReentrantLock> locker(ZNMethod::lock_for_nmethod(nm));
if (_bs_nm->is_armed(nm)) {
{
ICacheInvalidationContext icic;
// Heal barriers
ZNMethod::nmethod_patch_barriers(nm, &icic);
// Heal barriers
ZNMethod::nmethod_patch_barriers(nm);
// Heal oops
ZUncoloredRootProcessOopClosure cl(ZNMethod::color(nm));
ZNMethod::nmethod_oops_do_inner(nm, &cl, &icic);
}
// Heal oops
ZUncoloredRootProcessOopClosure cl(ZNMethod::color(nm));
ZNMethod::nmethod_oops_do_inner(nm, &cl);
log_trace(gc, nmethod)("nmethod: " PTR_FORMAT " visited by old remapping", p2i(nm));

View File

@ -59,7 +59,6 @@
#include "oops/oop.inline.hpp"
#include "runtime/continuation.hpp"
#include "runtime/handshake.hpp"
#include "runtime/icache.hpp"
#include "runtime/javaThread.hpp"
#include "runtime/prefetch.inline.hpp"
#include "runtime/safepointMechanism.hpp"
@ -719,15 +718,12 @@ public:
virtual void do_nmethod(nmethod* nm) {
ZLocker<ZReentrantLock> locker(ZNMethod::lock_for_nmethod(nm));
if (_bs_nm->is_armed(nm)) {
{
ICacheInvalidationContext icic;
// Heal barriers
ZNMethod::nmethod_patch_barriers(nm, &icic);
// Heal barriers
ZNMethod::nmethod_patch_barriers(nm);
// Heal oops
ZUncoloredRootMarkOopClosure cl(ZNMethod::color(nm));
ZNMethod::nmethod_oops_do_inner(nm, &cl, &icic);
}
// Heal oops
ZUncoloredRootMarkOopClosure cl(ZNMethod::color(nm));
ZNMethod::nmethod_oops_do_inner(nm, &cl);
// CodeCache unloading support
nm->mark_as_maybe_on_stack();
@ -757,6 +753,10 @@ public:
if (_bs_nm->is_armed(nm)) {
const uintptr_t prev_color = ZNMethod::color(nm);
// Heal oops
ZUncoloredRootMarkYoungOopClosure cl(prev_color);
ZNMethod::nmethod_oops_do_inner(nm, &cl);
// Disarm only the young marking, not any potential old marking cycle
const uintptr_t old_marked_mask = ZPointerMarkedMask ^ (ZPointerMarkedYoung0 | ZPointerMarkedYoung1);
@ -767,16 +767,9 @@ public:
// Check if disarming for young mark, completely disarms the nmethod entry barrier
const bool complete_disarm = ZPointer::is_store_good(new_disarm_value_ptr);
{
ICacheInvalidationContext icic;
if (complete_disarm) {
// We are about to completely disarm the nmethod, must take responsibility to patch all barriers before disarming
ZNMethod::nmethod_patch_barriers(nm, &icic);
}
// Heal oops
ZUncoloredRootMarkYoungOopClosure cl(prev_color);
ZNMethod::nmethod_oops_do_inner(nm, &cl, &icic);
if (complete_disarm) {
// We are about to completely disarm the nmethod, must take responsibility to patch all barriers before disarming
ZNMethod::nmethod_patch_barriers(nm);
}
_bs_nm->guard_with(nm, (int)untype(new_disarm_value_ptr));

View File

@ -50,7 +50,6 @@
#include "oops/oop.inline.hpp"
#include "runtime/atomicAccess.hpp"
#include "runtime/continuation.hpp"
#include "runtime/icache.hpp"
#include "utilities/debug.hpp"
static ZNMethodData* gc_data(const nmethod* nm) {
@ -246,16 +245,8 @@ void ZNMethod::set_guard_value(nmethod* nm, int value) {
}
void ZNMethod::nmethod_patch_barriers(nmethod* nm) {
ICacheInvalidationContext icic;
nmethod_patch_barriers(nm, &icic);
}
void ZNMethod::nmethod_patch_barriers(nmethod* nm, ICacheInvalidationContext* icic) {
ZBarrierSetAssembler* const bs_asm = ZBarrierSet::assembler();
ZArrayIterator<ZNMethodDataBarrier> iter(gc_data(nm)->barriers());
if (gc_data(nm)->barriers()->is_nonempty()) {
icic->set_has_modified_code();
}
for (ZNMethodDataBarrier barrier; iter.next(&barrier);) {
bs_asm->patch_barrier_relocation(barrier._reloc_addr, barrier._reloc_format);
}
@ -267,11 +258,6 @@ void ZNMethod::nmethod_oops_do(nmethod* nm, OopClosure* cl) {
}
void ZNMethod::nmethod_oops_do_inner(nmethod* nm, OopClosure* cl) {
ICacheInvalidationContext icic;
nmethod_oops_do_inner(nm, cl, &icic);
}
void ZNMethod::nmethod_oops_do_inner(nmethod* nm, OopClosure* cl, ICacheInvalidationContext* icic) {
// Process oops table
{
oop* const begin = nm->oops_begin();
@ -297,7 +283,7 @@ void ZNMethod::nmethod_oops_do_inner(nmethod* nm, OopClosure* cl, ICacheInvalida
// Process non-immediate oops
if (data->has_non_immediate_oops()) {
nm->fix_oop_relocations(icic);
nm->fix_oop_relocations();
}
}

View File

@ -56,11 +56,9 @@ public:
static void set_guard_value(nmethod* nm, int value);
static void nmethod_patch_barriers(nmethod* nm);
static void nmethod_patch_barriers(nmethod* nm, ICacheInvalidationContext* icic);
static void nmethod_oops_do(nmethod* nm, OopClosure* cl);
static void nmethod_oops_do_inner(nmethod* nm, OopClosure* cl);
static void nmethod_oops_do_inner(nmethod* nm, OopClosure* cl, ICacheInvalidationContext* icic);
static void nmethods_do_begin(bool secondary);
static void nmethods_do_end(bool secondary);

View File

@ -1978,8 +1978,6 @@ const int ObjectAlignmentInBytes = 8;
develop(uint, BinarySearchThreshold, 16, \
"Minimal number of elements in a sorted collection to prefer" \
"binary search over simple linear search." ) \
product(bool, UseSingleICacheInvalidation, false, DIAGNOSTIC, \
"Defer multiple ICache invalidation to single invalidation") \
\
// end of RUNTIME_FLAGS

View File

@ -129,27 +129,4 @@ class ICacheStubGenerator : public StubCodeGenerator {
void generate_icache_flush(ICache::flush_icache_stub_t* flush_icache_stub);
};
class DefaultICacheInvalidationContext : StackObj {
public:
NONCOPYABLE(DefaultICacheInvalidationContext);
DefaultICacheInvalidationContext() {}
~DefaultICacheInvalidationContext() {}
void set_has_modified_code() {}
};
#ifndef PD_ICACHE_INVALIDATION_CONTEXT
#define PD_ICACHE_INVALIDATION_CONTEXT DefaultICacheInvalidationContext
#endif // PD_ICACHE_INVALIDATION_CONTEXT
class ICacheInvalidationContext final : public PD_ICACHE_INVALIDATION_CONTEXT {
private:
NONCOPYABLE(ICacheInvalidationContext);
public:
using PD_ICACHE_INVALIDATION_CONTEXT::PD_ICACHE_INVALIDATION_CONTEXT;
};
#endif // SHARE_RUNTIME_ICACHE_HPP

View File

@ -1,426 +0,0 @@
/*
* 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.
*
*/
package compiler.runtime;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
import jdk.test.whitebox.WhiteBox;
import jtreg.SkippedException;
/*
* @test
* @bug 8370947
* @summary Test command-line options for UseSingleICacheInvalidation and NeoverseN1ICacheErratumMitigation
* @library /test/lib
* @requires os.arch == "aarch64"
* @requires os.family == "linux"
* @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.runtime.TestDeferredICacheInvalidationCmdOptions
*/
public class TestDeferredICacheInvalidationCmdOptions {
// CPU identifiers
private static final int CPU_ARM = 0x41;
private static final int NEOVERSE_N1_MODEL = 0xd0c;
private static boolean isAffected;
public static void main(String[] args) throws Exception {
// Parse CPU features and print CPU info
parseCPUFeatures();
System.out.println("Testing UseSingleICacheInvalidation and NeoverseN1ICacheErratumMitigation command-line options...");
// Test case 1: Check defaults on Neoverse N1 pre-r4p1 (if applicable)
testCase1_DefaultsOnNeoverseN1();
// Test case 2: Check NeoverseN1ICacheErratumMitigation is false on unaffected CPUs
testCase2_DefaultsOnUnaffectedCPUs();
// Test case 3: Check if NeoverseN1ICacheErratumMitigation is set to false on affected CPUs,
// UseSingleICacheInvalidation is also set to false
testCase3_ExplicitlyDisableErrataAffectsDeferred();
// Test case 4: Check JVM error if UseSingleICacheInvalidation=true
// but NeoverseN1ICacheErratumMitigation=false on affected CPUs
testCase4_ConflictingFlagsOnAffectedCPUs();
// Test case 5: Check explicit NeoverseN1ICacheErratumMitigation=true enables UseSingleICacheInvalidation
testCase5_ExplicitlyEnableErrataEnablesDeferred();
// Test case 6: Check both flags can be explicitly set to false
testCase6_ExplicitlyDisableBothFlags();
// Test case 7: Check UseSingleICacheInvalidation=false with NeoverseN1ICacheErratumMitigation=true
testCase7_ConflictingErrataWithoutDeferred();
// Test case 8: Check setting NeoverseN1ICacheErratumMitigation=true on unaffected CPU causes an error
testCase8_EnablingErrataOnUnaffectedCPU();
System.out.println("All test cases passed!");
}
/**
* Parse CPU features string from WhiteBox.getCPUFeatures() to extract:
* - cpuFamily: 0x41 for ARM
* - cpuVariant: major revision
* - cpuModel: e.g., 0xd0c for Neoverse N1
* - cpuRevision: minor revision
* - cpuModel2: secondary model (if present, in parentheses)
*
* Sets the static field isAffected and prints CPU info.
*
* Format: 0x%02x:0x%x:0x%03x:%d[(0x%03x)]
* Example: "0x41:0x3:0xd0c:0" or "0x41:0x3:0xd0c:0(0xd0c)"
*
* @throws SkippedException if not running on ARM CPU
*/
private static void parseCPUFeatures() {
WhiteBox wb = WhiteBox.getWhiteBox();
String cpuFeatures = wb.getCPUFeatures();
System.out.println("CPU Features string: " + cpuFeatures);
if (cpuFeatures == null || cpuFeatures.isEmpty()) {
throw new RuntimeException("No CPU features available");
}
int commaIndex = cpuFeatures.indexOf(",");
if (commaIndex == -1) {
throw new RuntimeException("Unexpected CPU features format (no comma): " + cpuFeatures);
}
String cpuPart = cpuFeatures.substring(0, commaIndex).trim();
String[] parts = cpuPart.split(":");
if (parts.length < 4) {
throw new RuntimeException("Unexpected CPU features format: " + cpuPart);
}
int cpuFamily = Integer.parseInt(parts[0].substring(2), 16);
if (cpuFamily != CPU_ARM) {
throw new SkippedException("Not running on ARM CPU (cpuFamily=0x" + Integer.toHexString(cpuFamily) + ")");
}
int cpuVariant = Integer.parseInt(parts[1].substring(2), 16);
int cpuModel = Integer.parseInt(parts[2].substring(2), 16);
int cpuModel2 = 0;
int model2Start = parts[3].indexOf("(");
String revisionStr = parts[3];
if (model2Start != -1) {
if (!parts[3].endsWith(")")) {
throw new RuntimeException("Unexpected CPU features format (missing closing parenthesis): " + parts[3]);
}
String model2Str = parts[3].substring(model2Start + 1, parts[3].length() - 1);
cpuModel2 = Integer.parseInt(model2Str.substring(2), 16);
revisionStr = parts[3].substring(0, model2Start);
}
int cpuRevision = Integer.parseInt(revisionStr);
// Neoverse N1 errata 1542419 affects r3p0, r3p1 and r4p0.
// It is fixed in r4p1 and later revisions.
if (cpuModel == NEOVERSE_N1_MODEL || cpuModel2 == NEOVERSE_N1_MODEL) {
isAffected = (cpuVariant == 3 && cpuRevision == 0) ||
(cpuVariant == 3 && cpuRevision == 1) ||
(cpuVariant == 4 && cpuRevision == 0);
}
printCPUInfo(cpuFamily, cpuVariant, cpuModel, cpuModel2, cpuRevision);
}
private static void printCPUInfo(int cpuFamily, int cpuVariant, int cpuModel, int cpuModel2, int cpuRevision) {
boolean isNeoverseN1 = (cpuFamily == CPU_ARM) &&
(cpuModel == NEOVERSE_N1_MODEL || cpuModel2 == NEOVERSE_N1_MODEL);
System.out.println("\n=== CPU Information ===");
System.out.println("CPU Family: 0x" + Integer.toHexString(cpuFamily));
System.out.println("CPU Variant: 0x" + Integer.toHexString(cpuVariant));
System.out.println("CPU Model: 0x" + Integer.toHexString(cpuModel));
if (cpuModel2 != 0) {
System.out.println("CPU Model2: 0x" + Integer.toHexString(cpuModel2));
}
System.out.println("CPU Revision: " + cpuRevision);
System.out.println("Is Neoverse N1: " + isNeoverseN1);
System.out.println("Is affected by errata 1542419: " + isAffected);
System.out.println("======================\n");
}
/**
* Test case 1: Check the UseSingleICacheInvalidation and NeoverseN1ICacheErratumMitigation
* are set to true for Neoverse N1 pre-r4p1.
*/
private static void testCase1_DefaultsOnNeoverseN1() throws Exception {
if (!isAffected) {
System.out.println("\nTest case 1: Skipping since CPU is not affected by Neoverse N1 errata 1542419");
return;
}
System.out.println("\nTest case 1: Check defaults on Neoverse N1 affected revisions");
ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+PrintFlagsFinal",
"-version");
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.shouldHaveExitValue(0);
String output_str = output.getOutput();
boolean hasSingleEnabled = output_str.matches("(?s).*bool\\s+UseSingleICacheInvalidation\\s*=\\s*true.*");
boolean hasErrataEnabled = output_str.matches("(?s).*bool\\s+NeoverseN1ICacheErratumMitigation\\s*=\\s*true.*");
System.out.println("UseSingleICacheInvalidation enabled: " + hasSingleEnabled);
System.out.println("NeoverseN1ICacheErratumMitigation enabled: " + hasErrataEnabled);
// If running on affected Neoverse N1, both should be true
if (!hasSingleEnabled || !hasErrataEnabled) {
throw new RuntimeException("On affected Neoverse N1, both flags should be enabled by default");
}
System.out.println("Correctly enabled on affected Neoverse N1");
}
/**
* Test case 2: Check NeoverseN1ICacheErratumMitigation is false on unaffected CPUs.
*/
private static void testCase2_DefaultsOnUnaffectedCPUs() throws Exception {
if (isAffected) {
System.out.println("\nTest case 2: Skipping since CPU is affected by Neoverse N1 errata 1542419");
return;
}
System.out.println("\nTest case 2: Check NeoverseN1ICacheErratumMitigation is false on unaffected CPUs");
ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+PrintFlagsFinal",
"-version");
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.shouldHaveExitValue(0);
String output_str = output.getOutput();
boolean hasErrataEnabled = output_str.matches("(?s).*bool\\s+NeoverseN1ICacheErratumMitigation\\s*=\\s*true.*");
System.out.println("NeoverseN1ICacheErratumMitigation enabled: " + hasErrataEnabled);
// On non-Neoverse N1 or unaffected Neoverse N1 CPUs, NeoverseN1ICacheErratumMitigation should be false
if (hasErrataEnabled) {
throw new RuntimeException("On unaffected CPUs, NeoverseN1ICacheErratumMitigation should be disabled");
}
System.out.println("Correctly disabled on unaffected CPU");
}
/**
* Test case 3: Check if NeoverseN1ICacheErratumMitigation is set to false via cmd on affected CPUs,
* UseSingleICacheInvalidation is set to false.
*/
private static void testCase3_ExplicitlyDisableErrataAffectsDeferred() throws Exception {
if (!isAffected) {
System.out.println("\nTest case 3: Skipping since CPU is not affected by Neoverse N1 errata 1542419");
return;
}
System.out.println("\nTest case 3: Explicitly disable NeoverseN1ICacheErratumMitigation, check UseSingleICacheInvalidation");
ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(
"-XX:+UnlockDiagnosticVMOptions",
"-XX:-NeoverseN1ICacheErratumMitigation",
"-XX:+PrintFlagsFinal",
"-version");
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.shouldHaveExitValue(0);
String output_str = output.getOutput();
boolean hasSingleDisabled = output_str.matches("(?s).*bool\\s+UseSingleICacheInvalidation\\s*=\\s*false.*");
boolean hasErrataDisabled = output_str.matches("(?s).*bool\\s+NeoverseN1ICacheErratumMitigation\\s*=\\s*false.*");
System.out.println("NeoverseN1ICacheErratumMitigation disabled: " + hasErrataDisabled);
System.out.println("UseSingleICacheInvalidation disabled: " + hasSingleDisabled);
if (!hasErrataDisabled) {
throw new RuntimeException("Failed to disable NeoverseN1ICacheErratumMitigation via command line");
}
// On affected CPUs, disabling errata should also disable UseSingleICacheInvalidation
if (!hasSingleDisabled) {
throw new RuntimeException("On affected CPU, disabling NeoverseN1ICacheErratumMitigation should also disable UseSingleICacheInvalidation");
}
System.out.println("Correctly synchronized on affected CPU");
}
/**
* Test case 4: Check JVM reports an error if UseSingleICacheInvalidation is set to true
* but NeoverseN1ICacheErratumMitigation is set to false on affected CPUs.
*/
private static void testCase4_ConflictingFlagsOnAffectedCPUs() throws Exception {
if (!isAffected) {
System.out.println("\nTest case 4: Skipping since CPU is not affected by Neoverse N1 errata 1542419");
return;
}
System.out.println("\nTest case 4: Try to set UseSingleICacheInvalidation=true with NeoverseN1ICacheErratumMitigation=false");
ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+UseSingleICacheInvalidation",
"-XX:-NeoverseN1ICacheErratumMitigation",
"-version");
OutputAnalyzer output = new OutputAnalyzer(pb.start());
if (output.getExitValue() == 0) {
throw new RuntimeException("On affected CPU, conflicting flags should cause error");
}
output.shouldContain("Error");
System.out.println("JVM correctly rejected conflicting flags on affected CPU");
}
/**
* Test case 5: Check explicit NeoverseN1ICacheErratumMitigation=true enables UseSingleICacheInvalidation.
*/
private static void testCase5_ExplicitlyEnableErrataEnablesDeferred() throws Exception {
if (!isAffected) {
System.out.println("\nTest case 5: Skipping since CPU is not affected by Neoverse N1 errata 1542419");
return;
}
System.out.println("\nTest case 5: Explicitly enable NeoverseN1ICacheErratumMitigation, check UseSingleICacheInvalidation");
ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+NeoverseN1ICacheErratumMitigation",
"-XX:+PrintFlagsFinal",
"-version");
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.shouldHaveExitValue(0);
String output_str = output.getOutput();
boolean hasSingleEnabled = output_str.matches("(?s).*bool\\s+UseSingleICacheInvalidation\\s*=\\s*true.*");
boolean hasErrataEnabled = output_str.matches("(?s).*bool\\s+NeoverseN1ICacheErratumMitigation\\s*=\\s*true.*");
System.out.println("NeoverseN1ICacheErratumMitigation enabled: " + hasErrataEnabled);
System.out.println("UseSingleICacheInvalidation enabled: " + hasSingleEnabled);
if (!hasErrataEnabled) {
throw new RuntimeException("Failed to enable NeoverseN1ICacheErratumMitigation via command line");
}
if (!hasSingleEnabled) {
throw new RuntimeException("On affected CPU, enabling NeoverseN1ICacheErratumMitigation should also enable UseSingleICacheInvalidation");
}
System.out.println("Correctly synchronized on affected CPU");
}
/**
* Test case 6: Check both flags can be explicitly set to false.
*/
private static void testCase6_ExplicitlyDisableBothFlags() throws Exception {
System.out.println("\nTest case 6: Explicitly disable both flags");
ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(
"-XX:+UnlockDiagnosticVMOptions",
"-XX:-UseSingleICacheInvalidation",
"-XX:-NeoverseN1ICacheErratumMitigation",
"-XX:+PrintFlagsFinal",
"-version");
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.shouldHaveExitValue(0);
String output_str = output.getOutput();
boolean hasSingleDisabled = output_str.matches("(?s).*bool\\s+UseSingleICacheInvalidation\\s*=\\s*false.*");
boolean hasErrataDisabled = output_str.matches("(?s).*bool\\s+NeoverseN1ICacheErratumMitigation\\s*=\\s*false.*");
System.out.println("UseSingleICacheInvalidation disabled: " + hasSingleDisabled);
System.out.println("NeoverseN1ICacheErratumMitigation disabled: " + hasErrataDisabled);
if (!hasErrataDisabled) {
throw new RuntimeException("Failed to disable NeoverseN1ICacheErratumMitigation via command line");
}
if (!hasSingleDisabled) {
throw new RuntimeException("Failed to disable UseSingleICacheInvalidation via command line");
}
System.out.println("Successfully disabled both flags");
}
/**
* Test case 7: Check UseSingleICacheInvalidation=false with NeoverseN1ICacheErratumMitigation=true.
* On affected CPUs, this should error (conflicting requirement).
*/
private static void testCase7_ConflictingErrataWithoutDeferred() throws Exception {
if (!isAffected) {
System.out.println("\nTest case 7: Skipping since CPU is not affected by Neoverse N1 errata 1542419");
return;
}
System.out.println("\nTest case 7: Try to set NeoverseN1ICacheErratumMitigation=true with UseSingleICacheInvalidation=false");
ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(
"-XX:+UnlockDiagnosticVMOptions",
"-XX:-UseSingleICacheInvalidation",
"-XX:+NeoverseN1ICacheErratumMitigation",
"-XX:+PrintFlagsFinal",
"-version");
OutputAnalyzer output = new OutputAnalyzer(pb.start());
// This should fail on affected CPUs (conflicting requirement)
if (output.getExitValue() == 0) {
throw new RuntimeException("On affected CPU, setting NeoverseN1ICacheErratumMitigation=true with UseSingleICacheInvalidation=false should cause an error");
}
output.shouldContain("Error");
System.out.println("JVM correctly rejected conflicting flags on affected CPU");
}
/**
* Test case 8: Check setting NeoverseN1ICacheErratumMitigation=true on unaffected CPU causes an error.
*/
private static void testCase8_EnablingErrataOnUnaffectedCPU() throws Exception {
if (isAffected) {
System.out.println("\nTest case 8: Skipping since CPU is affected by Neoverse N1 errata 1542419");
return;
}
System.out.println("\nTest case 8: Try to set NeoverseN1ICacheErratumMitigation=true on unaffected CPU");
ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+NeoverseN1ICacheErratumMitigation",
"-version");
OutputAnalyzer output = new OutputAnalyzer(pb.start());
// This should fail on unaffected CPUs (errata not present)
if (output.getExitValue() == 0) {
throw new RuntimeException("On unaffected CPU, setting NeoverseN1ICacheErratumMitigation=true should cause error");
}
output.shouldContain("Error");
System.out.println("JVM correctly rejected enabling errata flag on unaffected CPU");
}
}

View File

@ -1,306 +0,0 @@
/*
* 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.
*
*/
package gc;
/*
* @test id=parallel
* @bug 8370947
* @summary Check no assertion is triggered when UseSingleICacheInvalidation is enabled for ParallelGC
* @library /test/lib
* @requires vm.debug
* @requires vm.gc.Parallel
* @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 -XX:+UseParallelGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C1
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseParallelGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C1
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseParallelGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C2
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseParallelGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C2
*/
/*
* @test id=g1
* @bug 8370947
* @summary Check no assertion is triggered when UseSingleICacheInvalidation is enabled for G1GC
* @library /test/lib
* @requires vm.debug
* @requires vm.gc.G1
* @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 -XX:+UseG1GC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C1
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C1
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C2
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C2
*/
/*
* @test id=shenandoah
* @bug 8370947
* @summary Check no assertion is triggered when UseSingleICacheInvalidation is enabled for ShenandoahGC
* @library /test/lib
* @requires vm.debug
* @requires vm.gc.Shenandoah
* @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 -XX:+UseShenandoahGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C1
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseShenandoahGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C2
*/
/*
* @test id=genshen
* @bug 8370947
* @summary Check no assertion is triggered when UseSingleICacheInvalidation is enabled for generational ShenandoahGC
* @library /test/lib
* @requires vm.debug
* @requires vm.gc.Shenandoah
* @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 -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCMode=generational -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C1
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCMode=generational -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C1
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCMode=generational -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C2
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCMode=generational -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C2
*/
/*
* @test id=z
* @bug 8370947
* @summary Check no assertion is triggered when UseSingleICacheInvalidation is enabled for ZGC
* @library /test/lib
* @requires vm.debug
* @requires vm.gc.Z
* @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 -XX:+UseZGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C1
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseZGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C1
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseZGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C2
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseZGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C2
*/
/*
* Nmethods have GC barriers and OOPs embedded into their code. GCs can patch nmethod's code
* which requires icache invalidation. Doing invalidation per instruction can be expensive.
* CPU can support hardware dcache and icache coherence. This would allow to defer cache
* invalidation.
*
* There are assertions for deferred cache invalidation. This test checks that all of them
* are passed.
*/
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import jdk.test.whitebox.WhiteBox;
public class TestDeferredICacheInvalidation {
private static final WhiteBox WB = WhiteBox.getWhiteBox();
public static class A {
public String s1;
public String s2;
public String s3;
public String s4;
public String s5;
public String s6;
public String s7;
public String s8;
public String s9;
}
public static A a = new A();
private static int compLevel;
public static class B {
public static void test0() {
}
public static void test1() {
a.s1 = a.s1 + "1";
}
public static void test2() {
a.s1 = a.s1 + "1";
a.s2 = a.s2 + "2";
}
public static void test3() {
a.s1 = a.s1 + "1";
a.s2 = a.s2 + "2";
a.s3 = a.s3 + "3";
}
public static void test4() {
a.s1 = a.s1 + "1";
a.s2 = a.s2 + "2";
a.s3 = a.s3 + "3";
a.s4 = a.s4 + "4";
}
public static void test5() {
a.s1 = a.s1 + "1";
a.s2 = a.s2 + "2";
a.s3 = a.s3 + "3";
a.s4 = a.s4 + "4";
a.s5 = a.s5 + "5";
}
public static void test6() {
a.s1 = a.s1 + "1";
a.s2 = a.s2 + "2";
a.s3 = a.s3 + "3";
a.s4 = a.s4 + "4";
a.s5 = a.s5 + "5";
a.s6 = a.s6 + "6";
}
public static void test7() {
a.s1 = a.s1 + "1";
a.s2 = a.s2 + "2";
a.s3 = a.s3 + "3";
a.s4 = a.s4 + "4";
a.s5 = a.s5 + "5";
a.s6 = a.s6 + "6";
a.s7 = a.s7 + "7";
}
public static void test8() {
a.s1 = a.s1 + "1";
a.s2 = a.s2 + "2";
a.s3 = a.s3 + "3";
a.s4 = a.s4 + "4";
a.s5 = a.s5 + "5";
a.s6 = a.s6 + "6";
a.s7 = a.s7 + "7";
a.s8 = a.s8 + "8";
}
public static void test9() {
a.s1 = a.s1 + "1";
a.s2 = a.s2 + "2";
a.s3 = a.s3 + "3";
a.s4 = a.s4 + "4";
a.s5 = a.s5 + "5";
a.s6 = a.s6 + "6";
a.s7 = a.s7 + "7";
a.s8 = a.s8 + "8";
a.s9 = a.s9 + "9";
}
}
private static void compileMethods() throws Exception {
for (var m : B.class.getDeclaredMethods()) {
if (!m.getName().startsWith("test")) {
continue;
}
m.invoke(null);
WB.markMethodProfiled(m);
WB.enqueueMethodForCompilation(m, compLevel);
while (WB.isMethodQueuedForCompilation(m)) {
Thread.sleep(100);
}
if (WB.getMethodCompilationLevel(m) != compLevel) {
throw new IllegalStateException("Method " + m + " is not compiled at the compilation level: " + compLevel + ". Got: " + WB.getMethodCompilationLevel(m));
}
}
}
public static void youngGC() throws Exception {
a = null;
WB.youngGC();
}
public static void fullGC() throws Exception {
// Thread synchronization
final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
final AtomicBoolean running = new AtomicBoolean(true);
// Thread 1: GC thread that runs for 1 second with 100ms sleep intervals
Thread gcThread = new Thread(() -> {
final long startTime = System.currentTimeMillis();
final long duration = 1000;
try {
while (System.currentTimeMillis() - startTime < duration) {
writeLock.lock();
try {
a = new A();
WB.fullGC();
} finally {
writeLock.unlock();
}
Thread.sleep(100);
}
} catch (InterruptedException e) {
// Thread interrupted, exit
}
running.set(false);
});
// Threads 2-11: Test threads that execute test0() through test9()
Thread[] testThreads = new Thread[10];
for (int i = 0; i < 10; i++) {
final int testIdx = i;
testThreads[i] = new Thread(() -> {
try {
var method = B.class.getDeclaredMethod("test" + testIdx);
while (running.get()) {
readLock.lock();
try {
method.invoke(null);
} finally {
readLock.unlock();
}
}
} catch (Exception e) {
e.printStackTrace();
System.exit(10);
}
});
}
// Start all threads
gcThread.start();
for (Thread t : testThreads) {
t.start();
}
// Wait for all threads to complete
gcThread.join();
for (Thread t : testThreads) {
t.join();
}
}
public static void main(String[] args) throws Exception {
if (!Boolean.TRUE.equals(WB.getBooleanVMFlag("UseSingleICacheInvalidation"))) {
System.out.println("Skip. Test requires UseSingleICacheInvalidation enabled.");
return;
}
compLevel = (args[1].equals("C1")) ? 1 : 4;
compileMethods();
TestDeferredICacheInvalidation.class.getMethod(args[0]).invoke(null);
}
}

View File

@ -1,206 +0,0 @@
/*
* 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.
*
*/
package org.openjdk.bench.vm.gc;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.CompilerControl;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.Measurement;
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 org.openjdk.bench.util.InMemoryJavaCompiler;
import jdk.test.whitebox.WhiteBox;
import jdk.test.whitebox.code.NMethod;
/*
* Nmethods have OOPs and GC barriers emmedded into their code.
* GCs patch them which causes invalidation of nmethods' code.
*
* This benchmark can be used to estimate the cost of patching
* OOPs and GC barriers.
*
* We create 5000 nmethods which access fields of a class.
* We measure the time of different GC cycles to see
* the impact of patching nmethods.
*
* The benchmark parameters are method count and accessed field count.
*/
@BenchmarkMode(Mode.SingleShotTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
@Fork(value = 1, jvmArgsAppend = {
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+UnlockExperimentalVMOptions",
"-XX:+WhiteBoxAPI",
"-Xbootclasspath/a:lib-test/wb.jar",
"-XX:-UseCodeCacheFlushing"
})
@Warmup(iterations = 5)
@Measurement(iterations = 5)
public class GCPatchingNmethodCost {
private static final int COMP_LEVEL = 1;
private static final String FIELD_USER = "FieldUser";
public static Fields fields;
private static TestMethod[] methods = {};
private static byte[] BYTE_CODE;
private static WhiteBox WB;
@Param({"5000"})
public int methodCount;
@Param({"0", "2", "4", "8"})
public int accessedFieldCount;
public static class Fields {
public String f1;
public String f2;
public String f3;
public String f4;
public String f5;
public String f6;
public String f7;
public String f8;
public String f9;
}
private static final class TestMethod {
private final Method method;
public TestMethod(Method method) throws Exception {
this.method = method;
WB.testSetDontInlineMethod(method, true);
}
public void profile() throws Exception {
method.invoke(null);
WB.markMethodProfiled(method);
}
public void invoke() throws Exception {
method.invoke(null);
}
public void compile() throws Exception {
WB.enqueueMethodForCompilation(method, COMP_LEVEL);
while (WB.isMethodQueuedForCompilation(method)) {
Thread.onSpinWait();
}
if (WB.getMethodCompilationLevel(method) != COMP_LEVEL) {
throw new IllegalStateException("Method " + method + " is not compiled at the compilation level: " + COMP_LEVEL + ". Got: " + WB.getMethodCompilationLevel(method));
}
}
public NMethod getNMethod() {
return NMethod.get(method, false);
}
}
private static ClassLoader createClassLoader() {
return new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (!name.equals(FIELD_USER)) {
return super.loadClass(name);
}
return defineClass(name, BYTE_CODE, 0, BYTE_CODE.length);
}
};
}
private static void createTestMethods(int accessedFieldCount, int count) throws Exception {
String javaCode = "public class " + FIELD_USER + " {";
String field = GCPatchingNmethodCost.class.getName() + ".fields.f";
javaCode += "public static void accessFields() {";
for (int i = 1; i <= accessedFieldCount; i++) {
javaCode += field + i + "= " + field + i + " + " + i + ";";
}
javaCode += "}}";
BYTE_CODE = InMemoryJavaCompiler.compile(FIELD_USER, javaCode);
fields = new Fields();
methods = new TestMethod[count];
for (int i = 0; i < count; i++) {
var cl = createClassLoader().loadClass(FIELD_USER);
Method method = cl.getMethod("accessFields");
methods[i] = new TestMethod(method);
methods[i].profile();
methods[i].compile();
}
}
private static void initWhiteBox() {
WB = WhiteBox.getWhiteBox();
}
@Setup(Level.Trial)
public void setupCodeCache() throws Exception {
initWhiteBox();
createTestMethods(accessedFieldCount, methodCount);
System.gc();
}
@Setup(Level.Iteration)
public void setupIteration() {
fields = new Fields();
}
@Benchmark
public void youngGC() throws Exception {
fields = null;
WB.youngGC();
}
@Benchmark
public void fullGC() throws Exception {
fields = null;
WB.fullGC();
}
@Benchmark
public void systemGC() throws Exception {
fields = null;
System.gc();
}
}