mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-10 08:01:54 +00:00
Merge branch 'openjdk:master' into cas-alloc-1
This commit is contained in:
commit
341e2f86b9
@ -578,6 +578,11 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_HELPER],
|
||||
TOOLCHAIN_CFLAGS_JDK_CONLY="-fno-strict-aliasing" # technically NOT for CXX
|
||||
fi
|
||||
|
||||
if test "x$ENABLE_LINKTIME_GC" = xtrue; then
|
||||
TOOLCHAIN_CFLAGS_JDK="$TOOLCHAIN_CFLAGS_JDK -ffunction-sections -fdata-sections"
|
||||
TOOLCHAIN_CFLAGS_JVM="$TOOLCHAIN_CFLAGS_JVM -ffunction-sections -fdata-sections"
|
||||
fi
|
||||
|
||||
if test "x$OPENJDK_TARGET_OS" = xaix; then
|
||||
TOOLCHAIN_CFLAGS_JVM="$TOOLCHAIN_CFLAGS_JVM -ffunction-sections -ftls-model -fno-math-errno"
|
||||
TOOLCHAIN_CFLAGS_JDK="-ffunction-sections -fsigned-char"
|
||||
|
||||
@ -80,6 +80,10 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER],
|
||||
if test "x$CXX_IS_USER_SUPPLIED" = xfalse && test "x$CC_IS_USER_SUPPLIED" = xfalse; then
|
||||
UTIL_REQUIRE_TOOLCHAIN_PROGS(LLD, lld)
|
||||
fi
|
||||
|
||||
if test "x$ENABLE_LINKTIME_GC" = xtrue; then
|
||||
BASIC_LDFLAGS_JDK_ONLY="$BASIC_LDFLAGS_JDK_ONLY -Wl,--gc-sections"
|
||||
fi
|
||||
fi
|
||||
if test "x$OPENJDK_TARGET_OS" = xaix; then
|
||||
BASIC_LDFLAGS="-Wl,-b64 -Wl,-brtl -Wl,-bnorwexec -Wl,-blibpath:/usr/lib:lib -Wl,-bnoexpall \
|
||||
|
||||
@ -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.
|
||||
// Copyright (c) 2014, 2024, Red Hat, Inc. All rights reserved.
|
||||
// Copyright 2025 Arm Limited and/or its affiliates.
|
||||
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
@ -2467,11 +2467,8 @@ bool Matcher::is_generic_vector(MachOper* opnd) {
|
||||
return opnd->opcode() == VREG;
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
// Return whether or not this register is ever used as an argument.
|
||||
// This function is used on startup to build the trampoline stubs in
|
||||
// generateOptoStub. Registers not mentioned will be killed by the VM
|
||||
// call in the trampoline, and arguments in those registers not be
|
||||
// available to the callee.
|
||||
bool Matcher::can_be_java_arg(int reg)
|
||||
{
|
||||
return
|
||||
@ -2492,11 +2489,7 @@ bool Matcher::can_be_java_arg(int reg)
|
||||
reg == V6_num || reg == V6_H_num ||
|
||||
reg == V7_num || reg == V7_H_num;
|
||||
}
|
||||
|
||||
bool Matcher::is_spillable_arg(int reg)
|
||||
{
|
||||
return can_be_java_arg(reg);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint Matcher::int_pressure_limit()
|
||||
{
|
||||
@ -3814,11 +3807,6 @@ frame %{
|
||||
// Compiled code's Frame Pointer
|
||||
frame_pointer(R31);
|
||||
|
||||
// Interpreter stores its frame pointer in a register which is
|
||||
// stored to the stack by I2CAdaptors.
|
||||
// I2CAdaptors convert from interpreted java to compiled java.
|
||||
interpreter_frame_pointer(R29);
|
||||
|
||||
// Stack alignment requirement
|
||||
stack_alignment(StackAlignmentInBytes); // Alignment size in bytes (128-bit -> 16 bytes)
|
||||
|
||||
|
||||
@ -3814,8 +3814,8 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void sve_cpy(FloatRegister Zd, SIMD_RegVariant T, PRegister Pg, int imm8,
|
||||
bool isMerge, bool isFloat) {
|
||||
void _sve_cpy(FloatRegister Zd, SIMD_RegVariant T, PRegister Pg, int imm8,
|
||||
bool isMerge, bool isFloat) {
|
||||
starti;
|
||||
assert(T != Q, "invalid size");
|
||||
int sh = 0;
|
||||
@ -3839,11 +3839,11 @@ private:
|
||||
public:
|
||||
// SVE copy signed integer immediate to vector elements (predicated)
|
||||
void sve_cpy(FloatRegister Zd, SIMD_RegVariant T, PRegister Pg, int imm8, bool isMerge) {
|
||||
sve_cpy(Zd, T, Pg, imm8, isMerge, /*isFloat*/false);
|
||||
_sve_cpy(Zd, T, Pg, imm8, isMerge, /*isFloat*/false);
|
||||
}
|
||||
// SVE copy floating-point immediate to vector elements (predicated)
|
||||
void sve_cpy(FloatRegister Zd, SIMD_RegVariant T, PRegister Pg, double d) {
|
||||
sve_cpy(Zd, T, Pg, checked_cast<uint8_t>(pack(d)), /*isMerge*/true, /*isFloat*/true);
|
||||
_sve_cpy(Zd, T, Pg, checked_cast<uint8_t>(pack(d)), /*isMerge*/true, /*isFloat*/true);
|
||||
}
|
||||
|
||||
// SVE conditionally select elements from two vectors
|
||||
|
||||
@ -42,7 +42,6 @@ define_pd_global(bool, TieredCompilation, false);
|
||||
define_pd_global(intx, CompileThreshold, 1500 );
|
||||
|
||||
define_pd_global(intx, OnStackReplacePercentage, 933 );
|
||||
define_pd_global(intx, NewSizeThreadIncrease, 4*K );
|
||||
define_pd_global(size_t, InitialCodeCacheSize, 160*K);
|
||||
define_pd_global(size_t, ReservedCodeCacheSize, 32*M );
|
||||
define_pd_global(size_t, NonProfiledCodeHeapSize, 13*M );
|
||||
|
||||
@ -2875,3 +2875,24 @@ void C2_MacroAssembler::vector_expand_sve(FloatRegister dst, FloatRegister src,
|
||||
// dst = 00 87 00 65 00 43 00 21
|
||||
sve_tbl(dst, size, src, dst);
|
||||
}
|
||||
|
||||
// Optimized SVE cpy (imm, zeroing) instruction.
|
||||
//
|
||||
// `movi; cpy(imm, merging)` and `cpy(imm, zeroing)` have the same
|
||||
// functionality, but test results show that `movi; cpy(imm, merging)` has
|
||||
// higher throughput on some microarchitectures. This would depend on
|
||||
// microarchitecture and so may vary between implementations.
|
||||
void C2_MacroAssembler::sve_cpy(FloatRegister dst, SIMD_RegVariant T,
|
||||
PRegister pg, int imm8, bool isMerge) {
|
||||
if (VM_Version::prefer_sve_merging_mode_cpy() && !isMerge) {
|
||||
// Generates a NEON instruction `movi V<dst>.2d, #0`.
|
||||
// On AArch64, Z and V registers alias in the low 128 bits, so V<dst> is
|
||||
// the low 128 bits of Z<dst>. A write to V<dst> also clears all bits of
|
||||
// Z<dst> above 128, so this `movi` instruction effectively zeroes the
|
||||
// entire Z<dst> register. According to the Arm Software Optimization
|
||||
// Guide, `movi` is zero latency.
|
||||
movi(dst, T2D, 0);
|
||||
isMerge = true;
|
||||
}
|
||||
Assembler::sve_cpy(dst, T, pg, imm8, isMerge);
|
||||
}
|
||||
|
||||
@ -75,6 +75,8 @@
|
||||
unsigned vector_length_in_bytes);
|
||||
|
||||
public:
|
||||
using Assembler::sve_cpy;
|
||||
|
||||
// jdk.internal.util.ArraysSupport.vectorizedHashCode
|
||||
address arrays_hashcode(Register ary, Register cnt, Register result, FloatRegister vdata0,
|
||||
FloatRegister vdata1, FloatRegister vdata2, FloatRegister vdata3,
|
||||
@ -244,4 +246,7 @@
|
||||
void vector_expand_sve(FloatRegister dst, FloatRegister src, PRegister pg,
|
||||
FloatRegister tmp1, FloatRegister tmp2, BasicType bt,
|
||||
int vector_length_in_bytes);
|
||||
|
||||
void sve_cpy(FloatRegister dst, SIMD_RegVariant T, PRegister pg, int imm8,
|
||||
bool isMerge);
|
||||
#endif // CPU_AARCH64_C2_MACROASSEMBLER_AARCH64_HPP
|
||||
|
||||
@ -47,7 +47,6 @@ define_pd_global(intx, ConditionalMoveLimit, 3);
|
||||
define_pd_global(intx, FreqInlineSize, 325);
|
||||
define_pd_global(intx, MinJumpTableSize, 10);
|
||||
define_pd_global(intx, InteriorEntryAlignment, 16);
|
||||
define_pd_global(intx, NewSizeThreadIncrease, ScaleForWordSize(4*K));
|
||||
define_pd_global(intx, LoopUnrollLimit, 60);
|
||||
define_pd_global(intx, LoopPercentProfileLimit, 10);
|
||||
// InitialCodeCacheSize derived from specjbb2000 run.
|
||||
|
||||
@ -95,7 +95,7 @@ define_pd_global(intx, InlineSmallCode, 1000);
|
||||
"Use simplest and shortest implementation for array equals") \
|
||||
product(bool, UseSIMDForBigIntegerShiftIntrinsics, true, \
|
||||
"Use SIMD instructions for left/right shift of BigInteger") \
|
||||
product(bool, UseSIMDForSHA3Intrinsic, true, \
|
||||
product(bool, UseSIMDForSHA3Intrinsic, false, \
|
||||
"Use SIMD SHA3 instructions for SHA3 intrinsic") \
|
||||
product(bool, AvoidUnalignedAccesses, false, \
|
||||
"Avoid generating unaligned memory accesses") \
|
||||
|
||||
@ -11876,16 +11876,13 @@ class StubGenerator: public StubCodeGenerator {
|
||||
StubRoutines::_sha512_implCompress = generate_sha512_implCompress(StubId::stubgen_sha512_implCompress_id);
|
||||
StubRoutines::_sha512_implCompressMB = generate_sha512_implCompress(StubId::stubgen_sha512_implCompressMB_id);
|
||||
}
|
||||
if (UseSHA3Intrinsics) {
|
||||
|
||||
if (UseSHA3Intrinsics && UseSIMDForSHA3Intrinsic) {
|
||||
StubRoutines::_double_keccak = generate_double_keccak();
|
||||
if (UseSIMDForSHA3Intrinsic) {
|
||||
StubRoutines::_sha3_implCompress = generate_sha3_implCompress(StubId::stubgen_sha3_implCompress_id);
|
||||
StubRoutines::_sha3_implCompressMB = generate_sha3_implCompress(StubId::stubgen_sha3_implCompressMB_id);
|
||||
} else {
|
||||
StubRoutines::_sha3_implCompress = generate_sha3_implCompress_gpr(StubId::stubgen_sha3_implCompress_id);
|
||||
StubRoutines::_sha3_implCompressMB = generate_sha3_implCompress_gpr(StubId::stubgen_sha3_implCompressMB_id);
|
||||
}
|
||||
StubRoutines::_sha3_implCompress = generate_sha3_implCompress(StubId::stubgen_sha3_implCompress_id);
|
||||
StubRoutines::_sha3_implCompressMB = generate_sha3_implCompress(StubId::stubgen_sha3_implCompressMB_id);
|
||||
} else if (UseSHA3Intrinsics) {
|
||||
StubRoutines::_sha3_implCompress = generate_sha3_implCompress_gpr(StubId::stubgen_sha3_implCompress_id);
|
||||
StubRoutines::_sha3_implCompressMB = generate_sha3_implCompress_gpr(StubId::stubgen_sha3_implCompressMB_id);
|
||||
}
|
||||
|
||||
if (UsePoly1305Intrinsics) {
|
||||
|
||||
@ -365,16 +365,28 @@ void VM_Version::initialize() {
|
||||
FLAG_SET_DEFAULT(UseSHA256Intrinsics, false);
|
||||
}
|
||||
|
||||
if (UseSHA && VM_Version::supports_sha3()) {
|
||||
// Auto-enable UseSHA3Intrinsics on hardware with performance benefit.
|
||||
// Note that the evaluation of UseSHA3Intrinsics shows better performance
|
||||
if (UseSHA) {
|
||||
// No need to check VM_Version::supports_sha3(), since a fallback GPR intrinsic implementation is provided.
|
||||
if (FLAG_IS_DEFAULT(UseSHA3Intrinsics)) {
|
||||
FLAG_SET_DEFAULT(UseSHA3Intrinsics, true);
|
||||
}
|
||||
} else if (UseSHA3Intrinsics) {
|
||||
// Matches the documented and tested behavior: the -UseSHA option disables all SHA intrinsics.
|
||||
warning("UseSHA3Intrinsics requires that UseSHA is enabled.");
|
||||
FLAG_SET_DEFAULT(UseSHA3Intrinsics, false);
|
||||
}
|
||||
|
||||
if (UseSHA3Intrinsics && VM_Version::supports_sha3()) {
|
||||
// Auto-enable UseSIMDForSHA3Intrinsic on hardware with performance benefit.
|
||||
// Note that the evaluation of SHA3 extension Intrinsics shows better performance
|
||||
// on Apple and Qualcomm silicon but worse performance on Neoverse V1 and N2.
|
||||
if (_cpu == CPU_APPLE || _cpu == CPU_QUALCOMM) { // Apple or Qualcomm silicon
|
||||
if (FLAG_IS_DEFAULT(UseSHA3Intrinsics)) {
|
||||
FLAG_SET_DEFAULT(UseSHA3Intrinsics, true);
|
||||
if (FLAG_IS_DEFAULT(UseSIMDForSHA3Intrinsic)) {
|
||||
FLAG_SET_DEFAULT(UseSIMDForSHA3Intrinsic, true);
|
||||
}
|
||||
}
|
||||
} else if (UseSHA3Intrinsics && UseSIMDForSHA3Intrinsic) {
|
||||
}
|
||||
if (UseSHA3Intrinsics && UseSIMDForSHA3Intrinsic && !VM_Version::supports_sha3()) {
|
||||
warning("Intrinsics for SHA3-224, SHA3-256, SHA3-384 and SHA3-512 crypto hash functions not available on this CPU.");
|
||||
FLAG_SET_DEFAULT(UseSHA3Intrinsics, false);
|
||||
}
|
||||
|
||||
@ -55,6 +55,9 @@ protected:
|
||||
static int _max_supported_sve_vector_length;
|
||||
static bool _rop_protection;
|
||||
static uintptr_t _pac_mask;
|
||||
// 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 SpinWait _spin_wait;
|
||||
|
||||
@ -242,6 +245,8 @@ public:
|
||||
|
||||
static bool use_rop_protection() { return _rop_protection; }
|
||||
|
||||
static bool prefer_sve_merging_mode_cpy() { return _prefer_sve_merging_mode_cpy; }
|
||||
|
||||
// For common 64/128-bit unpredicated vector operations, we may prefer
|
||||
// emitting NEON instructions rather than the corresponding SVE instructions.
|
||||
static bool use_neon_for_vector(int vector_length_in_bytes) {
|
||||
|
||||
@ -1088,10 +1088,8 @@ bool Matcher::pd_clone_address_expressions(AddPNode* m, Matcher::MStack& mstack,
|
||||
return clone_base_plus_offset_address(m, mstack, address_visited);
|
||||
}
|
||||
|
||||
// Return whether or not this register is ever used as an argument. This
|
||||
// function is used on startup to build the trampoline stubs in generateOptoStub.
|
||||
// Registers not mentioned will be killed by the VM call in the trampoline, and
|
||||
// arguments in those registers not be available to the callee.
|
||||
#ifdef ASSERT
|
||||
// Return whether or not this register is ever used as an argument.
|
||||
bool Matcher::can_be_java_arg( int reg ) {
|
||||
if (reg == R_R0_num ||
|
||||
reg == R_R1_num ||
|
||||
@ -1102,10 +1100,7 @@ bool Matcher::can_be_java_arg( int reg ) {
|
||||
reg <= R_S13_num) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Matcher::is_spillable_arg( int reg ) {
|
||||
return can_be_java_arg(reg);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint Matcher::int_pressure_limit()
|
||||
{
|
||||
|
||||
@ -43,7 +43,6 @@ define_pd_global(bool, TieredCompilation, false);
|
||||
define_pd_global(intx, CompileThreshold, 1500 );
|
||||
|
||||
define_pd_global(intx, OnStackReplacePercentage, 933 );
|
||||
define_pd_global(size_t, NewSizeThreadIncrease, 4*K );
|
||||
define_pd_global(size_t, InitialCodeCacheSize, 160*K);
|
||||
define_pd_global(size_t, ReservedCodeCacheSize, 32*M );
|
||||
define_pd_global(size_t, NonProfiledCodeHeapSize, 13*M );
|
||||
|
||||
@ -47,7 +47,6 @@ define_pd_global(intx, ConditionalMoveLimit, 4);
|
||||
// C2 gets to use all the float/double registers
|
||||
define_pd_global(intx, FreqInlineSize, 175);
|
||||
define_pd_global(intx, InteriorEntryAlignment, 16); // = CodeEntryAlignment
|
||||
define_pd_global(size_t, NewSizeThreadIncrease, ScaleForWordSize(4*K));
|
||||
// The default setting 16/16 seems to work best.
|
||||
// (For _228_jack 16/16 is 2% better than 4/4, 16/4, 32/32, 32/16, or 16/32.)
|
||||
//define_pd_global(intx, OptoLoopAlignment, 16); // = 4*wordSize
|
||||
|
||||
@ -52,7 +52,6 @@ define_pd_global(size_t, CodeCacheExpansionSize, 32*K);
|
||||
define_pd_global(size_t, CodeCacheMinBlockLength, 1);
|
||||
define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K);
|
||||
define_pd_global(bool, NeverActAsServerClassMachine, true);
|
||||
define_pd_global(size_t, NewSizeThreadIncrease, 16*K);
|
||||
define_pd_global(size_t, InitialCodeCacheSize, 160*K);
|
||||
#endif // !COMPILER2
|
||||
|
||||
|
||||
@ -47,7 +47,6 @@ define_pd_global(intx, ConditionalMoveLimit, 3);
|
||||
define_pd_global(intx, FreqInlineSize, 325);
|
||||
define_pd_global(intx, MinJumpTableSize, 10);
|
||||
define_pd_global(intx, InteriorEntryAlignment, 16);
|
||||
define_pd_global(size_t, NewSizeThreadIncrease, ScaleForWordSize(4*K));
|
||||
define_pd_global(intx, RegisterCostAreaRatio, 16000);
|
||||
define_pd_global(intx, LoopUnrollLimit, 60);
|
||||
define_pd_global(intx, LoopPercentProfileLimit, 10);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2018 SAP SE. All rights reserved.
|
||||
* Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2026 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -24,6 +24,7 @@
|
||||
*/
|
||||
|
||||
#include "runtime/icache.hpp"
|
||||
#include "runtime/vm_version.hpp"
|
||||
|
||||
// Use inline assembler to implement icache flush.
|
||||
int ICache::ppc64_flush_icache(address start, int lines, int magic) {
|
||||
@ -67,6 +68,9 @@ int ICache::ppc64_flush_icache(address start, int lines, int magic) {
|
||||
|
||||
void ICacheStubGenerator::generate_icache_flush(ICache::flush_icache_stub_t* flush_icache_stub) {
|
||||
|
||||
guarantee(VM_Version::get_icache_line_size() >= ICache::line_size,
|
||||
"processors with smaller cache line size are no longer supported");
|
||||
|
||||
*flush_icache_stub = (ICache::flush_icache_stub_t)ICache::ppc64_flush_icache;
|
||||
|
||||
// First call to flush itself.
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2013 SAP SE. All rights reserved.
|
||||
* Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2026 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -35,9 +35,8 @@ class ICache : public AbstractICache {
|
||||
|
||||
public:
|
||||
enum {
|
||||
// Actually, cache line size is 64, but keeping it as it is to be
|
||||
// on the safe side on ALL PPC64 implementations.
|
||||
log2_line_size = 5,
|
||||
// Cache line size is 128 on all supported PPC64 implementations.
|
||||
log2_line_size = 7,
|
||||
line_size = 1 << log2_line_size
|
||||
};
|
||||
|
||||
|
||||
@ -2412,10 +2412,8 @@ bool Matcher::is_generic_vector(MachOper* opnd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return whether or not this register is ever used as an argument. This
|
||||
// function is used on startup to build the trampoline stubs in generateOptoStub.
|
||||
// Registers not mentioned will be killed by the VM call in the trampoline, and
|
||||
// arguments in those registers not be available to the callee.
|
||||
#ifdef ASSERT
|
||||
// Return whether or not this register is ever used as an argument.
|
||||
bool Matcher::can_be_java_arg(int reg) {
|
||||
// We must include the virtual halves in order to get STDs and LDs
|
||||
// instead of STWs and LWs in the trampoline stubs.
|
||||
@ -2447,10 +2445,7 @@ bool Matcher::can_be_java_arg(int reg) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Matcher::is_spillable_arg(int reg) {
|
||||
return can_be_java_arg(reg);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint Matcher::int_pressure_limit()
|
||||
{
|
||||
@ -3715,13 +3710,6 @@ frame %{
|
||||
// Compiled code's Frame Pointer.
|
||||
frame_pointer(R1); // R1_SP
|
||||
|
||||
// Interpreter stores its frame pointer in a register which is
|
||||
// stored to the stack by I2CAdaptors. I2CAdaptors convert from
|
||||
// interpreted java to compiled java.
|
||||
//
|
||||
// R14_state holds pointer to caller's cInterpreter.
|
||||
interpreter_frame_pointer(R14); // R14_state
|
||||
|
||||
stack_alignment(frame::alignment_in_bytes);
|
||||
|
||||
// Number of outgoing stack slots killed above the
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2025 SAP SE. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2026 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -475,19 +475,12 @@ void VM_Version::print_features() {
|
||||
|
||||
void VM_Version::determine_features() {
|
||||
#if defined(ABI_ELFv2)
|
||||
// 1 InstWord per call for the blr instruction.
|
||||
const int code_size = (num_features+1+2*1)*BytesPerInstWord;
|
||||
const int code_size = (num_features + 1 /*blr*/) * BytesPerInstWord;
|
||||
#else
|
||||
// 7 InstWords for each call (function descriptor + blr instruction).
|
||||
const int code_size = (num_features+1+2*7)*BytesPerInstWord;
|
||||
const int code_size = (num_features + 1 /*blr*/ + 6 /* fd */) * BytesPerInstWord;
|
||||
#endif
|
||||
int features = 0;
|
||||
|
||||
// create test area
|
||||
enum { BUFFER_SIZE = 2*4*K }; // Needs to be >=2* max cache line size (cache line size can't exceed min page size).
|
||||
char test_area[BUFFER_SIZE];
|
||||
char *mid_of_test_area = &test_area[BUFFER_SIZE>>1];
|
||||
|
||||
// Allocate space for the code.
|
||||
ResourceMark rm;
|
||||
CodeBuffer cb("detect_cpu_features", code_size, 0);
|
||||
@ -497,20 +490,13 @@ void VM_Version::determine_features() {
|
||||
_features = VM_Version::all_features_m;
|
||||
|
||||
// Emit code.
|
||||
void (*test)(address addr, uint64_t offset)=(void(*)(address addr, uint64_t offset))(void *)a->function_entry();
|
||||
void (*test)() = (void(*)())(void *)a->function_entry();
|
||||
uint32_t *code = (uint32_t *)a->pc();
|
||||
// Keep R3_ARG1 unmodified, it contains &field (see below).
|
||||
// Keep R4_ARG2 unmodified, it contains offset = 0 (see below).
|
||||
a->mfdscr(R0);
|
||||
a->darn(R7);
|
||||
a->brw(R5, R6);
|
||||
a->blr();
|
||||
|
||||
// Emit function to set one cache line to zero. Emit function descriptor and get pointer to it.
|
||||
void (*zero_cacheline_func_ptr)(char*) = (void(*)(char*))(void *)a->function_entry();
|
||||
a->dcbz(R3_ARG1); // R3_ARG1 = addr
|
||||
a->blr();
|
||||
|
||||
uint32_t *code_end = (uint32_t *)a->pc();
|
||||
a->flush();
|
||||
_features = VM_Version::unknown_m;
|
||||
@ -522,18 +508,9 @@ void VM_Version::determine_features() {
|
||||
Disassembler::decode((u_char*)code, (u_char*)code_end, tty);
|
||||
}
|
||||
|
||||
// Measure cache line size.
|
||||
memset(test_area, 0xFF, BUFFER_SIZE); // Fill test area with 0xFF.
|
||||
(*zero_cacheline_func_ptr)(mid_of_test_area); // Call function which executes dcbz to the middle.
|
||||
int count = 0; // count zeroed bytes
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) if (test_area[i] == 0) count++;
|
||||
guarantee(is_power_of_2(count), "cache line size needs to be a power of 2");
|
||||
_L1_data_cache_line_size = count;
|
||||
|
||||
// Execute code. Illegal instructions will be replaced by 0 in the signal handler.
|
||||
VM_Version::_is_determine_features_test_running = true;
|
||||
// We must align the first argument to 16 bytes because of the lqarx check.
|
||||
(*test)(align_up((address)mid_of_test_area, 16), 0);
|
||||
(*test)();
|
||||
VM_Version::_is_determine_features_test_running = false;
|
||||
|
||||
// determine which instructions are legal.
|
||||
@ -550,6 +527,10 @@ void VM_Version::determine_features() {
|
||||
}
|
||||
|
||||
_features = features;
|
||||
|
||||
_L1_data_cache_line_size = VM_Version::get_dcache_line_size();
|
||||
assert(_L1_data_cache_line_size >= DEFAULT_CACHE_LINE_SIZE,
|
||||
"processors with smaller cache line size are no longer supported");
|
||||
}
|
||||
|
||||
// Power 8: Configure Data Stream Control Register.
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2025 SAP SE. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2026 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -81,6 +81,9 @@ public:
|
||||
static uint64_t _dscr_val;
|
||||
|
||||
static void initialize_cpu_information(void);
|
||||
|
||||
static int get_dcache_line_size();
|
||||
static int get_icache_line_size();
|
||||
};
|
||||
|
||||
#endif // CPU_PPC_VM_VERSION_PPC_HPP
|
||||
|
||||
@ -42,7 +42,6 @@ define_pd_global(bool, TieredCompilation, false);
|
||||
define_pd_global(intx, CompileThreshold, 1500 );
|
||||
|
||||
define_pd_global(intx, OnStackReplacePercentage, 933 );
|
||||
define_pd_global(intx, NewSizeThreadIncrease, 4*K );
|
||||
define_pd_global(size_t, InitialCodeCacheSize, 160*K);
|
||||
define_pd_global(size_t, ReservedCodeCacheSize, 32*M );
|
||||
define_pd_global(size_t, NonProfiledCodeHeapSize, 13*M );
|
||||
|
||||
@ -47,7 +47,6 @@ define_pd_global(intx, ConditionalMoveLimit, 3);
|
||||
define_pd_global(intx, FreqInlineSize, 325);
|
||||
define_pd_global(intx, MinJumpTableSize, 10);
|
||||
define_pd_global(intx, InteriorEntryAlignment, 16);
|
||||
define_pd_global(intx, NewSizeThreadIncrease, ScaleForWordSize(4*K));
|
||||
define_pd_global(intx, LoopUnrollLimit, 60);
|
||||
define_pd_global(intx, LoopPercentProfileLimit, 10);
|
||||
// InitialCodeCacheSize derived from specjbb2000 run.
|
||||
|
||||
@ -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.
|
||||
// Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved.
|
||||
// Copyright (c) 2020, 2024, Huawei Technologies Co., Ltd. All rights reserved.
|
||||
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
@ -2060,11 +2060,8 @@ bool Matcher::is_generic_vector(MachOper* opnd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
// Return whether or not this register is ever used as an argument.
|
||||
// This function is used on startup to build the trampoline stubs in
|
||||
// generateOptoStub. Registers not mentioned will be killed by the VM
|
||||
// call in the trampoline, and arguments in those registers not be
|
||||
// available to the callee.
|
||||
bool Matcher::can_be_java_arg(int reg)
|
||||
{
|
||||
return
|
||||
@ -2085,11 +2082,7 @@ bool Matcher::can_be_java_arg(int reg)
|
||||
reg == F16_num || reg == F16_H_num ||
|
||||
reg == F17_num || reg == F17_H_num;
|
||||
}
|
||||
|
||||
bool Matcher::is_spillable_arg(int reg)
|
||||
{
|
||||
return can_be_java_arg(reg);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint Matcher::int_pressure_limit()
|
||||
{
|
||||
@ -2559,11 +2552,6 @@ frame %{
|
||||
// Compiled code's Frame Pointer
|
||||
frame_pointer(R2);
|
||||
|
||||
// Interpreter stores its frame pointer in a register which is
|
||||
// stored to the stack by I2CAdaptors.
|
||||
// I2CAdaptors convert from interpreted java to compiled java.
|
||||
interpreter_frame_pointer(R8);
|
||||
|
||||
// Stack alignment requirement
|
||||
stack_alignment(StackAlignmentInBytes); // Alignment size in bytes (128-bit -> 16 bytes)
|
||||
|
||||
|
||||
@ -52,7 +52,6 @@ define_pd_global(size_t, CodeCacheExpansionSize, 32*K);
|
||||
define_pd_global(size_t, CodeCacheMinBlockLength, 1);
|
||||
define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K);
|
||||
define_pd_global(bool, NeverActAsServerClassMachine, true);
|
||||
define_pd_global(size_t, NewSizeThreadIncrease, 16*K);
|
||||
define_pd_global(size_t, InitialCodeCacheSize, 160*K);
|
||||
#endif // !COMPILER2
|
||||
|
||||
|
||||
@ -46,7 +46,6 @@ define_pd_global(intx, OnStackReplacePercentage, 140);
|
||||
define_pd_global(intx, ConditionalMoveLimit, 4);
|
||||
define_pd_global(intx, FreqInlineSize, 325);
|
||||
define_pd_global(intx, InteriorEntryAlignment, 4);
|
||||
define_pd_global(size_t, NewSizeThreadIncrease, ScaleForWordSize(4*K));
|
||||
define_pd_global(intx, RegisterCostAreaRatio, 12000);
|
||||
define_pd_global(intx, LoopUnrollLimit, 60);
|
||||
define_pd_global(intx, LoopPercentProfileLimit, 10);
|
||||
|
||||
@ -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.
|
||||
// Copyright (c) 2017, 2024 SAP SE. All rights reserved.
|
||||
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
//
|
||||
@ -1890,10 +1890,8 @@ const int z_num_iarg_registers = sizeof(z_iarg_reg) / sizeof(z_iarg_reg[0]);
|
||||
|
||||
const int z_num_farg_registers = sizeof(z_farg_reg) / sizeof(z_farg_reg[0]);
|
||||
|
||||
// Return whether or not this register is ever used as an argument. This
|
||||
// function is used on startup to build the trampoline stubs in generateOptoStub.
|
||||
// Registers not mentioned will be killed by the VM call in the trampoline, and
|
||||
// arguments in those registers not be available to the callee.
|
||||
#ifdef ASSERT
|
||||
// Return whether or not this register is ever used as an argument.
|
||||
bool Matcher::can_be_java_arg(int reg) {
|
||||
// We return true for all registers contained in z_iarg_reg[] and
|
||||
// z_farg_reg[] and their virtual halves.
|
||||
@ -1917,10 +1915,7 @@ bool Matcher::can_be_java_arg(int reg) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Matcher::is_spillable_arg(int reg) {
|
||||
return can_be_java_arg(reg);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint Matcher::int_pressure_limit()
|
||||
{
|
||||
@ -2606,13 +2601,6 @@ frame %{
|
||||
// z/Architecture stack pointer
|
||||
frame_pointer(Z_R15); // Z_SP
|
||||
|
||||
// Interpreter stores its frame pointer in a register which is
|
||||
// stored to the stack by I2CAdaptors. I2CAdaptors convert from
|
||||
// interpreted java to compiled java.
|
||||
//
|
||||
// Z_state holds pointer to caller's cInterpreter.
|
||||
interpreter_frame_pointer(Z_R7); // Z_state
|
||||
|
||||
// Use alignment_in_bytes instead of log_2_of_alignment_in_bits.
|
||||
stack_alignment(frame::alignment_in_bytes);
|
||||
|
||||
|
||||
@ -5442,6 +5442,13 @@ void Assembler::pmovsxwd(XMMRegister dst, XMMRegister src) {
|
||||
emit_int16(0x23, (0xC0 | encode));
|
||||
}
|
||||
|
||||
void Assembler::pmovzxwd(XMMRegister dst, XMMRegister src) {
|
||||
assert(VM_Version::supports_sse4_1(), "");
|
||||
InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
|
||||
int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
|
||||
emit_int16(0x33, (0xC0 | encode));
|
||||
}
|
||||
|
||||
void Assembler::vpmovzxbw(XMMRegister dst, Address src, int vector_len) {
|
||||
assert(VM_Version::supports_avx(), "");
|
||||
InstructionMark im(this);
|
||||
|
||||
@ -1965,6 +1965,7 @@ private:
|
||||
void pmovsxbq(XMMRegister dst, XMMRegister src);
|
||||
void pmovsxbw(XMMRegister dst, XMMRegister src);
|
||||
void pmovsxwd(XMMRegister dst, XMMRegister src);
|
||||
void pmovzxwd(XMMRegister dst, XMMRegister src);
|
||||
void vpmovsxbd(XMMRegister dst, XMMRegister src, int vector_len);
|
||||
void vpmovsxbq(XMMRegister dst, XMMRegister src, int vector_len);
|
||||
void vpmovsxbw(XMMRegister dst, XMMRegister src, int vector_len);
|
||||
|
||||
@ -41,7 +41,6 @@ define_pd_global(bool, TieredCompilation, false);
|
||||
define_pd_global(intx, CompileThreshold, 1500 );
|
||||
|
||||
define_pd_global(intx, OnStackReplacePercentage, 933 );
|
||||
define_pd_global(size_t, NewSizeThreadIncrease, 4*K );
|
||||
define_pd_global(size_t, InitialCodeCacheSize, 160*K);
|
||||
define_pd_global(size_t, ReservedCodeCacheSize, 32*M );
|
||||
define_pd_global(size_t, NonProfiledCodeHeapSize, 13*M );
|
||||
|
||||
@ -1729,6 +1729,24 @@ void C2_MacroAssembler::reduce_operation_128(BasicType typ, int opcode, XMMRegis
|
||||
default: assert(false, "wrong type");
|
||||
}
|
||||
break;
|
||||
case Op_UMinReductionV:
|
||||
switch (typ) {
|
||||
case T_BYTE: vpminub(dst, dst, src, Assembler::AVX_128bit); break;
|
||||
case T_SHORT: vpminuw(dst, dst, src, Assembler::AVX_128bit); break;
|
||||
case T_INT: vpminud(dst, dst, src, Assembler::AVX_128bit); break;
|
||||
case T_LONG: evpminuq(dst, k0, dst, src, true, Assembler::AVX_128bit); break;
|
||||
default: assert(false, "wrong type");
|
||||
}
|
||||
break;
|
||||
case Op_UMaxReductionV:
|
||||
switch (typ) {
|
||||
case T_BYTE: vpmaxub(dst, dst, src, Assembler::AVX_128bit); break;
|
||||
case T_SHORT: vpmaxuw(dst, dst, src, Assembler::AVX_128bit); break;
|
||||
case T_INT: vpmaxud(dst, dst, src, Assembler::AVX_128bit); break;
|
||||
case T_LONG: evpmaxuq(dst, k0, dst, src, true, Assembler::AVX_128bit); break;
|
||||
default: assert(false, "wrong type");
|
||||
}
|
||||
break;
|
||||
case Op_AddReductionVF: addss(dst, src); break;
|
||||
case Op_AddReductionVD: addsd(dst, src); break;
|
||||
case Op_AddReductionVI:
|
||||
@ -1792,6 +1810,24 @@ void C2_MacroAssembler::reduce_operation_256(BasicType typ, int opcode, XMMRegis
|
||||
default: assert(false, "wrong type");
|
||||
}
|
||||
break;
|
||||
case Op_UMinReductionV:
|
||||
switch (typ) {
|
||||
case T_BYTE: vpminub(dst, src1, src2, vector_len); break;
|
||||
case T_SHORT: vpminuw(dst, src1, src2, vector_len); break;
|
||||
case T_INT: vpminud(dst, src1, src2, vector_len); break;
|
||||
case T_LONG: evpminuq(dst, k0, src1, src2, true, vector_len); break;
|
||||
default: assert(false, "wrong type");
|
||||
}
|
||||
break;
|
||||
case Op_UMaxReductionV:
|
||||
switch (typ) {
|
||||
case T_BYTE: vpmaxub(dst, src1, src2, vector_len); break;
|
||||
case T_SHORT: vpmaxuw(dst, src1, src2, vector_len); break;
|
||||
case T_INT: vpmaxud(dst, src1, src2, vector_len); break;
|
||||
case T_LONG: evpmaxuq(dst, k0, src1, src2, true, vector_len); break;
|
||||
default: assert(false, "wrong type");
|
||||
}
|
||||
break;
|
||||
case Op_AddReductionVI:
|
||||
switch (typ) {
|
||||
case T_BYTE: vpaddb(dst, src1, src2, vector_len); break;
|
||||
@ -2058,7 +2094,11 @@ void C2_MacroAssembler::reduce8B(int opcode, Register dst, Register src1, XMMReg
|
||||
psrldq(vtmp2, 1);
|
||||
reduce_operation_128(T_BYTE, opcode, vtmp1, vtmp2);
|
||||
movdl(vtmp2, src1);
|
||||
pmovsxbd(vtmp1, vtmp1);
|
||||
if (opcode == Op_UMinReductionV || opcode == Op_UMaxReductionV) {
|
||||
pmovzxbd(vtmp1, vtmp1);
|
||||
} else {
|
||||
pmovsxbd(vtmp1, vtmp1);
|
||||
}
|
||||
reduce_operation_128(T_INT, opcode, vtmp1, vtmp2);
|
||||
pextrb(dst, vtmp1, 0x0);
|
||||
movsbl(dst, dst);
|
||||
@ -2135,7 +2175,11 @@ void C2_MacroAssembler::reduce4S(int opcode, Register dst, Register src1, XMMReg
|
||||
reduce_operation_128(T_SHORT, opcode, vtmp1, vtmp2);
|
||||
}
|
||||
movdl(vtmp2, src1);
|
||||
pmovsxwd(vtmp1, vtmp1);
|
||||
if (opcode == Op_UMinReductionV || opcode == Op_UMaxReductionV) {
|
||||
pmovzxwd(vtmp1, vtmp1);
|
||||
} else {
|
||||
pmovsxwd(vtmp1, vtmp1);
|
||||
}
|
||||
reduce_operation_128(T_INT, opcode, vtmp1, vtmp2);
|
||||
pextrw(dst, vtmp1, 0x0);
|
||||
movswl(dst, dst);
|
||||
|
||||
@ -46,7 +46,6 @@ define_pd_global(intx, FreqInlineSize, 325);
|
||||
define_pd_global(intx, MinJumpTableSize, 10);
|
||||
define_pd_global(intx, LoopPercentProfileLimit, 10);
|
||||
define_pd_global(intx, InteriorEntryAlignment, 16);
|
||||
define_pd_global(size_t, NewSizeThreadIncrease, ScaleForWordSize(4*K));
|
||||
define_pd_global(intx, LoopUnrollLimit, 60);
|
||||
// InitialCodeCacheSize derived from specjbb2000 run.
|
||||
define_pd_global(size_t, InitialCodeCacheSize, 2496*K); // Integral multiple of CodeCacheExpansionSize
|
||||
|
||||
@ -961,7 +961,7 @@ void MacroAssembler::call(AddressLiteral entry, Register rscratch) {
|
||||
void MacroAssembler::ic_call(address entry, jint method_index) {
|
||||
RelocationHolder rh = virtual_call_Relocation::spec(pc(), method_index);
|
||||
// Needs full 64-bit immediate for later patching.
|
||||
mov64(rax, (int64_t)Universe::non_oop_word());
|
||||
Assembler::mov64(rax, (int64_t)Universe::non_oop_word());
|
||||
call(AddressLiteral(entry, rh));
|
||||
}
|
||||
|
||||
@ -1961,6 +1961,20 @@ void MacroAssembler::movflt(XMMRegister dst, AddressLiteral src, Register rscrat
|
||||
}
|
||||
}
|
||||
|
||||
void MacroAssembler::mov64(Register dst, int64_t imm64) {
|
||||
if (is_uimm32(imm64)) {
|
||||
movl(dst, checked_cast<uint32_t>(imm64));
|
||||
} else if (is_simm32(imm64)) {
|
||||
movq(dst, checked_cast<int32_t>(imm64));
|
||||
} else {
|
||||
Assembler::mov64(dst, imm64);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroAssembler::mov64(Register dst, int64_t imm64, relocInfo::relocType rtype, int format) {
|
||||
Assembler::mov64(dst, imm64, rtype, format);
|
||||
}
|
||||
|
||||
void MacroAssembler::movptr(Register dst, Register src) {
|
||||
movq(dst, src);
|
||||
}
|
||||
@ -1971,13 +1985,7 @@ void MacroAssembler::movptr(Register dst, Address src) {
|
||||
|
||||
// src should NEVER be a real pointer. Use AddressLiteral for true pointers
|
||||
void MacroAssembler::movptr(Register dst, intptr_t src) {
|
||||
if (is_uimm32(src)) {
|
||||
movl(dst, checked_cast<uint32_t>(src));
|
||||
} else if (is_simm32(src)) {
|
||||
movq(dst, checked_cast<int32_t>(src));
|
||||
} else {
|
||||
mov64(dst, src);
|
||||
}
|
||||
mov64(dst, src);
|
||||
}
|
||||
|
||||
void MacroAssembler::movptr(Address dst, Register src) {
|
||||
|
||||
@ -1869,6 +1869,9 @@ public:
|
||||
void mov_metadata(Register dst, Metadata* obj);
|
||||
void mov_metadata(Address dst, Metadata* obj, Register rscratch);
|
||||
|
||||
void mov64(Register dst, int64_t imm64);
|
||||
void mov64(Register dst, int64_t imm64, relocInfo::relocType rtype, int format);
|
||||
|
||||
void movptr(Register dst, Register src);
|
||||
void movptr(Register dst, Address src);
|
||||
void movptr(Register dst, AddressLiteral src);
|
||||
|
||||
@ -2726,11 +2726,8 @@ bool Matcher::is_short_branch_offset(int rule, int br_size, int offset) {
|
||||
return (-128 <= offset && offset <= 127);
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
// Return whether or not this register is ever used as an argument.
|
||||
// This function is used on startup to build the trampoline stubs in
|
||||
// generateOptoStub. Registers not mentioned will be killed by the VM
|
||||
// call in the trampoline, and arguments in those registers not be
|
||||
// available to the callee.
|
||||
bool Matcher::can_be_java_arg(int reg)
|
||||
{
|
||||
return
|
||||
@ -2750,11 +2747,7 @@ bool Matcher::can_be_java_arg(int reg)
|
||||
reg == XMM6_num || reg == XMM6b_num ||
|
||||
reg == XMM7_num || reg == XMM7b_num;
|
||||
}
|
||||
|
||||
bool Matcher::is_spillable_arg(int reg)
|
||||
{
|
||||
return can_be_java_arg(reg);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint Matcher::int_pressure_limit()
|
||||
{
|
||||
@ -3341,6 +3334,18 @@ bool Matcher::match_rule_supported_vector(int opcode, int vlen, BasicType bt) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case Op_UMinReductionV:
|
||||
case Op_UMaxReductionV:
|
||||
if (UseAVX == 0) {
|
||||
return false;
|
||||
}
|
||||
if (bt == T_LONG && !VM_Version::supports_avx512vl()) {
|
||||
return false;
|
||||
}
|
||||
if (UseAVX > 2 && size_in_bits == 512 && !VM_Version::supports_avx512vl()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case Op_MaxV:
|
||||
case Op_MinV:
|
||||
if (UseSSE < 4 && is_integral_type(bt)) {
|
||||
@ -4679,11 +4684,6 @@ frame
|
||||
// Compiled code's Frame Pointer
|
||||
frame_pointer(RSP);
|
||||
|
||||
// Interpreter stores its frame pointer in a register which is
|
||||
// stored to the stack by I2CAdaptors.
|
||||
// I2CAdaptors convert from interpreted java to compiled java.
|
||||
interpreter_frame_pointer(RBP);
|
||||
|
||||
// Stack alignment requirement
|
||||
stack_alignment(StackAlignmentInBytes); // Alignment size in bytes (128-bit -> 16 bytes)
|
||||
|
||||
@ -19371,6 +19371,8 @@ instruct reductionI(rRegI dst, rRegI src1, legVec src2, legVec vtmp1, legVec vtm
|
||||
match(Set dst (XorReductionV src1 src2));
|
||||
match(Set dst (MinReductionV src1 src2));
|
||||
match(Set dst (MaxReductionV src1 src2));
|
||||
match(Set dst (UMinReductionV src1 src2));
|
||||
match(Set dst (UMaxReductionV src1 src2));
|
||||
effect(TEMP vtmp1, TEMP vtmp2);
|
||||
format %{ "vector_reduction_int $dst,$src1,$src2 ; using $vtmp1, $vtmp2 as TEMP" %}
|
||||
ins_encode %{
|
||||
@ -19392,6 +19394,8 @@ instruct reductionL(rRegL dst, rRegL src1, legVec src2, legVec vtmp1, legVec vtm
|
||||
match(Set dst (XorReductionV src1 src2));
|
||||
match(Set dst (MinReductionV src1 src2));
|
||||
match(Set dst (MaxReductionV src1 src2));
|
||||
match(Set dst (UMinReductionV src1 src2));
|
||||
match(Set dst (UMaxReductionV src1 src2));
|
||||
effect(TEMP vtmp1, TEMP vtmp2);
|
||||
format %{ "vector_reduction_long $dst,$src1,$src2 ; using $vtmp1, $vtmp2 as TEMP" %}
|
||||
ins_encode %{
|
||||
@ -19411,6 +19415,8 @@ instruct reductionL_avx512dq(rRegL dst, rRegL src1, vec src2, vec vtmp1, vec vtm
|
||||
match(Set dst (XorReductionV src1 src2));
|
||||
match(Set dst (MinReductionV src1 src2));
|
||||
match(Set dst (MaxReductionV src1 src2));
|
||||
match(Set dst (UMinReductionV src1 src2));
|
||||
match(Set dst (UMaxReductionV src1 src2));
|
||||
effect(TEMP vtmp1, TEMP vtmp2);
|
||||
format %{ "vector_reduction_long $dst,$src1,$src2 ; using $vtmp1, $vtmp2 as TEMP" %}
|
||||
ins_encode %{
|
||||
@ -19639,6 +19645,8 @@ instruct reductionB(rRegI dst, rRegI src1, legVec src2, legVec vtmp1, legVec vtm
|
||||
match(Set dst (XorReductionV src1 src2));
|
||||
match(Set dst (MinReductionV src1 src2));
|
||||
match(Set dst (MaxReductionV src1 src2));
|
||||
match(Set dst (UMinReductionV src1 src2));
|
||||
match(Set dst (UMaxReductionV src1 src2));
|
||||
effect(TEMP vtmp1, TEMP vtmp2);
|
||||
format %{ "vector_reduction_byte $dst,$src1,$src2 ; using $vtmp1, $vtmp2 as TEMP" %}
|
||||
ins_encode %{
|
||||
@ -19657,6 +19665,8 @@ instruct reductionB_avx512bw(rRegI dst, rRegI src1, vec src2, vec vtmp1, vec vtm
|
||||
match(Set dst (XorReductionV src1 src2));
|
||||
match(Set dst (MinReductionV src1 src2));
|
||||
match(Set dst (MaxReductionV src1 src2));
|
||||
match(Set dst (UMinReductionV src1 src2));
|
||||
match(Set dst (UMaxReductionV src1 src2));
|
||||
effect(TEMP vtmp1, TEMP vtmp2);
|
||||
format %{ "vector_reduction_byte $dst,$src1,$src2 ; using $vtmp1, $vtmp2 as TEMP" %}
|
||||
ins_encode %{
|
||||
@ -19678,6 +19688,8 @@ instruct reductionS(rRegI dst, rRegI src1, legVec src2, legVec vtmp1, legVec vtm
|
||||
match(Set dst (XorReductionV src1 src2));
|
||||
match(Set dst (MinReductionV src1 src2));
|
||||
match(Set dst (MaxReductionV src1 src2));
|
||||
match(Set dst (UMinReductionV src1 src2));
|
||||
match(Set dst (UMaxReductionV src1 src2));
|
||||
effect(TEMP vtmp1, TEMP vtmp2);
|
||||
format %{ "vector_reduction_short $dst,$src1,$src2 ; using $vtmp1, $vtmp2 as TEMP" %}
|
||||
ins_encode %{
|
||||
|
||||
@ -37,16 +37,6 @@
|
||||
range, \
|
||||
constraint) \
|
||||
\
|
||||
/* Whether to allow the VM to run if EXTSHM=ON. EXTSHM is an environment */ \
|
||||
/* variable used on AIX to activate certain hacks which allow more shm segments */\
|
||||
/* for 32bit processes. For 64bit processes, it is pointless and may have */ \
|
||||
/* harmful side effects (e.g. for some reasonn prevents allocation of 64k pages */\
|
||||
/* via shmctl). */ \
|
||||
/* Per default we quit with an error if that variable is found; for certain */ \
|
||||
/* customer scenarios, we may want to be able to run despite that variable. */ \
|
||||
product(bool, AllowExtshm, false, DIAGNOSTIC, \
|
||||
"Allow VM to run with EXTSHM=ON.") \
|
||||
\
|
||||
/* Maximum expected size of the data segment. That correlates with the */ \
|
||||
/* maximum C Heap consumption we expect. */ \
|
||||
/* We need to leave "breathing space" for the data segment when */ \
|
||||
|
||||
@ -126,7 +126,6 @@ int mread_real_time(timebasestruct_t *t, size_t size_of_timebasestruct_t);
|
||||
|
||||
// for multipage initialization error analysis (in 'g_multipage_error')
|
||||
#define ERROR_MP_OS_TOO_OLD 100
|
||||
#define ERROR_MP_EXTSHM_ACTIVE 101
|
||||
#define ERROR_MP_VMGETINFO_FAILED 102
|
||||
#define ERROR_MP_VMGETINFO_CLAIMS_NO_SUPPORT_FOR_64K 103
|
||||
|
||||
@ -178,9 +177,6 @@ uint32_t os::Aix::_os_version = 0;
|
||||
// -1 = uninitialized, 0 - no, 1 - yes
|
||||
int os::Aix::_xpg_sus_mode = -1;
|
||||
|
||||
// -1 = uninitialized, 0 - no, 1 - yes
|
||||
int os::Aix::_extshm = -1;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// local variables
|
||||
|
||||
@ -1195,13 +1191,6 @@ void os::print_memory_info(outputStream* st) {
|
||||
const char* const ldr_cntrl = ::getenv("LDR_CNTRL");
|
||||
st->print_cr(" LDR_CNTRL=%s.", ldr_cntrl ? ldr_cntrl : "<unset>");
|
||||
|
||||
// Print out EXTSHM because it is an unsupported setting.
|
||||
const char* const extshm = ::getenv("EXTSHM");
|
||||
st->print_cr(" EXTSHM=%s.", extshm ? extshm : "<unset>");
|
||||
if ( (strcmp(extshm, "on") == 0) || (strcmp(extshm, "ON") == 0) ) {
|
||||
st->print_cr(" *** Unsupported! Please remove EXTSHM from your environment! ***");
|
||||
}
|
||||
|
||||
// Print out AIXTHREAD_GUARDPAGES because it affects the size of pthread stacks.
|
||||
const char* const aixthread_guardpages = ::getenv("AIXTHREAD_GUARDPAGES");
|
||||
st->print_cr(" AIXTHREAD_GUARDPAGES=%s.",
|
||||
@ -2133,8 +2122,6 @@ void os::init(void) {
|
||||
|
||||
// datapsize = 64k. Data segment, thread stacks are 64k paged.
|
||||
// This normally means that we can allocate 64k pages dynamically.
|
||||
// (There is one special case where this may be false: EXTSHM=on.
|
||||
// but we decided to not support that mode).
|
||||
assert0(g_multipage_support.can_use_64K_pages || g_multipage_support.can_use_64K_mmap_pages);
|
||||
set_page_size(64*K);
|
||||
|
||||
@ -2543,28 +2530,13 @@ void os::Aix::initialize_os_info() {
|
||||
void os::Aix::scan_environment() {
|
||||
|
||||
char* p;
|
||||
int rc;
|
||||
|
||||
// Warn explicitly if EXTSHM=ON is used. That switch changes how
|
||||
// System V shared memory behaves. One effect is that page size of
|
||||
// shared memory cannot be change dynamically, effectivly preventing
|
||||
// large pages from working.
|
||||
// This switch was needed on AIX 32bit, but on AIX 64bit the general
|
||||
// recommendation is (in OSS notes) to switch it off.
|
||||
// Reject EXTSHM=ON. That switch changes how System V shared memory behaves
|
||||
// and prevents allocation of 64k pages for the heap.
|
||||
p = ::getenv("EXTSHM");
|
||||
trcVerbose("EXTSHM=%s.", p ? p : "<unset>");
|
||||
if (p && strcasecmp(p, "ON") == 0) {
|
||||
_extshm = 1;
|
||||
log_warning(os)("*** Unsupported mode! Please remove EXTSHM from your environment! ***");
|
||||
if (!AllowExtshm) {
|
||||
// We allow under certain conditions the user to continue. However, we want this
|
||||
// to be a fatal error by default. On certain AIX systems, leaving EXTSHM=ON means
|
||||
// that the VM is not able to allocate 64k pages for the heap.
|
||||
// We do not want to run with reduced performance.
|
||||
vm_exit_during_initialization("EXTSHM is ON. Please remove EXTSHM from your environment.");
|
||||
}
|
||||
} else {
|
||||
_extshm = 0;
|
||||
vm_exit_during_initialization("EXTSHM is ON. Please remove EXTSHM from your environment.");
|
||||
}
|
||||
|
||||
// SPEC1170 behaviour: will change the behaviour of a number of POSIX APIs.
|
||||
|
||||
@ -49,11 +49,6 @@ class os::Aix {
|
||||
// 1 - SPEC1170 requested (XPG_SUS_ENV is ON)
|
||||
static int _xpg_sus_mode;
|
||||
|
||||
// -1 = uninitialized,
|
||||
// 0 - EXTSHM=OFF or not set
|
||||
// 1 - EXTSHM=ON
|
||||
static int _extshm;
|
||||
|
||||
static bool available_memory(physical_memory_size_type& value);
|
||||
static bool free_memory(physical_memory_size_type& value);
|
||||
static physical_memory_size_type physical_memory() { return _physical_memory; }
|
||||
@ -111,12 +106,6 @@ class os::Aix {
|
||||
return _xpg_sus_mode;
|
||||
}
|
||||
|
||||
// Returns true if EXTSHM=ON.
|
||||
static bool extshm() {
|
||||
assert(_extshm != -1, "not initialized");
|
||||
return _extshm;
|
||||
}
|
||||
|
||||
// result struct for get_meminfo()
|
||||
struct meminfo_t {
|
||||
|
||||
|
||||
36
src/hotspot/os_cpu/aix_ppc/vm_version_aix_ppc.cpp
Normal file
36
src/hotspot/os_cpu/aix_ppc/vm_version_aix_ppc.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2026 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "runtime/vm_version.hpp"
|
||||
|
||||
#include <sys/systemcfg.h>
|
||||
|
||||
int VM_Version::get_dcache_line_size() {
|
||||
return _system_configuration.dcache_line;
|
||||
}
|
||||
|
||||
int VM_Version::get_icache_line_size() {
|
||||
return _system_configuration.icache_line;
|
||||
}
|
||||
44
src/hotspot/os_cpu/linux_ppc/vm_version_linux_ppc.cpp
Normal file
44
src/hotspot/os_cpu/linux_ppc/vm_version_linux_ppc.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2026 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "runtime/vm_version.hpp"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
int VM_Version::get_dcache_line_size() {
|
||||
// This should work on all modern linux versions:
|
||||
int size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
|
||||
// It may fail with very old linux / glibc versions. We use DEFAULT_CACHE_LINE_SIZE in this case.
|
||||
// That is the correct value for all currently supported processors.
|
||||
return (size <= 0) ? DEFAULT_CACHE_LINE_SIZE : size;
|
||||
}
|
||||
|
||||
int VM_Version::get_icache_line_size() {
|
||||
// This should work on all modern linux versions:
|
||||
int size = sysconf(_SC_LEVEL1_ICACHE_LINESIZE);
|
||||
// It may fail with very old linux / glibc versions. We use DEFAULT_CACHE_LINE_SIZE in this case.
|
||||
// That is the correct value for all currently supported processors.
|
||||
return (size <= 0) ? DEFAULT_CACHE_LINE_SIZE : size;
|
||||
}
|
||||
@ -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
|
||||
@ -993,9 +993,6 @@ void ADLParser::frame_parse(void) {
|
||||
if (strcmp(token,"frame_pointer")==0) {
|
||||
frame_pointer_parse(frame, false);
|
||||
}
|
||||
if (strcmp(token,"interpreter_frame_pointer")==0) {
|
||||
interpreter_frame_pointer_parse(frame, false);
|
||||
}
|
||||
if (strcmp(token,"inline_cache_reg")==0) {
|
||||
inline_cache_parse(frame, false);
|
||||
}
|
||||
@ -1119,11 +1116,6 @@ void ADLParser::frame_pointer_parse(FrameForm *frame, bool native) {
|
||||
else { frame->_frame_pointer = frame_pointer; }
|
||||
}
|
||||
|
||||
//------------------------------interpreter_frame_pointer_parse----------------------------
|
||||
void ADLParser::interpreter_frame_pointer_parse(FrameForm *frame, bool native) {
|
||||
frame->_interpreter_frame_pointer_reg = parse_one_arg("interpreter frame pointer entry");
|
||||
}
|
||||
|
||||
//------------------------------inline_cache_parse-----------------------------
|
||||
void ADLParser::inline_cache_parse(FrameForm *frame, bool native) {
|
||||
frame->_inline_cache_reg = parse_one_arg("inline cache reg entry");
|
||||
|
||||
@ -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
|
||||
@ -120,7 +120,6 @@ protected:
|
||||
// Parse the components of the frame section
|
||||
void sync_stack_slots_parse(FrameForm *frame);
|
||||
void frame_pointer_parse(FrameForm *frame, bool native);
|
||||
void interpreter_frame_pointer_parse(FrameForm *frame, bool native);
|
||||
void inline_cache_parse(FrameForm *frame, bool native);
|
||||
void interpreter_arg_ptr_parse(FrameForm *frame, bool native);
|
||||
void interpreter_method_parse(FrameForm *frame, bool native);
|
||||
|
||||
@ -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
|
||||
@ -476,7 +476,6 @@ void AllocClass::forms_do(FormClosure* f) {
|
||||
FrameForm::FrameForm() {
|
||||
_sync_stack_slots = nullptr;
|
||||
_inline_cache_reg = nullptr;
|
||||
_interpreter_frame_pointer_reg = nullptr;
|
||||
_cisc_spilling_operand_name = nullptr;
|
||||
_frame_pointer = nullptr;
|
||||
_c_frame_pointer = nullptr;
|
||||
|
||||
@ -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
|
||||
@ -347,7 +347,6 @@ public:
|
||||
// Public Data
|
||||
char *_sync_stack_slots;
|
||||
char *_inline_cache_reg;
|
||||
char *_interpreter_frame_pointer_reg;
|
||||
char *_cisc_spilling_operand_name;
|
||||
char *_frame_pointer;
|
||||
char *_c_frame_pointer;
|
||||
|
||||
@ -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
|
||||
@ -4212,14 +4212,6 @@ void ArchDesc::buildFrameMethods(FILE *fp_cpp) {
|
||||
fprintf(fp_cpp,"int Matcher::inline_cache_reg_encode() {");
|
||||
fprintf(fp_cpp," return _regEncode[inline_cache_reg()]; }\n\n");
|
||||
|
||||
// Interpreter's Frame Pointer Register
|
||||
fprintf(fp_cpp,"OptoReg::Name Matcher::interpreter_frame_pointer_reg() {");
|
||||
if (_frame->_interpreter_frame_pointer_reg == nullptr)
|
||||
fprintf(fp_cpp," return OptoReg::Bad; }\n\n");
|
||||
else
|
||||
fprintf(fp_cpp," return OptoReg::Name(%s_num); }\n\n",
|
||||
_frame->_interpreter_frame_pointer_reg);
|
||||
|
||||
// Frame Pointer definition
|
||||
/* CNC - I can not contemplate having a different frame pointer between
|
||||
Java and native code; makes my head hurt to think about it.
|
||||
|
||||
@ -447,9 +447,6 @@ void CompilerConfig::set_jvmci_specific_flags() {
|
||||
if (FLAG_IS_DEFAULT(InitialCodeCacheSize)) {
|
||||
FLAG_SET_DEFAULT(InitialCodeCacheSize, MAX2(16*M, InitialCodeCacheSize));
|
||||
}
|
||||
if (FLAG_IS_DEFAULT(NewSizeThreadIncrease)) {
|
||||
FLAG_SET_DEFAULT(NewSizeThreadIncrease, MAX2(4*K, NewSizeThreadIncrease));
|
||||
}
|
||||
if (FLAG_IS_DEFAULT(Tier3DelayOn)) {
|
||||
// This effectively prevents the compile broker scheduling tier 2
|
||||
// (i.e., limited C1 profiling) compilations instead of tier 3
|
||||
|
||||
@ -58,7 +58,6 @@ define_pd_global(bool, TieredCompilation, false);
|
||||
define_pd_global(intx, CompileThreshold, 0);
|
||||
|
||||
define_pd_global(intx, OnStackReplacePercentage, 0);
|
||||
define_pd_global(size_t, NewSizeThreadIncrease, 4*K);
|
||||
define_pd_global(bool, InlineClassNatives, true);
|
||||
define_pd_global(bool, InlineUnsafeOps, true);
|
||||
define_pd_global(size_t, InitialCodeCacheSize, 160*K);
|
||||
|
||||
@ -1652,21 +1652,13 @@ jint G1CollectedHeap::initialize() {
|
||||
return JNI_OK;
|
||||
}
|
||||
|
||||
bool G1CollectedHeap::concurrent_mark_is_terminating() const {
|
||||
assert(_cm != nullptr, "_cm must have been created");
|
||||
assert(_cm->is_fully_initialized(), "thread must exist in order to check if mark is terminating");
|
||||
return _cm->cm_thread()->should_terminate();
|
||||
}
|
||||
|
||||
void G1CollectedHeap::stop() {
|
||||
// Stop all concurrent threads. We do this to make sure these threads
|
||||
// do not continue to execute and access resources (e.g. logging)
|
||||
// that are destroyed during shutdown.
|
||||
_cr->stop();
|
||||
_service_thread->stop();
|
||||
if (_cm->is_fully_initialized()) {
|
||||
_cm->cm_thread()->stop();
|
||||
}
|
||||
_cm->stop();
|
||||
}
|
||||
|
||||
void G1CollectedHeap::safepoint_synchronize_begin() {
|
||||
@ -1857,12 +1849,12 @@ void G1CollectedHeap::increment_old_marking_cycles_completed(bool concurrent,
|
||||
record_whole_heap_examined_timestamp();
|
||||
}
|
||||
|
||||
// We need to clear the "in_progress" flag in the CM thread before
|
||||
// We need to tell G1ConcurrentMark to update the state before
|
||||
// we wake up any waiters (especially when ExplicitInvokesConcurrent
|
||||
// is set) so that if a waiter requests another System.gc() it doesn't
|
||||
// incorrectly see that a marking cycle is still in progress.
|
||||
if (concurrent) {
|
||||
_cm->cm_thread()->set_idle();
|
||||
_cm->notify_concurrent_cycle_completed();
|
||||
}
|
||||
|
||||
// Notify threads waiting in System.gc() (with ExplicitGCInvokesConcurrent)
|
||||
@ -2565,11 +2557,9 @@ void G1CollectedHeap::start_concurrent_cycle(bool concurrent_operation_is_full_m
|
||||
assert(!_cm->in_progress(), "Can not start concurrent operation while in progress");
|
||||
MutexLocker x(G1CGC_lock, Mutex::_no_safepoint_check_flag);
|
||||
if (concurrent_operation_is_full_mark) {
|
||||
_cm->post_concurrent_mark_start();
|
||||
_cm->cm_thread()->start_full_mark();
|
||||
_cm->start_full_concurrent_cycle();
|
||||
} else {
|
||||
_cm->post_concurrent_undo_start();
|
||||
_cm->cm_thread()->start_undo_mark();
|
||||
_cm->start_undo_concurrent_cycle();
|
||||
}
|
||||
G1CGC_lock->notify();
|
||||
}
|
||||
|
||||
@ -915,9 +915,6 @@ public:
|
||||
// specified by the policy object.
|
||||
jint initialize() override;
|
||||
|
||||
// Returns whether concurrent mark threads (and the VM) are about to terminate.
|
||||
bool concurrent_mark_is_terminating() const;
|
||||
|
||||
void safepoint_synchronize_begin() override;
|
||||
void safepoint_synchronize_end() override;
|
||||
|
||||
|
||||
@ -581,6 +581,11 @@ PartialArrayStateManager* G1ConcurrentMark::partial_array_state_manager() const
|
||||
return _partial_array_state_manager;
|
||||
}
|
||||
|
||||
G1ConcurrentMarkThread* G1ConcurrentMark::cm_thread() const {
|
||||
assert(is_fully_initialized(), "must be");
|
||||
return _cm_thread;
|
||||
}
|
||||
|
||||
void G1ConcurrentMark::reset() {
|
||||
_has_aborted.store_relaxed(false);
|
||||
|
||||
@ -715,7 +720,6 @@ public:
|
||||
private:
|
||||
// Heap region closure used for clearing the _mark_bitmap.
|
||||
class G1ClearBitmapHRClosure : public G1HeapRegionClosure {
|
||||
private:
|
||||
G1ConcurrentMark* _cm;
|
||||
G1CMBitMap* _bitmap;
|
||||
bool _suspendible; // If suspendible, do yield checks.
|
||||
@ -959,7 +963,7 @@ void G1ConcurrentMark::pre_concurrent_start(GCCause::Cause cause) {
|
||||
_gc_tracer_cm->set_gc_cause(cause);
|
||||
}
|
||||
|
||||
void G1ConcurrentMark::post_concurrent_mark_start() {
|
||||
void G1ConcurrentMark::start_full_concurrent_cycle() {
|
||||
// Start Concurrent Marking weak-reference discovery.
|
||||
ReferenceProcessor* rp = _g1h->ref_processor_cm();
|
||||
rp->start_discovery(false /* always_clear */);
|
||||
@ -976,10 +980,26 @@ void G1ConcurrentMark::post_concurrent_mark_start() {
|
||||
// when marking is on. So, it's also called at the end of the
|
||||
// concurrent start pause to update the heap end, if the heap expands
|
||||
// during it. No need to call it here.
|
||||
|
||||
// Signal the thread to start work.
|
||||
cm_thread()->start_full_mark();
|
||||
}
|
||||
|
||||
void G1ConcurrentMark::post_concurrent_undo_start() {
|
||||
void G1ConcurrentMark::start_undo_concurrent_cycle() {
|
||||
root_regions()->cancel_scan();
|
||||
|
||||
// Signal the thread to start work.
|
||||
cm_thread()->start_undo_mark();
|
||||
}
|
||||
|
||||
void G1ConcurrentMark::notify_concurrent_cycle_completed() {
|
||||
cm_thread()->set_idle();
|
||||
}
|
||||
|
||||
void G1ConcurrentMark::stop() {
|
||||
if (is_fully_initialized()) {
|
||||
cm_thread()->stop();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1943,7 +1963,7 @@ bool G1ConcurrentMark::concurrent_cycle_abort() {
|
||||
// has been signalled is already rare), and this work should be negligible compared
|
||||
// to actual full gc work.
|
||||
|
||||
if (!is_fully_initialized() || (!cm_thread()->in_progress() && !_g1h->concurrent_mark_is_terminating())) {
|
||||
if (!is_fully_initialized() || (!cm_thread()->in_progress() && !cm_thread()->should_terminate())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -352,6 +352,7 @@ class G1ConcurrentMark : public CHeapObj<mtGC> {
|
||||
friend class G1CMRemarkTask;
|
||||
friend class G1CMRootRegionScanTask;
|
||||
friend class G1CMTask;
|
||||
friend class G1ClearBitMapTask;
|
||||
friend class G1ConcurrentMarkThread;
|
||||
|
||||
G1ConcurrentMarkThread* _cm_thread; // The thread doing the work
|
||||
@ -524,6 +525,9 @@ class G1ConcurrentMark : public CHeapObj<mtGC> {
|
||||
Atomic<HeapWord*>* _top_at_rebuild_starts;
|
||||
// True when Remark pause selected regions for rebuilding.
|
||||
bool _needs_remembered_set_rebuild;
|
||||
|
||||
G1ConcurrentMarkThread* cm_thread() const;
|
||||
|
||||
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.
|
||||
@ -602,8 +606,6 @@ public:
|
||||
G1RegionToSpaceMapper* bitmap_storage);
|
||||
~G1ConcurrentMark();
|
||||
|
||||
G1ConcurrentMarkThread* cm_thread() { return _cm_thread; }
|
||||
|
||||
G1CMBitMap* mark_bitmap() const { return (G1CMBitMap*)&_mark_bitmap; }
|
||||
|
||||
// Calculates the number of concurrent GC threads to be used in the marking phase.
|
||||
@ -632,8 +634,15 @@ public:
|
||||
// These two methods do the work that needs to be done at the start and end of the
|
||||
// concurrent start pause.
|
||||
void pre_concurrent_start(GCCause::Cause cause);
|
||||
void post_concurrent_mark_start();
|
||||
void post_concurrent_undo_start();
|
||||
|
||||
// Start the particular type of concurrent cycle. After this call threads may be running.
|
||||
void start_full_concurrent_cycle();
|
||||
void start_undo_concurrent_cycle();
|
||||
|
||||
void notify_concurrent_cycle_completed();
|
||||
|
||||
// Stop active components/the concurrent mark thread.
|
||||
void stop();
|
||||
|
||||
// Scan all the root regions and mark everything reachable from
|
||||
// them.
|
||||
|
||||
@ -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
|
||||
@ -366,6 +366,12 @@ static size_t target_heap_capacity(size_t used_bytes, uintx free_ratio) {
|
||||
}
|
||||
|
||||
size_t G1HeapSizingPolicy::full_collection_resize_amount(bool& expand, size_t allocation_word_size) {
|
||||
// User-requested Full GCs introduce GC load unrelated to heap size; reset CPU
|
||||
// usage tracking so heap resizing heuristics are driven only by GC pressure.
|
||||
if (GCCause::is_user_requested_gc(_g1h->gc_cause())) {
|
||||
reset_cpu_usage_tracking_data();
|
||||
}
|
||||
|
||||
const size_t capacity_after_gc = _g1h->capacity();
|
||||
// Capacity, free and used after the GC counted as full regions to
|
||||
// include the waste in the following calculations.
|
||||
|
||||
@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* 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/serial/cSpaceCounters.hpp"
|
||||
#include "memory/allocation.inline.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
|
||||
CSpaceCounters::CSpaceCounters(const char* name, int ordinal, size_t max_size,
|
||||
ContiguousSpace* s, GenerationCounters* gc)
|
||||
: _space(s) {
|
||||
if (UsePerfData) {
|
||||
EXCEPTION_MARK;
|
||||
ResourceMark rm;
|
||||
|
||||
const char* cns = PerfDataManager::name_space(gc->name_space(), "space",
|
||||
ordinal);
|
||||
|
||||
_name_space = NEW_C_HEAP_ARRAY(char, strlen(cns)+1, mtGC);
|
||||
strcpy(_name_space, cns);
|
||||
|
||||
const char* cname = PerfDataManager::counter_name(_name_space, "name");
|
||||
PerfDataManager::create_string_constant(SUN_GC, cname, name, CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(_name_space, "maxCapacity");
|
||||
_max_capacity = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Bytes,
|
||||
(jlong)max_size,
|
||||
CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(_name_space, "capacity");
|
||||
_capacity = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Bytes,
|
||||
_space->capacity(),
|
||||
CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(_name_space, "used");
|
||||
_used = PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes,
|
||||
_space->used(),
|
||||
CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(_name_space, "initCapacity");
|
||||
PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes,
|
||||
_space->capacity(), CHECK);
|
||||
}
|
||||
}
|
||||
|
||||
CSpaceCounters::~CSpaceCounters() {
|
||||
FREE_C_HEAP_ARRAY(char, _name_space);
|
||||
}
|
||||
|
||||
void CSpaceCounters::update_capacity() {
|
||||
_capacity->set_value(_space->capacity());
|
||||
}
|
||||
|
||||
void CSpaceCounters::update_used() {
|
||||
_used->set_value(_space->used());
|
||||
}
|
||||
|
||||
void CSpaceCounters::update_all() {
|
||||
update_used();
|
||||
update_capacity();
|
||||
}
|
||||
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* 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_SERIAL_CSPACECOUNTERS_HPP
|
||||
#define SHARE_GC_SERIAL_CSPACECOUNTERS_HPP
|
||||
|
||||
#include "gc/shared/generationCounters.hpp"
|
||||
#include "gc/shared/space.hpp"
|
||||
#include "runtime/perfData.hpp"
|
||||
|
||||
// A CSpaceCounters is a holder class for performance counters
|
||||
// that track a space;
|
||||
|
||||
class CSpaceCounters: public CHeapObj<mtGC> {
|
||||
private:
|
||||
PerfVariable* _capacity;
|
||||
PerfVariable* _used;
|
||||
PerfVariable* _max_capacity;
|
||||
|
||||
// Constant PerfData types don't need to retain a reference.
|
||||
// However, it's a good idea to document them here.
|
||||
// PerfConstant* _size;
|
||||
|
||||
ContiguousSpace* _space;
|
||||
char* _name_space;
|
||||
|
||||
public:
|
||||
|
||||
CSpaceCounters(const char* name, int ordinal, size_t max_size,
|
||||
ContiguousSpace* s, GenerationCounters* gc);
|
||||
|
||||
~CSpaceCounters();
|
||||
|
||||
void update_capacity();
|
||||
void update_used();
|
||||
void update_all();
|
||||
|
||||
const char* name_space() const { return _name_space; }
|
||||
};
|
||||
|
||||
#endif // SHARE_GC_SERIAL_CSPACECOUNTERS_HPP
|
||||
@ -39,6 +39,7 @@
|
||||
#include "gc/shared/gcTimer.hpp"
|
||||
#include "gc/shared/gcTrace.hpp"
|
||||
#include "gc/shared/gcTraceTime.inline.hpp"
|
||||
#include "gc/shared/hSpaceCounters.hpp"
|
||||
#include "gc/shared/oopStorageSet.inline.hpp"
|
||||
#include "gc/shared/referencePolicy.hpp"
|
||||
#include "gc/shared/referenceProcessorPhaseTimes.hpp"
|
||||
@ -248,12 +249,12 @@ DefNewGeneration::DefNewGeneration(ReservedSpace rs,
|
||||
min_size, max_size, _virtual_space.committed_size());
|
||||
_gc_counters = new CollectorCounters(policy, 0);
|
||||
|
||||
_eden_counters = new CSpaceCounters("eden", 0, _max_eden_size, _eden_space,
|
||||
_gen_counters);
|
||||
_from_counters = new CSpaceCounters("s0", 1, _max_survivor_size, _from_space,
|
||||
_gen_counters);
|
||||
_to_counters = new CSpaceCounters("s1", 2, _max_survivor_size, _to_space,
|
||||
_gen_counters);
|
||||
_eden_counters = new HSpaceCounters(_gen_counters->name_space(), "eden", 0,
|
||||
_max_eden_size, _eden_space->capacity());
|
||||
_from_counters = new HSpaceCounters(_gen_counters->name_space(), "s0", 1,
|
||||
_max_survivor_size, _from_space->capacity());
|
||||
_to_counters = new HSpaceCounters(_gen_counters->name_space(), "s1", 2,
|
||||
_max_survivor_size, _to_space->capacity());
|
||||
|
||||
update_counters();
|
||||
_old_gen = nullptr;
|
||||
@ -319,7 +320,7 @@ void DefNewGeneration::swap_spaces() {
|
||||
_to_space = s;
|
||||
|
||||
if (UsePerfData) {
|
||||
CSpaceCounters* c = _from_counters;
|
||||
HSpaceCounters* c = _from_counters;
|
||||
_from_counters = _to_counters;
|
||||
_to_counters = c;
|
||||
}
|
||||
@ -348,38 +349,6 @@ void DefNewGeneration::expand_eden_by(size_t delta_bytes) {
|
||||
post_resize();
|
||||
}
|
||||
|
||||
size_t DefNewGeneration::calculate_thread_increase_size(int threads_count) const {
|
||||
size_t thread_increase_size = 0;
|
||||
// Check an overflow at 'threads_count * NewSizeThreadIncrease'.
|
||||
if (threads_count > 0 && NewSizeThreadIncrease <= max_uintx / threads_count) {
|
||||
thread_increase_size = threads_count * NewSizeThreadIncrease;
|
||||
}
|
||||
return thread_increase_size;
|
||||
}
|
||||
|
||||
size_t DefNewGeneration::adjust_for_thread_increase(size_t new_size_candidate,
|
||||
size_t new_size_before,
|
||||
size_t alignment,
|
||||
size_t thread_increase_size) const {
|
||||
size_t desired_new_size = new_size_before;
|
||||
|
||||
if (NewSizeThreadIncrease > 0 && thread_increase_size > 0) {
|
||||
|
||||
// 1. Check an overflow at 'new_size_candidate + thread_increase_size'.
|
||||
if (new_size_candidate <= max_uintx - thread_increase_size) {
|
||||
new_size_candidate += thread_increase_size;
|
||||
|
||||
// 2. Check an overflow at 'align_up'.
|
||||
size_t aligned_max = ((max_uintx - alignment) & ~(alignment-1));
|
||||
if (new_size_candidate <= aligned_max) {
|
||||
desired_new_size = align_up(new_size_candidate, alignment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return desired_new_size;
|
||||
}
|
||||
|
||||
size_t DefNewGeneration::calculate_desired_young_gen_bytes() const {
|
||||
size_t old_size = SerialHeap::heap()->old_gen()->capacity();
|
||||
size_t new_size_before = _virtual_space.committed_size();
|
||||
@ -391,14 +360,8 @@ size_t DefNewGeneration::calculate_desired_young_gen_bytes() const {
|
||||
// All space sizes must be multiples of Generation::GenGrain.
|
||||
size_t alignment = Generation::GenGrain;
|
||||
|
||||
int threads_count = Threads::number_of_non_daemon_threads();
|
||||
size_t thread_increase_size = calculate_thread_increase_size(threads_count);
|
||||
|
||||
size_t new_size_candidate = old_size / NewRatio;
|
||||
// Compute desired new generation size based on NewRatio and NewSizeThreadIncrease
|
||||
// and reverts to previous value if any overflow happens
|
||||
size_t desired_new_size = adjust_for_thread_increase(new_size_candidate, new_size_before,
|
||||
alignment, thread_increase_size);
|
||||
size_t desired_new_size = align_up(new_size_candidate, alignment);
|
||||
|
||||
// Adjust new generation size
|
||||
desired_new_size = clamp(desired_new_size, min_new_size, max_new_size);
|
||||
@ -821,9 +784,9 @@ void DefNewGeneration::gc_epilogue() {
|
||||
|
||||
void DefNewGeneration::update_counters() {
|
||||
if (UsePerfData) {
|
||||
_eden_counters->update_all();
|
||||
_from_counters->update_all();
|
||||
_to_counters->update_all();
|
||||
_eden_counters->update_all(_eden_space->capacity(), _eden_space->used());
|
||||
_from_counters->update_all(_from_space->capacity(), _from_space->used());
|
||||
_to_counters->update_all(_to_space->capacity(), _to_space->used());
|
||||
_gen_counters->update_capacity(_virtual_space.committed_size());
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,7 +25,6 @@
|
||||
#ifndef SHARE_GC_SERIAL_DEFNEWGENERATION_HPP
|
||||
#define SHARE_GC_SERIAL_DEFNEWGENERATION_HPP
|
||||
|
||||
#include "gc/serial/cSpaceCounters.hpp"
|
||||
#include "gc/serial/generation.hpp"
|
||||
#include "gc/serial/tenuredGeneration.hpp"
|
||||
#include "gc/shared/ageTable.hpp"
|
||||
@ -38,7 +37,7 @@
|
||||
#include "utilities/stack.hpp"
|
||||
|
||||
class ContiguousSpace;
|
||||
class CSpaceCounters;
|
||||
class HSpaceCounters;
|
||||
class OldGenScanClosure;
|
||||
class YoungGenScanClosure;
|
||||
class DefNewTracer;
|
||||
@ -102,9 +101,9 @@ class DefNewGeneration: public Generation {
|
||||
|
||||
// Performance Counters
|
||||
GenerationCounters* _gen_counters;
|
||||
CSpaceCounters* _eden_counters;
|
||||
CSpaceCounters* _from_counters;
|
||||
CSpaceCounters* _to_counters;
|
||||
HSpaceCounters* _eden_counters;
|
||||
HSpaceCounters* _from_counters;
|
||||
HSpaceCounters* _to_counters;
|
||||
|
||||
// sizing information
|
||||
size_t _max_eden_size;
|
||||
@ -230,15 +229,6 @@ class DefNewGeneration: public Generation {
|
||||
// Initialize eden/from/to spaces.
|
||||
void init_spaces();
|
||||
|
||||
// Return adjusted new size for NewSizeThreadIncrease.
|
||||
// If any overflow happens, revert to previous new size.
|
||||
size_t adjust_for_thread_increase(size_t new_size_candidate,
|
||||
size_t new_size_before,
|
||||
size_t alignment,
|
||||
size_t thread_increase_size) const;
|
||||
|
||||
size_t calculate_thread_increase_size(int threads_count) const;
|
||||
|
||||
|
||||
// Scavenge support
|
||||
void swap_spaces();
|
||||
|
||||
@ -32,6 +32,7 @@
|
||||
#include "gc/shared/gcTimer.hpp"
|
||||
#include "gc/shared/gcTrace.hpp"
|
||||
#include "gc/shared/genArguments.hpp"
|
||||
#include "gc/shared/hSpaceCounters.hpp"
|
||||
#include "gc/shared/space.hpp"
|
||||
#include "gc/shared/spaceDecorator.hpp"
|
||||
#include "logging/log.hpp"
|
||||
@ -330,9 +331,9 @@ TenuredGeneration::TenuredGeneration(ReservedSpace rs,
|
||||
|
||||
_gc_counters = new CollectorCounters("Serial full collection pauses", 1);
|
||||
|
||||
_space_counters = new CSpaceCounters(gen_name, 0,
|
||||
_space_counters = new HSpaceCounters(_gen_counters->name_space(), gen_name, 0,
|
||||
_virtual_space.reserved_size(),
|
||||
_the_space, _gen_counters);
|
||||
_the_space->capacity());
|
||||
}
|
||||
|
||||
void TenuredGeneration::gc_prologue() {
|
||||
@ -367,7 +368,7 @@ void TenuredGeneration::update_promote_stats() {
|
||||
|
||||
void TenuredGeneration::update_counters() {
|
||||
if (UsePerfData) {
|
||||
_space_counters->update_all();
|
||||
_space_counters->update_all(_the_space->capacity(), _the_space->used());
|
||||
_gen_counters->update_capacity(_virtual_space.committed_size());
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,7 +25,6 @@
|
||||
#ifndef SHARE_GC_SERIAL_TENUREDGENERATION_HPP
|
||||
#define SHARE_GC_SERIAL_TENUREDGENERATION_HPP
|
||||
|
||||
#include "gc/serial/cSpaceCounters.hpp"
|
||||
#include "gc/serial/generation.hpp"
|
||||
#include "gc/serial/serialBlockOffsetTable.hpp"
|
||||
#include "gc/shared/generationCounters.hpp"
|
||||
@ -34,6 +33,7 @@
|
||||
|
||||
class CardTableRS;
|
||||
class ContiguousSpace;
|
||||
class HSpaceCounters;
|
||||
|
||||
// TenuredGeneration models the heap containing old (promoted/tenured) objects
|
||||
// contained in a single contiguous space. This generation is covered by a card
|
||||
@ -68,7 +68,7 @@ class TenuredGeneration: public Generation {
|
||||
ContiguousSpace* _the_space; // Actual space holding objects
|
||||
|
||||
GenerationCounters* _gen_counters;
|
||||
CSpaceCounters* _space_counters;
|
||||
HSpaceCounters* _space_counters;
|
||||
|
||||
// Avg amount promoted; used for avoiding promotion undo
|
||||
// This class does not update deviations if the sample is zero.
|
||||
|
||||
@ -289,7 +289,7 @@ protected:
|
||||
DEBUG_ONLY(bool is_in_or_null(const void* p) const { return p == nullptr || is_in(p); })
|
||||
|
||||
void set_gc_cause(GCCause::Cause v);
|
||||
GCCause::Cause gc_cause() { return _gc_cause; }
|
||||
GCCause::Cause gc_cause() const { return _gc_cause; }
|
||||
|
||||
oop obj_allocate(Klass* klass, size_t size, TRAPS);
|
||||
virtual oop array_allocate(Klass* klass, size_t size, int length, bool do_zero, TRAPS);
|
||||
|
||||
@ -480,11 +480,6 @@
|
||||
"Ratio of old/new generation sizes") \
|
||||
range(0, max_uintx-1) \
|
||||
\
|
||||
product_pd(size_t, NewSizeThreadIncrease, \
|
||||
"Additional size added to desired new generation size per " \
|
||||
"non-daemon thread (in bytes)") \
|
||||
range(0, max_uintx) \
|
||||
\
|
||||
product(uintx, QueuedAllocationWarningCount, 0, \
|
||||
"Number of times an allocation that queues behind a GC " \
|
||||
"will retry before printing a warning") \
|
||||
|
||||
@ -137,7 +137,7 @@ void ShenandoahCollectionSet::clear() {
|
||||
_live = 0;
|
||||
|
||||
_region_count = 0;
|
||||
_current_index = 0;
|
||||
_current_index.store_relaxed(0);
|
||||
|
||||
_young_bytes_to_evacuate = 0;
|
||||
_young_bytes_to_promote = 0;
|
||||
@ -155,11 +155,11 @@ ShenandoahHeapRegion* ShenandoahCollectionSet::claim_next() {
|
||||
// before hitting the (potentially contended) atomic index.
|
||||
|
||||
size_t max = _heap->num_regions();
|
||||
size_t old = AtomicAccess::load(&_current_index);
|
||||
size_t old = _current_index.load_relaxed();
|
||||
|
||||
for (size_t index = old; index < max; index++) {
|
||||
if (is_in(index)) {
|
||||
size_t cur = AtomicAccess::cmpxchg(&_current_index, old, index + 1, memory_order_relaxed);
|
||||
size_t cur = _current_index.compare_exchange(old, index + 1, memory_order_relaxed);
|
||||
assert(cur >= old, "Always move forward");
|
||||
if (cur == old) {
|
||||
// Successfully moved the claim index, this is our region.
|
||||
@ -179,9 +179,9 @@ ShenandoahHeapRegion* ShenandoahCollectionSet::next() {
|
||||
assert(Thread::current()->is_VM_thread(), "Must be VMThread");
|
||||
|
||||
size_t max = _heap->num_regions();
|
||||
for (size_t index = _current_index; index < max; index++) {
|
||||
for (size_t index = _current_index.load_relaxed(); index < max; index++) {
|
||||
if (is_in(index)) {
|
||||
_current_index = index + 1;
|
||||
_current_index.store_relaxed(index + 1);
|
||||
return _heap->get_region(index);
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,6 +32,7 @@
|
||||
#include "memory/allocation.hpp"
|
||||
#include "memory/reservedSpace.hpp"
|
||||
#include "memory/virtualspace.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
|
||||
class ShenandoahCollectionSet : public CHeapObj<mtGC> {
|
||||
friend class ShenandoahHeap;
|
||||
@ -80,7 +81,7 @@ private:
|
||||
size_t _old_available_bytes_collected;
|
||||
|
||||
shenandoah_padding(0);
|
||||
volatile size_t _current_index;
|
||||
Atomic<size_t> _current_index;
|
||||
shenandoah_padding(1);
|
||||
|
||||
public:
|
||||
@ -99,7 +100,7 @@ public:
|
||||
bool is_empty() const { return _region_count == 0; }
|
||||
|
||||
void clear_current_index() {
|
||||
_current_index = 0;
|
||||
_current_index.store_relaxed(0);
|
||||
}
|
||||
|
||||
inline bool is_in(ShenandoahHeapRegion* r) const;
|
||||
|
||||
@ -31,11 +31,11 @@
|
||||
|
||||
|
||||
void ShenandoahController::update_gc_id() {
|
||||
AtomicAccess::inc(&_gc_id);
|
||||
_gc_id.add_then_fetch((size_t)1);
|
||||
}
|
||||
|
||||
size_t ShenandoahController::get_gc_id() {
|
||||
return AtomicAccess::load(&_gc_id);
|
||||
return _gc_id.load_relaxed();
|
||||
}
|
||||
|
||||
void ShenandoahController::handle_alloc_failure(const ShenandoahAllocRequest& req, bool block) {
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
#include "gc/shared/gcCause.hpp"
|
||||
#include "gc/shenandoah/shenandoahAllocRequest.hpp"
|
||||
#include "gc/shenandoah/shenandoahSharedVariables.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
|
||||
/**
|
||||
* This interface exposes methods necessary for the heap to interact
|
||||
@ -38,7 +39,7 @@ class ShenandoahController: public ConcurrentGCThread {
|
||||
private:
|
||||
shenandoah_padding(0);
|
||||
// A monotonically increasing GC count.
|
||||
volatile size_t _gc_id;
|
||||
Atomic<size_t> _gc_id;
|
||||
shenandoah_padding(1);
|
||||
|
||||
protected:
|
||||
|
||||
@ -32,7 +32,6 @@
|
||||
#include "gc/shenandoah/shenandoahHeapRegionSet.hpp"
|
||||
#include "logging/logStream.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "runtime/atomicAccess.hpp"
|
||||
#include "runtime/perfData.inline.hpp"
|
||||
#include "utilities/defaultStream.hpp"
|
||||
|
||||
@ -106,8 +105,8 @@ void ShenandoahHeapRegionCounters::write_snapshot(PerfLongVariable** regions,
|
||||
void ShenandoahHeapRegionCounters::update() {
|
||||
if (ShenandoahRegionSampling) {
|
||||
jlong current = nanos_to_millis(os::javaTimeNanos());
|
||||
jlong last = _last_sample_millis;
|
||||
if (current - last > ShenandoahRegionSamplingRate && AtomicAccess::cmpxchg(&_last_sample_millis, last, current) == last) {
|
||||
jlong last = _last_sample_millis.load_relaxed();
|
||||
if (current - last > ShenandoahRegionSamplingRate && _last_sample_millis.compare_exchange(last, current) == last) {
|
||||
|
||||
ShenandoahHeap* heap = ShenandoahHeap::heap();
|
||||
_status->set_value(encode_heap_status(heap));
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
|
||||
#include "logging/logFileStreamOutput.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
|
||||
/**
|
||||
* This provides the following in JVMStat:
|
||||
@ -88,7 +89,7 @@ private:
|
||||
PerfLongVariable** _regions_data;
|
||||
PerfLongVariable* _timestamp;
|
||||
PerfLongVariable* _status;
|
||||
volatile jlong _last_sample_millis;
|
||||
Atomic<jlong> _last_sample_millis;
|
||||
|
||||
void write_snapshot(PerfLongVariable** regions,
|
||||
PerfLongVariable* ts,
|
||||
|
||||
@ -198,11 +198,11 @@ void BinaryMagnitudeSeq::clear() {
|
||||
for (int c = 0; c < BitsPerSize_t; c++) {
|
||||
_mags[c] = 0;
|
||||
}
|
||||
_sum = 0;
|
||||
_sum.store_relaxed(0);
|
||||
}
|
||||
|
||||
void BinaryMagnitudeSeq::add(size_t val) {
|
||||
AtomicAccess::add(&_sum, val);
|
||||
_sum.add_then_fetch(val);
|
||||
|
||||
int mag = log2i_graceful(val) + 1;
|
||||
|
||||
@ -237,7 +237,7 @@ size_t BinaryMagnitudeSeq::num() const {
|
||||
}
|
||||
|
||||
size_t BinaryMagnitudeSeq::sum() const {
|
||||
return _sum;
|
||||
return _sum.load_relaxed();
|
||||
}
|
||||
|
||||
int BinaryMagnitudeSeq::min_level() const {
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHNUMBERSEQ_HPP
|
||||
#define SHARE_GC_SHENANDOAH_SHENANDOAHNUMBERSEQ_HPP
|
||||
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "utilities/numberSeq.hpp"
|
||||
|
||||
// HDR sequence stores the low-resolution high-dynamic-range values.
|
||||
@ -59,7 +60,7 @@ public:
|
||||
// is not needed, it is preferred over HdrSeq.
|
||||
class BinaryMagnitudeSeq : public CHeapObj<mtGC> {
|
||||
private:
|
||||
size_t _sum;
|
||||
Atomic<size_t> _sum;
|
||||
size_t* _mags;
|
||||
|
||||
public:
|
||||
|
||||
@ -45,7 +45,7 @@ ShenandoahJavaThreadsIterator::ShenandoahJavaThreadsIterator(ShenandoahPhaseTimi
|
||||
}
|
||||
|
||||
uint ShenandoahJavaThreadsIterator::claim() {
|
||||
return AtomicAccess::fetch_then_add(&_claimed, _stride, memory_order_relaxed);
|
||||
return _claimed.fetch_then_add(_stride, memory_order_relaxed);
|
||||
}
|
||||
|
||||
void ShenandoahJavaThreadsIterator::threads_do(ThreadClosure* cl, uint worker_id) {
|
||||
|
||||
@ -33,6 +33,7 @@
|
||||
#include "gc/shenandoah/shenandoahSharedVariables.hpp"
|
||||
#include "gc/shenandoah/shenandoahUtils.hpp"
|
||||
#include "memory/iterator.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "runtime/threads.hpp"
|
||||
|
||||
template <bool CONCURRENT>
|
||||
@ -73,7 +74,7 @@ private:
|
||||
ThreadsListHandle _threads;
|
||||
uint const _length;
|
||||
uint const _stride;
|
||||
volatile uint _claimed;
|
||||
Atomic<uint> _claimed;
|
||||
ShenandoahPhaseTimings::Phase _phase;
|
||||
|
||||
uint claim();
|
||||
|
||||
@ -1024,7 +1024,7 @@ ShenandoahRegionChunkIterator::ShenandoahRegionChunkIterator(ShenandoahHeap* hea
|
||||
}
|
||||
|
||||
void ShenandoahRegionChunkIterator::reset() {
|
||||
_index = 0;
|
||||
_index.store_relaxed(0);
|
||||
}
|
||||
|
||||
ShenandoahReconstructRememberedSetTask::ShenandoahReconstructRememberedSetTask(ShenandoahRegionIterator* regions)
|
||||
|
||||
@ -973,7 +973,7 @@ private:
|
||||
const size_t _total_chunks;
|
||||
|
||||
shenandoah_padding(0);
|
||||
volatile size_t _index;
|
||||
Atomic<size_t> _index;
|
||||
shenandoah_padding(1);
|
||||
|
||||
size_t _region_index[_maximum_groups]; // The region index for the first region spanned by this group
|
||||
|
||||
@ -380,14 +380,14 @@ ShenandoahScanRemembered::process_region_slice(ShenandoahHeapRegion *region, siz
|
||||
}
|
||||
|
||||
inline bool ShenandoahRegionChunkIterator::has_next() const {
|
||||
return _index < _total_chunks;
|
||||
return _index.load_relaxed() < _total_chunks;
|
||||
}
|
||||
|
||||
inline bool ShenandoahRegionChunkIterator::next(struct ShenandoahRegionChunk *assignment) {
|
||||
if (_index >= _total_chunks) {
|
||||
if (_index.load_relaxed() >= _total_chunks) {
|
||||
return false;
|
||||
}
|
||||
size_t new_index = AtomicAccess::add(&_index, (size_t) 1, memory_order_relaxed);
|
||||
size_t new_index = _index.add_then_fetch((size_t) 1, memory_order_relaxed);
|
||||
if (new_index > _total_chunks) {
|
||||
// First worker that hits new_index == _total_chunks continues, other
|
||||
// contending workers return false.
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
#include "gc/shared/taskTerminator.hpp"
|
||||
#include "gc/shenandoah/shenandoahPadding.hpp"
|
||||
#include "nmt/memTag.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "runtime/atomicAccess.hpp"
|
||||
#include "runtime/javaThread.hpp"
|
||||
#include "runtime/mutex.hpp"
|
||||
@ -306,7 +307,7 @@ template <class T, MemTag MT>
|
||||
class ParallelClaimableQueueSet: public GenericTaskQueueSet<T, MT> {
|
||||
private:
|
||||
shenandoah_padding(0);
|
||||
volatile jint _claimed_index;
|
||||
Atomic<jint> _claimed_index;
|
||||
shenandoah_padding(1);
|
||||
|
||||
DEBUG_ONLY(uint _reserved; )
|
||||
@ -325,7 +326,7 @@ public:
|
||||
// reserve queues that not for parallel claiming
|
||||
void reserve(uint n) {
|
||||
assert(n <= size(), "Sanity");
|
||||
_claimed_index = (jint)n;
|
||||
_claimed_index.store_relaxed((jint)n);
|
||||
DEBUG_ONLY(_reserved = n;)
|
||||
}
|
||||
|
||||
@ -336,11 +337,11 @@ template <class T, MemTag MT>
|
||||
T* ParallelClaimableQueueSet<T, MT>::claim_next() {
|
||||
jint size = (jint)GenericTaskQueueSet<T, MT>::size();
|
||||
|
||||
if (_claimed_index >= size) {
|
||||
if (_claimed_index.load_relaxed() >= size) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
jint index = AtomicAccess::add(&_claimed_index, 1, memory_order_relaxed);
|
||||
jint index = _claimed_index.add_then_fetch(1, memory_order_relaxed);
|
||||
|
||||
if (index <= size) {
|
||||
return GenericTaskQueueSet<T, MT>::queue((uint)index - 1);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -74,7 +74,7 @@ public:
|
||||
|
||||
static ContainerType encode(ValueType value) {
|
||||
assert(((ContainerType)value & (FieldMask << ValueShift)) == (ContainerType)value, "Invalid value");
|
||||
return ((ContainerType)value >> ValueShift) << FieldShift;
|
||||
return checked_cast<ContainerType>(((ContainerType)value >> ValueShift) << FieldShift);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
@ -78,14 +78,10 @@ class OopMapForCacheEntry: public GenerateOopMap {
|
||||
int _stack_top;
|
||||
|
||||
virtual bool report_results() const { return false; }
|
||||
virtual bool possible_gc_point (BytecodeStream *bcs);
|
||||
virtual void fill_stackmap_prolog (int nof_gc_points);
|
||||
virtual void fill_stackmap_epilog ();
|
||||
virtual void fill_stackmap_for_opcodes (BytecodeStream *bcs,
|
||||
CellTypeState* vars,
|
||||
CellTypeState* stack,
|
||||
int stack_top);
|
||||
virtual void fill_init_vars (GrowableArray<intptr_t> *init_vars);
|
||||
|
||||
public:
|
||||
OopMapForCacheEntry(const methodHandle& method, int bci, OopMapCacheEntry *entry);
|
||||
@ -120,26 +116,6 @@ bool OopMapForCacheEntry::compute_map(Thread* current) {
|
||||
}
|
||||
|
||||
|
||||
bool OopMapForCacheEntry::possible_gc_point(BytecodeStream *bcs) {
|
||||
return false; // We are not reporting any result. We call result_for_basicblock directly
|
||||
}
|
||||
|
||||
|
||||
void OopMapForCacheEntry::fill_stackmap_prolog(int nof_gc_points) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
|
||||
void OopMapForCacheEntry::fill_stackmap_epilog() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
|
||||
void OopMapForCacheEntry::fill_init_vars(GrowableArray<intptr_t> *init_vars) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
|
||||
void OopMapForCacheEntry::fill_stackmap_for_opcodes(BytecodeStream *bcs,
|
||||
CellTypeState* vars,
|
||||
CellTypeState* stack,
|
||||
|
||||
@ -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
|
||||
@ -26,8 +26,10 @@
|
||||
#include "jfr/leakprofiler/chains/edgeUtils.hpp"
|
||||
#include "jfr/leakprofiler/sampling/objectSample.hpp"
|
||||
#include "jfr/leakprofiler/utilities/unifiedOopRef.inline.hpp"
|
||||
#include "jfr/recorder/service/jfrOptionSet.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "runtime/safepoint.hpp"
|
||||
#include "utilities/resizableHashTable.hpp"
|
||||
|
||||
StoredEdge::StoredEdge(const Edge* parent, UnifiedOopRef reference) : Edge(parent, reference), _gc_root_id(0), _skip_length(0) {}
|
||||
|
||||
@ -216,84 +218,62 @@ bool EdgeStore::put_edges(StoredEdge** previous, const Edge** current, size_t li
|
||||
return nullptr == *current;
|
||||
}
|
||||
|
||||
static GrowableArray<const StoredEdge*>* _leak_context_edges = nullptr;
|
||||
typedef ResizeableHashTable<uintptr_t, const StoredEdge*, AnyObj::C_HEAP, mtTracing> SampleToLeakContextEdgeMap;
|
||||
static SampleToLeakContextEdgeMap* _sample_to_leak_context_edge_map = nullptr;
|
||||
|
||||
EdgeStore::EdgeStore() : _edges(new EdgeHashTable(this)) {}
|
||||
|
||||
EdgeStore::~EdgeStore() {
|
||||
assert(_edges != nullptr, "invariant");
|
||||
delete _edges;
|
||||
delete _leak_context_edges;
|
||||
_leak_context_edges = nullptr;
|
||||
delete _sample_to_leak_context_edge_map;
|
||||
_sample_to_leak_context_edge_map = nullptr;
|
||||
}
|
||||
|
||||
static int leak_context_edge_idx(const ObjectSample* sample) {
|
||||
static const StoredEdge* leak_context_edge(const ObjectSample* sample) {
|
||||
assert(sample != nullptr, "invariant");
|
||||
return static_cast<int>(sample->object()->mark().value()) >> markWord::lock_bits;
|
||||
assert(_sample_to_leak_context_edge_map != nullptr, "invariant");
|
||||
const StoredEdge** edge = _sample_to_leak_context_edge_map->get(p2u(sample->object()));
|
||||
return edge != nullptr ? *edge : nullptr;
|
||||
}
|
||||
|
||||
bool EdgeStore::has_leak_context(const ObjectSample* sample) const {
|
||||
const int idx = leak_context_edge_idx(sample);
|
||||
if (idx == 0) {
|
||||
return false;
|
||||
}
|
||||
assert(idx > 0, "invariant");
|
||||
assert(_leak_context_edges != nullptr, "invariant");
|
||||
assert(idx < _leak_context_edges->length(), "invariant");
|
||||
assert(_leak_context_edges->at(idx) != nullptr, "invariant");
|
||||
return true;
|
||||
return _sample_to_leak_context_edge_map != nullptr && leak_context_edge(sample) != nullptr;
|
||||
}
|
||||
|
||||
const StoredEdge* EdgeStore::get(const ObjectSample* sample) const {
|
||||
assert(sample != nullptr, "invariant");
|
||||
if (_leak_context_edges != nullptr) {
|
||||
if (_sample_to_leak_context_edge_map != nullptr) {
|
||||
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
|
||||
const int idx = leak_context_edge_idx(sample);
|
||||
if (idx > 0) {
|
||||
assert(idx < _leak_context_edges->length(), "invariant");
|
||||
const StoredEdge* const edge =_leak_context_edges->at(idx);
|
||||
assert(edge != nullptr, "invariant");
|
||||
const StoredEdge* const edge = leak_context_edge(sample);
|
||||
if (edge != nullptr) {
|
||||
return edge;
|
||||
}
|
||||
}
|
||||
return get(UnifiedOopRef::encode_in_native(sample->object_addr()));
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
// max_idx to ensure idx fit in lower 32-bits of markword together with lock bits.
|
||||
static constexpr const int max_idx = right_n_bits(32 - markWord::lock_bits);
|
||||
static constexpr const unsigned max_map_size = max_jint >> 1;
|
||||
|
||||
static void store_idx_precondition(oop sample_object, int idx) {
|
||||
assert(sample_object != nullptr, "invariant");
|
||||
assert(sample_object->mark().is_marked(), "invariant");
|
||||
assert(idx > 0, "invariant");
|
||||
assert(idx <= max_idx, "invariant");
|
||||
}
|
||||
#endif
|
||||
|
||||
static void store_idx_in_markword(oop sample_object, int idx) {
|
||||
DEBUG_ONLY(store_idx_precondition(sample_object, idx);)
|
||||
const markWord idx_mark_word(sample_object->mark().value() | idx << markWord::lock_bits);
|
||||
sample_object->set_mark(idx_mark_word);
|
||||
assert(sample_object->mark().is_marked(), "must still be marked");
|
||||
}
|
||||
|
||||
static const int initial_size = 64;
|
||||
|
||||
static int save(const StoredEdge* edge) {
|
||||
assert(edge != nullptr, "invariant");
|
||||
if (_leak_context_edges == nullptr) {
|
||||
_leak_context_edges = new (mtTracing) GrowableArray<const StoredEdge*>(initial_size, mtTracing);
|
||||
_leak_context_edges->append(nullptr); // next idx now at 1, for disambiguation in markword.
|
||||
static inline unsigned map_size() {
|
||||
assert(JfrOptionSet::old_object_queue_size() > 0, "invariant");
|
||||
unsigned size = JfrOptionSet::old_object_queue_size();
|
||||
size = round_up_power_of_2(size);
|
||||
if (size < 1024) {
|
||||
return 1024;
|
||||
}
|
||||
return _leak_context_edges->append(edge);
|
||||
size <<= 1;
|
||||
return size >= max_map_size ? max_map_size : size;
|
||||
}
|
||||
|
||||
// We associate the leak context edge with the leak candidate object by saving the
|
||||
// edge in an array and storing the array idx (shifted) into the markword of the candidate object.
|
||||
static void associate_with_candidate(const StoredEdge* leak_context_edge) {
|
||||
assert(leak_context_edge != nullptr, "invariant");
|
||||
store_idx_in_markword(leak_context_edge->pointee(), save(leak_context_edge));
|
||||
if (_sample_to_leak_context_edge_map == nullptr) {
|
||||
const unsigned size = map_size();
|
||||
_sample_to_leak_context_edge_map = new (mtTracing) SampleToLeakContextEdgeMap(size, size);
|
||||
}
|
||||
assert(_sample_to_leak_context_edge_map != nullptr, "invariant");
|
||||
_sample_to_leak_context_edge_map->put(p2u(leak_context_edge->pointee()), leak_context_edge);
|
||||
}
|
||||
|
||||
StoredEdge* EdgeStore::associate_leak_context_with_candidate(const Edge* edge) {
|
||||
|
||||
@ -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
|
||||
@ -39,7 +39,6 @@ class StoredEdge : public Edge {
|
||||
size_t _skip_length;
|
||||
|
||||
public:
|
||||
StoredEdge();
|
||||
StoredEdge(const Edge* parent, UnifiedOopRef reference);
|
||||
StoredEdge(const Edge& edge);
|
||||
StoredEdge(const StoredEdge& edge);
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "runtime/handles.inline.hpp"
|
||||
|
||||
ArrayKlass::ArrayKlass() {
|
||||
ArrayKlass::ArrayKlass() : _dimension() {
|
||||
assert(CDSConfig::is_dumping_static_archive() || CDSConfig::is_using_archive(), "only for CDS");
|
||||
}
|
||||
|
||||
@ -88,9 +88,9 @@ Method* ArrayKlass::uncached_lookup_method(const Symbol* name,
|
||||
return super()->uncached_lookup_method(name, signature, OverpassLookupMode::skip, private_mode);
|
||||
}
|
||||
|
||||
ArrayKlass::ArrayKlass(Symbol* name, KlassKind kind) :
|
||||
ArrayKlass::ArrayKlass(int n, Symbol* name, KlassKind kind) :
|
||||
Klass(kind),
|
||||
_dimension(1),
|
||||
_dimension(n),
|
||||
_higher_dimension(nullptr),
|
||||
_lower_dimension(nullptr) {
|
||||
// Arrays don't add any new methods, so their vtable is the same size as
|
||||
|
||||
@ -38,7 +38,7 @@ class ArrayKlass: public Klass {
|
||||
private:
|
||||
// If you add a new field that points to any metaspace object, you
|
||||
// must add this field to ArrayKlass::metaspace_pointers_do().
|
||||
int _dimension; // This is n'th-dimensional array.
|
||||
const int _dimension; // This is n'th-dimensional array.
|
||||
ObjArrayKlass* volatile _higher_dimension; // Refers the (n+1)'th-dimensional array (if present).
|
||||
ArrayKlass* volatile _lower_dimension; // Refers the (n-1)'th-dimensional array (if present).
|
||||
|
||||
@ -46,7 +46,7 @@ class ArrayKlass: public Klass {
|
||||
// Constructors
|
||||
// The constructor with the Symbol argument does the real array
|
||||
// initialization, the other is a dummy
|
||||
ArrayKlass(Symbol* name, KlassKind kind);
|
||||
ArrayKlass(int n, Symbol* name, KlassKind kind);
|
||||
ArrayKlass();
|
||||
|
||||
public:
|
||||
@ -63,7 +63,6 @@ class ArrayKlass: public Klass {
|
||||
|
||||
// Instance variables
|
||||
int dimension() const { return _dimension; }
|
||||
void set_dimension(int dimension) { _dimension = dimension; }
|
||||
|
||||
ObjArrayKlass* higher_dimension() const { return _higher_dimension; }
|
||||
inline ObjArrayKlass* higher_dimension_acquire() const; // load with acquire semantics
|
||||
|
||||
@ -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
|
||||
@ -391,7 +391,6 @@ void CellTypeState::print(outputStream *os) {
|
||||
//
|
||||
|
||||
void GenerateOopMap::initialize_bb() {
|
||||
_gc_points = 0;
|
||||
_bb_count = 0;
|
||||
_bb_hdr_bits.reinitialize(method()->code_size());
|
||||
}
|
||||
@ -409,7 +408,7 @@ void GenerateOopMap::bb_mark_fct(GenerateOopMap *c, int bci, int *data) {
|
||||
}
|
||||
|
||||
|
||||
void GenerateOopMap::mark_bbheaders_and_count_gc_points() {
|
||||
void GenerateOopMap::mark_bbheaders() {
|
||||
initialize_bb();
|
||||
|
||||
bool fellThrough = false; // False to get first BB marked.
|
||||
@ -445,9 +444,6 @@ void GenerateOopMap::mark_bbheaders_and_count_gc_points() {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (possible_gc_point(&bcs))
|
||||
_gc_points++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2119,8 +2115,6 @@ bool GenerateOopMap::compute_map(Thread* current) {
|
||||
// if no code - do nothing
|
||||
// compiler needs info
|
||||
if (method()->code_size() == 0 || _max_locals + method()->max_stack() == 0) {
|
||||
fill_stackmap_prolog(0);
|
||||
fill_stackmap_epilog();
|
||||
return true;
|
||||
}
|
||||
// Step 1: Compute all jump targets and their return value
|
||||
@ -2129,7 +2123,7 @@ bool GenerateOopMap::compute_map(Thread* current) {
|
||||
|
||||
// Step 2: Find all basic blocks and count GC points
|
||||
if (!_got_error)
|
||||
mark_bbheaders_and_count_gc_points();
|
||||
mark_bbheaders();
|
||||
|
||||
// Step 3: Calculate stack maps
|
||||
if (!_got_error)
|
||||
@ -2186,9 +2180,6 @@ void GenerateOopMap::report_result() {
|
||||
// We now want to report the result of the parse
|
||||
_report_result = true;
|
||||
|
||||
// Prolog code
|
||||
fill_stackmap_prolog(_gc_points);
|
||||
|
||||
// Mark everything changed, then do one interpretation pass.
|
||||
for (int i = 0; i<_bb_count; i++) {
|
||||
if (_basic_blocks[i].is_reachable()) {
|
||||
@ -2197,14 +2188,6 @@ void GenerateOopMap::report_result() {
|
||||
}
|
||||
}
|
||||
|
||||
// Note: Since we are skipping dead-code when we are reporting results, then
|
||||
// the no. of encountered gc-points might be fewer than the previously number
|
||||
// we have counted. (dead-code is a pain - it should be removed before we get here)
|
||||
fill_stackmap_epilog();
|
||||
|
||||
// Report initvars
|
||||
fill_init_vars(_init_vars);
|
||||
|
||||
_report_result = false;
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2023, 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
|
||||
@ -348,17 +348,15 @@ class GenerateOopMap {
|
||||
|
||||
// Basicblock info
|
||||
BasicBlock * _basic_blocks; // Array of basicblock info
|
||||
int _gc_points;
|
||||
int _bb_count;
|
||||
ResourceBitMap _bb_hdr_bits;
|
||||
|
||||
// Basicblocks methods
|
||||
void initialize_bb ();
|
||||
void mark_bbheaders_and_count_gc_points();
|
||||
void mark_bbheaders();
|
||||
bool is_bb_header (int bci) const {
|
||||
return _bb_hdr_bits.at(bci);
|
||||
}
|
||||
int gc_points () const { return _gc_points; }
|
||||
int bb_count () const { return _bb_count; }
|
||||
void set_bbmark_bit (int bci);
|
||||
BasicBlock * get_basic_block_at (int bci) const;
|
||||
@ -450,7 +448,7 @@ class GenerateOopMap {
|
||||
int binsToHold (int no) { return ((no+(BitsPerWord-1))/BitsPerWord); }
|
||||
char *state_vec_to_string (CellTypeState* vec, int len);
|
||||
|
||||
// Helper method. Can be used in subclasses to fx. calculate gc_points. If the current instruction
|
||||
// Helper method. If the current instruction
|
||||
// is a control transfer, then calls the jmpFct all possible destinations.
|
||||
void ret_jump_targets_do (BytecodeStream *bcs, jmpFct_t jmpFct, int varNo,int *data);
|
||||
bool jump_targets_do (BytecodeStream *bcs, jmpFct_t jmpFct, int *data);
|
||||
@ -480,14 +478,7 @@ class GenerateOopMap {
|
||||
bool monitor_safe() { return _monitor_safe; }
|
||||
|
||||
// Specialization methods. Intended use:
|
||||
// - possible_gc_point must return true for every bci for which the stackmaps must be returned
|
||||
// - fill_stackmap_prolog is called just before the result is reported. The arguments tells the estimated
|
||||
// number of gc points
|
||||
// - fill_stackmap_for_opcodes is called once for each bytecode index in order (0...code_length-1)
|
||||
// - fill_stackmap_epilog is called after all results has been reported. Note: Since the algorithm does not report
|
||||
// stackmaps for deadcode, fewer gc_points might have been encountered than assumed during the epilog. It is the
|
||||
// responsibility of the subclass to count the correct number.
|
||||
// - fill_init_vars are called once with the result of the init_vars computation
|
||||
//
|
||||
// All these methods are used during a call to: compute_map. Note: Non of the return results are valid
|
||||
// after compute_map returns, since all values are allocated as resource objects.
|
||||
@ -496,14 +487,10 @@ class GenerateOopMap {
|
||||
virtual bool allow_rewrites () const { return false; }
|
||||
virtual bool report_results () const { return true; }
|
||||
virtual bool report_init_vars () const { return true; }
|
||||
virtual bool possible_gc_point (BytecodeStream *bcs) { ShouldNotReachHere(); return false; }
|
||||
virtual void fill_stackmap_prolog (int nof_gc_points) { ShouldNotReachHere(); }
|
||||
virtual void fill_stackmap_epilog () { ShouldNotReachHere(); }
|
||||
virtual void fill_stackmap_for_opcodes (BytecodeStream *bcs,
|
||||
CellTypeState* vars,
|
||||
CellTypeState* stack,
|
||||
int stackTop) { ShouldNotReachHere(); }
|
||||
virtual void fill_init_vars (GrowableArray<intptr_t> *init_vars) { ShouldNotReachHere();; }
|
||||
};
|
||||
|
||||
//
|
||||
@ -513,19 +500,13 @@ class GenerateOopMap {
|
||||
class ResolveOopMapConflicts: public GenerateOopMap {
|
||||
private:
|
||||
|
||||
bool _must_clear_locals;
|
||||
|
||||
virtual bool report_results() const { return false; }
|
||||
virtual bool report_init_vars() const { return true; }
|
||||
virtual bool allow_rewrites() const { return true; }
|
||||
virtual bool possible_gc_point (BytecodeStream *bcs) { return false; }
|
||||
virtual void fill_stackmap_prolog (int nof_gc_points) {}
|
||||
virtual void fill_stackmap_epilog () {}
|
||||
virtual void fill_stackmap_for_opcodes (BytecodeStream *bcs,
|
||||
CellTypeState* vars,
|
||||
CellTypeState* stack,
|
||||
int stack_top) {}
|
||||
virtual void fill_init_vars (GrowableArray<intptr_t> *init_vars) { _must_clear_locals = init_vars->length() > 0; }
|
||||
|
||||
#ifndef PRODUCT
|
||||
// Statistics
|
||||
@ -535,10 +516,8 @@ class ResolveOopMapConflicts: public GenerateOopMap {
|
||||
#endif
|
||||
|
||||
public:
|
||||
ResolveOopMapConflicts(const methodHandle& method) : GenerateOopMap(method) { _must_clear_locals = false; };
|
||||
|
||||
ResolveOopMapConflicts(const methodHandle& method) : GenerateOopMap(method) { }
|
||||
methodHandle do_potential_rewrite(TRAPS);
|
||||
bool must_clear_locals() const { return _must_clear_locals; }
|
||||
};
|
||||
|
||||
|
||||
@ -551,14 +530,10 @@ class GeneratePairingInfo: public GenerateOopMap {
|
||||
virtual bool report_results() const { return false; }
|
||||
virtual bool report_init_vars() const { return false; }
|
||||
virtual bool allow_rewrites() const { return false; }
|
||||
virtual bool possible_gc_point (BytecodeStream *bcs) { return false; }
|
||||
virtual void fill_stackmap_prolog (int nof_gc_points) {}
|
||||
virtual void fill_stackmap_epilog () {}
|
||||
virtual void fill_stackmap_for_opcodes (BytecodeStream *bcs,
|
||||
CellTypeState* vars,
|
||||
CellTypeState* stack,
|
||||
int stack_top) {}
|
||||
virtual void fill_init_vars (GrowableArray<intptr_t> *init_vars) {}
|
||||
public:
|
||||
GeneratePairingInfo(const methodHandle& method) : GenerateOopMap(method) {};
|
||||
|
||||
|
||||
@ -120,8 +120,7 @@ ObjArrayKlass* ObjArrayKlass::allocate_objArray_klass(ClassLoaderData* loader_da
|
||||
return oak;
|
||||
}
|
||||
|
||||
ObjArrayKlass::ObjArrayKlass(int n, Klass* element_klass, Symbol* name) : ArrayKlass(name, Kind) {
|
||||
set_dimension(n);
|
||||
ObjArrayKlass::ObjArrayKlass(int n, Klass* element_klass, Symbol* name) : ArrayKlass(n, name, Kind) {
|
||||
set_element_klass(element_klass);
|
||||
|
||||
Klass* bk;
|
||||
|
||||
@ -78,7 +78,7 @@ u2 TypeArrayKlass::compute_modifier_flags() const {
|
||||
return JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC;
|
||||
}
|
||||
|
||||
TypeArrayKlass::TypeArrayKlass(BasicType type, Symbol* name) : ArrayKlass(name, Kind) {
|
||||
TypeArrayKlass::TypeArrayKlass(BasicType type, Symbol* name) : ArrayKlass(1, name, Kind) {
|
||||
set_layout_helper(array_layout_helper(type));
|
||||
assert(is_array_klass(), "sanity");
|
||||
assert(is_typeArray_klass(), "sanity");
|
||||
|
||||
@ -2675,6 +2675,10 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
for( uint i=1; i<req(); ++i ) {// For all paths in
|
||||
Node *ii = in(i);
|
||||
Node *new_in = MemNode::optimize_memory_chain(ii, at, nullptr, phase);
|
||||
// MemNode::optimize_memory_chain above may kill us!
|
||||
if (outcnt() == 0) {
|
||||
return top;
|
||||
}
|
||||
if (ii != new_in ) {
|
||||
set_req_X(i, new_in, phase);
|
||||
progress = this;
|
||||
|
||||
@ -40,16 +40,60 @@ class PrintProperties
|
||||
{
|
||||
private:
|
||||
IdealGraphPrinter* _printer;
|
||||
void print_alias_properties(Node* node);
|
||||
void print_escape_properties(Node* node);
|
||||
|
||||
public:
|
||||
PrintProperties(IdealGraphPrinter* printer) : _printer(printer) {}
|
||||
void print_node_properties(Node* node);
|
||||
void print_node_details(Node* node);
|
||||
void print_lrg_properties(const LRG& lrg, const char* buffer);
|
||||
void print_property(int flag, const char* name);
|
||||
void print_property(int flag, const char* name, const char* val);
|
||||
void print_property(int flag, const char* name, int val);
|
||||
};
|
||||
|
||||
void PrintProperties::print_alias_properties(Node* node) {
|
||||
const TypePtr* adr_type = node->adr_type();
|
||||
Compile* C = _printer->C;
|
||||
if (adr_type != nullptr && C->have_alias_type(adr_type)) {
|
||||
Compile::AliasType* at = C->alias_type(adr_type);
|
||||
if (at != nullptr) {
|
||||
print_property(true, "alias_index", at->index());
|
||||
// The value of at->field(), if present, is already dumped in the
|
||||
// "source"/"destination" properties.
|
||||
const Type* element = at->element();
|
||||
if (element != nullptr) {
|
||||
stringStream element_stream;
|
||||
element->dump_on(&element_stream);
|
||||
print_property(true, "alias_element", element_stream.freeze());
|
||||
}
|
||||
print_property(at->is_rewritable(), "alias_is_rewritable");
|
||||
print_property(at->is_volatile(), "alias_is_volatile");
|
||||
print_property(at->general_index() != at->index(), "alias_general_index", at->general_index());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrintProperties::print_escape_properties(Node* node) {
|
||||
// Dump escape analysis state for relevant nodes.
|
||||
if (node->is_Allocate()) {
|
||||
AllocateNode* alloc = node->as_Allocate();
|
||||
print_property(alloc->_is_scalar_replaceable, "is_scalar_replaceable");
|
||||
print_property(alloc->_is_non_escaping, "is_non_escaping");
|
||||
print_property(alloc->does_not_escape_thread(), "does_not_escape_thread");
|
||||
}
|
||||
if (node->is_SafePoint() && node->as_SafePoint()->has_ea_local_in_scope()) {
|
||||
print_property(true, "has_ea_local_in_scope");
|
||||
}
|
||||
if (node->is_CallJava() && node->as_CallJava()->arg_escape()) {
|
||||
print_property(true, "arg_escape");
|
||||
}
|
||||
if (node->is_Initialize() && node->as_Initialize()->does_not_escape()) {
|
||||
print_property(true, "does_not_escape");
|
||||
}
|
||||
}
|
||||
|
||||
void PrintProperties::print_node_properties(Node* node) {
|
||||
const jushort flags = node->flags();
|
||||
print_property((flags & Node::Flag_is_Copy), "is_copy");
|
||||
@ -75,6 +119,15 @@ void PrintProperties::print_node_properties(Node* node) {
|
||||
}
|
||||
}
|
||||
|
||||
void PrintProperties::print_node_details(Node* node) {
|
||||
print_alias_properties(node);
|
||||
|
||||
print_escape_properties(node);
|
||||
|
||||
print_property(node->is_block_proj() != nullptr, "is_block_proj");
|
||||
print_property(node->is_block_start(), "is_block_start");
|
||||
}
|
||||
|
||||
void PrintProperties::print_lrg_properties(const LRG &lrg, const char *buffer) {
|
||||
print_property(true, "mask", buffer);
|
||||
print_property(true, "mask_size", lrg.mask_size());
|
||||
@ -651,61 +704,7 @@ void IdealGraphPrinter::visit_node(Node* n, bool edges) {
|
||||
assert(s2.size() < sizeof(buffer), "size in range");
|
||||
print_prop("dump_spec", buffer);
|
||||
|
||||
const TypePtr* adr_type = node->adr_type();
|
||||
if (adr_type != nullptr && C->have_alias_type(adr_type)) {
|
||||
Compile::AliasType* at = C->alias_type(adr_type);
|
||||
if (at != nullptr) {
|
||||
print_prop("alias_index", at->index());
|
||||
// The value of at->field(), if present, is already dumped in the
|
||||
// "source"/"destination" properties.
|
||||
const Type* element = at->element();
|
||||
if (element != nullptr) {
|
||||
stringStream element_stream;
|
||||
element->dump_on(&element_stream);
|
||||
print_prop("alias_element", element_stream.freeze());
|
||||
}
|
||||
if (at->is_rewritable()) {
|
||||
print_prop("alias_is_rewritable", "true");
|
||||
}
|
||||
if (at->is_volatile()) {
|
||||
print_prop("alias_is_volatile", "true");
|
||||
}
|
||||
if (at->general_index() != at->index()) {
|
||||
print_prop("alias_general_index", at->general_index());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (node->is_block_proj()) {
|
||||
print_prop("is_block_proj", "true");
|
||||
}
|
||||
|
||||
if (node->is_block_start()) {
|
||||
print_prop("is_block_start", "true");
|
||||
}
|
||||
|
||||
// Dump escape analysis state for relevant nodes.
|
||||
if (node->is_Allocate()) {
|
||||
AllocateNode* alloc = node->as_Allocate();
|
||||
if (alloc->_is_scalar_replaceable) {
|
||||
print_prop("is_scalar_replaceable", "true");
|
||||
}
|
||||
if (alloc->_is_non_escaping) {
|
||||
print_prop("is_non_escaping", "true");
|
||||
}
|
||||
if (alloc->does_not_escape_thread()) {
|
||||
print_prop("does_not_escape_thread", "true");
|
||||
}
|
||||
}
|
||||
if (node->is_SafePoint() && node->as_SafePoint()->has_ea_local_in_scope()) {
|
||||
print_prop("has_ea_local_in_scope", "true");
|
||||
}
|
||||
if (node->is_CallJava() && node->as_CallJava()->arg_escape()) {
|
||||
print_prop("arg_escape", "true");
|
||||
}
|
||||
if (node->is_Initialize() && node->as_Initialize()->does_not_escape()) {
|
||||
print_prop("does_not_escape", "true");
|
||||
}
|
||||
print_node.print_node_details(node);
|
||||
|
||||
const char *short_name = "short_name";
|
||||
if (strcmp(node->Name(), "Parm") == 0 && node->as_Proj()->_con >= TypeFunc::Parms) {
|
||||
|
||||
@ -221,12 +221,12 @@ public:
|
||||
// Convert a machine register to a machine register type, so-as to
|
||||
// properly match spill code.
|
||||
const int *_register_save_type;
|
||||
#ifdef ASSERT
|
||||
// Maps from machine register to boolean; true if machine register can
|
||||
// be holding a call argument in some signature.
|
||||
static bool can_be_java_arg( int reg );
|
||||
// Maps from machine register to boolean; true if machine register holds
|
||||
// a spillable argument.
|
||||
static bool is_spillable_arg( int reg );
|
||||
#endif
|
||||
|
||||
// Number of integer live ranges that constitute high register pressure
|
||||
static uint int_pressure_limit();
|
||||
// Number of float live ranges that constitute high register pressure
|
||||
@ -443,9 +443,6 @@ public:
|
||||
// The Method-klass-holder may be passed in the inline_cache_reg
|
||||
// and then expanded into the inline_cache_reg and a method_ptr register
|
||||
|
||||
// Interpreter's Frame Pointer Register
|
||||
static OptoReg::Name interpreter_frame_pointer_reg();
|
||||
|
||||
// Java-Native calling convention
|
||||
// (what you use when intercalling between Java and C++ code)
|
||||
|
||||
|
||||
@ -582,7 +582,6 @@ bool MemNode::detect_ptr_independence(Node* p1, AllocateNode* a1,
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Find an arraycopy ac that produces the memory state represented by parameter mem.
|
||||
// Return ac if
|
||||
// (a) can_see_stored_value=true and ac must have set the value for this load or if
|
||||
@ -697,178 +696,32 @@ ArrayCopyNode* MemNode::find_array_copy_clone(Node* ld_alloc, Node* mem) const {
|
||||
// (Currently, only LoadNode::Ideal has steps (c), (d). More later.)
|
||||
//
|
||||
Node* MemNode::find_previous_store(PhaseValues* phase) {
|
||||
Node* ctrl = in(MemNode::Control);
|
||||
Node* adr = in(MemNode::Address);
|
||||
intptr_t offset = 0;
|
||||
Node* base = AddPNode::Ideal_base_and_offset(adr, phase, offset);
|
||||
AllocateNode* alloc = AllocateNode::Ideal_allocation(base);
|
||||
AccessAnalyzer analyzer(phase, this);
|
||||
|
||||
const TypePtr* adr_type = this->adr_type();
|
||||
if (adr_type == nullptr) {
|
||||
// This means the access is dead
|
||||
return phase->C->top();
|
||||
} else if (adr_type->base() == TypePtr::AnyPtr) {
|
||||
assert(adr_type->ptr() == TypePtr::Null, "MemNode should never access a wide memory");
|
||||
// Give up, this will upset Compile::get_alias_index
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int alias_idx = phase->C->get_alias_index(adr_type);
|
||||
assert(alias_idx != Compile::AliasIdxTop, "must not be a dead node");
|
||||
assert(alias_idx != Compile::AliasIdxBot || !phase->C->do_aliasing(), "must not be a very wide access");
|
||||
|
||||
if (offset == Type::OffsetBot)
|
||||
return nullptr; // cannot unalias unless there are precise offsets
|
||||
|
||||
const bool adr_maybe_raw = check_if_adr_maybe_raw(adr);
|
||||
const TypeOopPtr *addr_t = adr->bottom_type()->isa_oopptr();
|
||||
|
||||
intptr_t size_in_bytes = memory_size();
|
||||
|
||||
Node* mem = in(MemNode::Memory); // start searching here...
|
||||
|
||||
int cnt = 50; // Cycle limiter
|
||||
for (;;) { // While we can dance past unrelated stores...
|
||||
if (--cnt < 0) break; // Caught in cycle or a complicated dance?
|
||||
|
||||
Node* prev = mem;
|
||||
if (mem->is_Store()) {
|
||||
Node* st_adr = mem->in(MemNode::Address);
|
||||
intptr_t st_offset = 0;
|
||||
Node* st_base = AddPNode::Ideal_base_and_offset(st_adr, phase, st_offset);
|
||||
if (st_base == nullptr) {
|
||||
// inscrutable pointer
|
||||
break;
|
||||
}
|
||||
|
||||
// If the bases are the same and the offsets are the same, it seems that this is the exact
|
||||
// store we are looking for, the caller will check if the type of the store matches using
|
||||
// MemNode::can_see_stored_value
|
||||
if (st_base == base && st_offset == offset) {
|
||||
return mem; // (b) found the store that this access observes
|
||||
}
|
||||
|
||||
// If it is provable that the memory accessed by mem does not overlap the memory accessed by
|
||||
// this, we may walk past mem.
|
||||
// For raw accesses, 2 accesses are independent if they have the same base and the offsets
|
||||
// say that they do not overlap.
|
||||
// For heap accesses, 2 accesses are independent if either the bases are provably different
|
||||
// at runtime or the offsets say that the accesses do not overlap.
|
||||
if ((adr_maybe_raw || check_if_adr_maybe_raw(st_adr)) && st_base != base) {
|
||||
// Raw accesses can only be provably independent if they have the same base
|
||||
break;
|
||||
}
|
||||
|
||||
// If the offsets say that the accesses do not overlap, then it is provable that mem and this
|
||||
// do not overlap. For example, a LoadI from Object+8 is independent from a StoreL into
|
||||
// Object+12, no matter what the bases are.
|
||||
if (st_offset != offset && st_offset != Type::OffsetBot) {
|
||||
const int MAX_STORE = MAX2(BytesPerLong, (int)MaxVectorSize);
|
||||
assert(mem->as_Store()->memory_size() <= MAX_STORE, "");
|
||||
if (st_offset >= offset + size_in_bytes ||
|
||||
st_offset <= offset - MAX_STORE ||
|
||||
st_offset <= offset - mem->as_Store()->memory_size()) {
|
||||
// Success: The offsets are provably independent.
|
||||
// (You may ask, why not just test st_offset != offset and be done?
|
||||
// The answer is that stores of different sizes can co-exist
|
||||
// in the same sequence of RawMem effects. We sometimes initialize
|
||||
// a whole 'tile' of array elements with a single jint or jlong.)
|
||||
mem = mem->in(MemNode::Memory);
|
||||
continue; // (a) advance through the independent store
|
||||
}
|
||||
}
|
||||
|
||||
// Same base and overlapping offsets, it seems provable that the accesses overlap, give up
|
||||
if (st_base == base) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Try to prove that 2 different base nodes at compile time are different values at runtime
|
||||
bool known_independent = false;
|
||||
if (detect_ptr_independence(base, alloc, st_base, AllocateNode::Ideal_allocation(st_base), phase)) {
|
||||
known_independent = true;
|
||||
}
|
||||
|
||||
if (known_independent) {
|
||||
mem = mem->in(MemNode::Memory);
|
||||
continue; // (a) advance through the independent store
|
||||
}
|
||||
} else if (mem->is_Proj() && mem->in(0)->is_Initialize()) {
|
||||
InitializeNode* st_init = mem->in(0)->as_Initialize();
|
||||
AllocateNode* st_alloc = st_init->allocation();
|
||||
if (st_alloc == nullptr) {
|
||||
break; // something degenerated
|
||||
}
|
||||
bool known_identical = false;
|
||||
bool known_independent = false;
|
||||
if (alloc == st_alloc) {
|
||||
known_identical = true;
|
||||
} else if (alloc != nullptr) {
|
||||
known_independent = true;
|
||||
} else if (all_controls_dominate(this, st_alloc)) {
|
||||
known_independent = true;
|
||||
}
|
||||
|
||||
if (known_independent) {
|
||||
// The bases are provably independent: Either they are
|
||||
// manifestly distinct allocations, or else the control
|
||||
// of this load dominates the store's allocation.
|
||||
if (alias_idx == Compile::AliasIdxRaw) {
|
||||
mem = st_alloc->in(TypeFunc::Memory);
|
||||
} else {
|
||||
mem = st_init->memory(alias_idx);
|
||||
}
|
||||
continue; // (a) advance through independent store memory
|
||||
}
|
||||
|
||||
// (b) at this point, if we are not looking at a store initializing
|
||||
// the same allocation we are loading from, we lose.
|
||||
if (known_identical) {
|
||||
// From caller, can_see_stored_value will consult find_captured_store.
|
||||
return mem; // let caller handle steps (c), (d)
|
||||
}
|
||||
|
||||
} else if (find_previous_arraycopy(phase, alloc, mem, false) != nullptr) {
|
||||
if (prev != mem) {
|
||||
// Found an arraycopy but it doesn't affect that load
|
||||
continue;
|
||||
}
|
||||
// Found an arraycopy that may affect that load
|
||||
return mem;
|
||||
} else if (mem->is_MergeMem()) {
|
||||
mem = mem->as_MergeMem()->memory_at(alias_idx);
|
||||
continue;
|
||||
} else if (addr_t != nullptr && addr_t->is_known_instance_field()) {
|
||||
// Can't use optimize_simple_memory_chain() since it needs PhaseGVN.
|
||||
if (mem->is_Proj() && mem->in(0)->is_Call()) {
|
||||
// ArrayCopyNodes processed here as well.
|
||||
CallNode *call = mem->in(0)->as_Call();
|
||||
if (!call->may_modify(addr_t, phase)) {
|
||||
mem = call->in(TypeFunc::Memory);
|
||||
continue; // (a) advance through independent call memory
|
||||
}
|
||||
} else if (mem->is_Proj() && mem->in(0)->is_MemBar()) {
|
||||
ArrayCopyNode* ac = nullptr;
|
||||
if (ArrayCopyNode::may_modify(addr_t, mem->in(0)->as_MemBar(), phase, ac)) {
|
||||
break;
|
||||
}
|
||||
mem = mem->in(0)->in(TypeFunc::Memory);
|
||||
continue; // (a) advance through independent MemBar memory
|
||||
} else if (mem->is_ClearArray()) {
|
||||
if (ClearArrayNode::step_through(&mem, (uint)addr_t->instance_id(), phase)) {
|
||||
// (the call updated 'mem' value)
|
||||
continue; // (a) advance through independent allocation memory
|
||||
} else {
|
||||
// Can not bypass initialization of the instance
|
||||
// we are looking for.
|
||||
return mem;
|
||||
}
|
||||
}
|
||||
Node* mem = in(MemNode::Memory); // start searching here...
|
||||
int cnt = 50; // Cycle limiter
|
||||
for (;; cnt--) {
|
||||
// While we can dance past unrelated stores...
|
||||
if (phase->type(mem) == Type::TOP) {
|
||||
// Encounter a dead node
|
||||
return phase->C->top();
|
||||
} else if (cnt <= 0) {
|
||||
// Caught in cycle or a complicated dance?
|
||||
return nullptr;
|
||||
} else if (mem->is_Phi()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Unless there is an explicit 'continue', we must bail out here,
|
||||
// because 'mem' is an inscrutable memory state (e.g., a call).
|
||||
break;
|
||||
AccessAnalyzer::AccessIndependence independence = analyzer.detect_access_independence(mem);
|
||||
if (independence.independent) {
|
||||
// (a) advance through the independent store
|
||||
mem = independence.mem;
|
||||
assert(mem != nullptr, "must not be nullptr");
|
||||
} else {
|
||||
// (b) found the store that this access observes if this is not null
|
||||
// Otherwise, give up if it is null
|
||||
return independence.mem;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr; // bail out
|
||||
@ -918,6 +771,174 @@ uint8_t MemNode::barrier_data(const Node* n) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
AccessAnalyzer::AccessAnalyzer(PhaseValues* phase, MemNode* n)
|
||||
: _phase(phase), _n(n), _memory_size(n->memory_size()), _alias_idx(-1) {
|
||||
Node* adr = _n->in(MemNode::Address);
|
||||
_offset = 0;
|
||||
_base = AddPNode::Ideal_base_and_offset(adr, _phase, _offset);
|
||||
_maybe_raw = MemNode::check_if_adr_maybe_raw(adr);
|
||||
_alloc = AllocateNode::Ideal_allocation(_base);
|
||||
_adr_type = _n->adr_type();
|
||||
|
||||
if (_adr_type != nullptr && _adr_type->base() != TypePtr::AnyPtr) {
|
||||
// Avoid the cases that will upset Compile::get_alias_index
|
||||
_alias_idx = _phase->C->get_alias_index(_adr_type);
|
||||
assert(_alias_idx != Compile::AliasIdxTop, "must not be a dead node");
|
||||
assert(_alias_idx != Compile::AliasIdxBot || !phase->C->do_aliasing(), "must not be a very wide access");
|
||||
}
|
||||
}
|
||||
|
||||
// Decide whether the memory accessed by '_n' and 'other' may overlap. This function may be used
|
||||
// when we want to walk the memory graph to fold a load, or when we want to hoist a load above a
|
||||
// loop when there are no stores that may overlap with the load inside the loop.
|
||||
AccessAnalyzer::AccessIndependence AccessAnalyzer::detect_access_independence(Node* other) const {
|
||||
assert(_phase->type(other) == Type::MEMORY, "must be a memory node %s", other->Name());
|
||||
assert(!other->is_Phi(), "caller must handle Phi");
|
||||
|
||||
if (_adr_type == nullptr) {
|
||||
// This means the access is dead
|
||||
return {false, _phase->C->top()};
|
||||
} else if (_adr_type->base() == TypePtr::AnyPtr) {
|
||||
// An example for this case is an access into the memory address 0 performed using Unsafe
|
||||
assert(_adr_type->ptr() == TypePtr::Null, "MemNode should never access a wide memory");
|
||||
return {false, nullptr};
|
||||
}
|
||||
|
||||
if (_offset == Type::OffsetBot) {
|
||||
// cannot unalias unless there are precise offsets
|
||||
return {false, nullptr};
|
||||
}
|
||||
|
||||
const TypeOopPtr* adr_oop_type = _adr_type->isa_oopptr();
|
||||
Node* prev = other;
|
||||
if (other->is_Store()) {
|
||||
Node* st_adr = other->in(MemNode::Address);
|
||||
intptr_t st_offset = 0;
|
||||
Node* st_base = AddPNode::Ideal_base_and_offset(st_adr, _phase, st_offset);
|
||||
if (st_base == nullptr) {
|
||||
// inscrutable pointer
|
||||
return {false, nullptr};
|
||||
}
|
||||
|
||||
// If the bases are the same and the offsets are the same, it seems that this is the exact
|
||||
// store we are looking for, the caller will check if the type of the store matches using
|
||||
// MemNode::can_see_stored_value
|
||||
if (st_base == _base && st_offset == _offset) {
|
||||
return {false, other};
|
||||
}
|
||||
|
||||
// If it is provable that the memory accessed by 'other' does not overlap the memory accessed
|
||||
// by '_n', we may walk past 'other'.
|
||||
// For raw accesses, 2 accesses are independent if they have the same base and the offsets
|
||||
// say that they do not overlap.
|
||||
// For heap accesses, 2 accesses are independent if either the bases are provably different
|
||||
// at runtime or the offsets say that the accesses do not overlap.
|
||||
if ((_maybe_raw || MemNode::check_if_adr_maybe_raw(st_adr)) && st_base != _base) {
|
||||
// Raw accesses can only be provably independent if they have the same base
|
||||
return {false, nullptr};
|
||||
}
|
||||
|
||||
// If the offsets say that the accesses do not overlap, then it is provable that 'other' and
|
||||
// '_n' do not overlap. For example, a LoadI from Object+8 is independent from a StoreL into
|
||||
// Object+12, no matter what the bases are.
|
||||
if (st_offset != _offset && st_offset != Type::OffsetBot) {
|
||||
const int MAX_STORE = MAX2(BytesPerLong, (int)MaxVectorSize);
|
||||
assert(other->as_Store()->memory_size() <= MAX_STORE, "");
|
||||
if (st_offset >= _offset + _memory_size ||
|
||||
st_offset <= _offset - MAX_STORE ||
|
||||
st_offset <= _offset - other->as_Store()->memory_size()) {
|
||||
// Success: The offsets are provably independent.
|
||||
// (You may ask, why not just test st_offset != offset and be done?
|
||||
// The answer is that stores of different sizes can co-exist
|
||||
// in the same sequence of RawMem effects. We sometimes initialize
|
||||
// a whole 'tile' of array elements with a single jint or jlong.)
|
||||
return {true, other->in(MemNode::Memory)};
|
||||
}
|
||||
}
|
||||
|
||||
// Same base and overlapping offsets, it seems provable that the accesses overlap, give up
|
||||
if (st_base == _base) {
|
||||
return {false, nullptr};
|
||||
}
|
||||
|
||||
// Try to prove that 2 different base nodes at compile time are different values at runtime
|
||||
bool known_independent = false;
|
||||
if (MemNode::detect_ptr_independence(_base, _alloc, st_base, AllocateNode::Ideal_allocation(st_base), _phase)) {
|
||||
known_independent = true;
|
||||
}
|
||||
|
||||
if (known_independent) {
|
||||
return {true, other->in(MemNode::Memory)};
|
||||
}
|
||||
} else if (other->is_Proj() && other->in(0)->is_Initialize()) {
|
||||
InitializeNode* st_init = other->in(0)->as_Initialize();
|
||||
AllocateNode* st_alloc = st_init->allocation();
|
||||
if (st_alloc == nullptr) {
|
||||
// Something degenerated
|
||||
return {false, nullptr};
|
||||
}
|
||||
bool known_identical = false;
|
||||
bool known_independent = false;
|
||||
if (_alloc == st_alloc) {
|
||||
known_identical = true;
|
||||
} else if (_alloc != nullptr) {
|
||||
known_independent = true;
|
||||
} else if (MemNode::all_controls_dominate(_n, st_alloc)) {
|
||||
known_independent = true;
|
||||
}
|
||||
|
||||
if (known_independent) {
|
||||
// The bases are provably independent: Either they are
|
||||
// manifestly distinct allocations, or else the control
|
||||
// of _n dominates the store's allocation.
|
||||
if (_alias_idx == Compile::AliasIdxRaw) {
|
||||
other = st_alloc->in(TypeFunc::Memory);
|
||||
} else {
|
||||
other = st_init->memory(_alias_idx);
|
||||
}
|
||||
return {true, other};
|
||||
}
|
||||
|
||||
// If we are not looking at a store initializing the same
|
||||
// allocation we are loading from, we lose.
|
||||
if (known_identical) {
|
||||
// From caller, can_see_stored_value will consult find_captured_store.
|
||||
return {false, other};
|
||||
}
|
||||
|
||||
} else if (_n->find_previous_arraycopy(_phase, _alloc, other, false) != nullptr) {
|
||||
// Find an arraycopy that may or may not affect the MemNode
|
||||
return {prev != other, other};
|
||||
} else if (other->is_MergeMem()) {
|
||||
return {true, other->as_MergeMem()->memory_at(_alias_idx)};
|
||||
} else if (adr_oop_type != nullptr && adr_oop_type->is_known_instance_field()) {
|
||||
// Can't use optimize_simple_memory_chain() since it needs PhaseGVN.
|
||||
if (other->is_Proj() && other->in(0)->is_Call()) {
|
||||
// ArrayCopyNodes processed here as well.
|
||||
CallNode* call = other->in(0)->as_Call();
|
||||
if (!call->may_modify(adr_oop_type, _phase)) {
|
||||
return {true, call->in(TypeFunc::Memory)};
|
||||
}
|
||||
} else if (other->is_Proj() && other->in(0)->is_MemBar()) {
|
||||
ArrayCopyNode* ac = nullptr;
|
||||
if (!ArrayCopyNode::may_modify(adr_oop_type, other->in(0)->as_MemBar(), _phase, ac)) {
|
||||
return {true, other->in(0)->in(TypeFunc::Memory)};
|
||||
}
|
||||
} else if (other->is_ClearArray()) {
|
||||
if (ClearArrayNode::step_through(&other, (uint)adr_oop_type->instance_id(), _phase)) {
|
||||
// (the call updated 'other' value)
|
||||
return {true, other};
|
||||
} else {
|
||||
// Can not bypass initialization of the instance
|
||||
// we are looking for.
|
||||
return {false, other};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {false, nullptr};
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Should LoadNode::Ideal() attempt to remove control edges?
|
||||
bool LoadNode::can_remove_control() const {
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
#ifndef SHARE_OPTO_MEMNODE_HPP
|
||||
#define SHARE_OPTO_MEMNODE_HPP
|
||||
|
||||
#include "memory/allocation.hpp"
|
||||
#include "opto/multnode.hpp"
|
||||
#include "opto/node.hpp"
|
||||
#include "opto/opcodes.hpp"
|
||||
@ -46,6 +47,8 @@ private:
|
||||
bool _unsafe_access; // Access of unsafe origin.
|
||||
uint8_t _barrier_data; // Bit field with barrier information
|
||||
|
||||
friend class AccessAnalyzer;
|
||||
|
||||
protected:
|
||||
#ifdef ASSERT
|
||||
const TypePtr* _adr_type; // What kind of memory is being addressed?
|
||||
@ -172,6 +175,45 @@ public:
|
||||
#endif
|
||||
};
|
||||
|
||||
// Analyze a MemNode to try to prove that it is independent from other memory accesses
|
||||
class AccessAnalyzer : StackObj {
|
||||
private:
|
||||
PhaseValues* const _phase;
|
||||
MemNode* const _n;
|
||||
Node* _base;
|
||||
intptr_t _offset;
|
||||
const int _memory_size;
|
||||
bool _maybe_raw;
|
||||
AllocateNode* _alloc;
|
||||
const TypePtr* _adr_type;
|
||||
int _alias_idx;
|
||||
|
||||
public:
|
||||
AccessAnalyzer(PhaseValues* phase, MemNode* n);
|
||||
|
||||
// The result of deciding whether a memory node 'other' writes into the memory which '_n'
|
||||
// observes.
|
||||
class AccessIndependence {
|
||||
public:
|
||||
// Whether 'other' writes into the memory which '_n' observes. This value is conservative, that
|
||||
// is, it is only true when it is provable that the memory accessed by the nodes is
|
||||
// non-overlapping.
|
||||
bool independent;
|
||||
|
||||
// If 'independent' is true, this is the memory input of 'other' that corresponds to the memory
|
||||
// location that '_n' observes. For example, if 'other' is a StoreNode, then 'mem' is its
|
||||
// memory input, if 'other' is a MergeMemNode, then 'mem' is the memory input corresponding to
|
||||
// the alias class of '_n'.
|
||||
// If 'independent' is false,
|
||||
// - 'mem' is non-nullptr if it seems that 'other' writes to the exact memory location '_n'
|
||||
// observes.
|
||||
// - 'mem' is nullptr otherwise.
|
||||
Node* mem;
|
||||
};
|
||||
|
||||
AccessIndependence detect_access_independence(Node* other) const;
|
||||
};
|
||||
|
||||
//------------------------------LoadNode---------------------------------------
|
||||
// Load value; requires Memory and Address
|
||||
class LoadNode : public MemNode {
|
||||
|
||||
@ -1539,15 +1539,20 @@ Node* URShiftINode::Ideal(PhaseGVN* phase, bool can_reshape) {
|
||||
Node *add = in(1);
|
||||
if (in1_op == Op_AddI) {
|
||||
Node *lshl = add->in(1);
|
||||
Node *y = add->in(2);
|
||||
if (lshl->Opcode() != Op_LShiftI) {
|
||||
lshl = add->in(2);
|
||||
y = add->in(1);
|
||||
}
|
||||
// Compare shift counts by value, not by node pointer, to also match a not-yet-normalized
|
||||
// negative constant (e.g. -1 vs 31)
|
||||
int lshl_con = 0;
|
||||
if (lshl->Opcode() == Op_LShiftI &&
|
||||
const_shift_count(phase, lshl, &lshl_con) &&
|
||||
(lshl_con & (BitsPerJavaInteger - 1)) == con) {
|
||||
Node *y_z = phase->transform( new URShiftINode(add->in(2),in(2)) );
|
||||
Node *sum = phase->transform( new AddINode( lshl->in(1), y_z ) );
|
||||
return new AndINode( sum, phase->intcon(mask) );
|
||||
Node *y_z = phase->transform(new URShiftINode(y, in(2)));
|
||||
Node *sum = phase->transform(new AddINode(lshl->in(1), y_z));
|
||||
return new AndINode(sum, phase->intcon(mask));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1699,13 +1704,18 @@ Node* URShiftLNode::Ideal(PhaseGVN* phase, bool can_reshape) {
|
||||
const TypeInt *t2 = phase->type(in(2))->isa_int();
|
||||
if (add->Opcode() == Op_AddL) {
|
||||
Node *lshl = add->in(1);
|
||||
Node *y = add->in(2);
|
||||
if (lshl->Opcode() != Op_LShiftL) {
|
||||
lshl = add->in(2);
|
||||
y = add->in(1);
|
||||
}
|
||||
// Compare shift counts by value, not by node pointer, to also match a not-yet-normalized
|
||||
// negative constant (e.g. -1 vs 63)
|
||||
int lshl_con = 0;
|
||||
if (lshl->Opcode() == Op_LShiftL &&
|
||||
const_shift_count(phase, lshl, &lshl_con) &&
|
||||
(lshl_con & (BitsPerJavaLong - 1)) == con) {
|
||||
Node* y_z = phase->transform(new URShiftLNode(add->in(2), in(2)));
|
||||
Node* y_z = phase->transform(new URShiftLNode(y, in(2)));
|
||||
Node* sum = phase->transform(new AddLNode(lshl->in(1), y_z));
|
||||
return new AndLNode(sum, phase->longcon(mask));
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -25,6 +25,7 @@
|
||||
#ifndef SHARE_OPTO_PHASETYPE_HPP
|
||||
#define SHARE_OPTO_PHASETYPE_HPP
|
||||
|
||||
#include "memory/allocation.hpp"
|
||||
#include "utilities/bitMap.inline.hpp"
|
||||
#include "utilities/stringUtils.hpp"
|
||||
|
||||
|
||||
@ -1529,7 +1529,7 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec
|
||||
GrowableArray<JavaThread*>* wantList = nullptr;
|
||||
|
||||
ObjectMonitor* mon = mark.has_monitor()
|
||||
? ObjectSynchronizer::read_monitor(current_thread, hobj(), mark)
|
||||
? ObjectSynchronizer::read_monitor(hobj(), mark)
|
||||
: nullptr;
|
||||
|
||||
if (mon != nullptr) {
|
||||
|
||||
@ -553,6 +553,7 @@ static SpecialFlag const special_jvm_flags[] = {
|
||||
{ "ParallelRefProcEnabled", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) },
|
||||
{ "ParallelRefProcBalancingEnabled", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) },
|
||||
{ "MaxRAM", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) },
|
||||
{ "NewSizeThreadIncrease", JDK_Version::undefined(), JDK_Version::jdk(27), JDK_Version::jdk(28) },
|
||||
|
||||
#ifdef ASSERT
|
||||
{ "DummyObsoleteTestFlag", JDK_Version::undefined(), JDK_Version::jdk(18), JDK_Version::undefined() },
|
||||
|
||||
@ -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
|
||||
@ -26,7 +26,7 @@
|
||||
#define SHARE_RUNTIME_ATOMICACCESS_HPP
|
||||
|
||||
#include "cppstdlib/type_traits.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
#include "memory/allStatic.hpp"
|
||||
#include "metaprogramming/enableIf.hpp"
|
||||
#include "metaprogramming/primitiveConversions.hpp"
|
||||
#include "runtime/orderAccess.hpp"
|
||||
@ -829,7 +829,7 @@ class AtomicAccess::PlatformBitops
|
||||
{};
|
||||
|
||||
template <ScopedFenceType T>
|
||||
class ScopedFenceGeneral: public StackObj {
|
||||
class ScopedFenceGeneral {
|
||||
public:
|
||||
void prefix() {}
|
||||
void postfix() {}
|
||||
|
||||
@ -1686,7 +1686,7 @@ bool Deoptimization::relock_objects(JavaThread* thread, GrowableArray<MonitorInf
|
||||
assert(mon_info->owner()->is_locked(), "object must be locked now");
|
||||
assert(obj->mark().has_monitor(), "must be");
|
||||
assert(!deoptee_thread->lock_stack().contains(obj()), "must be");
|
||||
assert(ObjectSynchronizer::read_monitor(thread, obj(), obj->mark())->has_owner(deoptee_thread), "must be");
|
||||
assert(ObjectSynchronizer::read_monitor(obj(), obj->mark())->has_owner(deoptee_thread), "must be");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Red Hat, Inc. All rights reserved.
|
||||
* Copyright Amazon.com Inc. or its affiliates. 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
|
||||
@ -254,7 +254,7 @@ inline void OMCache::set_monitor(ObjectMonitor *monitor) {
|
||||
|
||||
oop obj = monitor->object_peek();
|
||||
assert(obj != nullptr, "must be alive");
|
||||
assert(monitor == ObjectSynchronizer::get_monitor_from_table(JavaThread::current(), obj), "must exist in table");
|
||||
assert(monitor == ObjectSynchronizer::get_monitor_from_table(obj), "must exist in table");
|
||||
|
||||
OMCacheEntry to_insert = {obj, monitor};
|
||||
|
||||
|
||||
@ -854,7 +854,7 @@ bool ObjectMonitor::deflate_monitor(Thread* current) {
|
||||
}
|
||||
|
||||
if (UseObjectMonitorTable) {
|
||||
ObjectSynchronizer::deflate_monitor(current, obj, this);
|
||||
ObjectSynchronizer::deflate_monitor(obj, this);
|
||||
} else if (obj != nullptr) {
|
||||
// Install the old mark word if nobody else has already done it.
|
||||
install_displaced_markword_in_object(obj);
|
||||
@ -2541,19 +2541,19 @@ bool ObjectMonitor::try_spin(JavaThread* current) {
|
||||
// -----------------------------------------------------------------------------
|
||||
// wait_set management ...
|
||||
|
||||
ObjectWaiter::ObjectWaiter(JavaThread* current) {
|
||||
_next = nullptr;
|
||||
_prev = nullptr;
|
||||
_thread = current;
|
||||
_monitor = nullptr;
|
||||
_notifier_tid = 0;
|
||||
_recursions = 0;
|
||||
TState = TS_RUN;
|
||||
_is_wait = false;
|
||||
_at_reenter = false;
|
||||
_interrupted = false;
|
||||
_do_timed_park = false;
|
||||
_active = false;
|
||||
ObjectWaiter::ObjectWaiter(JavaThread* current)
|
||||
: _next(nullptr),
|
||||
_prev(nullptr),
|
||||
_thread(current),
|
||||
_monitor(nullptr),
|
||||
_notifier_tid(0),
|
||||
_recursions(0),
|
||||
TState(TS_RUN),
|
||||
_is_wait(false),
|
||||
_at_reenter(false),
|
||||
_interrupted(false),
|
||||
_do_timed_park(false),
|
||||
_active(false) {
|
||||
}
|
||||
|
||||
const char* ObjectWaiter::getTStateName(ObjectWaiter::TStates state) {
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
//
|
||||
// When you want to find a monitor associated with an object, you extract the
|
||||
// hash value of the object. Then calculate an index by taking the hash value
|
||||
// and bit-wise AND it with the capacity mask (e.g. size-1) of the OMT. Now
|
||||
// and bit-wise AND it with the capacity mask (i.e., size-1) of the OMT. Now
|
||||
// use that index into the OMT's array of pointers. If the pointer is non
|
||||
// null, check if it's a monitor pointer that is associated with the object.
|
||||
// If so you're done. If the pointer is non null, but associated with another
|
||||
@ -55,7 +55,7 @@
|
||||
// means that the monitor is simply not in the OMT.
|
||||
//
|
||||
// If the size of the pointer array is significantly larger than the number of
|
||||
// pointers in it, the chance of finding the monitor in the hash index
|
||||
// pointers in it, the chance of finding the monitor at the hash index
|
||||
// (without any further linear searching) is quite high. It is also straight
|
||||
// forward to generate C2 code for this, which for the fast path doesn't
|
||||
// contain any branching at all. See: C2_MacroAssembler::fast_lock().
|
||||
@ -69,10 +69,10 @@
|
||||
// old monitor pointers from the old table to the new.
|
||||
//
|
||||
// But since the OMT is a concurrent hash table and things needs to work for
|
||||
// other clients of the OMT while we grow it, it's gets a bit more
|
||||
// other clients of the OMT while we grow it, it gets a bit more
|
||||
// complicated.
|
||||
//
|
||||
// Both the new and (potentially several) old table(s) may exist at the same
|
||||
// The new and (potentially several) old table(s) may exist at the same
|
||||
// time. The newest is always called the "current", and the older ones are
|
||||
// singly linked using a "prev" pointer.
|
||||
//
|
||||
@ -82,7 +82,8 @@
|
||||
//
|
||||
// After that we start to go through all the indexes in the old table. If the
|
||||
// index is empty (the pointer is null) we put a "tombstone" into that index,
|
||||
// which will prevent any future concurrent insert ending up in that index.
|
||||
// which will prevent any future concurrent insert from ending up in that
|
||||
// index.
|
||||
//
|
||||
// If the index contains a monitor pointer, we insert that monitor pointer
|
||||
// into the OMT which can be considered as one generation newer. If the index
|
||||
@ -92,11 +93,11 @@
|
||||
// that is not null, not a tombstone and not removed, is considered to be a
|
||||
// pointer to a monitor.
|
||||
//
|
||||
// When all the monitor pointers from an old OMT has been transferred to the
|
||||
// When all the monitor pointers from an old OMT have been transferred to the
|
||||
// new OMT, the old table is unlinked.
|
||||
//
|
||||
// This copying from an old OMT to one generation newer OMT, will continue
|
||||
// until all the monitor pointers from old OMTs has been transferred to the
|
||||
// until all the monitor pointers from old OMTs have been transferred to the
|
||||
// newest "current" OMT.
|
||||
//
|
||||
// The memory for old, unlinked OMTs will be freed after a thread-local
|
||||
@ -255,7 +256,7 @@ public:
|
||||
}
|
||||
|
||||
ObjectMonitor* prepare_insert(oop obj, intptr_t hash) {
|
||||
// Acquire any tomb stones and relocations if prev transitioned to null.
|
||||
// Acquire any tombstones and relocations if prev transitioned to null.
|
||||
Table* prev = AtomicAccess::load_acquire(&_prev);
|
||||
if (prev != nullptr) {
|
||||
ObjectMonitor* result = prev->prepare_insert(obj, hash);
|
||||
@ -273,7 +274,7 @@ public:
|
||||
|
||||
if (entry == empty()) {
|
||||
// Found an empty slot to install the new monitor in.
|
||||
// To avoid concurrent inserts succeeding, place a tomb stone here.
|
||||
// To avoid concurrent inserts succeeding, place a tombstone here.
|
||||
Entry result = AtomicAccess::cmpxchg(bucket, entry, tombstone(), memory_order_relaxed);
|
||||
if (result == entry) {
|
||||
// Success! Nobody will try to insert here again, except reinsert from rehashing.
|
||||
@ -515,7 +516,7 @@ void ObjectMonitorTable::create() {
|
||||
_curr = new Table(128, nullptr);
|
||||
}
|
||||
|
||||
ObjectMonitor* ObjectMonitorTable::monitor_get(Thread* current, oop obj) {
|
||||
ObjectMonitor* ObjectMonitorTable::monitor_get(oop obj) {
|
||||
const intptr_t hash = obj->mark().hash();
|
||||
Table* curr = AtomicAccess::load_acquire(&_curr);
|
||||
ObjectMonitor* monitor = curr->get(obj, hash);
|
||||
@ -562,7 +563,7 @@ ObjectMonitorTable::Table* ObjectMonitorTable::grow_table(Table* curr) {
|
||||
return result;
|
||||
}
|
||||
|
||||
ObjectMonitor* ObjectMonitorTable::monitor_put_get(Thread* current, ObjectMonitor* monitor, oop obj) {
|
||||
ObjectMonitor* ObjectMonitorTable::monitor_put_get(ObjectMonitor* monitor, oop obj) {
|
||||
const intptr_t hash = obj->mark().hash();
|
||||
Table* curr = AtomicAccess::load_acquire(&_curr);
|
||||
|
||||
@ -577,7 +578,7 @@ ObjectMonitor* ObjectMonitorTable::monitor_put_get(Thread* current, ObjectMonito
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectMonitorTable::remove_monitor_entry(Thread* current, ObjectMonitor* monitor) {
|
||||
void ObjectMonitorTable::remove_monitor_entry(ObjectMonitor* monitor) {
|
||||
oop obj = monitor->object_peek();
|
||||
if (obj == nullptr) {
|
||||
// Defer removal until subsequent rebuilding.
|
||||
@ -586,7 +587,7 @@ void ObjectMonitorTable::remove_monitor_entry(Thread* current, ObjectMonitor* mo
|
||||
const intptr_t hash = obj->mark().hash();
|
||||
Table* curr = AtomicAccess::load_acquire(&_curr);
|
||||
curr->remove(obj, curr->as_entry(monitor), hash);
|
||||
assert(monitor_get(current, obj) != monitor, "should have been removed");
|
||||
assert(monitor_get(obj) != monitor, "should have been removed");
|
||||
}
|
||||
|
||||
// Before handshake; rehash and unlink tables.
|
||||
|
||||
@ -61,11 +61,11 @@ public:
|
||||
} SpecialPointerValues;
|
||||
|
||||
static void create();
|
||||
static ObjectMonitor* monitor_get(Thread* current, oop obj);
|
||||
static ObjectMonitor* monitor_put_get(Thread* current, ObjectMonitor* monitor, oop obj);
|
||||
static ObjectMonitor* monitor_get(oop obj);
|
||||
static ObjectMonitor* monitor_put_get(ObjectMonitor* monitor, oop obj);
|
||||
static void rebuild(GrowableArray<Table*>* delete_list);
|
||||
static void destroy(GrowableArray<Table*>* delete_list);
|
||||
static void remove_monitor_entry(Thread* current, ObjectMonitor* monitor);
|
||||
static void remove_monitor_entry(ObjectMonitor* monitor);
|
||||
|
||||
// Compiler support
|
||||
static address current_table_address();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user