Author SHA1 Message Date
DingliZhang c1e5a7b0b5 8390614: RISC-V: Support VerifyOops for AOT caching stub and code
OpenJDK GHA Sanity Checks / Prepare the run (push) Has been cancelled
OpenJDK GHA Sanity Checks / linux-x64-hs-nopch (push) Has been cancelled
OpenJDK GHA Sanity Checks / linux-x64-hs-zero (push) Has been cancelled
OpenJDK GHA Sanity Checks / linux-x64-hs-minimal (push) Has been cancelled
OpenJDK GHA Sanity Checks / linux-x64-hs-optimized (push) Has been cancelled
OpenJDK GHA Sanity Checks / linux-x64-static-libs (push) Has been cancelled
OpenJDK GHA Sanity Checks / linux-cross-compile (push) Has been cancelled
OpenJDK GHA Sanity Checks / alpine-linux-x64 (push) Has been cancelled
OpenJDK GHA Sanity Checks / macos-x64 (push) Has been cancelled
OpenJDK GHA Sanity Checks / docs (push) Has been cancelled
OpenJDK GHA Sanity Checks / linux-x64 (push) Has been cancelled
OpenJDK GHA Sanity Checks / linux-x64-static (push) Has been cancelled
OpenJDK GHA Sanity Checks / linux-aarch64 (push) Has been cancelled
OpenJDK GHA Sanity Checks / macos-aarch64 (push) Has been cancelled
OpenJDK GHA Sanity Checks / windows-x64 (push) Has been cancelled
OpenJDK GHA Sanity Checks / windows-aarch64 (push) Has been cancelled
JDK-8388417 added this support for x86 and AArch64 but left RISC-V behind.
Now that JDK-8390102 has enabled the AOT code cache on RISC-V, the verify_oop
stub is archived while it still materializes values that are only valid in the
JVM that dumped the cache: the verify_oop mask and bits are emitted as
immediates, and the address of the diagnostic string is emitted without a
relocation, so it dangles in the JVM that loads the cache.

Load the verify_oop mask and bits from AOTRuntimeConstants while dumping, and
preserve the diagnostic strings with AOTCodeCache::add_C_string() so that they
survive into a new JVM instance. The string address is materialized through
ExternalAddress() so that it carries a relocation and gets patched on load.

Skip the VerifyOops code when VerifyAdapterSharing is on, because the address
of the code string may differ between otherwise equivalent adapters.

Assert that the registers passed to the GC check_oop() implementations are
distinct.
2026-08-19 09:35:15 +08:00
Jorn Vernee 87e206b81e 8388792: False positives in jdk_foreign test suite
Reviewed-by: liach
2026-08-18 17:21:04 +00:00
Daisuke Yamazaki 8ac703a8d0 8386861: [Valhalla] emit_opSubstitutabilityCheck speedup
Reviewed-by: chagedorn, mdoerr
2026-08-18 16:21:06 +00:00
Aleksey Shipilev c82f3b8fdf 8388292: Shenandoah: Micro-optimize CompressedOops uses
Reviewed-by: wkemper, ruili
2026-08-18 16:17:47 +00:00
Cesar Soares Lucas 57649b487e 8389873: C2 crashes in ConnectionGraph::can_reduce_phi during escape analysis
Reviewed-by: thartmann, qamai
2026-08-18 16:14:57 +00:00
Aleksey Shipilev 29c7198bbb 8390307: Shenandoah: Print region age in region printouts
Reviewed-by: wkemper, ruili
2026-08-18 16:14:13 +00:00
Christian Hagedorn 60140b6e2b 8381564: [IR Framework] Fix race between accept thread and Driver VM thread
Reviewed-by: mchevalier, thartmann
2026-08-18 15:59:46 +00:00
Peijun Xu 8a507d85e9 8390102: RISC-V: Support AOT Code Cache
Reviewed-by: adinn, fyang, kvn
2026-08-18 13:55:24 +00:00
Erik Gahlin 3be31d10b5 8390133: JFR: OngoingStream reads stale header
Reviewed-by: mgronlun
2026-08-18 13:53:01 +00:00
Ivan Bereziuk c2e49da8f6 8390065: TestDockerMemoryMetrics.java fails in MetricsMemoryTester failcount
Reviewed-by: cnorrbin
2026-08-18 13:52:39 +00:00
Roland Westrelin 0741f25d3d 8390467: C2: _map != nullptr assert failure in LibraryCallKit::inline_Class_cast()
Reviewed-by: qamai, thartmann
2026-08-18 13:46:17 +00:00
Daniel SkantzandEmanuel Peter c2d84f0d7d 8362117: C2: compiler/stringopts/TestStackedConcatsAppendUncommonTrap.java fails with a wrong result due to invalidated liveness assumptions for data phis
Co-authored-by: Emanuel Peter <epeter@openjdk.org>
Reviewed-by: rcastanedalo, dlong, thartmann
2026-08-18 12:50:39 +00:00
Dušan Bálek 64e0cf4162 8389859: Missing TYPE_USE annotations on type variable uses
Reviewed-by: jlahoda
2026-08-18 10:14:53 +00:00
Prasanta Sadhukhan 4a653f8439 8390455: Fix more typos in java.desktop module
Reviewed-by: jdv, azvegint
2026-08-18 08:27:12 +00:00
80 changed files with 2362 additions and 586 deletions
+3 -7
View File
@@ -3180,13 +3180,9 @@ void LIR_Assembler::emit_opSubstitutabilityCheck(LIR_OpSubstitutabilityCheck* op
} else {
Register tmp1 = op->tmp1()->as_register();
Register tmp2 = op->tmp2()->as_register();
if (left == right) { // same operand, so clearly the same klasses, let's save the check
__ b(*op->stub()->entry()); // -> do slow check
} else {
__ cmp_klasses_from_objects(CR0, left, right, tmp1, tmp2);
__ bc_far_optimized(Assembler::bcondCRbiIs1, __ bi0(CR0, Assembler::equal),
*op->stub()->entry()); // same klass -> do slow check
}
__ cmp_klasses_from_objects(CR0, left, right, tmp1, tmp2);
__ bc_far_optimized(Assembler::bcondCRbiIs1, __ bi0(CR0, Assembler::equal),
*op->stub()->entry()); // same klass -> do slow check
// fall through to L_oops_not_equal
}
@@ -36,6 +36,7 @@
#include "ci/ciInlineKlass.hpp"
#include "ci/ciInstance.hpp"
#include "ci/ciObjArrayKlass.hpp"
#include "code/aotCodeCache.hpp"
#include "code/compiledIC.hpp"
#include "gc/shared/collectedHeap.hpp"
#include "nativeInst_riscv.hpp"
@@ -43,6 +44,7 @@
#include "oops/oop.inline.hpp"
#include "runtime/frame.inline.hpp"
#include "runtime/sharedRuntime.hpp"
#include "runtime/threadIdentifier.hpp"
#include "utilities/powerOfTwo.hpp"
#include "vmreg_riscv.inline.hpp"
@@ -441,6 +443,19 @@ void LIR_Assembler::const2reg(LIR_Opr src, LIR_Opr dest, LIR_PatchCode patch_cod
case T_LONG:
assert(patch_code == lir_patch_none, "no patching handled here");
#if INCLUDE_CDS
if (AOTCodeCache::is_on_for_dump()) {
address b = c->as_pointer();
if (b == (address)ThreadIdentifier::unsafe_offset()) {
__ la(dest->as_register_lo(), ExternalAddress(b));
break;
}
if (AOTRuntimeConstants::contains(b)) {
__ load_aotrc_address(dest->as_register_lo(), b);
break;
}
}
#endif
__ mv(dest->as_register_lo(), (intptr_t)c->as_jlong());
break;
@@ -69,7 +69,7 @@ private:
_call_stub_size = 11 * MacroAssembler::instruction_size +
1 * MacroAssembler::instruction_size + wordSize,
// See emit_exception_handler for detail
_exception_handler_size = DEBUG_ONLY(256) NOT_DEBUG(32), // or smaller
_exception_handler_size = DEBUG_ONLY(1*K) NOT_DEBUG(175), // or smaller
// See emit_deopt_handler for detail
// far_call (2) + j (1)
_deopt_handler_size = 1 * MacroAssembler::instruction_size +
@@ -24,6 +24,7 @@
*/
#include "asm/macroAssembler.inline.hpp"
#include "code/aotCodeCache.hpp"
#include "gc/g1/g1BarrierSet.hpp"
#include "gc/g1/g1BarrierSetAssembler.hpp"
#include "gc/g1/g1BarrierSetRuntime.hpp"
@@ -257,9 +258,22 @@ static void generate_post_barrier(MacroAssembler* masm,
assert(thread == xthread, "must be");
assert_different_registers(store_addr, new_val, thread, tmp1, tmp2, noreg);
// Does store cross heap regions?
__ xorr(tmp1, store_addr, new_val); // tmp1 := store address ^ new value
__ srli(tmp1, tmp1, G1HeapRegion::LogOfHRGrainBytes); // tmp1 := ((store address ^ new value) >> LogOfHRGrainBytes)
__ beqz(tmp1, done);
#if INCLUDE_CDS
// AOT code needs to load the barrier grain shift from the aot
// runtime constants area in the code cache otherwise we can compile
// it as an immediate operand
if (AOTCodeCache::is_on_for_dump()) {
__ xorr(tmp1, store_addr, new_val);
__ lwu(tmp2, ExternalAddress(AOTRuntimeConstants::grain_shift_address()));
__ srl(tmp1, tmp1, tmp2);
__ beqz(tmp1, done);
} else
#endif
{
__ xorr(tmp1, store_addr, new_val); // tmp1 := store address ^ new value
__ srli(tmp1, tmp1, G1HeapRegion::LogOfHRGrainBytes); // tmp1 := ((store address ^ new value) >> LogOfHRGrainBytes)
__ beqz(tmp1, done);
}
// Crosses regions, storing null?
if (new_val_may_be_null) {
@@ -24,6 +24,7 @@
*/
#include "classfile/classLoaderData.hpp"
#include "code/aotCodeCache.hpp"
#include "gc/shared/barrierSet.hpp"
#include "gc/shared/barrierSetAssembler.hpp"
#include "gc/shared/barrierSetNMethod.hpp"
@@ -370,10 +371,20 @@ void BarrierSetAssembler::c2i_entry_barrier(MacroAssembler* masm) {
}
void BarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& error) {
assert_different_registers(obj, tmp1, tmp2);
// Check if the oop is in the right area of memory
__ mv(tmp2, (intptr_t) Universe::verify_oop_mask());
__ andr(tmp1, obj, tmp2);
__ mv(tmp2, (intptr_t) Universe::verify_oop_bits());
#if INCLUDE_CDS
if (AOTCodeCache::is_on_for_dump()) {
__ ld(tmp2, ExternalAddress(AOTRuntimeConstants::verify_oop_mask_address()));
__ andr(tmp1, obj, tmp2);
__ ld(tmp2, ExternalAddress(AOTRuntimeConstants::verify_oop_bits_address()));
} else
#endif
{
__ mv(tmp2, (intptr_t) Universe::verify_oop_mask());
__ andr(tmp1, obj, tmp2);
__ mv(tmp2, (intptr_t) Universe::verify_oop_bits());
}
// Compare tmp1 and tmp2.
__ bne(tmp1, tmp2, error);
@@ -24,6 +24,7 @@
*
*/
#include "code/aotCodeCache.hpp"
#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp"
#include "gc/shenandoah/mode/shenandoahMode.hpp"
#include "gc/shenandoah/shenandoahBarrierSet.hpp"
@@ -219,8 +220,14 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm,
// Test for in-cset
if (is_strong) {
__ mv(t1, ShenandoahHeap::in_cset_fast_test_addr());
__ srli(t0, x10, ShenandoahHeapRegion::region_size_bytes_shift_jint());
if (AOTCodeCache::is_on_for_dump()) {
__ ld(t1, ExternalAddress(AOTRuntimeConstants::cset_base_address()));
__ lwu(t0, ExternalAddress(AOTRuntimeConstants::grain_shift_address()));
__ srl(t0, x10, t0);
} else {
__ mv(t1, ShenandoahHeap::in_cset_fast_test_addr());
__ srli(t0, x10, ShenandoahHeapRegion::region_size_bytes_shift_jint());
}
__ add(t1, t1, t0);
__ lbu(t1, Address(t1));
__ test_bit(t0, t1, 0);
@@ -434,10 +441,20 @@ void ShenandoahBarrierSetAssembler::try_peek_weak_handle_in_nmethod(MacroAssembl
}
void ShenandoahBarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& L_error) {
assert_different_registers(obj, tmp1, tmp2);
// Check if the oop is in the right area of memory
__ mv(tmp2, (intptr_t) Universe::verify_oop_mask());
__ andr(tmp1, obj, tmp2);
__ mv(tmp2, (intptr_t) Universe::verify_oop_bits());
#if INCLUDE_CDS
if (AOTCodeCache::is_on_for_dump()) {
__ ld(tmp2, ExternalAddress(AOTRuntimeConstants::verify_oop_mask_address()));
__ andr(tmp1, obj, tmp2);
__ ld(tmp2, ExternalAddress(AOTRuntimeConstants::verify_oop_bits_address()));
} else
#endif
{
__ mv(tmp2, (intptr_t) Universe::verify_oop_mask());
__ andr(tmp1, obj, tmp2);
__ mv(tmp2, (intptr_t) Universe::verify_oop_bits());
}
// Compare tmp1 and tmp2.
__ bne(tmp1, tmp2, L_error);
@@ -815,8 +832,14 @@ void ShenandoahBarrierStubC2::lrb(MacroAssembler& masm) {
__ mv(_tmp2, _obj);
}
__ mv(_tmp1, ShenandoahHeap::in_cset_fast_test_addr());
__ srli(_tmp2, _tmp2, ShenandoahHeapRegion::region_size_bytes_shift_jint());
if (AOTCodeCache::is_on_for_dump()) {
__ lwu(_tmp1, ExternalAddress(AOTRuntimeConstants::grain_shift_address()));
__ srl(_tmp2, _tmp2, _tmp1);
__ ld(_tmp1, ExternalAddress(AOTRuntimeConstants::cset_base_address()));
} else {
__ mv(_tmp1, ShenandoahHeap::in_cset_fast_test_addr());
__ srli(_tmp2, _tmp2, ShenandoahHeapRegion::region_size_bytes_shift_jint());
}
__ add(_tmp1, _tmp1, _tmp2);
__ lbu(_tmp1, Address(_tmp1, 0));
maybe_far_jump_if_zero(masm, _tmp1);
@@ -24,6 +24,7 @@
*/
#include "asm/macroAssembler.inline.hpp"
#include "code/aotCodeCache.hpp"
#include "code/codeBlob.hpp"
#include "code/vmreg.inline.hpp"
#include "gc/z/zAddress.hpp"
@@ -1007,6 +1008,7 @@ void ZBarrierSetAssembler::generate_c1_store_barrier_stub(LIR_Assembler* ce,
#define __ masm->
void ZBarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& error) {
assert_different_registers(obj, tmp1, tmp2);
// C1 calls verify_oop in the middle of barriers, before they have been uncolored
// and after being colored. Therefore, we must deal with colored oops as well.
Label done;
@@ -1044,9 +1046,18 @@ void ZBarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Registe
__ bind(check_zaddress);
// Check if the oop is the right area of memory
__ mv(tmp1, (intptr_t) Universe::verify_oop_mask());
__ andr(tmp1, tmp1, obj);
__ mv(obj, (intptr_t) Universe::verify_oop_bits());
#if INCLUDE_CDS
if (AOTCodeCache::is_on_for_dump()) {
__ ld(tmp1, ExternalAddress(AOTRuntimeConstants::verify_oop_mask_address()));
__ andr(tmp1, tmp1, obj);
__ ld(obj, ExternalAddress(AOTRuntimeConstants::verify_oop_bits_address()));
} else
#endif
{
__ mv(tmp1, (intptr_t) Universe::verify_oop_mask());
__ andr(tmp1, tmp1, obj);
__ mv(obj, (intptr_t) Universe::verify_oop_bits());
}
__ bne(tmp1, obj, error);
__ bind(done);
+102 -29
View File
@@ -26,7 +26,9 @@
#include "asm/assembler.hpp"
#include "asm/assembler.inline.hpp"
#include "cds/archiveBuilder.hpp"
#include "ci/ciInlineKlass.hpp"
#include "code/aotCodeCache.hpp"
#include "code/compiledIC.hpp"
#include "compiler/disassembler.hpp"
#include "gc/shared/barrierSet.hpp"
@@ -514,7 +516,11 @@ void MacroAssembler::clinit_barrier(Register klass, Register tmp, Label* L_fast_
}
void MacroAssembler::_verify_oop(Register reg, const char* s, const char* file, int line) {
if (!VerifyOops) { return; }
if (!VerifyOops || VerifyAdapterSharing) {
// Below address of the code string confuses VerifyAdapterSharing
// because it may differ between otherwise equivalent adapters.
return;
}
// Pass register number to verify_oop_subroutine
const char* b = nullptr;
@@ -522,7 +528,15 @@ void MacroAssembler::_verify_oop(Register reg, const char* s, const char* file,
ResourceMark rm;
stringStream ss;
ss.print("verify_oop: %s: %s (%s:%d)", reg->name(), s, file, line);
b = code_string(ss.as_string());
#if INCLUDE_CDS
if (AOTCodeCache::is_on_for_dump() && !code_section()->scratch_emit()) {
// This will duplicate string to preserve it.
b = AOTCodeCache::add_C_string(ss.as_string());
} else
#endif
{
b = code_string(ss.as_string());
}
}
BLOCK_COMMENT("verify_oop {");
@@ -534,7 +548,7 @@ void MacroAssembler::_verify_oop(Register reg, const char* s, const char* file,
// on the address of the char buffer so that the size of mach nodes for
// scratch emit and normal emit matches.
IncompressibleScope scope(this); // Fixed length
movptr(t0, (address) b);
la(t0, ExternalAddress((address)b));
}
// Call indirectly to solve generation ordering problem
@@ -699,7 +713,9 @@ void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_
}
void MacroAssembler::_verify_oop_addr(Address addr, const char* s, const char* file, int line) {
if (!VerifyOops) {
if (!VerifyOops || VerifyAdapterSharing) {
// Below address of the code string confuses VerifyAdapterSharing
// because it may differ between otherwise equivalent adapters.
return;
}
@@ -708,7 +724,15 @@ void MacroAssembler::_verify_oop_addr(Address addr, const char* s, const char* f
ResourceMark rm;
stringStream ss;
ss.print("verify_oop_addr: %s (%s:%d)", s, file, line);
b = code_string(ss.as_string());
#if INCLUDE_CDS
if (AOTCodeCache::is_on_for_dump() && !code_section()->scratch_emit()) {
// This will duplicate string to preserve it.
b = AOTCodeCache::add_C_string(ss.as_string());
} else
#endif
{
b = code_string(ss.as_string());
}
}
BLOCK_COMMENT("verify_oop_addr {");
@@ -726,7 +750,7 @@ void MacroAssembler::_verify_oop_addr(Address addr, const char* s, const char* f
// on the address of the char buffer so that the size of mach nodes for
// scratch emit and normal emit matches.
IncompressibleScope scope(this); // Fixed length
movptr(t0, (address) b);
la(t0, ExternalAddress((address)b));
}
// Call indirectly to solve generation ordering problem
@@ -879,9 +903,13 @@ void MacroAssembler::resolve_global_jobject(Register value, Register tmp1, Regis
}
void MacroAssembler::stop(const char* msg) {
BLOCK_COMMENT(msg);
// Skip AOT caching C strings in scratch buffer.
const char* str = (code_section()->scratch_emit()) ? msg : AOTCodeCache::add_C_string(msg);
BLOCK_COMMENT(str);
// load msg into a0 so we can access it from the signal handler
// ExternalAddress enables saving and restoring via the code cache
la(c_rarg0, ExternalAddress((address) str));
illegal_instruction(Assembler::csr::time);
emit_int64((uintptr_t)msg);
}
void MacroAssembler::unimplemented(const char* what) {
@@ -911,10 +939,8 @@ void MacroAssembler::emit_static_call_stub() {
void MacroAssembler::call_VM_leaf_base(address entry_point,
int number_of_arguments,
Label *retaddr) {
int32_t offset = 0;
push_reg(RegSet::of(t1, xmethod), sp); // push << t1 & xmethod >> to sp
movptr(t1, entry_point, offset, t0);
jalr(t1, offset);
rt_call(entry_point, t1, t0);
if (retaddr != nullptr) {
bind(*retaddr);
}
@@ -1164,16 +1190,17 @@ void MacroAssembler::jalr(Register Rs, int32_t offset) {
Assembler::jalr(x1, Rs, offset);
}
void MacroAssembler::rt_call(address dest, Register tmp) {
assert(tmp != x5, "tmp register must not be x5.");
void MacroAssembler::rt_call(address dest, Register tmp1, Register tmp2) {
assert_different_registers(tmp1, x5);
assert_different_registers(tmp1, tmp2);
RuntimeAddress target(dest);
if (CodeCache::contains(dest)) {
far_call(target, tmp);
far_call(target, tmp1);
} else {
relocate(target.rspec(), [&] {
int32_t offset;
movptr(tmp, target.target(), offset);
jalr(tmp, offset);
movptr(tmp1, target.target(), offset, tmp2);
jalr(tmp1, offset);
});
}
}
@@ -3054,7 +3081,7 @@ int MacroAssembler::patch_oop(address insn_addr, address o) {
void MacroAssembler::reinit_heapbase() {
if (UseCompressedOops) {
if (Universe::is_fully_initialized()) {
if (Universe::is_fully_initialized() && !AOTCodeCache::is_on_for_dump()) {
mv(xheapbase, CompressedOops::base());
} else {
ld(xheapbase, ExternalAddress(CompressedOops::base_addr()));
@@ -3925,19 +3952,28 @@ void MacroAssembler::decode_klass_not_null(Register dst, Register src, Register
assert_different_registers(dst, tmp);
assert_different_registers(src, tmp);
if (CompressedKlassPointers::base() == nullptr) {
Register xbase = tmp;
if (AOTCodeCache::is_on_for_dump()) {
// We are generating code during AOT buildup that will run in *future* processes
// with likely different encoding settings. Therefore, we have to load the
// encoding base dynamically, we cannot just bake it in as immediate.
// Note that we only need to do this for base. The encoding shift would be the
// same between build time and runtime: the standard precomputed shift.
assert(CompressedKlassPointers::shift() == ArchiveBuilder::precomputed_narrow_klass_shift(),
"unexpected compressed klass shift!");
ld(xbase, ExternalAddress(CompressedKlassPointers::base_addr()));
} else if (CompressedKlassPointers::base() == nullptr) {
if (CompressedKlassPointers::shift() != 0) {
slli(dst, src, CompressedKlassPointers::shift());
} else {
mv(dst, src);
}
return;
} else {
mv(xbase, (uintptr_t)CompressedKlassPointers::base());
}
Register xbase = tmp;
mv(xbase, (uintptr_t)CompressedKlassPointers::base());
if (CompressedKlassPointers::shift() != 0) {
// dst = (src << shift) + xbase
shadd(dst, src, xbase, dst /* temporary, dst != xbase */, CompressedKlassPointers::shift());
@@ -3952,6 +3988,28 @@ void MacroAssembler::encode_klass_not_null(Register r, Register tmp) {
}
void MacroAssembler::encode_klass_not_null(Register dst, Register src, Register tmp) {
Register xbase = dst;
if (dst == src) {
xbase = tmp;
}
if (AOTCodeCache::is_on_for_dump()) {
// We are generating code during AOT buildup that will run in *future* processes
// with likely different encoding settings. Therefore, we have to load the
// encoding base dynamically and must not take the base-value dependent zext
// short cut below. Note that we only need to do this for base; the encoding
// shift is the same at build and run time: the standard precomputed shift.
assert(CompressedKlassPointers::shift() == ArchiveBuilder::precomputed_narrow_klass_shift(),
"unexpected compressed klass shift!");
assert_different_registers(src, xbase);
ld(xbase, ExternalAddress(CompressedKlassPointers::base_addr()));
sub(dst, src, xbase);
if (CompressedKlassPointers::shift() != 0) {
srli(dst, dst, CompressedKlassPointers::shift());
}
return;
}
if (CompressedKlassPointers::base() == nullptr) {
if (CompressedKlassPointers::shift() != 0) {
srli(dst, src, CompressedKlassPointers::shift());
@@ -3967,11 +4025,6 @@ void MacroAssembler::encode_klass_not_null(Register dst, Register src, Register
return;
}
Register xbase = dst;
if (dst == src) {
xbase = tmp;
}
assert_different_registers(src, xbase);
mv(xbase, (uintptr_t)CompressedKlassPointers::base());
sub(dst, src, xbase);
@@ -5353,8 +5406,7 @@ void MacroAssembler::get_thread(Register thread) {
RegSet::range(x28, x31) + ra - thread;
push_reg(saved_regs, sp);
mv(t1, CAST_FROM_FN_PTR(address, Thread::current));
jalr(t1);
rt_call(CAST_FROM_FN_PTR(address, Thread::current), t1, t0);
if (thread != c_rarg0) {
mv(thread, c_rarg0);
}
@@ -5364,12 +5416,33 @@ void MacroAssembler::get_thread(Register thread) {
}
void MacroAssembler::load_byte_map_base(Register reg) {
#if INCLUDE_CDS
if (AOTCodeCache::is_on_for_dump()) {
address byte_map_base_adr = AOTRuntimeConstants::card_table_base_address();
ld(reg, ExternalAddress(byte_map_base_adr));
return;
}
#endif
CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set();
// Strictly speaking the card table base isn't an address at all, and it might
// even be negative. It is thus materialised as a constant.
mv(reg, (uint64_t)ctbs->card_table_base_const());
}
void MacroAssembler::load_aotrc_address(Register reg, address a) {
#if INCLUDE_CDS
assert(AOTRuntimeConstants::contains(a), "address out of range for data area");
if (AOTCodeCache::is_on_for_dump()) {
// all aotrc field addresses should be registered in the AOTCodeCache address table
la(reg, ExternalAddress(a));
} else {
mv(reg, (intptr_t)a);
}
#else
ShouldNotReachHere();
#endif
}
void MacroAssembler::build_frame(int framesize) {
assert(framesize >= 2, "framesize must include space for FP/RA");
assert(framesize % (2*wordSize) == 0, "must preserve 2*wordSize alignment");
@@ -28,6 +28,7 @@
#define CPU_RISCV_MACROASSEMBLER_RISCV_HPP
#include "asm/assembler.inline.hpp"
#include "code/aotCodeCache.hpp"
#include "code/vmreg.hpp"
#include "metaprogramming/enableIf.hpp"
#include "oops/compressedOops.hpp"
@@ -770,7 +771,7 @@ class MacroAssembler: public Assembler {
// is used to keep the entry address for jalr/movptr.
// Uses call() for intra code cache, else movptr + jalr.
// Clobebrs t1
void rt_call(address dest, Register tmp = t1);
void rt_call(address dest, Register tmp1 = t1, Register tmp2 = noreg);
// ret: jalr x0, 0(x1)
inline void ret() {
@@ -1291,6 +1292,9 @@ public:
void load_byte_map_base(Register reg);
// Load a constant address in the AOT Runtime Constants area
void load_aotrc_address(Register reg, address a);
void bang_stack_with_offset(int offset) {
// stack grows down, caller passes positive offset
assert(offset > 0, "must bang with negative offset");
+44 -23
View File
@@ -1557,16 +1557,12 @@ uint MachSpillCopyNode::implementation(C2_MacroAssembler *masm, PhaseRegAlloc *r
// address also needs t0 to materialize an offset outside the 12-bit range.
if (src_lo_rc == rc_stack && dst_lo_rc == rc_stack) {
int last_dst_offset = dst_offset;
if (bottom_type()->isa_vect() != nullptr) {
if (!bottom_type()->isa_pvectmask()) {
assert(ideal_reg() == Op_VecA, "Must be");
int vector_reg_size_in_bytes = Matcher::scalable_vector_reg_size(T_BYTE);
last_dst_offset += vector_reg_size_in_bytes - 8;
} else {
assert(ideal_reg() == Op_RegVectMask, "Must be");
int vmask_size_in_bytes = Matcher::scalable_predicate_reg_slots() * 32 / 8;
last_dst_offset += vmask_size_in_bytes - 4;
}
if (bottom_type()->isa_pvectmask()) {
int vmask_size_in_bytes = Matcher::scalable_predicate_reg_slots() * 32 / 8;
last_dst_offset += vmask_size_in_bytes - 4;
} else if (ideal_reg() == Op_VecA) {
int vector_reg_size_in_bytes = Matcher::scalable_vector_reg_size(T_BYTE);
last_dst_offset += vector_reg_size_in_bytes - 8;
}
if (masm != nullptr && !Assembler::is_simm12(last_dst_offset)) {
@@ -1578,9 +1574,9 @@ uint MachSpillCopyNode::implementation(C2_MacroAssembler *masm, PhaseRegAlloc *r
}
}
if (bottom_type()->isa_vect() != nullptr && masm != nullptr) {
if (!bottom_type()->isa_pvectmask()) {
assert(ideal_reg() == Op_VecA, "Must be");
if (bottom_type()->isa_vect() != nullptr) {
uint ireg = ideal_reg();
if (ireg == Op_VecA && masm) {
int vector_reg_size_in_bytes = Matcher::scalable_vector_reg_size(T_BYTE);
if (src_lo_rc == rc_stack && dst_lo_rc == rc_stack) {
// stack to stack
@@ -1599,8 +1595,7 @@ uint MachSpillCopyNode::implementation(C2_MacroAssembler *masm, PhaseRegAlloc *r
} else {
ShouldNotReachHere();
}
} else {
assert(ideal_reg() == Op_RegVectMask, "Must be");
} else if (bottom_type()->isa_pvectmask() && masm) {
int vmask_size_in_bytes = Matcher::scalable_predicate_reg_slots() * 32 / 8;
if (src_lo_rc == rc_stack && dst_lo_rc == rc_stack) {
// stack to stack
@@ -1703,11 +1698,14 @@ uint MachSpillCopyNode::implementation(C2_MacroAssembler *masm, PhaseRegAlloc *r
st->print("%s", Matcher::regName[dst_lo]);
}
if (bottom_type()->isa_vect() && !bottom_type()->isa_pvectmask()) {
assert(ideal_reg() == Op_VecA, "Must be");
int vsize = Matcher::scalable_vector_reg_size(T_BYTE) * 8;
int vsize = 0;
if (ideal_reg() == Op_VecA) {
vsize = Matcher::scalable_vector_reg_size(T_BYTE) * 8;
} else {
ShouldNotReachHere();
}
st->print("\t# vector spill size = %d", vsize);
} else if (bottom_type()->isa_pvectmask()) {
assert(ideal_reg() == Op_RegVectMask, "Must be");
} else if (ideal_reg() == Op_RegVectMask) {
assert(Matcher::supports_scalable_vector(), "bad register type for spill");
int vsize = Matcher::scalable_predicate_reg_slots() * 32;
st->print("\t# vmask spill size = %d", vsize);
@@ -2423,10 +2421,7 @@ encode %{
// Make the anchor frame walkable
__ la(t0, retaddr);
__ sd(t0, Address(xthread, JavaThread::last_Java_pc_offset()));
int32_t offset = 0;
// No relocation needed
__ movptr(t1, entry, offset, t0); // lui + lui + slli + add
__ jalr(t1, offset);
__ rt_call(entry, t1, t0);
__ bind(retaddr);
__ post_call_nop();
}
@@ -2823,6 +2818,18 @@ operand immP_1()
interface(CONST_INTER);
%}
// AOT Runtime Constants Address
operand immAOTRuntimeConstantsAddress()
%{
// Check if the address is in the range of AOT Runtime Constants
predicate(AOTRuntimeConstants::contains((address)(n->get_ptr())));
match(ConP);
op_cost(0);
format %{ %}
interface(CONST_INTER);
%}
// Int Immediate: low 16-bit mask
operand immI_16bits()
%{
@@ -4784,6 +4791,20 @@ instruct loadConP1(iRegPNoSp dst, immP_1 con)
ins_pipe(ialu_imm);
%}
instruct loadAOTRCAddress(iRegPNoSp dst, immAOTRuntimeConstantsAddress con)
%{
match(Set dst con);
ins_cost(ALU_COST);
format %{ "la $dst, $con\t# aotrc, #@loadAOTRCAddress" %}
ins_encode %{
__ load_aotrc_address($dst$$Register, (address)$con$$constant);
%}
ins_pipe(ialu_imm);
%}
// Load Narrow Pointer Constant
instruct loadConN(iRegNNoSp dst, immN con)
%{
+20 -5
View File
@@ -26,6 +26,7 @@
#ifdef COMPILER2
#include "asm/macroAssembler.hpp"
#include "asm/macroAssembler.inline.hpp"
#include "code/aotCodeCache.hpp"
#include "code/vmreg.hpp"
#include "interpreter/interpreter.hpp"
#include "opto/runtime.hpp"
@@ -58,10 +59,15 @@ public:
//------------------------------generate_uncommon_trap_blob--------------------
UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() {
const char* name = OptoRuntime::stub_name(StubId::c2_uncommon_trap_id);
CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::C2Blob, BlobId::c2_uncommon_trap_id);
if (blob != nullptr) {
return blob->as_uncommon_trap_blob();
}
// Allocate space for the code
ResourceMark rm;
// Setup code generation tools
const char* name = OptoRuntime::stub_name(StubId::c2_uncommon_trap_id);
CodeBuffer buffer(name, 2048, 1024);
if (buffer.blob() == nullptr) {
return nullptr;
@@ -243,8 +249,10 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() {
// Make sure all code is generated
masm->flush();
return UncommonTrapBlob::create(&buffer, oop_maps,
SimpleRuntimeFrame::framesize >> 1);
UncommonTrapBlob* ut_blob = UncommonTrapBlob::create(&buffer, oop_maps,
SimpleRuntimeFrame::framesize >> 1);
AOTCodeCache::store_code_blob(*ut_blob, AOTCodeEntry::C2Blob, BlobId::c2_uncommon_trap_id);
return ut_blob;
}
//------------------------------generate_exception_blob---------------------------
@@ -278,10 +286,15 @@ ExceptionBlob* OptoRuntime::generate_exception_blob() {
assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned");
const char* name = OptoRuntime::stub_name(StubId::c2_exception_id);
CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::C2Blob, BlobId::c2_exception_id);
if (blob != nullptr) {
return blob->as_exception_blob();
}
// Allocate space for the code
ResourceMark rm;
// Setup code generation tools
const char* name = OptoRuntime::stub_name(StubId::c2_exception_id);
CodeBuffer buffer(name, 2048, 1024);
if (buffer.blob() == nullptr) {
return nullptr;
@@ -380,6 +393,8 @@ ExceptionBlob* OptoRuntime::generate_exception_blob() {
masm->flush();
// Set exception blob
return ExceptionBlob::create(&buffer, oop_maps, SimpleRuntimeFrame::framesize >> 1);
ExceptionBlob* ex_blob = ExceptionBlob::create(&buffer, oop_maps, SimpleRuntimeFrame::framesize >> 1);
AOTCodeCache::store_code_blob(*ex_blob, AOTCodeEntry::C2Blob, BlobId::c2_exception_id);
return ex_blob;
}
#endif // COMPILER2
+36 -4
View File
@@ -27,6 +27,7 @@
#include "asm/macroAssembler.hpp"
#include "asm/macroAssembler.inline.hpp"
#include "classfile/symbolTable.hpp"
#include "code/aotCodeCache.hpp"
#include "code/compiledIC.hpp"
#include "code/debugInfoRec.hpp"
#include "code/vtableStubs.hpp"
@@ -2123,6 +2124,12 @@ void SharedRuntime::generate_deopt_blob() {
// Setup code generation tools
int pad = 0;
const char* name = SharedRuntime::stub_name(StubId::shared_deopt_id);
CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::SharedBlob, BlobId::shared_deopt_id);
if (blob != nullptr) {
_deopt_blob = blob->as_deoptimization_blob();
return;
}
CodeBuffer buffer(name, 2048 + pad, 1024);
MacroAssembler* masm = new MacroAssembler(&buffer);
int frame_size_in_words = -1;
@@ -2427,6 +2434,8 @@ void SharedRuntime::generate_deopt_blob() {
_deopt_blob = DeoptimizationBlob::create(&buffer, oop_maps, 0, exception_offset, reexecute_offset, frame_size_in_words);
assert(_deopt_blob != nullptr, "create deoptimization blob fail!");
_deopt_blob->set_unpack_with_exception_in_tls_offset(exception_in_tls_offset);
AOTCodeCache::store_code_blob(*_deopt_blob, AOTCodeEntry::SharedBlob, BlobId::shared_deopt_id);
}
// Number of stack slots between incoming argument block and the start of
@@ -2453,13 +2462,18 @@ VMReg SharedRuntime::thread_register() {
SafepointBlob* SharedRuntime::generate_handler_blob(StubId id, address call_ptr) {
assert(is_polling_page_id(id), "expected a polling page stub id");
const char* name = SharedRuntime::stub_name(id);
CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::SharedBlob, StubInfo::blob(id));
if (blob != nullptr) {
return blob->as_safepoint_blob();
}
ResourceMark rm;
OopMapSet *oop_maps = new OopMapSet();
assert_cond(oop_maps != nullptr);
OopMap* map = nullptr;
// Allocate space for the code. Setup code generation tools.
const char* name = SharedRuntime::stub_name(id);
CodeBuffer buffer(name, 2048, 1024);
MacroAssembler* masm = new MacroAssembler(&buffer);
assert_cond(masm != nullptr);
@@ -2564,7 +2578,10 @@ SafepointBlob* SharedRuntime::generate_handler_blob(StubId id, address call_ptr)
masm->flush();
// Fill-out other meta info
return SafepointBlob::create(&buffer, oop_maps, frame_size_in_words);
SafepointBlob* sp_blob = SafepointBlob::create(&buffer, oop_maps, frame_size_in_words);
AOTCodeCache::store_code_blob(*sp_blob, AOTCodeEntry::SharedBlob, StubInfo::blob(id));
return sp_blob;
}
//
@@ -2579,10 +2596,15 @@ RuntimeStub* SharedRuntime::generate_resolve_blob(StubId id, address destination
assert(StubRoutines::forward_exception_entry() != nullptr, "must be generated before");
assert(is_resolve_id(id), "expected a resolve stub id");
const char* name = SharedRuntime::stub_name(id);
CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::SharedBlob, StubInfo::blob(id));
if (blob != nullptr) {
return blob->as_runtime_stub();
}
// allocate space for the code
ResourceMark rm;
const char* name = SharedRuntime::stub_name(id);
CodeBuffer buffer(name, 1000, 512);
MacroAssembler* masm = new MacroAssembler(&buffer);
assert_cond(masm != nullptr);
@@ -2653,7 +2675,10 @@ RuntimeStub* SharedRuntime::generate_resolve_blob(StubId id, address destination
masm->flush();
// return the blob
return RuntimeStub::new_runtime_stub(name, &buffer, frame_complete, frame_size_in_words, oop_maps, true);
RuntimeStub* rs_blob = RuntimeStub::new_runtime_stub(name, &buffer, frame_complete, frame_size_in_words, oop_maps, true);
AOTCodeCache::store_code_blob(*rs_blob, AOTCodeEntry::SharedBlob, StubInfo::blob(id));
return rs_blob;
}
// Continuation point for throwing of implicit exceptions that are
@@ -2698,6 +2723,11 @@ RuntimeStub* SharedRuntime::generate_throw_exception(StubId id, address runtime_
const char* timer_msg = "SharedRuntime generate_throw_exception";
TraceTime timer(timer_msg, TRACETIME_LOG(Info, startuptime));
CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::SharedBlob, StubInfo::blob(id));
if (blob != nullptr) {
return blob->as_runtime_stub();
}
CodeBuffer code(name, insts_size, locs_size);
OopMapSet* oop_maps = new OopMapSet();
MacroAssembler* masm = new MacroAssembler(&code);
@@ -2756,6 +2786,8 @@ RuntimeStub* SharedRuntime::generate_throw_exception(StubId id, address runtime_
(framesize >> (LogBytesPerWord - LogBytesPerInt)),
oop_maps, false);
assert(stub != nullptr, "create runtime stub fail!");
AOTCodeCache::store_code_blob(*stub, AOTCodeEntry::SharedBlob, StubInfo::blob(id));
return stub;
}
File diff suppressed because it is too large Load Diff
+11 -1
View File
@@ -507,7 +507,17 @@ ATTRIBUTE_ALIGNED(4096) juint StubRoutines::riscv::_crc_table[] =
};
#if INCLUDE_CDS
// nothing to do for riscv
extern void StubGenerator_init_AOTAddressTable(GrowableArray<address>& external_addresses);
void StubRoutines::init_AOTAddressTable() {
ResourceMark rm;
GrowableArray<address> external_addresses;
// publish static addresses referred to by riscv generator
// n.b. we have to use an extern call here because class
// StubGenerator, which provides the static method that knows how to
// add the relevant addresses, is declared in a source file rather
// than in a separately includeable header.
StubGenerator_init_AOTAddressTable(external_addresses);
AOTCodeCache::publish_external_addresses(external_addresses);
}
#endif // INCLUDE_CDS
@@ -30,6 +30,7 @@
#include "runtime/vm_version.hpp"
#include "utilities/formatBuffer.hpp"
#include "utilities/macros.hpp"
#include "utilities/ostream.hpp"
#include <ctype.h>
@@ -509,3 +510,62 @@ bool VM_Version::is_intrinsic_supported(vmIntrinsicID id) {
}
return true;
}
int VM_Version::cpu_features_size() {
return sizeof(RVExtFeatures);
}
void VM_Version::store_cpu_features(void* buf) {
memcpy(buf, RVExtFeatures::current(), sizeof(RVExtFeatures));
}
bool VM_Version::verify_aot_code_cache_features(void* features_buffer) {
RVExtFeatures* features_to_test = (RVExtFeatures*)features_buffer;
return RVExtFeatures::current()->verify_aot_code_cache_features(features_to_test);
}
// Print one feature using the same spelling as features_string(): single letter
// extensions appear as "rvc"/"rvv" and multi-character extensions with a lower
// case leading character ("Zba" -> "zba"). Must stay in sync with the feature
// string built in VM_Version::setup_cpu_available_features().
void VM_Version::print_feature_name(stringStream& ss, RVFeatureValue* feature) {
const char* pretty = feature->pretty();
if (strlen(pretty) == 1) {
ss.print("rv%s", pretty);
} else {
ss.print("%c%s", (char)tolower(pretty[0]), &pretty[1]);
}
}
void VM_Version::insert_features_names(RVExtFeatures* features, stringStream& ss) {
const char* sep = "";
int i = 0;
while (i < RVExtFeatures::MAX_CPU_FEATURE_INDEX) {
if (features->support_feature(i)) {
ss.print("%s", sep);
print_feature_name(ss, _feature_list[i]);
sep = ", ";
}
i += 1;
}
}
void VM_Version::get_cpu_features_name(void* features_buffer, stringStream& ss) {
RVExtFeatures* features = (RVExtFeatures*)features_buffer;
insert_features_names(features, ss);
}
void VM_Version::get_missing_features_name(void* features_set1, void* features_set2, stringStream& ss) {
RVExtFeatures* rv_ext_features_set1 = (RVExtFeatures*)features_set1;
RVExtFeatures* rv_ext_features_set2 = (RVExtFeatures*)features_set2;
const char* sep = "";
int i = 0;
while (i < RVExtFeatures::MAX_CPU_FEATURE_INDEX) {
if (rv_ext_features_set1->support_feature(i) && !rv_ext_features_set2->support_feature(i)) {
ss.print("%s", sep);
print_feature_name(ss, _feature_list[i]);
sep = ", ";
}
i += 1;
}
}
@@ -36,6 +36,7 @@
#include "utilities/sizes.hpp"
class RiscvHwprobe;
class stringStream;
class VM_Version : public Abstract_VM_Version {
friend RiscvHwprobe;
@@ -396,6 +397,15 @@ private:
int idx = element_index(f);
return (_features_bitmap[idx] & feature_bit(f)) != 0;
}
bool verify_aot_code_cache_features(RVExtFeatures* features_to_test) const {
for (int i = 0; i < element_count(); i++) {
if (_features_bitmap[i] != features_to_test->_features_bitmap[i]) {
return false;
}
}
return true;
}
};
// enable extensions based on profile, current supported profiles:
@@ -523,6 +533,17 @@ private:
// Check intrinsic support
static bool is_intrinsic_supported(vmIntrinsicID id);
// AOT Code Cache support
static int cpu_features_size();
static void store_cpu_features(void* buf);
static bool verify_aot_code_cache_features(void* features_buffer);
static void get_cpu_features_name(void* features_buffer, stringStream& ss);
static void get_missing_features_name(void* features_set1, void* features_set2, stringStream& ss);
private:
static void print_feature_name(stringStream& ss, RVFeatureValue* feature);
static void insert_features_names(RVExtFeatures* features, stringStream& ss);
};
#endif // CPU_RISCV_VM_VERSION_RISCV_HPP
+2 -6
View File
@@ -1628,12 +1628,8 @@ void LIR_Assembler::emit_opSubstitutabilityCheck(LIR_OpSubstitutabilityCheck* op
} else {
Register tmp1 = op->tmp1()->as_register();
Register tmp2 = op->tmp2()->as_register();
if (left == right) { // same operand, so clearly the same klasses, let's save the check
__ jmp (*op->stub()->entry()); // -> do slow check
} else {
__ cmp_klasses_from_objects(left, right, tmp1, tmp2);
__ jcc(Assembler::equal, *op->stub()->entry()); // same klass -> do slow check
}
__ cmp_klasses_from_objects(left, right, tmp1, tmp2);
__ jcc(Assembler::equal, *op->stub()->entry()); // same klass -> do slow check
// fall through to L_oops_not_equal
}
@@ -77,6 +77,11 @@
#define REG_LR 1
#define REG_FP 8
// First argument register (x10), used to pass the stop() message
// to the signal handler.
#ifndef REG_A0
#define REG_A0 10
#endif
#define REG_BCP 22
NOINLINE address os::current_stack_pointer() {
@@ -240,11 +245,8 @@ bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info,
stub = SharedRuntime::handle_unsafe_access(thread, next_pc);
}
} else if (sig == SIGILL && nativeInstruction_at(pc)->is_stop()) {
// Pull a pointer to the error message out of the instruction
// stream.
const uint64_t *detail_msg_ptr
= (uint64_t*)(pc + NativeInstruction::instruction_size);
const char *detail_msg = (const char *)*detail_msg_ptr;
// A pointer to the message will have been placed in a0
const char *detail_msg = (const char *)(uc->uc_mcontext.__gregs[REG_A0]);
const char *msg = "stop";
if (TraceTraps) {
tty->print_cr("trap: %s: (SIGILL)", msg);
+5
View File
@@ -3418,6 +3418,11 @@ void LIRGenerator::substitutability_check(If* x, LIRItem& left, LIRItem& right)
void LIRGenerator::substitutability_check_common(Value left_val, Value right_val, LIRItem& left, LIRItem& right,
LIR_Opr equal_result, LIR_Opr not_equal_result, LIR_Opr result,
CodeEmitInfo* info) {
if (left.result() == right.result()) {
__ move(equal_result, result);
return;
}
LIR_Opr tmp1 = LIR_OprFact::illegalOpr;
LIR_Opr tmp2 = LIR_OprFact::illegalOpr;
+7 -5
View File
@@ -204,7 +204,7 @@ uint AOTCodeCache::max_aot_code_size() {
// At this point all AOT class linking seetings are finilized
// and AOT cache is open so we can map AOT code region.
void AOTCodeCache::initialize() {
#if defined(ZERO) || !(defined(AMD64) || defined(AARCH64))
#if defined(ZERO) || !(defined(AMD64) || defined(AARCH64) || defined(RISCV64))
log_info(aot, codecache, init)("AOT Code Cache is not supported on this platform.");
disable_caching();
return;
@@ -263,7 +263,7 @@ void AOTCodeCache::initialize() {
FLAG_SET_DEFAULT(ForceUnreachable, true);
}
FLAG_SET_DEFAULT(DelayCompilerStubsGeneration, false);
#endif // defined(AMD64) || defined(AARCH64)
#endif // defined(AMD64) || defined(AARCH64) || defined(RISCV64)
}
static AOTCodeCache* opened_cache = nullptr; // Use this until we verify the cache
@@ -471,7 +471,7 @@ void AOTCodeCache::Config::record(uint cpu_features_offset) {
_useUnalignedLoadStores = UseUnalignedLoadStores;
#endif
#if defined(AARCH64) && !defined(ZERO)
#if (defined(AARCH64) || defined(RISCV64)) && !defined(ZERO)
_avoidUnalignedAccesses = AvoidUnalignedAccesses;
#endif
@@ -601,14 +601,14 @@ bool AOTCodeCache::Config::verify(AOTCodeCache* cache) const {
}
#endif // defined(X86) && !defined(ZERO)
#if defined(AARCH64) && !defined(ZERO)
#if (defined(AARCH64) || defined(RISCV64)) && !defined(ZERO)
// switching on AvoidUnalignedAccesses may affect validity of array
// copy stubs and nmethods
if (!_avoidUnalignedAccesses && AvoidUnalignedAccesses) {
log_config_mismatch(_avoidUnalignedAccesses, AvoidUnalignedAccesses, "AvoidUnalignedAccesses");
return false;
}
#endif // defined(AARCH64) && !defined(ZERO)
#endif // (defined(AARCH64) || defined(RISCV64)) && !defined(ZERO)
return true;
}
@@ -1941,6 +1941,8 @@ void AOTCodeAddressTable::init_extrs() {
ADD_EXTERNAL_ADDRESS(SharedRuntime::allocate_inline_types);
#if defined(AARCH64) && !defined(ZERO)
ADD_EXTERNAL_ADDRESS(JavaThread::aarch64_get_thread_helper);
#endif
#if (defined(AARCH64) || defined(RISCV64)) && !defined(ZERO)
ADD_EXTERNAL_ADDRESS(BarrierSetAssembler::patching_epoch_addr());
#endif
+16 -1
View File
@@ -352,11 +352,26 @@ public:
#define AOTCODECACHE_CONFIGS_X86_DO(do_var, do_fun)
#endif
#if defined(RISCV64) && !defined(ZERO)
#define AOTCODECACHE_CONFIGS_RISCV_DO(do_var, do_fun) \
do_var(intx, BlockZeroingLowLimit) /* zero blocks stub */ \
do_var(bool, UseBlockZeroing) /* zero blocks stub and nmethods */ \
do_var(bool, UseConservativeFence) /* fence encoding in stubs and nmethods */ \
do_var(bool, UseCtxFencei) /* method entry barrier stub */ \
do_var(bool, UseSecondarySupersCache) /* secondary supers cache in nmethods */ \
do_var(bool, UseZabha) /* narrow cmpxchg selection in nmethods */ \
do_fun(int, RVZicbozBlockSize, (int)VM_Version::zicboz_block_size.value()) \
// END
#else
#define AOTCODECACHE_CONFIGS_RISCV_DO(do_var, do_fun)
#endif
#define AOTCODECACHE_CONFIGS_DO(do_var, do_fun) \
AOTCODECACHE_CONFIGS_GENERIC_DO(do_var, do_fun) \
AOTCODECACHE_CONFIGS_COMPILER2_DO(do_var, do_fun) \
AOTCODECACHE_CONFIGS_AARCH64_DO(do_var, do_fun) \
AOTCODECACHE_CONFIGS_X86_DO(do_var, do_fun) \
AOTCODECACHE_CONFIGS_RISCV_DO(do_var, do_fun) \
// END
#define AOTCODECACHE_DECLARE_VAR(type, name) type _saved_ ## name;
@@ -377,7 +392,7 @@ protected:
bool _useUnalignedLoadStores;
#endif
#if defined(AARCH64) && !defined(ZERO)
#if (defined(AARCH64) || defined(RISCV64)) && !defined(ZERO)
bool _avoidUnalignedAccesses;
#endif
@@ -187,7 +187,7 @@ inline void ShenandoahBarrierSet::satb_barrier(T *field) {
if (ShenandoahSATBBarrier && _heap->is_concurrent_mark_in_progress()) {
T heap_oop = RawAccess<>::oop_load(field);
if (!CompressedOops::is_null(heap_oop)) {
enqueue(CompressedOops::decode(heap_oop));
enqueue(CompressedOops::decode_not_null(heap_oop));
}
}
}
@@ -1465,10 +1465,8 @@ void ShenandoahHeap::print_heap_regions_on(outputStream* st) const {
st->print_cr("Heap Regions:");
st->print_cr("Region state: EU=empty-uncommitted, EC=empty-committed, R=regular, H=humongous start, HP=pinned humongous start");
st->print_cr(" HC=humongous continuation, CS=collection set, TR=trash, P=pinned, CSP=pinned collection set");
st->print_cr("BTE=bottom/top/end, TAMS=top-at-mark-start");
st->print_cr("UWM=update watermark, U=used");
st->print_cr("T=TLAB allocs, G=GCLAB allocs");
st->print_cr("S=shared allocs, L=live data");
st->print_cr("A=age, BTE=bottom/top/end, TAMS=top-at-mark-start, UWM=update watermark, U=used");
st->print_cr("T=TLAB allocs, G=GCLAB allocs, S=shared allocs, L=live data");
st->print_cr("CP=critical pins");
for (size_t i = 0; i < num_regions(); i++) {
@@ -139,7 +139,7 @@ inline void ShenandoahHeap::conc_update_with_forwarded(T* p) {
// Either we succeed in updating the reference, or something else gets in our way.
// We don't care if that is another concurrent GC update, or another mutator update.
atomic_update_oop(fwd, p, obj);
atomic_update_oop(fwd, p, o);
}
}
}
@@ -196,14 +196,14 @@ inline void ShenandoahHeap::atomic_update_oop(oop update, oop* addr, oop compare
inline void ShenandoahHeap::atomic_update_oop(oop update, narrowOop* addr, narrowOop compare) {
assert(is_aligned(addr, sizeof(narrowOop)), "Address should be aligned: " PTR_FORMAT, p2i(addr));
narrowOop u = CompressedOops::encode(update);
narrowOop u = CompressedOops::encode_not_null(update);
AtomicAccess::cmpxchg(addr, compare, u, memory_order_release);
}
inline void ShenandoahHeap::atomic_update_oop(oop update, narrowOop* addr, oop compare) {
assert(is_aligned(addr, sizeof(narrowOop)), "Address should be aligned: " PTR_FORMAT, p2i(addr));
narrowOop c = CompressedOops::encode(compare);
narrowOop u = CompressedOops::encode(update);
narrowOop u = CompressedOops::encode_not_null(update);
AtomicAccess::cmpxchg(addr, c, u, memory_order_release);
}
@@ -214,14 +214,14 @@ inline bool ShenandoahHeap::atomic_update_oop_check(oop update, oop* addr, oop c
inline bool ShenandoahHeap::atomic_update_oop_check(oop update, narrowOop* addr, narrowOop compare) {
assert(is_aligned(addr, sizeof(narrowOop)), "Address should be aligned: " PTR_FORMAT, p2i(addr));
narrowOop u = CompressedOops::encode(update);
narrowOop u = CompressedOops::encode_not_null(update);
return (narrowOop) AtomicAccess::cmpxchg(addr, compare, u, memory_order_release) == compare;
}
inline bool ShenandoahHeap::atomic_update_oop_check(oop update, narrowOop* addr, oop compare) {
assert(is_aligned(addr, sizeof(narrowOop)), "Address should be aligned: " PTR_FORMAT, p2i(addr));
narrowOop c = CompressedOops::encode(compare);
narrowOop u = CompressedOops::encode(update);
narrowOop u = CompressedOops::encode_not_null(update);
return CompressedOops::decode(AtomicAccess::cmpxchg(addr, c, u, memory_order_release)) == compare;
}
@@ -236,7 +236,7 @@ inline void ShenandoahHeap::atomic_clear_oop(oop* addr, oop compare) {
inline void ShenandoahHeap::atomic_clear_oop(narrowOop* addr, oop compare) {
assert(is_aligned(addr, sizeof(narrowOop)), "Address should be aligned: " PTR_FORMAT, p2i(addr));
narrowOop cmp = CompressedOops::encode(compare);
narrowOop cmp = CompressedOops::encode_not_null(compare);
AtomicAccess::cmpxchg(addr, cmp, narrowOop(), memory_order_relaxed);
}
@@ -424,6 +424,7 @@ void ShenandoahHeapRegion::print_on(outputStream* st) const {
}
st->print("|%s", shenandoah_affiliation_code(affiliation()));
st->print("|A %2d", age());
#define SHR_PTR_FORMAT "%12" PRIxPTR
@@ -63,7 +63,7 @@ JRT_LEAF(narrowOop, ShenandoahRuntime::load_reference_barrier_strong_narrow_narr
assert(!CompressedOops::is_null(src), "Filtered by caller");
oop s = CompressedOops::decode_not_null(src);
oop r = ShenandoahBarrierSet::barrier_set()->load_reference_barrier_mutator<ON_STRONG_OOP_REF, narrowOop>(s, load_addr);
return CompressedOops::encode(r);
return CompressedOops::encode_not_null(r);
JRT_END
JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier_weak(oopDesc* src, oop* load_addr))
+2 -2
View File
@@ -399,7 +399,7 @@ bool ConnectionGraph::compute_escape() {
if (VerifyReduceAllocationMerges) {
for (uint i = 0; i < reducible_merges.size(); i++ ) {
Node* n = reducible_merges.at(i);
if (!can_reduce_phi(n->as_Phi())) {
if (n->outcnt() > 0 && !can_reduce_phi(n->as_Phi())) {
TraceReduceAllocationMerges = true;
n->dump(2);
n->dump(-2);
@@ -666,7 +666,7 @@ bool ConnectionGraph::can_reduce_phi(PhiNode* ophi) const {
// If there was an error attempting to reduce allocation merges for this
// method we might have disabled the compilation and be retrying with RAM
// disabled.
if (!_compile->do_reduce_allocation_merges() || ophi->region()->Opcode() != Op_Region) {
if (!_compile->do_reduce_allocation_merges() || ophi->region() == nullptr || ophi->region()->Opcode() != Op_Region) {
return false;
}
+2 -3
View File
@@ -4631,7 +4631,7 @@ bool LibraryCallKit::inline_Class_cast() {
}
// Not-subtype or the mirror's klass ptr is nullptr (in case it is a primitive).
enum { _bad_type_path = 1, _prim_path = 2, _npe_path = 3, PATH_LIMIT };
enum { _bad_type_path = 1, _prim_path = 2, PATH_LIMIT };
RegionNode* region = new RegionNode(PATH_LIMIT);
record_for_igvn(region);
@@ -4653,8 +4653,7 @@ bool LibraryCallKit::inline_Class_cast() {
region->init_req(_bad_type_path, bad_type_ctrl);
}
if (region->in(_prim_path) != top() ||
region->in(_bad_type_path) != top() ||
region->in(_npe_path) != top()) {
region->in(_bad_type_path) != top()) {
// Let Interpreter throw ClassCastException.
PreserveJVMState pjvms(this);
if (new_cast_failure_map != nullptr) {
+91 -3
View File
@@ -52,6 +52,9 @@ class StringConcat : public ResourceObj {
Node_List _control; // List of control nodes that will be deleted
Node_List _uncommon_traps; // Uncommon traps that needs to be rewritten
// to restart at the initial JVMState.
Unique_Node_List _allowed_compares; // validate_control_flow() needs to know which compare nodes are
// accepted users of call results. In case of stacked concats,
// these need to be persisted across merges for validation.
static constexpr uint STACKED_CONCAT_UPPER_BOUND = 256; // argument limit for a merged concat.
// The value 256 was derived by measuring
@@ -286,6 +289,10 @@ void StringConcat::eliminate_unneeded_control() {
StringConcat* StringConcat::merge(StringConcat* other, Node* arg) {
StringConcat* result = new StringConcat(_stringopts, _end);
Unique_Node_List null_check_ifs;
Unique_Node_List skipped_phis;
for (uint x = 0; x < _control.size(); x++) {
Node* n = _control.at(x);
if (n->is_Call()) {
@@ -311,6 +318,17 @@ StringConcat* StringConcat::merge(StringConcat* other, Node* arg) {
result->append(other->argument(y), other->mode(y));
}
arguments_appended += other->num_arguments();
// Cache elements for later verification.
if (argument(x)->is_Phi()) {
Node* phi = argument(x);
assert(phi->as_Phi()->is_diamond_phi() > 0, "must be a diamond phi (ref. skip_string_null_check).");
Node* iff = phi->in(0)->in(1)->in(0);
Node* bol = iff->in(1);
Node* cmpp = bol->as_Bool()->in(1);
null_check_ifs.push(iff);
skipped_phis.push(phi);
result->_allowed_compares.push(cmpp);
}
} else {
result->append(argx, mode(x));
arguments_appended++;
@@ -327,13 +345,55 @@ StringConcat* StringConcat::merge(StringConcat* other, Node* arg) {
return nullptr;
}
}
// Verify that the diamond region isn't shared with non-null check phis;
// and that the associated bool doesn't have external uses.
for (uint i = 0; i < skipped_phis.size(); i++) {
Node* n = skipped_phis.at(i);
Node* r = n->in(0);
for (SimpleDUIterator j(r); j.has_next(); j.next()) {
Node* n2 = j.get();
if (n2->is_Phi() && !n2->is_memory_phi() && !skipped_phis.member(n2)) {
#ifndef PRODUCT
if (PrintOptimizeStringConcat) {
tty->print_cr("null-check diamond region has external phi uses");
}
#endif
return nullptr;
}
}
Node* iff = n->in(0)->in(1)->in(0);
Node* bol = iff->in(1);
for (SimpleDUIterator j(bol); j.has_next(); j.next()) {
if (!null_check_ifs.member(j.get())) {
#ifndef PRODUCT
if (PrintOptimizeStringConcat) {
tty->print_cr("null-check diamond bool has external uses.");
}
#endif
return nullptr;
}
}
}
result->set_allocation(other->_begin);
for (uint i = 0; i < _constructors.size(); i++) {
result->add_constructor(_constructors.at(i));
}
for (uint i = 0; i < other->_constructors.size(); i++) {
result->add_constructor(other->_constructors.at(i));
}
// We add previous _allowed_compares in case of repeated stacked concatenation.
for (uint i = 0; i < _allowed_compares.size(); i++) {
result->_allowed_compares.push(_allowed_compares.at(i));
}
for (uint i = 0; i < other->_allowed_compares.size(); i++) {
result->_allowed_compares.push(other->_allowed_compares.at(i));
}
result->_multiple = true;
return result;
}
@@ -936,6 +996,10 @@ bool StringConcat::validate_control_flow() {
int null_check_count = 0;
Unique_Node_List ctrl_path;
// Local version of _allowed_compares that stores allowed comparisons discovered during traversal
// but that we won't persist across merges.
Unique_Node_List local_allowed_compares;
assert(_control.contains(_begin), "missing");
assert(_control.contains(_end), "missing");
@@ -993,11 +1057,31 @@ bool StringConcat::validate_control_flow() {
Node* v2 = cmp->in(2);
Node* otherproj = iff->proj_out(1 - ptr->as_Proj()->_con);
// Null check of the return of append which can simply be eliminated
// Either a null check of the return of append which can simply be eliminated,
// or possibly of a toString during stacked concats.
if (b->_test._test == BoolTest::ne &&
v2->bottom_type() == TypePtr::NULL_PTR &&
v1->is_Proj() && ctrl_path.member(v1->in(0))) {
// null check of the return value of the append
if (!is_SB_toString(v1->in(0))) {
// append type
assert(v1->in(0)->as_CallStaticJava()->method()->name() == ciSymbols::append_name(), "must be");
local_allowed_compares.push(cmp);
} else {
// toString
assert(_multiple, "if not _multiple, we should not have a toString on this control path");
if (!_allowed_compares.member(cmp)) {
// Should have been populated during merge if valid.
// This should also be caught in result use verification later but we can fail early here.
fail = true;
#ifndef PRODUCT
if (PrintOptimizeStringConcat) {
tty->print_cr("Failing as toString()-dependent compare is not part of a recognized string null check.");
cmp->dump();
}
#endif
break;
}
}
null_check_count++;
if (otherproj->outcnt() == 1) {
CallStaticJavaNode* call = otherproj->unique_out()->isa_CallStaticJava();
@@ -1022,6 +1106,8 @@ bool StringConcat::validate_control_flow() {
((v1->is_Proj() && is_SB_toString(v1->in(0)) && ctrl_path.member(v1->in(0))) ||
(v2->is_Proj() && is_SB_toString(v2->in(0)) && ctrl_path.member(v2->in(0))))) {
// iftrue -> if -> bool -> cmpp -> resproj -> tostring
assert(!_allowed_compares.member(cmp) && !local_allowed_compares.member(cmp), "Unsound dependency on intermediate values");
// Would be caught by containment analysis later but we can fail early here.
fail = true;
break;
}
@@ -1147,7 +1233,9 @@ bool StringConcat::validate_control_flow() {
continue;
}
int opc = use->Opcode();
if (opc == Op_CmpP || opc == Op_Node) {
if (opc == Op_Node ||
(opc == Op_CmpP && (use->outcnt() == 1) // The cmpp validation assumes a unique use.
&& (local_allowed_compares.member(use) || _allowed_compares.member(use)))) {
ctrl_path.push(use);
continue;
}
@@ -1359,7 +1359,7 @@ public class LWWindowPeer
if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
focusLog.fine("ungrabbing on " + grabbingWindow);
}
// ungrab a simple window if its owner looses activation.
// ungrab a simple window if its owner loses activation.
grabbingWindow.ungrab();
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -101,8 +101,8 @@ struct _ImageSDOps
CGDataProviderRef dataProvider;
// Pointer in memory that is used for create the CGBitmapContext and the CGDataProvider (used for imgRef). This is a native
// copy of the pixels for the Image. There is a spearate copy of the pixels that lives in Java heap. There are two main
// reasons why we keep those pixels spearate: 1) CG doesn't support all the Java pixel formats 2) The Garbage collector can
// copy of the pixels for the Image. There is a separate copy of the pixels that lives in Java heap. There are two main
// reasons why we keep those pixels separate: 1) CG doesn't support all the Java pixel formats 2) The Garbage collector can
// move the java pixels at any time. There are possible workarounds for both problems. Number 2) seems to be a more serious issue, since
// we can solve 1) by only supporting certain image types.
void * nativePixels;
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1810,7 +1810,7 @@ public class PNGMetadata extends IIOMetadata implements Cloneable {
* Latin-1 [ISO-8859-1] characters and spaces; that is, only
* character codes 32-126 and 161-255 decimal are allowed.
* For Latin-1 value fields the 0x10 (linefeed) control
* character is aloowed too.
* character is allowed too.
*
* See: http://www.w3.org/TR/PNG/#11keywords
*/
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -377,7 +377,7 @@ public abstract class TIFFDecompressor {
/**
* The width of the source region that will actually be copied
* into the destination image, taking into account all
* susbampling, offsetting, and clipping.
* subsampling, offsetting, and clipping.
*
* <p> The active source width will always be equal to
* {@code (dstWidth - 1)*subsampleX + 1}.
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,7 @@
package com.sun.media.sound;
/**
* This class is used to store modulator/artiuclation data.
* This class is used to store modulator/articulation data.
* A modulator connects one synthesizer source to
* a destination. For example a note on velocity
* can be mapped to the gain of the synthesized voice.
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1221,7 +1221,7 @@ public final class SoftSynthesizer implements AudioSynthesizer,
// Always create external_channels array
// with 16 or more channels
// so getChannels works correctly
// when the synhtesizer is closed.
// when the synthesizer is closed.
if (channels.length < 16)
external_channels = new SoftChannelProxy[16];
else
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2016, 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
@@ -48,7 +48,7 @@ public final class WaveFileReader extends SunFileReader {
StandardFileFormat getAudioFileFormatImpl(final InputStream stream)
throws UnsupportedAudioFileException, IOException {
// assumes sream is rewound
// assumes stream is rewound
int nread = 0;
int fmt;
@@ -908,7 +908,7 @@ public class Dialog extends Window {
}
// This call is required as the show() method of the Dialog class
// does not invoke the super.show(). So wried... :(
// does not invoke the super.show(). So weird... :(
mixOnShowing();
peer.setVisible(true); // now guaranteed never to block
@@ -417,7 +417,7 @@ public abstract class AbstractButton extends JComponent implements ItemSelectabl
* the label.
*
* @return an <code>Insets</code> object specifying the margin
* between the botton's border and the label
* between the button's border and the label
* @see #setMargin
*/
public Insets getMargin() {
@@ -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
@@ -534,7 +534,7 @@ public class ProgressMonitor implements Accessible
* AccessibleJLabel
* AccessibleJProgressBar
*
* The abstraction presented to assitive technologies by
* The abstraction presented to assistive technologies by
* the AccessibleProgressMonitor is that a dialog contains a
* progress monitor with three children: a message, a note
* label and a progress bar.
@@ -532,7 +532,7 @@ public class BasicComboBoxUI extends ComboBoxUI {
}
/**
* Creates the default renderer that will be used in a non-editiable combo
* Creates the default renderer that will be used in a non-editable combo
* box. A default renderer will used only if a renderer has not been
* explicitly set with <code>setRenderer</code>.
*
@@ -747,7 +747,7 @@ public class BasicListUI extends ListUI
/**
* Unregisters keyboard actions installed from
* <code>installKeyboardActions</code>.
* This method is called at uninstallUI() time - subclassess should
* This method is called at uninstallUI() time - subclasses should
* ensure that all of the keyboard actions registered at installUI
* time are removed here.
*
@@ -105,7 +105,7 @@ public class SynthComboBoxUI extends BasicComboBoxUI implements
private ButtonHandler buttonHandler;
/**
* Handler for repainting combo when editor component gains/looses focus
* Handler for repainting combo when editor component gains/loses focus
*/
private EditorFocusHandler editorFocusHandler;
@@ -766,7 +766,7 @@ public class SynthComboBoxUI extends BasicComboBoxUI implements
}
/**
* Handler for repainting combo when editor component gains/looses focus
* Handler for repainting combo when editor component gains/loses focus
*/
private static class EditorFocusHandler implements FocusListener,
PropertyChangeListener {
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -3164,7 +3164,7 @@ public class StyleSheet extends StyleContext {
/**
* SelectorMapping contains a specifitiy, as an integer, and an associated
* SelectorMapping contains a specificity, as an integer, and an associated
* Style. It can also reference children <code>SelectorMapping</code>s,
* so that it behaves like a tree.
* <p>
@@ -30,7 +30,7 @@ import java.awt.Component;
/**
* Sent when one of the following events occur on the grabbed window: <ul>
* <li> it looses focus, but not to one of the owned windows
* <li> it loses focus, but not to one of the owned windows
* <li> mouse click on the outside area happens (except for one of the owned windows)
* <li> switch to another application or desktop happens
* <li> click in the non-client area of the owning window or this window happens
@@ -68,7 +68,7 @@ import sun.java2d.DisposerRecord;
* The FontDesignMetrics class expresses font metrics in terms of arbitrary
* <i>typographic units</i> (not points) chosen by the font supplier
* and used in the underlying platform font representations. These units are
* defined by dividing the em-square into a grid. The em-sqaure is the
* defined by dividing the em-square into a grid. The em-square is the
* theoretical square whose dimensions are the full body height of the
* font. A typographic unit is the smallest measurable unit in the
* em-square. The number of units-per-em is determined by the font
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -39,7 +39,7 @@ public class FontManagerNativeLibrary {
top of freetype library (that is used in binary form).
This wrapper is compiled into fontmanager and this make
fontmanger library depending on freetype library.
font manager library depending on freetype library.
On Windows DLL's in the JRE's BIN directory cannot be
found by windows DLL loading as that directory is not
@@ -2617,7 +2617,7 @@ public abstract class SunFontManager implements FontSupport, FontManagerForSGE {
* - family name is not the same as the full name of an installed font
* - full name is not the same as the family name of an installed font
* The last two of these may initially look odd but the reason is
* that (unfortunately) Font constructors do not distinuguish these.
* that (unfortunately) Font constructors do not distinguish these.
* An extreme example of such a problem would be a font which has
* family name "Dialog.Plain" and full name of "Dialog".
* The one arguably overly stringent restriction here is that if an
@@ -75,7 +75,7 @@ public class DesktopProperty implements UIDefaults.ActiveValue {
/**
* Cleans up any lingering state held by unrefeernced
* Cleans up any lingering state held by unreferenced
* DesktopProperties.
*/
public static void flushUnreferencedProperties() {
@@ -519,7 +519,7 @@ static int GET_ARRAYS(JNIEnv *env, imageIODataPtr data, const JOCTET **next_byte
/*
* Set up error handling to use setjmp/longjmp. This is the third such
* setup, as both the AWT jpeg decoder and the com.sun... JPEG classes
* setup thier own. Ultimately these should be integrated, as they all
* setup their own. Ultimately these should be integrated, as they all
* do pretty much the same thing.
*/
@@ -2358,7 +2358,7 @@ imageio_term_destination (j_compress_ptr cinfo)
JNIEnv *env = (JNIEnv *)JNU_GetEnv(the_jvm, JNI_VERSION_1_2);
/* find out how much needs to be written */
/* this conversion from size_t to jint is safe, because the lenght of the buffer is limited by jint */
/* this conversion from size_t to jint is safe, because the length of the buffer is limited by jint */
jint datacount = (jint)(sb->bufferLength - dest->free_in_buffer);
if (datacount != 0) {
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -31,10 +31,10 @@ package sun.awt.X11;
* Standard X Atom are defined by X11 and these atoms are defined in this class
* for convenience. Common X Atoms like {@code XA_WM_NAME} are used to communicate with the
* Window manager to let it know the Window name. The use and protocol for these
* atoms are defined in the Inter client communications converntions manual.
* atoms are defined in the Inter client communications conventions manual.
* User specified XAtoms are defined by specifying a name that gets Interned
* by the XServer and an {@code XAtom} object is returned. An {@code XAtom} can also be created
* by using a pre-exisiting atom like {@code XA_WM_CLASS}. A {@code display} has to be specified
* by using a pre-existing atom like {@code XA_WM_CLASS}. A {@code display} has to be specified
* in order to create an {@code XAtom}. <p> <p>
*
* Once an {@code XAtom} instance is created, you can call get and set property methods to
@@ -485,7 +485,7 @@ public class TypeAnnotations {
if (type.hasTag(TypeTag.ARRAY)) {
ret = rewriteArrayType(typetree, (ArrayType)type, annotations, onlyTypeAnnotations, pos);
} else if (type.hasTag(TypeTag.TYPEVAR)) {
ret = type.annotatedType(onlyTypeAnnotations);
ret = type.annotatedType(annotations);
} else if (type.getKind() == TypeKind.UNION) {
// There is a TypeKind, but no TypeTag.
UnionClassType ut = (UnionClassType) type;
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -167,10 +167,11 @@ public final class OngoingStream extends EventByteStream {
byte[] bytes = new byte[Math.max(HEADER_SIZE, size)];
for (int attempts = 0; attempts < 25; attempts++) {
// read twice and check files state to avoid simultaneous change by JVM
input.position(0);
input.readFully(bytes, 0, HEADER_SIZE);
input.position(0);
input.readFully(headerBytes);
input.positionPhysical(0);
input.readPhysicalFully(bytes, 0, HEADER_SIZE);
input.positionPhysical(0);
input.readPhysicalFully(headerBytes, 0, HEADER_SIZE);
input.position(HEADER_SIZE);
if (bytes[HEADER_FILE_STATE_POSITION] != MODIFYING_STATE) {
if (bytes[HEADER_FILE_STATE_POSITION] == headerBytes[HEADER_FILE_STATE_POSITION]) {
ByteBuffer buffer = ByteBuffer.wrap(bytes);
@@ -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
@@ -110,6 +110,10 @@ public final class RecordingInput implements DataInput, AutoCloseable {
return file.readLong();
}
void readPhysicalFully(byte[] dest, int offset, int length) throws IOException {
file.readFully(dest, offset, length);
}
@Override
public final byte readByte() throws IOException {
if (!currentBlock.contains(position)) {
@@ -21,7 +21,7 @@
* questions.
*/
package compiler.c2;
package compiler.escapeAnalysis;
/*
* @test
@@ -29,12 +29,12 @@ package compiler.c2;
* @summary Test case where we had escape analysis tell us that we can possibly eliminate
* the array allocation, then MergeStores introduces a mismatched store, which
* the actual elimination does not verify for. That led to wrong results.
* @run main/othervm -XX:CompileCommand=compileonly,compiler.c2.TestMergeStoresAndAllocationElimination::test
* -XX:CompileCommand=exclude,compiler.c2.TestMergeStoresAndAllocationElimination::dontinline
* @run main/othervm -XX:CompileCommand=compileonly,compiler.escapeAnalysis.TestMergeStoresAndAllocationElimination::test
* -XX:CompileCommand=exclude,compiler.escapeAnalysis.TestMergeStoresAndAllocationElimination::dontinline
* -XX:-TieredCompilation -Xbatch
* -XX:+IgnoreUnrecognizedVMOptions -XX:-CICompileOSR
* compiler.c2.TestMergeStoresAndAllocationElimination
* @run main compiler.c2.TestMergeStoresAndAllocationElimination
* compiler.escapeAnalysis.TestMergeStoresAndAllocationElimination
* @run main compiler.escapeAnalysis.TestMergeStoresAndAllocationElimination
*/
public class TestMergeStoresAndAllocationElimination {
@@ -27,10 +27,10 @@
* @summary Check that the JVM is able to dump the heap even when there are ReduceAllocationMerge in the scope.
* @library /test/lib /
* @requires vm.flavor == "server"
* @run main/othervm compiler.c2.TestReduceAllocationAndHeapDump
* @run main/othervm compiler.escapeAnalysis.TestReduceAllocationAndHeapDump
*/
package compiler.c2;
package compiler.escapeAnalysis;
import java.io.File;
import jdk.test.lib.process.OutputAnalyzer;
@@ -49,8 +49,8 @@ public class TestReduceAllocationAndHeapDump {
"-XX:CompileThresholdScaling=0.01",
"-XX:+HeapDumpAfterFullGC",
"-XX:HeapDumpPath=" + dumpDirectory.getAbsolutePath(),
"-XX:CompileCommand=compileonly,compiler.c2.HeapDumper::testIt",
"-XX:CompileCommand=exclude,compiler.c2.HeapDumper::dummy",
"-XX:CompileCommand=compileonly,compiler.escapeAnalysis.HeapDumper::testIt",
"-XX:CompileCommand=exclude,compiler.escapeAnalysis.HeapDumper::dummy",
HeapDumper.class.getName()
};
@@ -29,10 +29,10 @@
* @requires vm.compiler2.enabled
* @run main/othervm -XX:CompileCommand=compileonly,*TestReduceAllocationAndMemoryLoop*::test*
* -XX:-TieredCompilation -Xbatch
* compiler.c2.TestReduceAllocationAndMemoryLoop
* compiler.escapeAnalysis.TestReduceAllocationAndMemoryLoop
*/
package compiler.c2;
package compiler.escapeAnalysis;
public class TestReduceAllocationAndMemoryLoop {
public static void main(String[] args) throws Exception {
@@ -35,10 +35,10 @@
* -XX:-TieredCompilation
* -Xbatch
* -Xcomp
* compiler.c2.TestReduceAllocationAndNonExactAllocate
* compiler.escapeAnalysis.TestReduceAllocationAndNonExactAllocate
*/
package compiler.c2;
package compiler.escapeAnalysis;
import jdk.internal.misc.Unsafe;
@@ -31,10 +31,10 @@
* @run main/othervm -XX:CompileCommand=compileonly,*TestReduceAllocationAndNullableLoads*::*
* -XX:CompileCommand=dontinline,*TestReduceAllocationAndNullableLoads*::*
* -XX:-TieredCompilation -Xcomp
* compiler.c2.TestReduceAllocationAndNullableLoads
* compiler.escapeAnalysis.TestReduceAllocationAndNullableLoads
*/
package compiler.c2;
package compiler.escapeAnalysis;
public class TestReduceAllocationAndNullableLoads {
public static void main(String[] args) {
@@ -29,11 +29,11 @@
* @run main/othervm -XX:CompileCommand=compileonly,*TestReduceAllocationAndPointerComparisons*::*
* -XX:CompileCommand=dontinline,*TestReduceAllocationAndPointerComparisons*::*
* -XX:-TieredCompilation -Xcomp
* compiler.c2.TestReduceAllocationAndPointerComparisons
* @run main compiler.c2.TestReduceAllocationAndPointerComparisons
* compiler.escapeAnalysis.TestReduceAllocationAndPointerComparisons
* @run main compiler.escapeAnalysis.TestReduceAllocationAndPointerComparisons
*/
package compiler.c2;
package compiler.escapeAnalysis;
public class TestReduceAllocationAndPointerComparisons {
public static void main(String[] args) {
@@ -0,0 +1,92 @@
/*
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8389873
* @summary Check that Reduce Allocation Merges correctly handle the situation
* where the Phi was optimized out by IGVN during CG construction.
* @run main/othervm -XX:CompileCommand=compileonly,*${test.main.class}*::*
* -Xcomp -XX:-TieredCompilation ${test.main.class}
* @run main ${test.main.class}
*/
package compiler.escapeAnalysis;
public class TestReduceAllocationOptimizedOutPhi {
static int var_369;
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
test();
}
}
static void test() {
switch (new Foo().var_2) {
case 3:
var_369 = (1.0002043F == new Foo().var_2 ? new Bar() : new Bar()).var_151;
var_369 = 4;
}
for (short var_480 = 0; var_480 < 1; var_480++) {
var_369 = new Bar().var_151;
}
}
}
class Foo {
long var_1 = 9;
char var_2 = 'B';
Foo() {
int var_121 = 9;
for (var_121 = 9; var_121 >= 0; var_121--) {
double var_122 = 2.2250738585072014E-308 - (Integer) 0;
float var_123 = '@' * (Integer) (Byte.valueOf((byte) 9) * var_121);
}
long var_124 = (Float.intBitsToFloat(608) == '(' ? 7L : 5);
byte var_127 = 5;
for (var_127 = 55; var_127 >= 0; var_127--) {
if (370 <= -Byte.valueOf((byte) 14)) {
long var_131 = 71L + (Integer) (-Character.valueOf('d'));
++var_131;
var_131--;
var_131 >>>= var_131;
}
}
byte var_132 = 0;
for (var_132 = 0; var_132 < 6; var_132++) {
byte var_134 = ++var_127;
short var_135 = 2046;
double var_136 = -var_132 < 0.18568599F ? 0.44624205067529954 * Long.valueOf(1048575) + '~' : Short.valueOf((short) 4085);
byte var_137 = 3;
double var_138 = -Integer.valueOf(6) - 0.9994681372166424 * Long.valueOf(3) + (Integer) (+Short.valueOf((short) 512));
int var_141 = (Integer) (Byte.valueOf((byte) 7) * Character.valueOf('U')) - 2;
}
}
}
class Bar {
Byte var_151 = 3;
}
@@ -26,10 +26,10 @@
* @bug 8361140
* @summary Test ConnectionGraph::reduce_phi_on_cmp when OptimizePtrCompare is disabled
* @library /test/lib /
* @run driver compiler.c2.TestReducePhiOnCmpWithNoOptPtrCompare
* @run driver compiler.escapeAnalysis.TestReducePhiOnCmpWithNoOptPtrCompare
*/
package compiler.c2;
package compiler.escapeAnalysis;
import java.util.Random;
import jdk.test.lib.Asserts;
@@ -87,4 +87,4 @@ public class TestReducePhiOnCmpWithNoOptPtrCompare {
return (p.x == x) && (p.y == y);
}
}
}
}
@@ -28,10 +28,10 @@
* @library /test/lib /
* @requires vm.debug & vm.compiler2.enabled
* @compile -XDstringConcat=inline TestScalarReplacementMaxLiveNodes.java
* @run main/othervm/timeout=480 compiler.c2.TestScalarReplacementMaxLiveNodes
* @run main/othervm/timeout=480 compiler.escapeAnalysis.TestScalarReplacementMaxLiveNodes
* @run main/othervm/timeout=480 -Xbatch -XX:-OptimizeStringConcat -XX:-TieredCompilation
* -XX:+UnlockDiagnosticVMOptions -XX:+ReduceAllocationMerges
* -XX:CompileCommand=dontinline,compiler.c2.TestScalarReplacementMaxLiveNodes::test
* -XX:CompileCommand=dontinline,compiler.escapeAnalysis.TestScalarReplacementMaxLiveNodes::test
* -XX:CompileCommand=compileonly,*TestScalarReplacementMaxLiveNodes*::*test*
* -XX:CompileCommand=inline,*String*::*
* -XX:CompileCommand=dontinline,*StringBuilder*::ensureCapacityInternal
@@ -39,9 +39,9 @@
* -XX:NodeCountInliningCutoff=220000
* -XX:DesiredMethodLimit=100000
* -XX:+IgnoreUnrecognizedVMOptions -XX:CompileTaskTimeout=0
* compiler.c2.TestScalarReplacementMaxLiveNodes
* compiler.escapeAnalysis.TestScalarReplacementMaxLiveNodes
*/
package compiler.c2;
package compiler.escapeAnalysis;
public class TestScalarReplacementMaxLiveNodes {
public static void main(String[] args) {
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2026 IBM Corporation. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8390467
* @summary C2: _map != nullptr assert failure in LibraryCallKit::inline_Class_cast()
* @run main/othervm -XX:CompileOnly=${test.main.class}::test1 -Xcomp ${test.main.class}
*/
package compiler.intrinsics;
public class TestClassCastDeadPath {
public static void main(String[] args) {
B b = new B();
C c = new C();
A.class.cast(b);
try {
test1(c);
} catch (ClassCastException cce) {
}
}
private static void test1(Object o) {
if (!(o instanceof I)) {
throw new RuntimeException("never taken");
}
A.class.cast(o);
}
static abstract class A {
}
static class B extends A {
}
interface I {
}
static class C implements I {
}
}
@@ -36,6 +36,8 @@ import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -49,12 +51,15 @@ import java.util.regex.Pattern;
* @see TestFrameworkSocket
*/
public class TestVMProcess {
private static final boolean VERBOSE = Boolean.getBoolean("Verbose");
private static final boolean PREFER_COMMAND_LINE_FLAGS = Boolean.getBoolean("PreferCommandLineFlags");
private static final int WARMUP_ITERATIONS = Integer.getInteger("Warmup", -1);
private static final boolean VERIFY_VM = Boolean.getBoolean("VerifyVM") && Platform.isDebugBuild();
private static final boolean REPORT_STDOUT = Boolean.getBoolean("ReportStdout");
private static final boolean VERBOSE = Boolean.getBoolean("Verbose");
private static final boolean EXCLUDE_RANDOM = Boolean.getBoolean("ExcludeRandom");
private static final boolean REPORT_STDOUT = Boolean.getBoolean("ReportStdout");
private static final boolean DUMP_OUTPUT = VERBOSE || EXCLUDE_RANDOM || REPORT_STDOUT;
private static final String FATAL_ERROR_MARKER = "# A fatal error has been detected by the Java Runtime Environment:";
private static String lastTestVMOutput = "";
@@ -72,11 +77,131 @@ public class TestVMProcess {
prepareTestVMFlags(additionalFlags, socket, testClass, helperClasses, defaultWarmup,
allowNotCompilable, testClassesOnBootClassPath);
start();
// Test VM has exited. Do not close the socket, yet, because the accept and/or reader threads could still
// be processing its connection. We wait for the socket result and only then close the socket by leaving
// this scope.
testVmData = processTestVmResult(socket, allowNotCompilable);
} // Socket closed here.
}
private TestVMData processTestVmResult(TestFrameworkSocket socket, boolean allowNotCompilable) {
if (oa.getExitValue() == 0) {
dumpTestVmOutputIfRequested();
return readAndDumpTestVmData(socket, allowNotCompilable);
}
checkTestVMExitCode();
if (isTestFormatViolation()) {
// When a test is malformed, we only show the violation. This kind of failure should be caught during the
// development phase of new tests.
dumpTestVmOutputIfRequested();
throw createTestFormatException();
}
if (noTestsRun()) {
// If no test was selected, we just show the exception message. This kind of failure only happens during
// debugging when specifying an empty test set with property flags.
dumpTestVmOutputIfRequested();
throw createNoTestsRunException();
}
throw createTestVMExceptionForNonZeroExit(socket, allowNotCompilable);
}
/**
* Dump the Test VM output if flags request it.
*/
private void dumpTestVmOutputIfRequested() {
if (DUMP_OUTPUT) {
System.out.println("Test VM Output");
System.out.println("--------------");
System.out.println(oa.getOutput());
}
}
private TestVMData readAndDumpTestVmData(TestFrameworkSocket socket, boolean allowNotCompilable) {
String hotspotPidFileName = String.format("hotspot_pid%d.log", oa.pid());
testVmData = socket.testVmData(hotspotPidFileName, allowNotCompilable);
testVmData.printJavaMessages();
TestVMData testVMData = socket.testVmData(hotspotPidFileName, allowNotCompilable);
testVMData.printJavaMessages();
return testVMData;
}
private boolean isTestFormatViolation() {
return oa.getStderr().contains("TestFormat.throwIfAnyFailures");
}
private TestFormatException createTestFormatException() {
Pattern pattern = Pattern.compile("Violations \\(\\d+\\)[\\s\\S]*(?=/============/)");
Matcher matcher = pattern.matcher(oa.getStderr());
TestFramework.check(matcher.find(), "Must find violation matches");
return new TestFormatException(System.lineSeparator() + System.lineSeparator() + matcher.group());
}
private boolean noTestsRun() {
return oa.getStderr().contains("NoTestsRunException");
}
private NoTestsRunException createNoTestsRunException() {
return new NoTestsRunException(">>> No tests run due to empty set specified with -DTest and/or -DExclude. " +
"Make sure to define a set of at least one @Test method");
}
private TestVMException createTestVMExceptionForNonZeroExit(TestFrameworkSocket socket, boolean allowNotCompilable) {
String secondaryException = "";
try {
readAndDumpTestVmData(socket, allowNotCompilable);
} catch (RuntimeException e) {
// We observed a message processing exception. We treat it as secondary failure because messages could be
// incomplete when the VM crashed or not even sent by the Test VM when it exits early on start-up
// (e.g. passing in an unknown VM flag).
secondaryException = buildSecondaryExceptionInfo(e);
}
// Primary exception: non-zero Test VM exit.
return new TestVMException(buildExceptionInfo() + secondaryException);
}
private String buildSecondaryExceptionInfo(RuntimeException e) {
String secondaryException;
StringWriter stringWriter = new StringWriter();
e.printStackTrace(new PrintWriter(stringWriter));
secondaryException = System.lineSeparator() +
"Secondary Message-Processing Exception" + System.lineSeparator() +
"--------------------------------------" + System.lineSeparator() +
stringWriter;
return secondaryException;
}
/**
* Get more detailed information about the exception in a pretty format.
*/
private String buildExceptionInfo() {
StringBuilder builder = new StringBuilder();
builder.append("Test VM exited with code ").append(oa.getExitValue()).append(System.lineSeparator());
if (hasFatalErrorMarker() || DUMP_OUTPUT) {
// Also dump the Test VM output if we experience a JVM error to show assertion failures etc.
builder.append(System.lineSeparator())
.append(System.lineSeparator())
.append("Test VM - Standard Output").append(System.lineSeparator())
.append("-------------------------").append(System.lineSeparator())
.append(oa.getStdout());
}
builder.append(System.lineSeparator())
.append(commandLine)
.append(System.lineSeparator())
.append(System.lineSeparator())
.append("Test VM - Error Output").append(System.lineSeparator())
.append("----------------------").append(System.lineSeparator())
.append(oa.getStderr())
.append(System.lineSeparator())
.append(System.lineSeparator());
return builder.toString();
}
/**
* Best-effort VM crash detection by matching the start of the fatal error message. This covers most of the crashes
* but fails when the Test VM was killed externally or when the output is unavailable or truncated which could
* happen in native stack overflow cases.
*/
private boolean hasFatalErrorMarker() {
return oa.getExitValue() != 0 && oa.getOutput().contains(FATAL_ERROR_MARKER);
}
public String getCommandLine() {
@@ -173,55 +298,4 @@ public class TestVMProcess {
+ System.lineSeparator();
lastTestVMOutput = oa.getOutput();
}
private void checkTestVMExitCode() {
final int exitCode = oa.getExitValue();
if (EXCLUDE_RANDOM || REPORT_STDOUT || (VERBOSE && exitCode == 0)) {
System.out.println("--- OUTPUT TestFramework Test VM ---");
System.out.println(oa.getOutput());
}
if (exitCode != 0) {
throwTestVMException();
}
}
/**
* Exit code was non-zero of Test VM. Check the stderr to determine what kind of exception that should be thrown to
* react accordingly later.
*/
private void throwTestVMException() {
String stdErr = oa.getStderr();
if (stdErr.contains("TestFormat.throwIfAnyFailures")) {
Pattern pattern = Pattern.compile("Violations \\(\\d+\\)[\\s\\S]*(?=/============/)");
Matcher matcher = pattern.matcher(stdErr);
TestFramework.check(matcher.find(), "Must find violation matches");
throw new TestFormatException(System.lineSeparator() + System.lineSeparator() + matcher.group());
} else if (stdErr.contains("NoTestsRunException")) {
throw new NoTestsRunException(">>> No tests run due to empty set specified with -DTest and/or -DExclude. " +
"Make sure to define a set of at least one @Test method");
} else {
throw new TestVMException(getExceptionInfo());
}
}
/**
* Get more detailed information about the exception in a pretty format.
*/
private String getExceptionInfo() {
int exitCode = oa.getExitValue();
String stdErr = oa.getStderr();
String stdOut = "";
boolean osIsWindows = Platform.isWindows();
boolean JVMHadError = (!osIsWindows && exitCode == 134) || (osIsWindows && exitCode == -1);
if (JVMHadError) {
// Also dump the stdout if we experience a JVM error (e.g. to show hit assertions etc.).
stdOut = System.lineSeparator() + System.lineSeparator() + "Standard Output" + System.lineSeparator()
+ "---------------" + System.lineSeparator() + oa.getOutput();
}
return "TestFramework Test VM exited with code " + exitCode + System.lineSeparator() + stdOut
+ System.lineSeparator() + commandLine + System.lineSeparator() + System.lineSeparator()
+ "Error Output" + System.lineSeparator() + "------------" + System.lineSeparator() + stdErr
+ System.lineSeparator() + System.lineSeparator();
}
}
@@ -30,6 +30,8 @@ import compiler.lib.ir_framework.driver.network.testvm.java.JavaMessageParser;
import compiler.lib.ir_framework.driver.network.testvm.java.JavaMessages;
import compiler.lib.ir_framework.test.network.TestVmSocket;
import jdk.test.lib.Utils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -41,16 +43,36 @@ import java.util.concurrent.*;
*/
public class TestFrameworkSocket implements AutoCloseable {
private static final String SERVER_PORT_PROPERTY = "ir.framework.server.port";
private static final int SOCKET_TIMEOUT_IN_MS = (int)Utils.adjustTimeout(10_000L);
private final int serverSocketPort;
private final ServerSocket serverSocket;
private final ExecutorService acceptExecutor;
private final ExecutorService clientExecutor;
// Make these volatile such that the main thread can observe an update written by the worker threads in the executor
// services to avoid stale values.
/*
* CompletableFuture shared by the Driver VM and the accept/reader threads.
*
* Lifecycle:
* 1. The future is created before the accept loop task is submitted.
* 2. The Driver VM starts the Test VM. The socket and the executors remain open while the Driver VM waits on the
* future to be completed.
* 3. The accept thread accepts the Test VM connection and reads the identity handshake.
* 4. The accept thread now schedules a reader for incoming Test VM messages.
* 5. During normal execution, the Test VM closes the connection before exiting. The reader completes the future
* with the parsed Test VM messages.
* 6. Accepting, identity handshake, task submission, or message reading failures complete the future exceptionally.
* 7. The Driver VM obtains the result, observes a failure, or times out before the socket and executors are closed.
*
* Note: The future must be created eagerly such that the Driver VM can wait on it even before the accept thread
* has accepted the Test VM connection. The accept thread might only be scheduled after the Test VM has exited.
* This is possible because the server socket is already listening and the OS can queue the connection until
* the accept thread processes it.
*/
private final CompletableFuture<JavaMessages> javaMessagesFuture;
// Written by the Driver VM thread and read by the accept thread.
private volatile boolean running;
private volatile Future<JavaMessages> javaFuture;
public TestFrameworkSocket() {
try {
@@ -62,6 +84,7 @@ public class TestFrameworkSocket implements AutoCloseable {
serverSocketPort = serverSocket.getLocalPort();
acceptExecutor = Executors.newSingleThreadExecutor();
clientExecutor = Executors.newCachedThreadPool();
javaMessagesFuture = new CompletableFuture<>();
if (TestFramework.VERBOSE) {
System.out.println("TestFramework server socket uses port " + serverSocketPort);
}
@@ -73,50 +96,40 @@ public class TestFrameworkSocket implements AutoCloseable {
public void start() {
running = true;
CountDownLatch calledAcceptLoopLatch = new CountDownLatch(1);
startAcceptLoop(calledAcceptLoopLatch);
}
private void startAcceptLoop(CountDownLatch calledAcceptLoopLatch) {
acceptExecutor.submit(() -> acceptLoop(calledAcceptLoopLatch));
waitUntilAcceptLoopRuns(calledAcceptLoopLatch);
}
private void waitUntilAcceptLoopRuns(CountDownLatch calledAcceptLoopLatch) {
try {
if (!calledAcceptLoopLatch.await(10, TimeUnit.SECONDS)) {
throw new IllegalStateException("acceptLoop did not start in time");
}
} catch (Exception e) {
throw new TestFrameworkException("Could not start TestFrameworkSocket", e);
}
acceptExecutor.submit(this::acceptLoop);
}
/**
* Main loop to wait for new client connections and handling them upon connection request.
*/
private void acceptLoop(CountDownLatch calledAcceptLoopLatch) {
calledAcceptLoopLatch.countDown();
private void acceptLoop() {
while (running) {
try {
acceptNewClientConnection();
} catch (SocketException e) {
} catch (SocketException e) {
if (!running || serverSocket.isClosed()) {
// Normal shutdown
return;
}
running = false;
throw new TestFrameworkException("Server socket error", e);
throwServerSocketError(e);
} catch (TestFrameworkException e) {
running = false;
throw e;
throwTestFrameworkException(e);
} catch (Exception e) {
running = false;
throw new TestFrameworkException("Server socket error", e);
throwServerSocketError(e);
}
}
}
private void throwServerSocketError(Exception e) {
throwTestFrameworkException(new TestFrameworkException("Server socket error", e));
}
private void throwTestFrameworkException(TestFrameworkException testFrameworkException) {
running = false;
javaMessagesFuture.completeExceptionally(testFrameworkException);
throw testFrameworkException;
}
/**
* Accept new client connection by first reading the identity of the connection (either coming from Java or C2)
* and then submitting a task accordingly to manage incoming messages on that connection/socket.
@@ -137,11 +150,11 @@ public class TestFrameworkSocket implements AutoCloseable {
private String readIdentity(Socket client, BufferedReader reader) throws IOException {
String identity;
try {
client.setSoTimeout(10000);
client.setSoTimeout(SOCKET_TIMEOUT_IN_MS);
identity = reader.readLine();
TestFramework.check(identity != null, "end of stream has been reached without reading the identity");
} catch (SocketTimeoutException e) {
throw new TestFrameworkException("Did not receive initial identity message after 10s", e);
throw new TestFrameworkException("Timed out while waiting for initial identity message", e);
} finally {
client.setSoTimeout(0);
}
@@ -154,7 +167,9 @@ public class TestFrameworkSocket implements AutoCloseable {
*/
private void submitTask(String identity, Socket client, BufferedReader reader) {
if (identity.equals(TestVmSocket.IDENTITY)) {
javaFuture = clientExecutor.submit(new TestVmMessageReader<>(client, reader, new JavaMessageParser()));
TestVmMessageReader<JavaMessages> messageReader =
new TestVmMessageReader<>(client, reader, new JavaMessageParser());
javaMessagesFuture.completeAsync(messageReader::call, clientExecutor);
} else {
throw new TestFrameworkException("Unrecognized identity: " + identity);
}
@@ -179,9 +194,26 @@ public class TestFrameworkSocket implements AutoCloseable {
private JavaMessages testVmMessages() {
try {
return javaFuture.get();
// Note: The Test VM may have already exited while the accept and message reader thread are still processing
// the connection. Let's wait until they are finished.
return javaMessagesFuture.get(SOCKET_TIMEOUT_IN_MS, TimeUnit.MILLISECONDS);
} catch (ExecutionException e) {
throw new TestFrameworkException("No test VM messages were received", e);
} catch (TimeoutException e) {
throw new RuntimeException("Timed out while waiting for Test VM messages." + System.lineSeparator() +
System.lineSeparator() +
"Did any of the following happen?" + System.lineSeparator() +
"(1) TestFramework.addFlags(-DReproduce=true)" + System.lineSeparator() +
"(2) TestFramework.addFlags(--version) or any other VM flag that prevents " +
" TestVM.main() from being called?" + System.lineSeparator() +
"(3) The Test VM crashed before calling TestVM.main()" + System.lineSeparator() +
System.lineSeparator() +
"(1) and (2) are unsupported and are expected to fail." + System.lineSeparator() +
"-> Please change your test!" + System.lineSeparator() +
"(3) The IR Framework cannot handle early VM crashes." + System.lineSeparator() +
"-> Please change your test if such a crash was anticipated!" +
System.lineSeparator() + System.lineSeparator() +
"In all other cases, please file an IR Framework bug!", e);
} catch (Exception e) {
throw new TestFrameworkException("Error while fetching Test VM Future", e);
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,13 +23,15 @@
/*
* @test
* @bug 8356246
* @bug 8356246 8362117
* @summary Test stacked string concatenations where the toString of the first StringBuilder
* is used as a shared test by two diamond Ifs in the second StringBuilder.
* @run main/othervm compiler.stringopts.TestStackedConcatsSharedTest
* @run main/othervm -XX:-TieredCompilation -Xcomp
* -XX:CompileOnly=compiler.stringopts.TestStackedConcatsSharedTest::*
* compiler.stringopts.TestStackedConcatsSharedTest
* (f): make sure we don't crash outright
* (g): external null checks depending on the same test/removed call should not give a wrong result.
* (h): multiple phis attached to the same diamond region; only one is a proper null check phi.
* (i): non-null check phi reused after intermediate stacked concat: check for correct result
* @run main/othervm ${test.main.class}
* @run main/othervm -XX:-TieredCompilation -Xcomp -XX:CompileOnly=${test.main.class}::* ${test.main.class}
*/
package compiler.stringopts;
@@ -42,6 +44,21 @@ public class TestStackedConcatsSharedTest {
if (!s.equals("")) {
throw new RuntimeException("wrong result");
}
s = g();
if (!s.equals("abcabcabc")) {
System.out.println(s);
throw new RuntimeException("wrong result");
}
s = h();
if (!s.equals("abcabcnotnull")) {
System.out.println(s);
throw new RuntimeException("wrong result");
}
s = i();
if (!s.equals("abcabcnotnull")) {
System.out.println(s);
throw new RuntimeException("wrong result");
}
}
static String f() {
@@ -52,4 +69,37 @@ public class TestStackedConcatsSharedTest {
s = new StringBuilder(String.valueOf(s)).append(String.valueOf(s)).toString();
return s;
}
static String g() {
String s = "abc";
s = new StringBuilder(s).toString();
s = new StringBuilder(String.valueOf(s)).append(String.valueOf(s)).toString() + (s == null ? "def" : "abc");
return s;
}
static String h() {
String s1 = new String("abc");
String s2 = new StringBuilder(s1).append(s1).toString();
String arg2 = "";
if (s2 == null) {
arg2 = "null";
} else {
arg2 = "notnull";
}
return new StringBuilder(s2).append(arg2).toString();
}
static String i() {
String s1 = new String("abc");
String s2 = new StringBuilder(s1).append(s1).toString();
String arg2 = "";
if (s2 == null) {
arg2 = "null";
} else {
arg2 = "notnull";
}
String s3 = new StringBuilder(s2).toString();
String s4 = new StringBuilder(s3).append(arg2).toString();
return s4;
}
}
@@ -0,0 +1,76 @@
/*
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8362117
* @summary Basic IR checks to verify that merge validation does not break concat optimizations.
* @library /test/lib /
* @run driver ${test.main.class}
*/
package compiler.stringopts;
import compiler.lib.ir_framework.*;
public class TestStringConcatIR {
public static void main(String[] args) {
TestFramework.runWithFlags();
}
@Run(test = {"stackedConcat", "stackedConcatNullCheck"})
public void runMethodA() {
stackedConcat();
stackedConcatNullCheck();
}
@Test
@IR(counts = {IRNode.CALL, ">= 9"}, phase = {CompilePhase.BEFORE_STRINGOPTS}) // at least init, append, tostring x 3
@IR(counts = {IRNode.ALLOC, "= 3"}, phase = {CompilePhase.BEFORE_STRINGOPTS})
@IR(counts = {IRNode.CALL, "= 0"}, phase = {CompilePhase.ITER_GVN1})
@IR(counts = {IRNode.ALLOC, "= 1"}, phase = {CompilePhase.ITER_GVN1})
@IR(counts = {IRNode.STORE_B, "= 16"}, phase = {CompilePhase.ITER_GVN1})
static String stackedConcat() {
String s = "ab";
s = new StringBuilder(s).append(s).toString();
s = new StringBuilder(s).append(s).toString();
s = new StringBuilder(s).append(s).toString();
return s;
}
@Test
@IR(applyIf = {"TieredCompilation", "true"},
counts = {IRNode.CALL, ">= 9", IRNode.ALLOC, "= 3"},
phase = {CompilePhase.BEFORE_STRINGOPTS})
@IR(applyIf = {"TieredCompilation", "true"},
counts = {IRNode.CALL, "= 0", IRNode.ALLOC, "= 1", IRNode.STORE_B, "= 24"},
phase = {CompilePhase.ITER_GVN1})
static String stackedConcatNullCheck() {
String s = "abc";
s = new StringBuilder(String.valueOf(s)).append(s).toString();
s = new StringBuilder(String.valueOf(s)).append(String.valueOf(s)).toString();
s = new StringBuilder(s).append(String.valueOf(s)).toString();
return s;
}
}
@@ -0,0 +1,142 @@
/*
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8362117
* @summary Prevent crashes and miscompilations when external constructs
* could be confused for StringConcat append/toString-null checks.
* @run main/othervm ${test.main.class}
* @run main/othervm -Xbatch
* -XX:CompileOnly=${test.main.class}::test* ${test.main.class}
* @run main/othervm -Xbatch
* -XX:CompileThreshold=500
* -XX:CompileOnly=${test.main.class}::test* ${test.main.class}
* @run main/othervm -Xbatch
* -XX:-TieredCompilation
* -XX:CompileOnly=${test.main.class}::test* ${test.main.class}
*/
package compiler.stringopts;
public class TestStringConcatValidateMerge {
public static void main (String... args) {
String gold = test1(false);
for (int i = 0; i < 10_000; i++) {
test1((i & 1) == 0);
}
String val = test1(false);
if (!val.equals(gold)) {
throw new RuntimeException("wrong value: " + val + " vs " + gold);
}
for (int t = 0; t < 10_000; t++) {
// The following line is probably important for profiling.
try { new String((String) null); } catch (NullPointerException e) {}
test2();
}
for (int t = 0; t < 10_000; t++) {
try {
if (t % 2 != 0) {
test3(null, "B");
} else {
test3("A", null);
}
} catch (NullPointerException e) {
// expected
}
}
for (int i = 0; i < 100_000; i++) {
test4();
}
for (int i = 0; i < 100_000; i++) {
test5(i % 2 == 0);
}
gold = test6(new StringBuilder(" "));
for (int i = 0; i < 100_000; i++) {
val = test6(new StringBuilder(" "));
}
if (!val.equals(gold)) {
throw new RuntimeException("wrong result.");
}
}
// test1-3: StringOpts can't stack as SB1 is used in a compare in SB2 (previously confused as a valid string null check).
// test4: can't remove SB1's toString as it's used in an external comparison that needs it -> reject stacking
// test5: hand-written branching that changes return value (previously mistaken to be a valid string null check).
// test6: merge an unresolved stringbuilder with the intermediate value used in a compare: reject single concat.
// JDK-8385429
static String test1(boolean flag) {
String s = new StringBuilder("ABC").toString();
return new StringBuilder().append(s).append(s == null ? "x" : "y").append(flag ? "z" : s).toString();
}
// JDK-8385428
static int test2() {
String s1 = (("a" == null) ? "b" : "c") + 'd';
String s2 = new StringBuilder(s1).toString();
String s3 = new StringBuilder(s2).append(s1).append(s2 == null ? "" : s2).toString();
return s3.length();
}
// JDK-8385415
static Object test3(String a, String b) {
String s1 = new String(b);
String s2 = new StringBuffer(s1).append(s1).toString();
return new StringBuffer(s2).append(a).append(s2 == null ? "" : s2).toString();
}
// JDK-8384130
static String test4() {
String s = new StringBuilder().toString();
return new StringBuilder(s).toString() == s ? "a" : "b";
}
static String test5(boolean test) {
String s1 = new String("b");
String s2 = new StringBuilder(s1).append(s1).toString();
String arg1 = "";
String arg2 = "";
if (s2 == null) {
arg2 = "null";
} else {
arg2 = "Some other string";
}
return new StringBuffer(s2).append(arg2).toString();
}
static String test6(StringBuilder c) {
StringBuilder s = new StringBuilder().append(" ");
String ret = s.append(s == c ? "abc" : " ").toString();
return ret;
}
}
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8362117
* @summary Similar type of test scenarios as in TestStringConcatValidateMerge.java
* but for problems which manifested with -Xcomp
* (f): stringopts shouldn't confuse ternary expression with string null check
* and fold away diamond phi arbitrarily leading to wrong result when depending on
* toString of SB1.
* (g): variant of (f) with append instead of toString
* @library /test/lib /
* @run main/othervm ${test.main.class}
* @run main/othervm -XX:-TieredCompilation -Xcomp
* -XX:CompileOnly=${test.main.class}::* ${test.main.class}
*/
package compiler.stringopts;
import jdk.test.lib.Asserts;
public class TestStringConcatValidateMergeXcomp {
public static void main (String... args) {
new StringBuilder(); // load the class
f();
g();
}
static String f() {
String s = "a";
s = new StringBuilder().append(s).append(s).toString();
s = new StringBuilder().append(s).append((s == "xx") ? s : "aa").toString();
Asserts.assertEQ(s, "aaaa"); // in particular, we should not have s.equals("aaxx");
return s;
}
static String g() {
String s = "a";
StringBuilder sb0 = new StringBuilder();
s = new StringBuilder().append(s).append(s).toString();
StringBuilder sb2 = new StringBuilder().append(s);
s = sb2.append((sb2 == sb0) ? "xx" : "aa").toString();
Asserts.assertEQ(s, "aaaa"); // in particular, we should not have s.equals("aaxx").
return s;
}
}
@@ -27,7 +27,7 @@
* @summary CPU feature compatibility test for AOT Code Cache
* @requires vm.cds.supports.aot.code.caching
* @requires vm.compMode != "Xcomp" & vm.compMode != "Xint"
* @requires os.simpleArch == "x64" | os.simpleArch == "aarch64"
* @requires os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64"
* @comment The test verifies AOT checks during VM startup and not code generation.
* No need to run it with -Xcomp.
* @library /test/lib /test/setup_aot
@@ -74,6 +74,11 @@ public class AOTCodeCPUFeatureIncompatibilityTest {
testIncompatibleFeature("-XX:-UseCRC32", "crc32", IncompatibilityMode.MISSING);
testIncompatibleFeature("-XX:-UseCRC32", "crc32", IncompatibilityMode.ADDITIONAL);
}
} else if (Platform.isRISCV64()) {
if (isZbaSupported(cpuFeatures)) {
testIncompatibleFeature("-XX:-UseZba", "zba", IncompatibilityMode.MISSING);
testIncompatibleFeature("-XX:-UseZba", "zba", IncompatibilityMode.ADDITIONAL);
}
}
}
@@ -87,12 +92,14 @@ public class AOTCodeCPUFeatureIncompatibilityTest {
if (mode == IncompatibilityMode.MISSING) {
return new String[] {"-Xlog:aot+codecache*=debug"};
} else {
return new String[] {vmOption, "-Xlog:aot+codecache*=debug"};
// UnlockDiagnosticVMOptions must precede vmOption because
// some tested CPU feature flags are diagnostic (e.g. UseZba on riscv).
return new String[] {"-XX:+UnlockDiagnosticVMOptions", vmOption, "-Xlog:aot+codecache*=debug"};
}
} else if (runMode == RunMode.PRODUCTION) {
if (mode == IncompatibilityMode.MISSING) {
return new String[] {vmOption,
"-XX:+UnlockDiagnosticVMOptions",
return new String[] {"-XX:+UnlockDiagnosticVMOptions",
vmOption,
// Prevent exiting VM on failure
"-XX:-AbortVMOnAOTCodeFailure",
"-Xlog:aot+codecache*=debug"};
@@ -149,4 +156,9 @@ public class AOTCodeCPUFeatureIncompatibilityTest {
static boolean isCRC32Supported(List<String> cpuFeatures) {
return cpuFeatures.contains("crc32");
}
// Only used on riscv64 platform
static boolean isZbaSupported(List<String> cpuFeatures) {
return cpuFeatures.contains("zba");
}
}
@@ -0,0 +1,85 @@
/*
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8381564
* @requires vm.debug == true & vm.compMode != "Xint" & vm.compiler2.enabled & vm.flagless
* @summary Test that different ways to avoid executing main() are reported as correctly as failure.
* @library /test/lib /testlibrary_tests /
* @run driver ${test.main.class}
*/
package testlibrary_tests.ir_framework.tests;
import compiler.lib.ir_framework.*;
import jdk.test.lib.Asserts;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
public class TestNoMainExecution {
public static void main(String[] args) {
// We do not handshake because this flag disables socket communication
run("-DReproduce=true");
// We do not reach main() because there is no source file involved.
run("--version");
// We do not reach main() because we crash already at start-up.
runWithVmCrash();
}
private static void run(String... flags) {
try {
TestFramework.runWithFlags(flags);
Asserts.fail("should throw");
} catch (RuntimeException e) {
String errorMessage = e.getMessage();
// We expect a useful help message - match its header.
Asserts.assertTrue(errorMessage.contains("Did any of the following happen?"), errorMessage);
}
}
private static void runWithVmCrash() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream oldErr = System.err;
try (PrintStream ps = new PrintStream(baos)) {
System.setErr(ps);
try {
TestFramework.runWithFlags("-Xcomp", "-XX:+CICountNative", "-XX:CICrashAt=1");
Asserts.fail("should throw");
} catch (RuntimeException e) {
// With a VM crash, the message is found on the normal stderr instead.
System.setErr(oldErr);
String output = baos.toString();
Asserts.assertTrue(output.contains("Did any of the following happen?"));
}
}
}
@Test
public void test() {}
}
+18 -7
View File
@@ -53,9 +53,7 @@ import java.nio.LongBuffer;
import java.nio.MappedByteBuffer;
import java.nio.ShortBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -603,11 +601,24 @@ public class TestByteBuffer {
}
}
@Test(expectedExceptions = UnsupportedOperationException.class)
@Test
public void testMapCustomPath() throws IOException {
Path path = Path.of(URI.create("jrt:/"));
try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 0L, Arena.ofAuto());
// Zip file systems do support creating file channels
// but do not support memory mapping those files
Path scratch = Path.of("testMapCustomPath");
Files.createDirectories(scratch);
Path zipFile = scratch.resolve("test.zip");
try (FileSystem zipFs = FileSystems.newFileSystem(zipFile, Map.of("create", true))) {
// create test file
Path testFile = zipFs.getPath("/test_file.txt");
Files.writeString(testFile, "testing", StandardOpenOption.CREATE_NEW);
// now try to map it
try (FileChannel fileChannel = FileChannel.open(testFile, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
assertThrows(UnsupportedOperationException.class,
() -> fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 0L, Arena.ofAuto()));
}
}
}
@@ -34,10 +34,7 @@ import java.util.List;
import java.util.Optional;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotEquals;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.*;
public class TestFunctionDescriptor extends NativeTestHelper {
@@ -116,16 +113,6 @@ public class TestFunctionDescriptor extends NativeTestHelper {
assertEquals(cmt, MethodType.methodType(int.class, int.class, MemorySegment.class, MemorySegment.class));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadCarrierMethodType() {
FunctionDescriptor fd = FunctionDescriptor.of(C_INT,
C_INT,
MemoryLayout.structLayout(C_INT, C_INT),
MemoryLayout.sequenceLayout(3, C_INT),
MemoryLayout.paddingLayout(4));
fd.toMethodType(); // should throw
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testIllegalInsertArgNegIndex() {
FunctionDescriptor fd = FunctionDescriptor.of(C_INT);
+14 -7
View File
@@ -307,10 +307,12 @@ public class TestLayouts {
}
}
@Test(dataProvider="layoutsAndAlignments", expectedExceptions = IllegalArgumentException.class)
@Test(dataProvider="layoutsAndAlignments")
public void testBadSequenceElementAlignmentTooBig(MemoryLayout layout, long byteAlign) {
layout = layout.withByteAlignment(layout.byteSize() * 2); // hyper-align
MemoryLayout.sequenceLayout(1, layout);
MemoryLayout elementLayout = layout.withByteAlignment(nextPowerOfTwo(layout.byteSize() * 2)); // hyper-align
IllegalArgumentException iae = expectThrows(IllegalArgumentException.class,
() -> MemoryLayout.sequenceLayout(1, elementLayout));
assertEquals(iae.getMessage(), "Element layout size is not multiple of alignment");
}
@Test(dataProvider="layoutsAndAlignments")
@@ -348,15 +350,16 @@ public class TestLayouts {
}
}
@Test(dataProvider="layoutsAndAlignments", expectedExceptions = IllegalArgumentException.class)
@Test(dataProvider="layoutsAndAlignments")
public void testBadStruct(MemoryLayout layout, long byteAlign) {
layout = layout.withByteAlignment(layout.byteSize() * 2); // hyper-align
MemoryLayout.structLayout(layout, layout);
MemoryLayout elementLayout = layout.withByteAlignment(nextPowerOfTwo(layout.byteSize() * 2)); // hyper-align
IllegalArgumentException iae = expectThrows(IllegalArgumentException.class,
() -> MemoryLayout.structLayout(elementLayout, elementLayout));
assertTrue(iae.getMessage().contains("Invalid alignment constraint for member layout"));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testSequenceElement() {
SequenceLayout layout = MemoryLayout.sequenceLayout(10, JAVA_INT);
// Step must be != 0
PathElement.sequenceElement(3, 0);
}
@@ -543,4 +546,8 @@ public class TestLayouts {
ValueLayout.JAVA_LONG,
ValueLayout.JAVA_DOUBLE,
};
private static long nextPowerOfTwo(long input) {
return 1L << -Long.numberOfLeadingZeros(input - 1);
}
}
@@ -73,24 +73,6 @@ public class TestMemoryAlignment {
}
}
@Test(dataProvider = "alignments")
public void testUnalignedAccess(long align) {
ValueLayout layout = ValueLayout.JAVA_INT
.withOrder(ByteOrder.BIG_ENDIAN);
assertEquals(layout.byteAlignment(), 4);
ValueLayout aligned = layout.withByteAlignment(align);
try (Arena arena = Arena.ofConfined()) {
MemoryLayout alignedGroup = MemoryLayout.structLayout(MemoryLayout.paddingLayout(1), aligned);
assertEquals(alignedGroup.byteAlignment(), align);
VarHandle vh = aligned.varHandle();
MemorySegment segment = arena.allocate(alignedGroup);;
vh.set(segment.asSlice(1L), 0L, -42);
assertEquals(align, 8); //this is the only case where access is aligned
} catch (IllegalArgumentException ex) {
assertNotEquals(align, 8); //if align != 8, access is always unaligned
}
}
@Test(dataProvider = "alignments")
public void testUnalignedPath(long align) {
MemoryLayout layout = ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN);
+9 -5
View File
@@ -29,9 +29,7 @@
import java.lang.foreign.*;
import java.lang.invoke.VarHandle;
import java.util.LinkedList;
import java.util.List;
import java.util.Spliterator;
import java.util.*;
import java.util.concurrent.CountedCompleter;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.atomic.AtomicLong;
@@ -147,13 +145,19 @@ public class TestSpliterator {
.elements(MemoryLayout.sequenceLayout(0, ValueLayout.JAVA_INT));
}
@Test(expectedExceptions = IllegalArgumentException.class)
@Test
public void testHyperAligned() {
Arena scope = Arena.ofAuto();
MemorySegment segment = scope.allocate(8, 1);
// compute an alignment constraint (in bytes) which exceed that of the native segment
long bigByteAlign = Long.lowestOneBit(segment.address()) << 1;
segment.elements(MemoryLayout.sequenceLayout(2, ValueLayout.JAVA_INT.withByteAlignment(bigByteAlign)));
MemoryLayout elementLayout = MemoryLayout.structLayout(
Collections.nCopies(Math.toIntExact(bigByteAlign), ValueLayout.JAVA_BYTE).toArray(MemoryLayout[]::new))
.withByteAlignment(bigByteAlign);
SequenceLayout layout = MemoryLayout.sequenceLayout(2, elementLayout);
IllegalArgumentException iae = expectThrows(IllegalArgumentException.class,
() -> segment.elements(layout));
assertEquals(iae.getMessage(), "Incompatible alignment constraints");
}
static long sumSingle(long acc, MemorySegment segment) {
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -65,43 +65,58 @@ public class MetricsMemoryTester {
}
private static void testMemoryFailCount() {
long memAndSwapLimit = Metrics.systemMetrics().getMemoryAndSwapLimit();
long memLimit = Metrics.systemMetrics().getMemoryLimit();
final Metrics metrics = Metrics.systemMetrics();
final long memAndSwapLimit = metrics.getMemoryAndSwapLimit();
final long memLimit = metrics.getMemoryLimit();
// We need swap to execute this test or will SEGV
if (memAndSwapLimit <= memLimit) {
System.out.println("No swap memory limits. Ignoring test!");
} else {
long count = Metrics.systemMetrics().getMemoryFailCount();
final int M = 1024 * 1024;
// Allocate 512M of data in 1M chunks per iteration
byte[][] bytes = new byte[64 * 8][];
boolean atLeastOneAllocationWorked = false;
for (int i = 0; i < 64 * 8; i++) {
try {
bytes[i] = new byte[1024 * 1024];
atLeastOneAllocationWorked = true;
// Break out as soon as we see an increase in failcount
// to avoid getting killed by the OOM killer.
if (Metrics.systemMetrics().getMemoryFailCount() > count) {
break;
}
} catch (Error e) { // OOM error
break;
}
// We need swap to execute this test. Otherwise OOM killer acts with
// SIGKILL before we read the fail counter.
final long maxHeapSize = Runtime.getRuntime().maxMemory();
if (maxHeapSize <= memLimit || maxHeapSize >= memAndSwapLimit) {
throw new RuntimeException(
"Expected memory limit < maximum heap < memory-and-swap limit: "
+ "memory=" + memLimit / M + "M, "
+ "heap=" + maxHeapSize / M + "M, "
+ "memory-and-swap=" + memAndSwapLimit / M + "M");
}
final long initialFailCount = metrics.getMemoryFailCount();
System.out.println("Initial memory fail count: " + initialFailCount);
// Allocate 512M of data in 1M chunks per iteration
byte[][] bytes = new byte[512][];
for (int i = 0; i < 512; i++) {
if (i % 8 == 0) {
System.out.printf("Allocated: %3dM, Memory usage: %3dM, Memory and swap: %3dM\n",
i,
metrics.getMemoryUsage() / M,
metrics.getMemoryAndSwapUsage() / M);
} else {
System.out.print(".");
}
if (!atLeastOneAllocationWorked) {
System.out.println("Allocation failed immediately. Ignoring test!");
return;
}
// Be sure bytes allocations don't get optimized out
System.out.println("DEBUG: Bytes allocation length 1: " + bytes[0].length);
if (Metrics.systemMetrics().getMemoryFailCount() <= count) {
throw new RuntimeException("Memory fail count : new : ["
+ Metrics.systemMetrics().getMemoryFailCount() + "]"
+ ", old : [" + count + "]");
bytes[i] = new byte[M];
Arrays.fill(bytes[i], (byte) 1); // dirty every page
// Break out as soon as we see an increase in failcount
if (metrics.getMemoryFailCount() > initialFailCount) {
break;
}
}
// Be sure bytes allocations don't get optimized out
System.out.println("\nDEBUG: Bytes allocation length 1: " + bytes[0].length);
final long newCount = metrics.getMemoryFailCount();
System.out.println("Final memory fail count: " + newCount);
if (newCount <= initialFailCount) {
throw new RuntimeException("Memory fail count did not increase: initial="
+ initialFailCount + ", final=" + newCount);
}
System.out.println("TEST PASSED!!!");
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -76,7 +76,7 @@ public class TestDockerMemoryMetrics {
}
testOomKillFlag("100m", true);
testMemoryFailCount("128m");
testMemoryFailCount("128m" /*memory*/, "768m" /*max_heap*/, "1024m" /*memory_n_swap*/);
testMemorySoftLimit("500m","200m");
@@ -105,36 +105,47 @@ public class TestDockerMemoryMetrics {
DockerTestUtils.dockerRunJava(opts).shouldHaveExitValue(0).shouldContain("TEST PASSED!!!");
}
private static void testMemoryFailCount(String value) throws Exception {
Common.logNewTestCase("testMemoryFailCount" + value);
private static void testMemoryFailCount(String memory, String heap, String memoryAndSwap) throws Exception {
Common.logNewTestCase("testMemoryFailCount, memory = " + memory
+ ", heap = " + heap
+ ", memory + swap = " + memoryAndSwap);
// Check whether swapping really works for this test
// On some systems there is no swap space enabled. And running
// 'java -Xms{mem-limit} -Xmx{mem-limit} -XX:+AlwaysPreTouch -version'
// 'java -Xms{heap} -Xmx{heap} -XX:+AlwaysPreTouch -version'
// would fail due to swap space size being 0. Note that when swap is
// properly enabled on the system the container gets the same amount
// of swap as is configured for memory. Thus, 2x{mem-limit} is the actual
// memory and swap bound for this pre-test.
// properly enabled, the explicit memory-and-swap limit gives the JVM
// enough headroom to exceed the physical memory limit without being
// killed by the OOM killer.
DockerRunOptions preOpts =
new DockerRunOptions(imageName, "/jdk/bin/java", "-version");
preOpts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/")
.addDockerOpts("--memory=" + value)
.addDockerOpts("--memory=" + memory)
.addDockerOpts("--memory-swap=" + memoryAndSwap)
.addJavaOpts("-XX:+AlwaysPreTouch")
.addJavaOpts("-Xms" + value)
.addJavaOpts("-Xmx" + value);
.addJavaOptsAppended("-XX:InitialHeapSize=" + heap)
.addJavaOptsAppended("-XX:MaxHeapSize=" + heap);
OutputAnalyzer oa = DockerTestUtils.dockerRunJava(preOpts);
String output = oa.getOutput();
if (!output.contains("version")) {
throw new SkippedException("Swapping doesn't work for this test.");
}
// 0 128 1024
// |---o----------------|---------------------------X--------------)-------------|
// START memory.max growth target MaxHeapSize memory+swap limit
// o~~~~~>~>~>~>~>~>~>~>~>~>~>~>~>~>~>~>~> (growth) OOM
// failcount: 0 1 2 3 . . . N
//
DockerRunOptions opts =
new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsMemoryTester");
opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/")
.addDockerOpts("--memory=" + value)
.addJavaOpts("-Xmx" + value)
.addDockerOpts("--memory=" + memory)
.addDockerOpts("--memory-swap=" + memoryAndSwap)
.addJavaOpts("-cp", "/test-classes/")
.addJavaOpts("--add-exports", "java.base/jdk.internal.platform=ALL-UNNAMED")
// set the required heap size *after* inherited jtreg options
.addJavaOptsAppended("-XX:MaxHeapSize=" + heap)
.addClassOptions("failcount");
oa = DockerTestUtils.dockerRunJava(opts);
output = oa.getOutput();
+1 -1
View File
@@ -477,7 +477,7 @@ public class VMProps implements Callable<Map<String, String>> {
protected String vmCDSSupportsAOTCodeCaching() {
if ("true".equals(vmCDSSupportsAOTClassLinking()) &&
!"zero".equals(vmFlavor()) &&
(Platform.isX64() || Platform.isAArch64())) {
(Platform.isX64() || Platform.isAArch64() || Platform.isRISCV64())) {
return "true";
} else {
return "false";
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,7 +23,7 @@
/*
* @test
* @bug 8013852 8031744 8225377 8323684
* @bug 8013852 8031744 8225377 8323684 8389859
* @summary Annotations on types
* @library /tools/javac/lib
* @modules jdk.compiler/com.sun.tools.javac.api
@@ -103,7 +103,9 @@ public class BasicAnnoTests extends JavacTestingAbstractProcessor {
new NameToAnnotationEntry("BasicAnnoTests.TB", BasicAnnoTests.TB.class),
new NameToAnnotationEntry("BasicAnnoTests.TC", BasicAnnoTests.TC.class),
new NameToAnnotationEntry("BasicAnnoTests.TCs", BasicAnnoTests.TCs.class),
new NameToAnnotationEntry("BasicAnnoTests.TD", BasicAnnoTests.TD.class));
new NameToAnnotationEntry("BasicAnnoTests.TD", BasicAnnoTests.TD.class),
new NameToAnnotationEntry("BasicAnnoTests.DTF", BasicAnnoTests.DTF.class),
new NameToAnnotationEntry("BasicAnnoTests.DTP", BasicAnnoTests.DTP.class));
static class NameToAnnotationEntry extends AbstractMap.SimpleEntry<String, Class<? extends Annotation>> {
public NameToAnnotationEntry(String key, Class<? extends Annotation> entry) {
@@ -531,6 +533,16 @@ public class BasicAnnoTests extends JavacTestingAbstractProcessor {
int value();
}
@Target({ElementType.TYPE_USE, ElementType.FIELD})
public @interface DTF {
int value();
}
@Target({ElementType.TYPE_USE, ElementType.PARAMETER})
public @interface DTP {
int value();
}
// Test cases
// TODO: add more cases for arrays
@@ -697,6 +709,29 @@ public class BasicAnnoTests extends JavacTestingAbstractProcessor {
@Test(posn=6, annoType = TB.class, expect = "61")
<T> void m60(@TA(60) @TB(61) String t) { }
// Test dual target annotations on uses of type variables
@Test(posn=6, annoType = DTP.class, expect = "61")
<T> void m61(@DTP(61) T t) { }
@Test(posn=7, annoType = DTP.class, expect = "62")
<T> void m62(@DTP(62) T[] t) { }
class Inner63<T> {
@Test(posn=0, annoType = DTF.class, expect = "63")
@DTF(63) T f;
}
// Test dual target annotations on uses of Class types
@Test(posn=6, annoType = DTP.class, expect = "64")
<T> void m64(@DTP(64) String t) { }
@Test(posn=7, annoType = DTP.class, expect = "65")
<T> void m65(@DTP(65) String[] t) { }
class Inner66<T> {
@Test(posn=0, annoType = DTF.class, expect = "66")
@DTF(66) String f;
}
class Inner70<T> {
@Test(posn=0, annoType = TA.class, expect = "70")
@Test(posn=0, annoType = TB.class, expect = "71")