diff --git a/make/Main.gmk b/make/Main.gmk index a78fc509ff98..1c3ee71f3028 100644 --- a/make/Main.gmk +++ b/make/Main.gmk @@ -751,6 +751,18 @@ $(eval $(call SetupTarget, test-image-lib, \ DEPS := build-test-lib, \ )) +$(eval $(call SetupTarget, build-hotspot-compiler-testlibrary, \ + MAKEFILE := hotspot/test/BuildCompilerTestlibrary, \ + TARGET := build-hotspot-compiler-testlibrary, \ + DEPS := exploded-image build-test-lib, \ +)) + +$(eval $(call SetupTarget, test-image-hotspot-compiler-testlibrary, \ + MAKEFILE := hotspot/test/BuildCompilerTestlibrary, \ + TARGET := test-image-hotspot-compiler-testlibrary, \ + DEPS := build-hotspot-compiler-testlibrary, \ +)) + $(eval $(call SetupTarget, build-test-setup-aot, \ MAKEFILE := test/BuildTestSetupAOT, \ DEPS := interim-langtools exploded-image, \ @@ -1303,7 +1315,7 @@ all-docs-bundles: docs-jdk-bundles docs-javase-bundles docs-reference-bundles test-image: prepare-test-image test-image-jdk-jtreg-native \ test-image-demos-jdk test-image-libtest-jtreg-native \ test-image-lib test-image-lib-native \ - test-image-setup-aot + test-image-setup-aot test-image-hotspot-compiler-testlibrary ifneq ($(JVM_TEST_IMAGE_TARGETS), ) # If JVM_TEST_IMAGE_TARGETS is externally defined, use it instead of the diff --git a/make/hotspot/test/BuildCompilerTestlibrary.gmk b/make/hotspot/test/BuildCompilerTestlibrary.gmk new file mode 100644 index 000000000000..0da107229fa9 --- /dev/null +++ b/make/hotspot/test/BuildCompilerTestlibrary.gmk @@ -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. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +include MakeFileStart.gmk + +################################################################################ +# This file builds the Hotspot compiler testlibrary. +################################################################################ + +include CopyFiles.gmk +include JavaCompilation.gmk + +############################################################################### + +COMPILER_TESTLIBRARY_BASEDIR := $(TOPDIR)/test/hotspot/jtreg/compiler/lib +COMPILER_TESTLIBRARY_SUPPORT := $(SUPPORT_OUTPUTDIR)/test/compiler-testlibrary +COMPILER_TESTLIBRARY_JAR := $(COMPILER_TESTLIBRARY_SUPPORT)/compiler-testlibrary.jar +TEST_LIB_SUPPORT := $(SUPPORT_OUTPUTDIR)/test/lib +WB_CP := $(TEST_LIB_SUPPORT)/wb_classes +TEST_LIB_CP := $(TEST_LIB_SUPPORT)/test-lib_classes + +$(eval $(call SetupJavaCompilation, BUILD_COMPILER_TESTLIBRARY, \ + TARGET_RELEASE := $(TARGET_RELEASE_NEWJDK_UPGRADED), \ + SRC := $(COMPILER_TESTLIBRARY_BASEDIR), \ + BIN := $(COMPILER_TESTLIBRARY_SUPPORT)/classes, \ + JAR := $(COMPILER_TESTLIBRARY_JAR), \ + JAVAC_FLAGS := -cp $(WB_CP) \ + -cp $(TEST_LIB_CP) \ + --add-exports java.base/jdk.internal.math=ALL-UNNAMED, \ +)) + +TARGETS += $(BUILD_COMPILER_TESTLIBRARY) + +build-hotspot-compiler-testlibrary: $(TARGETS) + +################################################################################ +# Targets for building test-image. +################################################################################ + +# Copy to hotspot jtreg test image +$(eval $(call SetupCopyFiles, COPY_COMPILER_TESTLIBRARY, \ + DEST := $(TEST_IMAGE_DIR)/compiler-testlibrary, \ + FILES := $(COMPILER_TESTLIBRARY_JAR), \ +)) + +IMAGE_TARGETS += $(COPY_COMPILER_TESTLIBRARY) + +test-image-hotspot-compiler-testlibrary: $(IMAGE_TARGETS) + +.PHONY: build-hotspot-compiler-testlibrary test-image-hotspot-compiler-testlibrary + +################################################################################ + +include MakeFileEnd.gmk diff --git a/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp b/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp index 130d29498007..db0d5e007a02 100644 --- a/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp @@ -308,7 +308,7 @@ void DowncallLinker::StubGenerator::generate() { // Restore cpu control state after JNI call __ restore_cpu_control_state_after_jni(rscratch1, tmp1); - __ mov(tmp1, _thread_in_native_trans); + __ mov(tmp1, _thread_in_vm); __ strw(tmp1, Address(rthread, JavaThread::thread_state_offset())); // Force this write out before the read below diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 78f8a86dcf34..6d280d9e8ab9 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -1972,7 +1972,9 @@ void MacroAssembler::verify_secondary_supers_table(Register r_sub_klass, mov(r1, r_sub_klass); // r1 <- r4 mov(r2, /*expected*/rscratch1); // r2 <- r8 mov(r3, result); // r3 <- r5 - mov(r4, (address)("mismatch")); // r4 <- const + const char* msg = "mismatch"; + const char* str = (code_section()->scratch_emit()) ? msg : AOTCodeCache::add_C_string(msg); + lea(r4, ExternalAddress((address)str)); // r4 <- const rt_call(CAST_FROM_FN_PTR(address, Klass::on_secondary_supers_verification_failure), rscratch2); should_not_reach_here(); } @@ -2043,7 +2045,7 @@ void MacroAssembler::_verify_oop(Register reg, const char* s, const char* file, stp(rscratch2, lr, Address(pre(sp, -2 * wordSize))); mov(r0, reg); - movptr(rscratch1, (uintptr_t)(address)b); + lea(rscratch1, ExternalAddress((address)b)); // call indirectly to solve generation ordering problem lea(rscratch2, RuntimeAddress(StubRoutines::verify_oop_subroutine_entry_address())); @@ -2094,7 +2096,7 @@ void MacroAssembler::_verify_oop_addr(Address addr, const char* s, const char* f } else { ldr(r0, addr); } - movptr(rscratch1, (uintptr_t)(address)b); + lea(rscratch1, ExternalAddress((address)b)); // call indirectly to solve generation ordering problem lea(rscratch2, RuntimeAddress(StubRoutines::verify_oop_subroutine_entry_address())); diff --git a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp index cb14d3eada1e..60065ab19406 100644 --- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp @@ -2047,14 +2047,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, Label safepoint_in_progress, safepoint_in_progress_done; - // Switch thread to "native transition" state before reading the synchronization state. - // This additional state is necessary because reading and testing the synchronization - // state is not atomic w.r.t. GC, as this scenario demonstrates: - // Java thread A, in _thread_in_native state, loads _not_synchronized and is preempted. - // VM thread changes sync state to synchronizing and suspends threads for GC. - // Thread A is resumed to finish this native method, but doesn't block here since it - // didn't see any synchronization is progress, and escapes. - __ mov(rscratch1, _thread_in_native_trans); + __ mov(rscratch1, _thread_in_vm); __ strw(rscratch1, Address(rthread, JavaThread::thread_state_offset())); diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index 61e6e48db73e..ca78fc0952f2 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -827,7 +827,7 @@ class StubGenerator: public StubCodeGenerator { assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); #endif BLOCK_COMMENT("call MacroAssembler::debug"); - __ mov(rscratch1, CAST_FROM_FN_PTR(address, MacroAssembler::debug64)); + __ lea(rscratch1, RuntimeAddress(CAST_FROM_FN_PTR(address, MacroAssembler::debug64))); __ blr(rscratch1); __ hlt(0); @@ -12826,7 +12826,7 @@ class StubGenerator: public StubCodeGenerator { // Native caller has no idea how to handle exceptions, // so we just crash here. Up to callee to catch exceptions. __ verify_oop(r0); - __ movptr(rscratch1, CAST_FROM_FN_PTR(uint64_t, UpcallLinker::handle_uncaught_exception)); + __ lea(rscratch1, RuntimeAddress(CAST_FROM_FN_PTR(address, UpcallLinker::handle_uncaught_exception))); __ blr(rscratch1); __ should_not_reach_here(); diff --git a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp index dacb47d15944..9c53800dd34b 100644 --- a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp @@ -1422,7 +1422,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { __ verify_sve_vector_length(); // change thread state - __ mov(rscratch1, _thread_in_native_trans); + __ mov(rscratch1, _thread_in_vm); __ lea(rscratch2, Address(rthread, JavaThread::thread_state_offset())); __ stlrw(rscratch1, rscratch2); diff --git a/src/hotspot/cpu/arm/sharedRuntime_arm.cpp b/src/hotspot/cpu/arm/sharedRuntime_arm.cpp index d0fba14d8aa4..593ba159aa7b 100644 --- a/src/hotspot/cpu/arm/sharedRuntime_arm.cpp +++ b/src/hotspot/cpu/arm/sharedRuntime_arm.cpp @@ -1263,9 +1263,9 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, __ c2bool(R0); } - // Do a safepoint check while thread is in transition state + // Do a safepoint check Label call_safepoint_runtime, return_to_java; - __ mov(Rtemp, _thread_in_native_trans); + __ mov(Rtemp, _thread_in_vm); __ str_32(Rtemp, Address(Rthread, JavaThread::thread_state_offset())); // make sure the store is observed before reading the SafepointSynchronize state and further mem refs @@ -1292,6 +1292,9 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, Label slow_unlock, unlock_done; if (method->is_synchronized()) { + // Get locked oop from the handle we passed to jni + __ ldr(sync_obj, Address(sync_handle)); + log_trace(fastlock)("SharedRuntime unlock fast"); __ fast_unlock(sync_obj, R2 /* t1 */, tmp /* t2 */, Rtemp /* t3 */, 7 /* savemask */, slow_unlock); diff --git a/src/hotspot/cpu/arm/templateInterpreterGenerator_arm.cpp b/src/hotspot/cpu/arm/templateInterpreterGenerator_arm.cpp index 6ecdc29cf451..99f30a8f2669 100644 --- a/src/hotspot/cpu/arm/templateInterpreterGenerator_arm.cpp +++ b/src/hotspot/cpu/arm/templateInterpreterGenerator_arm.cpp @@ -1014,7 +1014,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { } // Do safepoint check - __ mov(Rtemp, _thread_in_native_trans); + __ mov(Rtemp, _thread_in_vm); __ str_32(Rtemp, Address(Rthread, JavaThread::thread_state_offset())); // Force this write out before the read below diff --git a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp index 8f2eb05cbd5a..d6051da562a3 100644 --- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp @@ -2250,7 +2250,7 @@ void LIR_Assembler::emit_alloc_obj(LIR_OpAllocObj* op) { void LIR_Assembler::emit_alloc_array(LIR_OpAllocArray* op) { LP64_ONLY( __ extsw(op->len()->as_register(), op->len()->as_register()); ) - if (UseSlowPath || + if (UseSlowPath || op->always_slow_path() || (!UseFastNewObjectArray && (is_reference_type(op->type()))) || (!UseFastNewTypeArray && (!is_reference_type(op->type())))) { __ b(*op->stub()->entry()); diff --git a/src/hotspot/cpu/ppc/downcallLinker_ppc.cpp b/src/hotspot/cpu/ppc/downcallLinker_ppc.cpp index d149fc33ac38..d550c33b1122 100644 --- a/src/hotspot/cpu/ppc/downcallLinker_ppc.cpp +++ b/src/hotspot/cpu/ppc/downcallLinker_ppc.cpp @@ -297,7 +297,7 @@ void DowncallLinker::StubGenerator::generate() { Label L_after_reguard; if (_needs_transition) { - __ li(tmp, _thread_in_native_trans); + __ li(tmp, _thread_in_vm); __ release(); __ stw(tmp, in_bytes(JavaThread::thread_state_offset()), R16_thread); if (!UseSystemMemoryBarrier) { diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc_sha.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc_sha.cpp index bdf2d8d268ac..c7f9544be11f 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc_sha.cpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc_sha.cpp @@ -613,7 +613,6 @@ void MacroAssembler::sha512_update_sha_state(const Register state, VectorRegister ini_e = VR14; VectorRegister ini_g = VR16; static const VectorRegister inis[] = {ini_a, ini_c, ini_e, ini_g}; - static const int total_inis = sizeof(inis)/sizeof(VectorRegister); Label state_save_aligned, after_state_save_aligned; diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp index 364ed7de3b07..553934953873 100644 --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp @@ -2617,21 +2617,8 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, } // Publish thread state - // -------------------------------------------------------------------------- - - // Switch thread to "native transition" state before reading the - // synchronization state. This additional state is necessary because reading - // and testing the synchronization state is not atomic w.r.t. GC, as this - // scenario demonstrates: - // - Java thread A, in _thread_in_native state, loads _not_synchronized - // and is preempted. - // - VM thread changes sync state to synchronizing and suspends threads - // for GC. - // - Thread A is resumed to finish this native method, but doesn't block - // here since it didn't see any synchronization in progress, and escapes. - - // Transition from _thread_in_native to _thread_in_native_trans. - __ li(R0, _thread_in_native_trans); + // Transition from _thread_in_native to _thread_in_vm. + __ li(R0, _thread_in_vm); __ release(); // TODO: PPC port assert(4 == JavaThread::sz_thread_state(), "unexpected field size"); __ stw(R0, thread_(thread_state)); @@ -2682,10 +2669,10 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, // Publish thread state. // -------------------------------------------------------------------------- - // Thread state is thread_in_native_trans. Any safepoint blocking has + // Thread state is _thread_in_vm. Any safepoint blocking has // already happened so we can now change state to _thread_in_Java. - // Transition from _thread_in_native_trans to _thread_in_Java. + // Transition from _thread_in_vm to _thread_in_Java. __ li(R0, _thread_in_Java); __ lwsync(); // Acquire safepoint and suspend state, release thread state. // TODO: PPC port assert(4 == JavaThread::sz_thread_state(), "unexpected field size"); diff --git a/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp b/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp index 0fee34392847..35042e841e66 100644 --- a/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp @@ -1484,21 +1484,10 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { // In order for GC to work, don't clear the last_Java_sp until after // blocking. - //============================================================================= - // Switch thread to "native transition" state before reading the - // synchronization state. This additional state is necessary - // because reading and testing the synchronization state is not - // atomic w.r.t. GC, as this scenario demonstrates: Java thread A, - // in _thread_in_native state, loads _not_synchronized and is - // preempted. VM thread changes sync state to synchronizing and - // suspends threads for GC. Thread A is resumed to finish this - // native method, but doesn't block here since it didn't see any - // synchronization in progress, and escapes. - // We use release_store_fence to update values like the thread state, where // we don't want the current thread to continue until all our prior memory // accesses (including the new thread state) are visible to other threads. - __ li(R0/*thread_state*/, _thread_in_native_trans); + __ li(R0/*thread_state*/, _thread_in_vm); __ release(); __ stw(R0/*thread_state*/, thread_(thread_state)); if (!UseSystemMemoryBarrier) { @@ -1506,9 +1495,8 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { } // Now before we return to java we must look for a current safepoint - // (a new safepoint can not start since we entered native_trans). - // We must check here because a current safepoint could be modifying - // the callers registers right this moment. + // (a new safepoint can not start since we entered _thread_in_vm). + // We must check here because a current safepoint could be in progress. // Acquire isn't strictly necessary here because of the fence, but // sync_state is declared to be volatile, so we do it anyway @@ -1538,7 +1526,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { //============================================================================= // <<<<<< Back in Interpreter Frame >>>>> - // We are in thread_in_native_trans here and back in the normal + // We are in _thread_in_vm here and back in the normal // interpreter frame. We don't have to do anything special about // safepoints and we can switch to Java mode anytime we are ready. diff --git a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp index 8b9d3d7c4344..dfb59f071d34 100644 --- a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp @@ -2647,10 +2647,12 @@ void TemplateTable::getfield_or_static(int byte_no, bool is_static, RewriteContr Rscratch = R11_scratch1; // used by load_field_cp_cache_entry // R12_scratch2 used by load_field_cp_cache_entry - static address field_branch_table[number_of_states], + static address field_rw_branch_table[number_of_states], + field_norw_branch_table[number_of_states], static_branch_table[number_of_states]; - address* branch_table = (is_static || rc == may_not_rewrite) ? static_branch_table : field_branch_table; + address* branch_table = is_static ? static_branch_table : + (rc == may_rewrite ? field_rw_branch_table : field_norw_branch_table); // Get field offset. resolve_cache_and_index_for_field(byte_no, Rcache, Rscratch); @@ -2698,14 +2700,7 @@ void TemplateTable::getfield_or_static(int byte_no, bool is_static, RewriteContr #ifdef ASSERT __ bind(LFlagInvalid); __ stop("got invalid flag"); -#endif - if (!is_static && rc == may_not_rewrite) { - // We reuse the code from is_static. It's jumped to via the table above. - return; - } - -#ifdef ASSERT // __ bind(Lvtos); address pc_before_fence = __ pc(); __ fence(); // Volatile entry point (one instruction before non-volatile_entry point). diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp index b4277f729aee..c0504ba23da7 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp @@ -2021,47 +2021,49 @@ void C2_MacroAssembler::enc_cmpEqNe_imm0_branch(int cmpFlag, Register op1, Label } void C2_MacroAssembler::enc_cmove(int cmpFlag, Register op1, Register op2, Register dst, Register src) { - bool is_unsigned = (cmpFlag & unsigned_branch_mask) == unsigned_branch_mask; - int op_select = cmpFlag & (~unsigned_branch_mask); + if (dst != src) { + bool is_unsigned = (cmpFlag & unsigned_branch_mask) == unsigned_branch_mask; + int op_select = cmpFlag & (~unsigned_branch_mask); - switch (op_select) { - case BoolTest::eq: - cmov_eq(op1, op2, dst, src); - break; - case BoolTest::ne: - cmov_ne(op1, op2, dst, src); - break; - case BoolTest::le: - if (is_unsigned) { - cmov_leu(op1, op2, dst, src); - } else { - cmov_le(op1, op2, dst, src); - } - break; - case BoolTest::ge: - if (is_unsigned) { - cmov_geu(op1, op2, dst, src); - } else { - cmov_ge(op1, op2, dst, src); - } - break; - case BoolTest::lt: - if (is_unsigned) { - cmov_ltu(op1, op2, dst, src); - } else { - cmov_lt(op1, op2, dst, src); - } - break; - case BoolTest::gt: - if (is_unsigned) { - cmov_gtu(op1, op2, dst, src); - } else { - cmov_gt(op1, op2, dst, src); - } - break; - default: - assert(false, "unsupported compare condition"); - ShouldNotReachHere(); + switch (op_select) { + case BoolTest::eq: + cmov_eq(op1, op2, dst, src); + break; + case BoolTest::ne: + cmov_ne(op1, op2, dst, src); + break; + case BoolTest::le: + if (is_unsigned) { + cmov_leu(op1, op2, dst, src); + } else { + cmov_le(op1, op2, dst, src); + } + break; + case BoolTest::ge: + if (is_unsigned) { + cmov_geu(op1, op2, dst, src); + } else { + cmov_ge(op1, op2, dst, src); + } + break; + case BoolTest::lt: + if (is_unsigned) { + cmov_ltu(op1, op2, dst, src); + } else { + cmov_lt(op1, op2, dst, src); + } + break; + case BoolTest::gt: + if (is_unsigned) { + cmov_gtu(op1, op2, dst, src); + } else { + cmov_gt(op1, op2, dst, src); + } + break; + default: + assert(false, "unsupported compare condition"); + ShouldNotReachHere(); + } } } diff --git a/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp b/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp index f9d7ce78ff0e..b11abb912ee5 100644 --- a/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp +++ b/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp @@ -308,7 +308,7 @@ void DowncallLinker::StubGenerator::generate() { __ restore_cpu_control_state_after_jni(t0); __ block_comment("{ thread native2java"); - __ mv(t0, _thread_in_native_trans); + __ mv(t0, _thread_in_vm); __ sw(t0, Address(xthread, JavaThread::thread_state_offset())); // Force this write out before the read below diff --git a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp index 2139ffd52345..f0f5abbfcaf9 100644 --- a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp @@ -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" @@ -372,10 +373,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); diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp index 647846d523b2..03841fa48cb5 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp @@ -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,11 +220,14 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, // Test for in-cset if (is_strong) { +#if INCLUDE_CDS 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 { + } else +#endif + { __ mv(t1, ShenandoahHeap::in_cset_fast_test_addr()); __ srli(t0, x10, ShenandoahHeapRegion::region_size_bytes_shift_jint()); } @@ -440,10 +444,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); diff --git a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp index 143a7e765911..9fbc59fe5ce3 100644 --- a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp @@ -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); diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index 15d853f919b5..ec044b6f824d 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -28,6 +28,7 @@ #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" @@ -523,20 +524,25 @@ 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 {"); push_reg(RegSet::of(ra, t0, t1, c_rarg0), sp); mv(c_rarg0, reg); // c_rarg0 : x10 - { - // The length of the instruction sequence emitted should not depend - // 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); - } + // The length of the instruction sequence emitted should not depend + // on the address of the char buffer so that the size of mach nodes for + // scratch emit and normal emit matches. + la(t0, ExternalAddress((address)b)); // Call indirectly to solve generation ordering problem ld(t1, RuntimeAddress(StubRoutines::verify_oop_subroutine_entry_address())); @@ -709,7 +715,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 {"); @@ -722,13 +736,10 @@ void MacroAssembler::_verify_oop_addr(Address addr, const char* s, const char* f ld(x10, addr); } - { - // The length of the instruction sequence emitted should not depend - // 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); - } + // The length of the instruction sequence emitted should not depend + // on the address of the char buffer so that the size of mach nodes for + // scratch emit and normal emit matches. + la(t0, ExternalAddress((address)b)); // Call indirectly to solve generation ordering problem ld(t1, RuntimeAddress(StubRoutines::verify_oop_subroutine_entry_address())); @@ -1255,13 +1266,33 @@ void MacroAssembler::wrap_label(Register r1, Register r2, Label &L, #undef INSN -// cmov +// cmov_zicond_eqz: dst = (cond == 0) ? src : dst +void MacroAssembler::cmov_zicond_eqz(Register dst, Register src, Register cond, Register tmp) { + assert(UseZicond, "UseZicond must be enabled"); + assert_different_registers(dst, src, cond); + czero_eqz(dst, dst, cond); + if (src != zr) { + czero_nez(tmp, src, cond); + add(dst, dst, tmp); + } +} + +// cmov_zicond_nez: dst = (cond != 0) ? src : dst +void MacroAssembler::cmov_zicond_nez(Register dst, Register src, Register cond, Register tmp) { + assert(UseZicond, "UseZicond must be enabled"); + assert_different_registers(dst, src, cond); + czero_nez(dst, dst, cond); + if (src != zr) { + czero_eqz(tmp, src, cond); + add(dst, dst, tmp); + } +} + void MacroAssembler::cmov_eq(Register cmp1, Register cmp2, Register dst, Register src) { + assert_different_registers(dst, src); if (UseZicond) { xorr(t0, cmp1, cmp2); - czero_eqz(dst, dst, t0); - czero_nez(t0 , src, t0); - orr(dst, dst, t0); + cmov_zicond_eqz(dst, src, t0, t0); return; } Label no_set; @@ -1271,11 +1302,10 @@ void MacroAssembler::cmov_eq(Register cmp1, Register cmp2, Register dst, Registe } void MacroAssembler::cmov_ne(Register cmp1, Register cmp2, Register dst, Register src) { + assert_different_registers(dst, src); if (UseZicond) { xorr(t0, cmp1, cmp2); - czero_nez(dst, dst, t0); - czero_eqz(t0 , src, t0); - orr(dst, dst, t0); + cmov_zicond_nez(dst, src, t0, t0); return; } Label no_set; @@ -1285,11 +1315,10 @@ void MacroAssembler::cmov_ne(Register cmp1, Register cmp2, Register dst, Registe } void MacroAssembler::cmov_le(Register cmp1, Register cmp2, Register dst, Register src) { + assert_different_registers(dst, src); if (UseZicond) { slt(t0, cmp2, cmp1); - czero_eqz(dst, dst, t0); - czero_nez(t0, src, t0); - orr(dst, dst, t0); + cmov_zicond_eqz(dst, src, t0, t0); return; } Label no_set; @@ -1299,11 +1328,10 @@ void MacroAssembler::cmov_le(Register cmp1, Register cmp2, Register dst, Registe } void MacroAssembler::cmov_leu(Register cmp1, Register cmp2, Register dst, Register src) { + assert_different_registers(dst, src); if (UseZicond) { sltu(t0, cmp2, cmp1); - czero_eqz(dst, dst, t0); - czero_nez(t0, src, t0); - orr(dst, dst, t0); + cmov_zicond_eqz(dst, src, t0, t0); return; } Label no_set; @@ -1313,11 +1341,10 @@ void MacroAssembler::cmov_leu(Register cmp1, Register cmp2, Register dst, Regist } void MacroAssembler::cmov_ge(Register cmp1, Register cmp2, Register dst, Register src) { + assert_different_registers(dst, src); if (UseZicond) { slt(t0, cmp1, cmp2); - czero_eqz(dst, dst, t0); - czero_nez(t0, src, t0); - orr(dst, dst, t0); + cmov_zicond_eqz(dst, src, t0, t0); return; } Label no_set; @@ -1327,11 +1354,10 @@ void MacroAssembler::cmov_ge(Register cmp1, Register cmp2, Register dst, Registe } void MacroAssembler::cmov_geu(Register cmp1, Register cmp2, Register dst, Register src) { + assert_different_registers(dst, src); if (UseZicond) { sltu(t0, cmp1, cmp2); - czero_eqz(dst, dst, t0); - czero_nez(t0, src, t0); - orr(dst, dst, t0); + cmov_zicond_eqz(dst, src, t0, t0); return; } Label no_set; @@ -1341,11 +1367,10 @@ void MacroAssembler::cmov_geu(Register cmp1, Register cmp2, Register dst, Regist } void MacroAssembler::cmov_lt(Register cmp1, Register cmp2, Register dst, Register src) { + assert_different_registers(dst, src); if (UseZicond) { slt(t0, cmp1, cmp2); - czero_nez(dst, dst, t0); - czero_eqz(t0, src, t0); - orr(dst, dst, t0); + cmov_zicond_nez(dst, src, t0, t0); return; } Label no_set; @@ -1355,11 +1380,10 @@ void MacroAssembler::cmov_lt(Register cmp1, Register cmp2, Register dst, Registe } void MacroAssembler::cmov_ltu(Register cmp1, Register cmp2, Register dst, Register src) { + assert_different_registers(dst, src); if (UseZicond) { sltu(t0, cmp1, cmp2); - czero_nez(dst, dst, t0); - czero_eqz(t0, src, t0); - orr(dst, dst, t0); + cmov_zicond_nez(dst, src, t0, t0); return; } Label no_set; @@ -1369,11 +1393,10 @@ void MacroAssembler::cmov_ltu(Register cmp1, Register cmp2, Register dst, Regist } void MacroAssembler::cmov_gt(Register cmp1, Register cmp2, Register dst, Register src) { + assert_different_registers(dst, src); if (UseZicond) { slt(t0, cmp2, cmp1); - czero_nez(dst, dst, t0); - czero_eqz(t0, src, t0); - orr(dst, dst, t0); + cmov_zicond_nez(dst, src, t0, t0); return; } Label no_set; @@ -1383,11 +1406,10 @@ void MacroAssembler::cmov_gt(Register cmp1, Register cmp2, Register dst, Registe } void MacroAssembler::cmov_gtu(Register cmp1, Register cmp2, Register dst, Register src) { + assert_different_registers(dst, src); if (UseZicond) { sltu(t0, cmp2, cmp1); - czero_nez(dst, dst, t0); - czero_eqz(t0, src, t0); - orr(dst, dst, t0); + cmov_zicond_nez(dst, src, t0, t0); return; } Label no_set; @@ -5353,7 +5375,9 @@ void MacroAssembler::verify_secondary_supers_table(Register r_sub_klass, mv(x11, r_sub_klass); mv(x12, tmp3); mv(x13, result); - mv(x14, (address)("mismatch")); + const char* msg = "mismatch"; + const char* str = (code_section()->scratch_emit()) ? msg : AOTCodeCache::add_C_string(msg); + la(x14, ExternalAddress((address) str)); rt_call(CAST_FROM_FN_PTR(address, Klass::on_secondary_supers_verification_failure)); should_not_reach_here(); } diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index dffb85455f0b..9af9fad06d08 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -685,6 +685,9 @@ class MacroAssembler: public Assembler { void bltz(Register Rs, const address dest); void bgtz(Register Rs, const address dest); + void cmov_zicond_eqz(Register dst, Register src, Register cond, Register tmp = t0); + void cmov_zicond_nez(Register dst, Register src, Register cond, Register tmp = t0); + void cmov_eq(Register cmp1, Register cmp2, Register dst, Register src); void cmov_ne(Register cmp1, Register cmp2, Register dst, Register src); void cmov_le(Register cmp1, Register cmp2, Register dst, Register src); diff --git a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp index ec0a5f5d9b3a..f28230f23bc5 100644 --- a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp +++ b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp @@ -1817,14 +1817,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, Label safepoint_in_progress, safepoint_in_progress_done; - // Switch thread to "native transition" state before reading the synchronization state. - // This additional state is necessary because reading and testing the synchronization - // state is not atomic w.r.t. GC, as this scenario demonstrates: - // Java thread A, in _thread_in_native state, loads _not_synchronized and is preempted. - // VM thread changes sync state to synchronizing and suspends threads for GC. - // Thread A is resumed to finish this native method, but doesn't block here since it - // didn't see any synchronization is progress, and escapes. - __ mv(t0, _thread_in_native_trans); + __ mv(t0, _thread_in_vm); __ sw(t0, Address(xthread, JavaThread::thread_state_offset())); diff --git a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp index b5b1b89ca72a..ef23498da5c7 100644 --- a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp @@ -1213,7 +1213,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { // Force all preceding writes to be observed prior to thread state change __ membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore); - __ mv(t0, _thread_in_native_trans); + __ mv(t0, _thread_in_vm); __ sw(t0, Address(xthread, JavaThread::thread_state_offset())); // Force this write out before the read below diff --git a/src/hotspot/cpu/s390/downcallLinker_s390.cpp b/src/hotspot/cpu/s390/downcallLinker_s390.cpp index f1c41d05b5cf..4fe4c31567a0 100644 --- a/src/hotspot/cpu/s390/downcallLinker_s390.cpp +++ b/src/hotspot/cpu/s390/downcallLinker_s390.cpp @@ -247,7 +247,7 @@ void DowncallLinker::StubGenerator::generate() { if (_needs_transition) { __ block_comment("thread_native2java {"); - __ set_thread_state(_thread_in_native_trans); + __ set_thread_state(_thread_in_vm); if (!UseSystemMemoryBarrier) { __ z_fence(); // Order state change wrt. safepoint poll. diff --git a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp index 06b6b74c9fd9..b6b22102d011 100644 --- a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp +++ b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp @@ -2745,16 +2745,8 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, break; } - // Switch thread to "native transition" state before reading the synchronization state. - // This additional state is necessary because reading and testing the synchronization - // state is not atomic w.r.t. GC, as this scenario demonstrates: - // - Java thread A, in _thread_in_native state, loads _not_synchronized and is preempted. - // - VM thread changes sync state to synchronizing and suspends threads for GC. - // - Thread A is resumed to finish this native method, but doesn't block here since it - // didn't see any synchronization in progress, and escapes. - - // Transition from _thread_in_native to _thread_in_native_trans. - __ set_thread_state(_thread_in_native_trans); + // Transition from _thread_in_native to _thread_in_vm. + __ set_thread_state(_thread_in_vm); // Safepoint synchronization //-------------------------------------------------------------------- @@ -2795,10 +2787,10 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, } //-------------------------------------------------------------------- - // Thread state is thread_in_native_trans. Any safepoint blocking has + // Thread state is _thread_in_vm. Any safepoint blocking has // already happened so we can now change state to _thread_in_Java. //-------------------------------------------------------------------- - // Transition from _thread_in_native_trans to _thread_in_Java. + // Transition from _thread_in_vm to _thread_in_Java. __ set_thread_state(_thread_in_Java); // Check preemption for Object.wait() diff --git a/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp b/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp index 8e235a8c3c87..c0a1b06954da 100644 --- a/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp @@ -1575,26 +1575,14 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { // In order for GC to work, don't clear the last_Java_sp until after // blocking. - //============================================================================= - // Switch thread to "native transition" state before reading the - // synchronization state. This additional state is necessary - // because reading and testing the synchronization state is not - // atomic w.r.t. GC, as this scenario demonstrates: Java thread A, - // in _thread_in_native state, loads _not_synchronized and is - // preempted. VM thread changes sync state to synchronizing and - // suspends threads for GC. Thread A is resumed to finish this - // native method, but doesn't block here since it didn't see any - // synchronization is progress, and escapes. - - __ set_thread_state(_thread_in_native_trans); + __ set_thread_state(_thread_in_vm); if (!UseSystemMemoryBarrier) { __ z_fence(); } // Now before we return to java we must look for a current safepoint - // (a new safepoint can not start since we entered native_trans). - // We must check here because a current safepoint could be modifying - // the callers registers right this moment. + // (a new safepoint can not start since we entered _thread_in_vm). + // We must check here because a current safepoint could be in progress. // Check for safepoint operation in progress and/or pending suspend requests. { @@ -1612,7 +1600,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { //============================================================================= // Back in Interpreter Frame. - // We are in thread_in_native_trans here and back in the normal + // We are in _thread_in_vm here and back in the normal // interpreter frame. We don't have to do anything special about // safepoints and we can switch to Java mode anytime we are ready. diff --git a/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp b/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp index e3bf5f17fe9b..2480e68e7b86 100644 --- a/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp +++ b/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp @@ -310,7 +310,7 @@ void DowncallLinker::StubGenerator::generate() { __ block_comment("{ thread native2java"); __ restore_cpu_control_state_after_jni(rscratch1); - __ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_native_trans); + __ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_vm); // Force this write out before the read below if (!UseSystemMemoryBarrier) { diff --git a/src/hotspot/cpu/x86/globals_x86.hpp b/src/hotspot/cpu/x86/globals_x86.hpp index c41be7a67724..66ed3abd0cc7 100644 --- a/src/hotspot/cpu/x86/globals_x86.hpp +++ b/src/hotspot/cpu/x86/globals_x86.hpp @@ -108,7 +108,7 @@ define_pd_global(bool, InlineTypeReturnedAsFields, true); "Highest supported AVX instructions set on x86/x64") \ range(0, 3) \ \ - product(bool, UseAPX, false, EXPERIMENTAL, \ + product(bool, UseAPX, false, \ "Use Intel Advanced Performance Extensions") \ \ product(bool, UseKNLSetting, false, DIAGNOSTIC, \ diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index d5b39f8b0eb8..dd6a6ed51ed4 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -4607,7 +4607,7 @@ void MacroAssembler::lookup_secondary_supers_table_var(Register r_sub_klass, assert(Array::length_offset_in_bytes() == 0, "Adjust this code"); cmpq(r_super_klass, Address(r_array_base, r_array_index, Address::times_8)); - jccb(Assembler::equal, L_success); + jcc(Assembler::equal, L_success); // Restore slot to its true value movb(slot, Address(r_super_klass, Klass::hash_slot_offset())); @@ -4618,7 +4618,7 @@ void MacroAssembler::lookup_secondary_supers_table_var(Register r_sub_klass, // Is there another entry to check? Consult the bitmap. btq(r_bitmap, 1); - jccb(Assembler::carryClear, L_failure); + jcc(Assembler::carryClear, L_failure); // Calls into the stub generated by lookup_secondary_supers_table_slow_path. // Arguments: r_super_klass, r_array_base, r_array_index, r_bitmap. @@ -4757,21 +4757,6 @@ void MacroAssembler::lookup_secondary_supers_table_slow_path(Register r_super_kl } } -struct VerifyHelperArguments { - Klass* _super; - Klass* _sub; - intptr_t _linear_result; - intptr_t _table_result; -}; - -static void verify_secondary_supers_table_helper(const char* msg, VerifyHelperArguments* args) { - Klass::on_secondary_supers_verification_failure(args->_super, - args->_sub, - args->_linear_result, - args->_table_result, - msg); -} - // Make sure that the hashed lookup and a linear scan agree. void MacroAssembler::verify_secondary_supers_table(Register r_sub_klass, Register r_super_klass, @@ -4814,15 +4799,31 @@ void MacroAssembler::verify_secondary_supers_table(Register r_sub_klass, cmpl(linear_result, result); jcc(Assembler::equal, L_done); - { // To avoid calling convention issues, build a record on the stack - // and pass the pointer to that instead. + { // Push values on stack and load them into argument registers + // to avoid overlaping registers issue. push(result); push(linear_result); push(r_sub_klass); push(r_super_klass); - movptr(c_rarg1, rsp); - movptr(c_rarg0, (uintptr_t) "mismatch"); - call(RuntimeAddress(CAST_FROM_FN_PTR(address, verify_secondary_supers_table_helper))); + movptr(c_rarg0, Address(rsp, 0 * wordSize)); // super + movptr(c_rarg1, Address(rsp, 1 * wordSize)); // sub + movptr(c_rarg2, Address(rsp, 2 * wordSize)); // linear_result + movptr(c_rarg3, Address(rsp, 3 * wordSize)); // table_result + const char* msg = "mismatch"; + const char* str = (code_section()->scratch_emit()) ? msg : AOTCodeCache::add_C_string(msg); + lea(rscratch1, ExternalAddress((address)str)); +#ifdef _WIN64 + // Win64 pass only 4 arguments in registers, push message on stack. + // Windows always allocates space for its register args and we + // need one more for 5th argument. + subq(rsp, (frame::arg_reg_save_area_bytes + wordSize)); + andq(rsp, -StackAlignmentInBytes); // align stack as required by ABI + movptr(Address(rsp, frame::arg_reg_save_area_bytes), rscratch1); +#else + movptr(c_rarg4, rscratch1); + andq(rsp, -StackAlignmentInBytes); // align stack as required by ABI +#endif + call(RuntimeAddress(CAST_FROM_FN_PTR(address, Klass::on_secondary_supers_verification_failure))); should_not_reach_here(); } bind(L_done); diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp index 37df7acf9426..8d257565c939 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp @@ -2456,14 +2456,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, default : ShouldNotReachHere(); } - // Switch thread to "native transition" state before reading the synchronization state. - // This additional state is necessary because reading and testing the synchronization - // state is not atomic w.r.t. GC, as this scenario demonstrates: - // Java thread A, in _thread_in_native state, loads _not_synchronized and is preempted. - // VM thread changes sync state to synchronizing and suspends threads for GC. - // Thread A is resumed to finish this native method, but doesn't block here since it - // didn't see any synchronization is progress, and escapes. - __ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_native_trans); + __ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_vm); // Force this write out before the read below if (!UseSystemMemoryBarrier) { diff --git a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp index 7da01709d726..631d23801d33 100644 --- a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp +++ b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp @@ -956,7 +956,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { // change thread state __ movl(Address(thread, JavaThread::thread_state_offset()), - _thread_in_native_trans); + _thread_in_vm); // Force this write out before the read below if (!UseSystemMemoryBarrier) { diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index a51faa0bea62..12cdadf026b6 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -1061,8 +1061,7 @@ void VM_Version::get_processor_features() { // Currently APX support is only enabled for targets supporting AVX512VL feature. if (supports_apx_f() && os_supports_apx_egprs() && supports_avx512vl()) { if (FLAG_IS_DEFAULT(UseAPX)) { - FLAG_SET_DEFAULT(UseAPX, false); // by default UseAPX is false - clear_feature(CPU_APX_F); + FLAG_SET_DEFAULT(UseAPX, true); // by default UseAPX is false; enable if supported. } else if (!UseAPX) { clear_feature(CPU_APX_F); } @@ -1077,6 +1076,14 @@ void VM_Version::get_processor_features() { FLAG_SET_DEFAULT(UseAPX, false); } } +#if defined(COMPILER2) + if (UseAPX) { + // Increase InlineSmallCode by 10% + if (FLAG_IS_DEFAULT(InlineSmallCode)) { + FLAG_SET_DEFAULT(InlineSmallCode, InlineSmallCode * 1.10); + } + } +#endif CHECK_CPU_FEATURE(UseCLMUL, CLMUL, supports_clmul(), "CLMUL" MULTI_INST_WARNING_MSG); CHECK_CPU_FEATURE(UseAES, AES, supports_aes(), "AES" MULTI_INST_WARNING_MSG); @@ -1146,6 +1153,10 @@ void VM_Version::get_processor_features() { cpu_family(), _model, _stepping, os::cpu_microcode_revision()); ss.print(", "); int features_offset = (int)ss.size(); + if (compute_fast_bmi2()) { + _features.set_feature(CPU_FAST_BMI2); + } + insert_features_names(_features, ss); _cpu_info_string = ss.as_string(true); @@ -1999,6 +2010,51 @@ bool VM_Version::compute_has_intel_jcc_erratum() { } } +// The BMI2 instruction set includes PEXT (parallel bits extract) and PDEP +// (parallel bits deposit), which are used to intrinsify Integer/Long.compress +// and Integer/Long.expand (added in JDK 19, see https://bugs.openjdk.org/browse/JDK-8283893). +// +// While all BMI2-capable CPUs can execute these instructions, PEXT and PDEP +// are unique in that some vendors implement them via microcode rather than +// native ALU hardware. The microcoded versions are significantly slower (high latency/low throughput) +// than the manual bitwise fallback used in the Java implementation. +// Conversely, all other BMI2 instructions (BZHI, MULX, RORX, SARX, SHRX, SHLX) +// execute efficiently on every BMI2-capable CPU and are unaffected by this check. +// +// The logic in this method is based on official optimization guides from hardware vendors, +// to guarantee that microcode implementations of PEXT/PDEP are not used. +bool VM_Version::compute_fast_bmi2() { + if (!supports_bmi2()) { + return false; + } + + if (is_intel()) { + // All Intel CPUs with BMI2 (Haswell+) implement PEXT/PDEP natively. + // 3-cycle latency, 1-per-cycle throughput on a dedicated ALU port. + // Source: Intel Intrinsics Guide, https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html + return true; + } + + if (is_amd()) { + // AMD added BMI2 in Excavator (Family 0x15, model 0x60+) but used + // microcode for PEXT/PDEP through all of Zen 2 (Family 0x17). + // Native ALU hardware support arrived with Zen 3 (Family 0x19). + // Source: AMD Software Optimization Guide (doc #56665), Section 2.10.2, https://developer.amd.com/resources/developer-guides-manuals/ + uint32_t family = extended_cpu_family(); + return family >= CPU_FAMILY_AMD_19H; + } + + // Zhaoxin added BMI2 support in Lujiazui (KX-6000+). + // Based on community benchmarks(https://uops.info/html-instr/PDEP_R64_R64_R64.html), + // PEXT/PDEP performance is known to be similarly poor to pre-Zen3 AMD, suggesting a microcode implementation. + // This cannot be confirmed as Zhaoxin publishes no public optimization guide. + + // On VIA/Centaur CNS, BMI2 is implemented in hardware with PDEP/PEXT executing at two per cycle (better than Haswell). + // Intel acquired Centaur in 2021, and CNS never reached production, so we don't check for it. + + return false; +} + // On Xen, the cpuid instruction returns // eax / registers[0]: Version of Xen // ebx / registers[1]: chars 'XenV' diff --git a/src/hotspot/cpu/x86/vm_version_x86.hpp b/src/hotspot/cpu/x86/vm_version_x86.hpp index d268665d0910..459180b2fe62 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.hpp +++ b/src/hotspot/cpu/x86/vm_version_x86.hpp @@ -439,7 +439,8 @@ class VM_Version : public Abstract_VM_Version { decl(AVX512_FP16, avx512_fp16 ) /* AVX512 FP16 ISA support*/ \ decl(AVX10_1, avx10_1 ) /* AVX10 512 bit vector ISA Version 1 support*/ \ decl(AVX10_2, avx10_2 ) /* AVX10 512 bit vector ISA Version 2 support*/ \ - decl(HYBRID, hybrid ) /* Hybrid architecture */ + decl(HYBRID, hybrid ) /* Hybrid architecture */ \ + decl(FAST_BMI2, fast_bmi2 ) /* Native Hardware support for PEXT/PDEP BMI2 instructions */ #define DECLARE_CPU_FEATURE_FLAG(id, name) CPU_##id, CPU_FEATURE_FLAGS(DECLARE_CPU_FEATURE_FLAG) @@ -725,6 +726,7 @@ class VM_Version : public Abstract_VM_Version { } static bool compute_has_intel_jcc_erratum(); + static bool compute_fast_bmi2(); static bool os_supports_avx_vectors(); static bool os_supports_apx_egprs(); @@ -912,6 +914,7 @@ class VM_Version : public Abstract_VM_Version { static bool supports_hv() { return _features.supports_feature(CPU_HV); } static bool supports_serialize() { return _features.supports_feature(CPU_SERIALIZE); } static bool supports_hybrid() { return _features.supports_feature(CPU_HYBRID); } + static bool supports_fast_bmi2() { return _features.supports_feature(CPU_FAST_BMI2); } static bool supports_f16c() { return _features.supports_feature(CPU_F16C); } static bool supports_pku() { return _features.supports_feature(CPU_PKU); } static bool supports_ospke() { return _features.supports_feature(CPU_OSPKE); } diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index 217edb03f122..e88013a50939 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -3220,7 +3220,7 @@ bool Matcher::match_rule_supported(int opcode) { break; case Op_CompressBits: case Op_ExpandBits: - if (!VM_Version::supports_bmi2()) { + if (!VM_Version::supports_fast_bmi2()) { return false; } break; diff --git a/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp b/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp index 810d9db8d58e..89a021ddb294 100644 --- a/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp +++ b/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp @@ -427,7 +427,7 @@ int ZeroInterpreter::native_entry(Method* method, intptr_t UNUSED, TRAPS) { // ThreadStateTransition::transition_from_native() cannot be used // here because it does not check for asynchronous exceptions. // We have to manage the transition ourself. - thread->set_thread_state_fence(_thread_in_native_trans); + thread->set_thread_state_fence(_thread_in_vm); // Handle safepoint operations, pending suspend requests, // and pending asynchronous exceptions. diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index edd80bb7427d..b69feab2cb4d 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -340,7 +340,8 @@ bool CgroupV2MemoryController::read_memory_limit_in_bytes(physical_memory_size_t } } } - result = limit; + // treat exceeding physical memory as unlimited + result = exceeds_physical_mem ? value_unlimited : limit; return true; } diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 06645f47eb2b..7cbfc2c821b4 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -127,6 +127,7 @@ static FILETIME process_creation_time; static FILETIME process_exit_time; static FILETIME process_user_time; static FILETIME process_kernel_time; +static HANDLE heap_file_handle = INVALID_HANDLE_VALUE; #if defined(_M_ARM64) #define __CPU__ aarch64 @@ -3244,6 +3245,20 @@ int os::create_file_for_heap(const char* dir) { warning("Problem opening file for heap (%s)", os::strerror(errno)); return -1; } + + guarantee(heap_file_handle == INVALID_HANDLE_VALUE, + "Heap backing file already exists"); + + HANDLE process = GetCurrentProcess(); + HANDLE file_handle = (HANDLE)_get_osfhandle(fd); + + if (!DuplicateHandle(process, file_handle, process, &heap_file_handle, + 0, FALSE, DUPLICATE_SAME_ACCESS)) { + warning("Could not retain handle to heap backing file (error %lu)", GetLastError()); + ::close(fd); + return -1; + } + return fd; } diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 2fc23700d828..93d9061efb98 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -61,6 +61,7 @@ #include "memory/universe.hpp" #include "oops/compressedOops.inline.hpp" #include "oops/fieldStreams.inline.hpp" +#include "oops/flatArrayOop.inline.hpp" #include "oops/objArrayOop.inline.hpp" #include "oops/oop.inline.hpp" #include "oops/oopCast.inline.hpp" @@ -1742,6 +1743,107 @@ void HeapShared::init_box_classes(TRAPS) { } } +// Used by HeapShared::find_inline_classes(). +class HeapShared::InlineKlassFinder : public FieldClosure { + KlassSubGraphInfo* _subgraph_info; + InstanceKlass* _ik; + address _obj; +public: + // obj points to the "logical address" of: + // (a) a regular heap object, or + // (b) an element of a flattened array, or + // (c) a flattened field embedded inside a heap object. + // For (a), obj is the same as the address of the heap object. + // For (b) and (c), obj points to InlineKlass::cast(_ik)->payload_offset() bytes below + // the payload. + InlineKlassFinder(KlassSubGraphInfo* subgraph_info, InstanceKlass* ik, address obj) + : _subgraph_info(subgraph_info), _ik(ik), _obj(obj) { + precond(obj != nullptr); + precond(ik->has_inlined_fields()); + } + + // This function is called on every field of _ik. + void do_field(fieldDescriptor* fd) override { + if (fd->is_flat()) { + precond(fd->field_type() == T_OBJECT); + precond(_ik == fd->field_holder()); + + // The type of this flattened field + InlineKlass* vk = _ik->get_inline_type_field_klass(fd->index()); + + // The "logical address" of this flattened field + address field_addr = _obj + fd->offset() - vk->payload_offset(); + + if (fd->is_null_free_inline_type() || !vk->is_payload_marked_as_null(field_addr)) { + // Found a non-null flattened instance of vk. Let's record vk. + add_inline_class(_subgraph_info, vk); + if (vk->has_inlined_fields()) { + InlineKlassFinder finder(_subgraph_info, vk, field_addr); + finder.find(); + } + } + } + } + + void find() { + _ik->do_nonstatic_fields(this); + } +}; + +void HeapShared::add_inline_class(KlassSubGraphInfo* subgraph_info, InlineKlass* k) { + subgraph_info->add_subgraph_object_klass(k); + if (InstanceKlass::cast(k)->is_enum_subclass() + || (subgraph_info == _dump_time_special_subgraph)) { + AOTArtifactFinder::add_aot_inited_class(k); + } +} + +// Recursively scan for any InlineKlass K that has least one non-null flattened instance +// inside orig_obj. K should be recorded with add_inline_class(). +// +// Reason for doing this: +// +// value class Point { short x; short y; ... } +// value class Line { +// @NullRestricted Point p1; +// @NullRestricted Point p2; ... } +// +// Klasses of non-flattened instances are already recorded by HeapShared::archive_object(). +// +// If only a single instance of Line is archived, HeapShared::archive_object() would +// have never visited a (non-flattened) instance of Point, but we must store Point in +// AOT-initialized state. This function finds Point. +void HeapShared::find_inline_classes(KlassSubGraphInfo* subgraph_info, oop orig_obj) { + Klass* klass = orig_obj->klass(); + + if (klass->is_flatArray_klass()) { + FlatArrayKlass* fak = FlatArrayKlass::cast(klass); + precond(orig_obj->is_flatArray()); + flatArrayOop fa = oop_cast(orig_obj); + InlineKlass* elem_k = fak->element_klass(); + bool added = false; + for (int i = 0; i < fa->length(); i++) { + if (fak->is_null_free_array_klass() || !fa->obj_at_is_null(i)) { + if (!added) { + add_inline_class(subgraph_info, elem_k); + } + if (elem_k->has_inlined_fields()) { + // "logical address" of the i-th array element. + address elem = static_cast
(fa->value_at_addr(i, fak->layout_helper())) - elem_k->payload_offset(); + InlineKlassFinder finder(subgraph_info, elem_k, elem); + finder.find(); + } + } + } + } else if (klass->is_instance_klass()) { + InstanceKlass* ik = InstanceKlass::cast(klass); + if (ik->has_inlined_fields()) { + InlineKlassFinder finder(subgraph_info, ik, cast_from_oop
(orig_obj)); + finder.find(); + } + } +} + // (1) If orig_obj has not been archived yet, archive it. // (2) If orig_obj has not been seen yet (since start_recording_subgraph() was called), // trace all objects that are reachable from it, and make sure these objects are archived. @@ -1877,6 +1979,8 @@ bool HeapShared::walk_one_object(PendingOopStack* stack, int level, KlassSubGrap orig_obj->oop_iterate(&pusher); } + find_inline_classes(subgraph_info, orig_obj); + if (CDSConfig::is_dumping_aot_linked_classes()) { // The enum klasses are archived with aot-initialized mirror. // See AOTClassInitializer::can_archive_initialized_mirror(). diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp index ba7ec626d909..993719909d81 100644 --- a/src/hotspot/share/cds/heapShared.hpp +++ b/src/hotspot/share/cds/heapShared.hpp @@ -365,11 +365,14 @@ class HeapShared: AllStatic { }; class OopFieldPusher; + class InlineKlassFinder; using PendingOopStack = GrowableArrayCHeap; static PendingOop _object_being_archived; static bool walk_one_object(PendingOopStack* stack, int level, KlassSubGraphInfo* subgraph_info, oop orig_obj, oop referrer); + static void find_inline_classes(KlassSubGraphInfo* subgraph_info, oop orig_obj); + static void add_inline_class(KlassSubGraphInfo* subgraph_info, InlineKlass* k); static void reset_archived_object_states(TRAPS); static void ensure_determinism(TRAPS); diff --git a/src/hotspot/share/ci/ciMethod.cpp b/src/hotspot/share/ci/ciMethod.cpp index be5c6182d9f8..db4bf40271a6 100644 --- a/src/hotspot/share/ci/ciMethod.cpp +++ b/src/hotspot/share/ci/ciMethod.cpp @@ -820,17 +820,7 @@ ciMethod* ciMethod::find_monomorphic_target(ciInstanceKlass* caller, if (target() == root_m->get_Method()) { return root_m; } - if (!root_m->is_public() && - !root_m->is_protected()) { - // If we are going to reason about inheritance, it's easiest - // if the method in question is public, protected, or private. - // If the answer is not root_m, it is conservatively correct - // to return null, even if the CHA encountered irrelevant - // methods in other packages. - // %%% TO DO: Work out logic for package-private methods - // with the same name but different vtable indexes. - return nullptr; - } + return CURRENT_THREAD_ENV->get_method(target()); } diff --git a/src/hotspot/share/ci/ciObjArrayKlass.cpp b/src/hotspot/share/ci/ciObjArrayKlass.cpp index a90b23b558d0..1aa8b398ea49 100644 --- a/src/hotspot/share/ci/ciObjArrayKlass.cpp +++ b/src/hotspot/share/ci/ciObjArrayKlass.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -137,7 +137,8 @@ ciSymbol* ciObjArrayKlass::construct_array_name(ciSymbol* element_name, // ciObjArrayKlass::make_impl // // Implementation of make. -ciObjArrayKlass* ciObjArrayKlass::make_impl(ciKlass* element_klass, bool refined_type, bool null_free, bool atomic) { +ciObjArrayKlass* ciObjArrayKlass::make_impl(ciKlass* element_klass, bool refined_type, bool null_free, + bool atomic, bool force_ref_layout) { if (element_klass->is_loaded()) { EXCEPTION_CONTEXT; // The element klass is loaded @@ -158,13 +159,19 @@ ciObjArrayKlass* ciObjArrayKlass::make_impl(ciKlass* element_klass, bool refined .with_null_restricted(null_free) .with_non_atomic(!atomic); - array = ObjArrayKlass::cast(array)->klass_with_properties(props, THREAD); + if (force_ref_layout) { + const ArrayDescription description(Klass::RefArrayKlassKind, props, LayoutKind::REFERENCE); + array = ObjArrayKlass::cast(array)->klass_from_description(description, THREAD); + } else { + array = ObjArrayKlass::cast(array)->klass_with_properties(props, THREAD); + } if (HAS_PENDING_EXCEPTION) { CLEAR_PENDING_EXCEPTION; CURRENT_THREAD_ENV->record_out_of_memory_failure(); return ciEnv::unloaded_ciobjarrayklass(); } assert(array != nullptr, "klass_with_properties should return a klass or throw"); + assert(!force_ref_layout || array->is_refArray_klass(), "must be a reference array klass"); if (array->is_flatArray_klass()) { return CURRENT_THREAD_ENV->get_flat_array_klass(array); } else { @@ -186,8 +193,9 @@ ciObjArrayKlass* ciObjArrayKlass::make_impl(ciKlass* element_klass, bool refined // ciObjArrayKlass::make // // Make an array klass corresponding to the specified primitive type. -ciObjArrayKlass* ciObjArrayKlass::make(ciKlass* element_klass, bool refined_type, bool null_free, bool atomic) { - GUARDED_VM_ENTRY(return make_impl(element_klass, refined_type, null_free, atomic);) +ciObjArrayKlass* ciObjArrayKlass::make(ciKlass* element_klass, bool refined_type, bool null_free, + bool atomic, bool force_ref_layout) { + GUARDED_VM_ENTRY(return make_impl(element_klass, refined_type, null_free, atomic, force_ref_layout);) } ciObjArrayKlass* ciObjArrayKlass::make(ciKlass* element_klass, int dims) { diff --git a/src/hotspot/share/ci/ciObjArrayKlass.hpp b/src/hotspot/share/ci/ciObjArrayKlass.hpp index b79e65afe8da..ddea7278ec8c 100644 --- a/src/hotspot/share/ci/ciObjArrayKlass.hpp +++ b/src/hotspot/share/ci/ciObjArrayKlass.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,7 +50,8 @@ class ciObjArrayKlass : public ciArrayKlass { return ObjArrayKlass::cast(get_Klass()); } - static ciObjArrayKlass* make_impl(ciKlass* element_klass, bool refined_type = false, bool null_free = false, bool atomic = true); + static ciObjArrayKlass* make_impl(ciKlass* element_klass, bool refined_type = false, bool null_free = false, + bool atomic = true, bool force_reference_layout = false); static ciSymbol* construct_array_name(ciSymbol* element_name, int dimension); @@ -69,7 +70,8 @@ class ciObjArrayKlass : public ciArrayKlass { // What kind of ciObject is this? bool is_obj_array_klass() const { return true; } - static ciObjArrayKlass* make(ciKlass* element_klass, bool refined_type = true, bool null_free = false, bool atomic = true); + static ciObjArrayKlass* make(ciKlass* element_klass, bool refined_type = true, bool null_free = false, + bool atomic = true, bool force_ref_layout = false); static ciObjArrayKlass* make(ciKlass* element_klass, int dims); virtual ciKlass* exact_klass(); diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index 99aef4bb6c76..cd4b32d80465 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -2080,7 +2080,7 @@ void MethodAnnotationCollector::apply_to(const methodHandle& m) { void ClassFileParser::ClassAnnotationCollector::apply_to(InstanceKlass* ik) { assert(ik != nullptr, "invariant"); - if (has_annotation(_jdk_internal_vm_annotation_Contended)) { + if (ik->is_identity_class() && has_annotation(_jdk_internal_vm_annotation_Contended)) { ik->set_is_contended(is_contended()); } if (has_annotation(_jdk_internal_ValueBased)) { @@ -5676,8 +5676,9 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, oop_map_blocks->copy(ik->start_of_nonstatic_oop_maps()); } - if (_has_contended_fields || _parsed_annotations->is_contended() || - ( _super_klass != nullptr && _super_klass->has_contended_annotations())) { + if (ik->is_identity_class() && + (_has_contended_fields || _parsed_annotations->is_contended() || + (_super_klass != nullptr && _super_klass->has_contended_annotations()))) { ik->set_has_contended_annotations(true); } @@ -6409,7 +6410,7 @@ void ClassFileParser::post_process_parsed_stream(const ClassFileStream* const st _layout_info = new FieldLayoutInfo(); FieldLayoutBuilder lb(class_name(), loader_data(), super_klass(), _cp, /*_fields*/ _temp_field_info, - _parsed_annotations->is_contended(), is_inline_type(), + access_flags().is_identity_class() && _parsed_annotations->is_contended(), is_inline_type(), access_flags().is_abstract() && !access_flags().is_identity_class() && !access_flags().is_interface(), _must_be_atomic, _layout_info, _inline_layout_info_array); lb.build_layout(); diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index c29f5726236b..d51b5ca68d1f 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -39,6 +39,7 @@ #include "gc/shared/gcConfig.hpp" #include "logging/logStream.hpp" #include "memory/memoryReserver.hpp" +#include "oops/klass.hpp" #include "prims/jvmtiThreadState.hpp" #include "prims/upcallLinker.hpp" #include "runtime/deoptimization.hpp" @@ -2098,10 +2099,11 @@ void AOTCodeAddressTable::init_extrs() { ADD_EXTERNAL_ADDRESS(OptoRuntime::vthread_start_final_transition_C); ADD_EXTERNAL_ADDRESS(OptoRuntime::vthread_start_transition_C); ADD_EXTERNAL_ADDRESS(OptoRuntime::vthread_end_transition_C); - // already added for #if defined(AARCH64) && ! defined(PRODUCT) ADD_EXTERNAL_ADDRESS(JavaThread::verify_cross_modify_fence_failure); #endif // AARCH64 && !PRODUCT + // Used by lookup_secondary_supers_table + ADD_EXTERNAL_ADDRESS(Klass::on_secondary_supers_verification_failure); } #endif // COMPILER2 diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp index 4cd8af1370d0..dd259497d341 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp @@ -30,6 +30,7 @@ #include "gc/shenandoah/shenandoahAllocRate.inline.hpp" #include "gc/shenandoah/shenandoahCollectionSet.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahUtils.hpp" #include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "logging/log.hpp" #include "logging/logTag.hpp" @@ -40,6 +41,7 @@ #define PROPERFMT_F "%.1f %s" #define PROPERFMT_F_ARGS(s) byte_size_in_proper_unit(s), proper_unit_for_byte_size(s) +#define PROPERFMTARGS_SIGNED(s) (s).value, (s).unit // These are used to decide if we want to make any adjustments at all // at the end of a successful concurrent cycle. @@ -379,21 +381,24 @@ bool ShenandoahAdaptiveHeuristics::trigger_average_allocation_rate(const Shenand // a sample period of roughly 15 ms, spanning approximately 120 ms of execution. bool ShenandoahAdaptiveHeuristics::trigger_accelerating_allocation_rate(const ShenandoahAnticipatedConsumption& rate, const size_t allocatable_bytes) { if (rate.momentary_consumption() > allocatable_bytes) { + const ShenandoahSignedSize momentary_rate = ShenandoahSignedSize::get(rate.momentary_rate()); assert(rate.accelerated_consumption() == 0, "Momentary trigger is meant to exclude acceleration trigger"); log_trigger("Momentary spike consumption (" PROPERFMT ") exceeds free headroom (" PROPERFMT ") at " "current rate (" PROPERFMT_F "/s) for anticipated GC duration (%.2f ms)", PROPERFMTARGS(rate.momentary_consumption()), PROPERFMTARGS(allocatable_bytes), - PROPERFMT_F_ARGS(rate.momentary_rate()), rate.duration_seconds() * 1000); + PROPERFMTARGS_SIGNED(momentary_rate), rate.duration_seconds() * 1000); accept_trigger_with_type(RATE); return true; } if (rate.accelerated_consumption() > allocatable_bytes) { + const ShenandoahSignedSize predicted_rate = ShenandoahSignedSize::get(rate.predicted_rate()); + const ShenandoahSignedSize acceleration = ShenandoahSignedSize::get(rate.acceleration()); assert(rate.momentary_consumption() == 0, "Acceleration trigger is meant to exclude momentary trigger"); log_trigger("Accelerated consumption (" PROPERFMT ") exceeds free headroom (" PROPERFMT ") at " "current rate (" PROPERFMT_F "/s) with acceleration (" PROPERFMT_F "/s/s) for anticipated GC duration (%.2f ms)", PROPERFMTARGS(rate.accelerated_consumption()), PROPERFMTARGS(allocatable_bytes), - PROPERFMT_F_ARGS(rate.predicted_rate()), PROPERFMT_F_ARGS(rate.acceleration()), rate.duration_seconds() * 1000); + PROPERFMTARGS_SIGNED(predicted_rate), PROPERFMTARGS_SIGNED(acceleration), rate.duration_seconds() * 1000); accept_trigger_with_type(RATE); return true; } @@ -404,15 +409,21 @@ bool ShenandoahAdaptiveHeuristics::trigger_accelerating_allocation_rate(const Sh void ShenandoahAdaptiveHeuristics::maybe_log_rate_trigger_parameters(const ShenandoahAnticipatedConsumption &consumption, size_t allocatable_bytes) const { if (log_is_enabled(Debug, gc, sampling)) { + const ShenandoahSignedSize momentary_rate = ShenandoahSignedSize::get(consumption.momentary_rate()); + const ShenandoahSignedSize predicted_rate = ShenandoahSignedSize::get(consumption.predicted_rate()); + const ShenandoahSignedSize baseline_rate = ShenandoahSignedSize::get(consumption.baseline_rate()); + const ShenandoahSignedSize acceleration = ShenandoahSignedSize::get(consumption.acceleration()); log_debug(gc, sampling)( "%s: Anticipated cycle duration: %.3fs, head room: " PROPERFMT ", margin of error: %.3f " "Baseline consumption: " PROPERFMT ", Baseline rate: " PROPERFMT_F "/s, " "Momentary consumption: " PROPERFMT ", Momentary rate: " PROPERFMT_F "/s, " - "Accelerated consumption: " PROPERFMT ", Predicted rate: " PROPERFMT_F "/s, Acceleration: %.3f", + "Accelerated consumption: " PROPERFMT ", Predicted rate: " PROPERFMT_F "/s, " + "Acceleration: " PROPERFMT_F "/s", _space_info->name(), consumption.duration_seconds(), PROPERFMTARGS(allocatable_bytes), _margin_of_error_sd, - PROPERFMTARGS(consumption.baseline_consumption()), PROPERFMT_F_ARGS(consumption.baseline_rate()), - PROPERFMTARGS(consumption.momentary_consumption()), PROPERFMT_F_ARGS(consumption.momentary_rate()), - PROPERFMTARGS(consumption.accelerated_consumption()), PROPERFMT_F_ARGS(consumption.predicted_rate()), consumption.acceleration() + PROPERFMTARGS(consumption.baseline_consumption()), PROPERFMTARGS_SIGNED(baseline_rate), + PROPERFMTARGS(consumption.momentary_consumption()), PROPERFMTARGS_SIGNED(momentary_rate), + PROPERFMTARGS(consumption.accelerated_consumption()), PROPERFMTARGS_SIGNED(predicted_rate), + PROPERFMTARGS_SIGNED(acceleration) ); } } @@ -428,3 +439,4 @@ size_t ShenandoahAdaptiveHeuristics::min_free_threshold(size_t capacity) const { #undef PROPERFMT_F #undef PROPERFMT_F_ARGS +#undef PROPERFMTARGS_SIGNED diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp index ca4dfc71c61a..dbc795651f22 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp @@ -38,8 +38,6 @@ #include "logging/log.hpp" #include "utilities/quickSort.hpp" -using idx_t = ShenandoahSimpleBitMap::idx_t; - static int compare_by_aged_live(AgedRegionData a, AgedRegionData b) { if (a._live_data < b._live_data) return -1; @@ -77,6 +75,73 @@ ShenandoahGenerationalHeuristics::ShenandoahGenerationalHeuristics(ShenandoahGen : ShenandoahAdaptiveHeuristics(generation), _generation(generation), _add_regions_to_old(0) { } +void ShenandoahGenerationalHeuristics::prepare_for_abbreviated_cycle() { + auto const heap = ShenandoahGenerationalHeap::heap(); + adjust_reserves_for_abbreviated(heap); + + ShenandoahInPlacePromotionPlanner in_place_promotions(heap); + ShenandoahAgeCensus* census = heap->age_census(); + if (census->get_tenurable_bytes(census->effective_threshold()) != 0) { + prepare_regions_for_promotion(in_place_promotions, heap, nullptr); + } + in_place_promotions.complete_planning(); + + // Only these in-place promotion regions will be tenured this cycle + const size_t tenurable_this_cycle = in_place_promotions.live_bytes(); + compute_promotion_potential(heap, tenurable_this_cycle); + + ShenandoahTracer::report_promotion_info(heap->collection_set(), in_place_promotions); +} + +void ShenandoahGenerationalHeuristics::adjust_reserves_for_abbreviated(ShenandoahGenerationalHeap* heap) { + // We are not going to evacuate because this is an abbreviated cycle. Reset the reserves. + heap->young_generation()->set_evacuation_reserve(0UL); + heap->old_generation()->set_evacuation_reserve(0UL); + heap->old_generation()->set_promoted_reserve(0UL); +} + +size_t ShenandoahGenerationalHeuristics::prepare_regions_for_promotion(ShenandoahInPlacePromotionPlanner& in_place_promotions, + ShenandoahGenerationalHeap* heap, + AgedRegionData* sorted_regions) { + // There should be no regions configured for subsequent in-place-promotions carried over from the previous cycle. + assert_no_in_place_promotions(); + size_t candidates = 0; + for (size_t i = 0, num_regions = heap->num_regions(); i < num_regions; i++) { + ShenandoahHeapRegion* const r = heap->get_region(i); + if (r->is_empty() || !r->has_live() || !r->is_young()) { + // skip over regions that aren't young with some live data + continue; + } + + if (!r->is_regular()) { + if (r->is_humongous_start() && heap->is_tenurable(r)) { + in_place_promotions.prepare(r); + } + // Nothing else to be done for humongous regions + continue; + } + + if (heap->is_tenurable(r)) { + if (in_place_promotions.is_eligible(r)) { + // We prefer to promote this region in place because it has a small amount of garbage and a large usage. + // Note that if this region has been used recently for allocation, it will not be promoted, and it will + // not be selected for promotion by evacuation. + in_place_promotions.prepare(r); + } else if (sorted_regions != nullptr) { + // Record this promotion-eligible candidate region. After sorting and selecting the best candidates below, + // we may still decide to exclude this promotion-eligible region from the current collection set. If this + // happens, we will consider this region as part of the anticipated promotion potential for the next GC + // pass; see further below. + sorted_regions[candidates]._region = r; + sorted_regions[candidates]._live_data = r->get_live_data_bytes(); + candidates++; + } + } + } + + return candidates; +} + void ShenandoahGenerationalHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* collection_set, RegionData* data, size_t data_size, size_t free) { @@ -99,13 +164,7 @@ void ShenandoahGenerationalHeuristics::choose_collection_set_from_regiondata(She heap->shenandoah_policy()->record_mixed_cycle(); } - ShenandoahTracer::report_promotion_info(collection_set, - in_place_promotions.humongous_region_stats().count, - in_place_promotions.humongous_region_stats().garbage, - in_place_promotions.humongous_region_stats().free, - in_place_promotions.regular_region_stats().count, - in_place_promotions.regular_region_stats().garbage, - in_place_promotions.regular_region_stats().free); + ShenandoahTracer::report_promotion_info(collection_set, in_place_promotions); } void ShenandoahGenerationalHeuristics::compute_evacuation_budgets(ShenandoahInPlacePromotionPlanner& in_place_promotions, @@ -285,75 +344,46 @@ void ShenandoahGenerationalHeuristics::add_tenured_regions_to_collection_set(con // reserved in the young generation. size_t ShenandoahGenerationalHeuristics::select_aged_regions(ShenandoahInPlacePromotionPlanner& in_place_promotions, const size_t old_promotion_reserve) { - - // There should be no regions configured for subsequent in-place-promotions carried over from the previous cycle. - assert_no_in_place_promotions(); - auto const heap = ShenandoahGenerationalHeap::heap(); - size_t candidates = 0; - // Sort the promotion-eligible regions in order of increasing live-data-bytes so that we can first reclaim regions that require // less evacuation effort. This prioritizes garbage first, expanding the allocation pool early before we reclaim regions that - // have more live data. - const idx_t num_regions = heap->num_regions(); - + // have more live data. This also prepares regions for in-place promotions ResourceMark rm; - AgedRegionData* sorted_regions = NEW_RESOURCE_ARRAY(AgedRegionData, num_regions); - - for (idx_t i = 0; i < num_regions; i++) { - ShenandoahHeapRegion* const r = heap->get_region(i); - if (r->is_empty() || !r->has_live() || !r->is_young()) { - // skip over regions that aren't young with some live data - continue; - } - - if (!r->is_regular()) { - if (r->is_humongous_start() && heap->is_tenurable(r)) { - in_place_promotions.prepare(r); - } - // Nothing else to be done for humongous regions - continue; - } - - if (heap->is_tenurable(r)) { - if (in_place_promotions.is_eligible(r)) { - // We prefer to promote this region in place because it has a small amount of garbage and a large usage. - // Note that if this region has been used recently for allocation, it will not be promoted and it will - // not be selected for promotion by evacuation. - in_place_promotions.prepare(r); - } else { - // Record this promotion-eligible candidate region. After sorting and selecting the best candidates below, - // we may still decide to exclude this promotion-eligible region from the current collection set. If this - // happens, we will consider this region as part of the anticipated promotion potential for the next GC - // pass; see further below. - sorted_regions[candidates]._region = r; - sorted_regions[candidates]._live_data = r->get_live_data_bytes(); - candidates++; - } - } - } - + AgedRegionData* sorted_regions = NEW_RESOURCE_ARRAY(AgedRegionData, heap->num_regions()); + const size_t candidates = prepare_regions_for_promotion(in_place_promotions, heap, sorted_regions); in_place_promotions.complete_planning(); add_tenured_regions_to_collection_set(old_promotion_reserve, heap, candidates, sorted_regions); - const uint tenuring_threshold = heap->age_census()->tenuring_threshold(); - const size_t tenurable_this_cycle = heap->age_census()->get_tenurable_bytes(tenuring_threshold); - const size_t tenurable_next_cycle = heap->age_census()->get_tenurable_bytes(tenuring_threshold - 1); - assert(tenurable_next_cycle >= tenurable_this_cycle, - "Tenurable next cycle (" PROPERFMT ") should include tenurable this cycle (" PROPERFMT ")", - PROPERFMTARGS(tenurable_next_cycle), PROPERFMTARGS(tenurable_this_cycle)); - - const size_t max_promotions = tenurable_this_cycle * ShenandoahPromoEvacWaste; + // Act as though everything that can be tenured, will be tenured. This overestimates how much will be promoted, + // but has the effect of tending to keep the old generation smaller because it believes less will be tenured + // on the next cycle. + const size_t tenurable_this_cycle = heap->age_census()->get_tenurable_bytes(); + const size_t max_promotions = compute_promotion_potential(heap, tenurable_this_cycle); const size_t old_consumed = MIN2(max_promotions, old_promotion_reserve); + return old_consumed; +} + +size_t ShenandoahGenerationalHeuristics::compute_promotion_potential(ShenandoahGenerationalHeap* const heap, + size_t tenurable_this_cycle) { + const uint effective_threshold = heap->age_census()->effective_threshold(); + const uint next_threshold = effective_threshold > 0 ? effective_threshold - 1 : 0; + const size_t tenurable_next_cycle = heap->age_census()->get_tenurable_bytes(next_threshold); + + // This assertion still holds even when tenurable_this_cycle is derived from in-place-promotions + // alone. The census cohort index is the region age plus the object's age, so every byte in a pip + // region is counted in the census. + assert(tenurable_next_cycle >= tenurable_this_cycle, + "Tenurable next cycle (" PROPERFMT ") should include tenurable this cycle (" PROPERFMT ")", + PROPERFMTARGS(tenurable_next_cycle), PROPERFMTARGS(tenurable_this_cycle)); // Don't include the bytes we expect to promote in this cycle in the next cycle - const size_t promo_potential = (tenurable_next_cycle - tenurable_this_cycle) * ShenandoahPromoEvacWaste; + const size_t remaining = tenurable_next_cycle > tenurable_this_cycle ? tenurable_next_cycle - tenurable_this_cycle : 0; + const size_t promo_potential = remaining * ShenandoahPromoEvacWaste; heap->old_generation()->set_promotion_potential(promo_potential); log_info(gc, ergo)("Promotion potential of aged regions with sufficient garbage: " PROPERFMT, PROPERFMTARGS(promo_potential)); - - return old_consumed; + return tenurable_this_cycle * ShenandoahPromoEvacWaste; } // Having chosen the collection set, adjust the budgets for generational mode based on its composition. Note diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp index 1860e3d4c0f2..574e65aa6c0f 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp @@ -33,6 +33,7 @@ class ShenandoahGeneration; class ShenandoahHeap; class ShenandoahCollectionSet; class RegionData; +class ShenandoahGenerationalHeap; typedef struct { ShenandoahHeapRegion* _region; @@ -58,12 +59,24 @@ class ShenandoahGenerationalHeuristics : public ShenandoahAdaptiveHeuristics { void record_cycle_end() override; protected: + // Select regions for in place promotion and zero evacuation reserves + void prepare_for_abbreviated_cycle() override; + // Wraps budget computation, subclass region selection, budget adjustment, and tracing. void choose_collection_set_from_regiondata(ShenandoahCollectionSet* set, RegionData* data, size_t data_size, size_t free) override; private: + // When we decide to do an abbreviated cycle, withdraw reserves so memory can be made available to mutators. + void adjust_reserves_for_abbreviated(ShenandoahGenerationalHeap* heap); + + // Select regions for in place promotion and optionally record tenurable regions that are not eligible + // for in-place promotion in the given sorted_regions array for possible inclusion in the collection set. + size_t prepare_regions_for_promotion(ShenandoahInPlacePromotionPlanner& in_place_promotions, + ShenandoahGenerationalHeap* heap, + AgedRegionData* sorted_regions); + // Compute evacuation budgets prior to choosing collection set. void compute_evacuation_budgets(ShenandoahInPlacePromotionPlanner& in_place_promotions, ShenandoahHeap* const heap); @@ -77,11 +90,10 @@ class ShenandoahGenerationalHeuristics : public ShenandoahAdaptiveHeuristics { // being those of at least tenuring_threshold age that have lower garbage // density. // - // Updates promotion_potential and pad_for_promote_in_place fields - // of the heap. Returns bytes of live object memory in the preselected - // regions, which are marked in the preselected_regions() indicator - // array of the heap's collection set, which should be initialized - // to false. + // Updates promotion_potential field of the heap. Returns bytes of live object + // memory in the preselected regions, which are marked in the + // preselected_regions() indicator array of the heap's collection set, which + // should be initialized to false. size_t select_aged_regions(ShenandoahInPlacePromotionPlanner& in_place_promotions, const size_t old_promotion_reserve); // Select regions for inclusion in the collection set that are tenured, but do @@ -90,6 +102,9 @@ class ShenandoahGenerationalHeuristics : public ShenandoahAdaptiveHeuristics { ShenandoahGenerationalHeap *const heap, size_t candidates, AgedRegionData* sorted_regions); + // Updates the anticipated promotions for the next cycle and returns the maximum promotions for the current cycle + size_t compute_promotion_potential(ShenandoahGenerationalHeap* heap, size_t tenurable_this_cycle); + // Adjust evacuation budgets after choosing collection set. On entry, the instance variable _regions_to_xfer // represents regions to be transferred to old based on decisions made in top_off_collection_set() void adjust_evacuation_budgets(ShenandoahGenerationalHeap* const heap, diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp index d9f3bdee8282..5c07fcb6b59f 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp @@ -226,14 +226,14 @@ void ShenandoahGlobalHeuristics::choose_global_collection_set(ShenandoahCollecti size_t delta_bytes = budget.young_evac.reserve() - heap->young_generation()->get_evacuation_reserve(); size_t delta_regions = delta_bytes / region_size_bytes; size_t regions_to_transfer = MIN2(unaffiliated_old_regions, delta_regions); - log_info(gc)("Global GC moves %zu unaffiliated regions from old collector to young collector reserves", regions_to_transfer); + log_info(gc, ergo)("Global GC moves %zu unaffiliated regions from old collector to young collector reserves", regions_to_transfer); ssize_t negated_regions = -regions_to_transfer; heap->free_set()->move_unaffiliated_regions_from_collector_to_old_collector(negated_regions); } else if (heap->young_generation()->get_evacuation_reserve() > budget.young_evac.reserve()) { size_t delta_bytes = heap->young_generation()->get_evacuation_reserve() - budget.young_evac.reserve(); size_t delta_regions = delta_bytes / region_size_bytes; size_t regions_to_transfer = MIN2(unaffiliated_young_regions, delta_regions); - log_info(gc)("Global GC moves %zu unaffiliated regions from young collector to old collector reserves", regions_to_transfer); + log_info(gc, ergo)("Global GC moves %zu unaffiliated regions from young collector to old collector reserves", regions_to_transfer); heap->free_set()->move_unaffiliated_regions_from_collector_to_old_collector(regions_to_transfer); } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp index 74b62003f86c..a0aec3c70a29 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp @@ -152,9 +152,10 @@ void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collec if (immediate_percent <= ShenandoahImmediateThreshold) { choose_collection_set_from_regiondata(collection_set, candidates, cand_idx, immediate_garbage + free); - } else if (heap->mode()->is_generational()) { - adjust_reserves_for_abbreviated(heap); + } else { + prepare_for_abbreviated_cycle(); } + collection_set->summarize(total_garbage, immediate_garbage, immediate_regions); ShenandoahTracer::report_evacuation_info(collection_set, free_regions, immediate_regions, immediate_garbage); } @@ -163,13 +164,6 @@ void ShenandoahHeuristics::start_idle_span() { // do nothing } -void ShenandoahHeuristics::adjust_reserves_for_abbreviated(ShenandoahHeap* heap) { - // We are not going to evacuate because this is an abbreviated cycle. Reset the reserves. - heap->young_generation()->set_evacuation_reserve(0UL); - heap->old_generation()->set_evacuation_reserve(0UL); - heap->old_generation()->set_promoted_reserve(0UL); -} - void ShenandoahHeuristics::record_degenerated_cycle_start(bool out_of_cycle) { if (out_of_cycle) { _precursor_cycle_start = _cycle_start = os::elapsedTime(); diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp index b50c20042625..1deb98c3f8a9 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp @@ -83,9 +83,6 @@ class ShenandoahHeuristics : public CHeapObj { double _most_recent_trigger_evaluation_time; double _most_recent_planned_sleep_interval; - // When we decide to do an abbreviated cycle, withdraw reserves so memory can be made available to mutators. - void adjust_reserves_for_abbreviated(ShenandoahHeap* heap); - protected: static constexpr uint Moving_Average_Samples = 10; // Number of samples to store in moving averages @@ -196,6 +193,9 @@ class ShenandoahHeuristics : public CHeapObj { RegionData* data, size_t data_size, size_t free) = 0; + // Called when immediate garbage threshold is reached. + virtual void prepare_for_abbreviated_cycle() {} + virtual void adjust_penalty(intx step); inline void accept_trigger() { diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp index 333451a81258..44bc683cbd6c 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp @@ -321,7 +321,7 @@ bool ShenandoahOldHeuristics::finalize_mixed_evacs() { } decrease_unprocessed_old_collection_candidates_live_memory(_evacuated_old_bytes); if (_included_old_regions > 0) { - log_info(gc)("Old-gen mixed evac (%zu regions, evacuating %zu%s, reclaiming: %zu%s)", + log_info(gc, ergo)("Old-gen mixed evac (%zu regions, evacuating %zu%s, reclaiming: %zu%s)", _included_old_regions, byte_size_in_proper_unit(_evacuated_old_bytes), proper_unit_for_byte_size(_evacuated_old_bytes), byte_size_in_proper_unit(_collected_old_bytes), proper_unit_for_byte_size(_collected_old_bytes)); @@ -339,10 +339,10 @@ bool ShenandoahOldHeuristics::finalize_mixed_evacs() { // if they are all pinned we transition to a state that will allow us to make these uncollected // (pinned) regions parsable. if (all_candidates_are_pinned()) { - log_info(gc)("All candidate regions " UINT32_FORMAT " are pinned", unprocessed_old_collection_candidates()); + log_info(gc, ergo)("All candidate regions " UINT32_FORMAT " are pinned", unprocessed_old_collection_candidates()); _old_generation->abandon_mixed_evacuations(); } else { - log_info(gc)("No regions selected for mixed collection. " + log_info(gc, ergo)("No regions selected for mixed collection. " "Old evacuation budget: " PROPERFMT ", Next candidate: " UINT32_FORMAT ", Last candidate: " UINT32_FORMAT, PROPERFMTARGS(_old_evacuation_reserve), _next_old_collection_candidate, _last_old_collection_candidate); @@ -382,7 +382,7 @@ bool ShenandoahOldHeuristics::top_off_collection_set(ShenandoahCollectionSet* co regions_for_old_expansion = 0; } if (regions_for_old_expansion > 0) { - log_info(gc)("Augmenting old-gen evacuation budget from unexpended young-generation reserve by %zu regions", + log_info(gc, ergo)("Augmenting old-gen evacuation budget from unexpended young-generation reserve by %zu regions", regions_for_old_expansion); add_regions_to_old = regions_for_old_expansion; size_t budget_supplement = region_size_bytes * regions_for_old_expansion; @@ -844,7 +844,7 @@ void ShenandoahOldHeuristics::adjust_old_garbage_threshold() { } else { _old_garbage_threshold = ShenandoahOldGarbageThreshold - adjustment_potential / 3; } - log_info(gc)("Adjusting old garbage threshold to %lu because Old Generation used regions represents %lu%% of heap", + log_info(gc, ergo)("Adjusting old garbage threshold to %lu because Old Generation used regions represents %lu%% of heap", _old_garbage_threshold, percent_used); } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp index 5636dee3ae2d..8cc8e31cf291 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp @@ -189,6 +189,13 @@ class ShenandoahAgeCensus: public CHeapObj { // Visible for testing. Use is_tenurable for consistent tenuring comparisons. uint tenuring_threshold() const { return _tenuring_threshold[_epoch]; } + // Returns zero when always_tenure is true (which is only true when the gc + // is triggered by WB.fullGC()). Note that zero itself is the floor, so do + // not subtract from it to get the younger cohort. + uint effective_threshold() const { + return is_always_tenure() ? 0 : tenuring_threshold(); + } + // Return true if this age is at or above the tenuring threshold, or if always tenure is enabled. bool is_tenurable(uint age) const { return age >= tenuring_threshold() || _always_tenure; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index fe7e5211e0a6..09ca7e4315d4 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -106,7 +106,7 @@ ShenandoahGC::ShenandoahDegenPoint ShenandoahConcurrentGC::degen_point() const { void ShenandoahConcurrentGC::entry_concurrent_update_refs_prepare(ShenandoahHeap* const heap) { TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent init update refs", ""); - ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_update_refs_prepare); + ShenandoahConcurrentSubphase gc_phase(msg, ShenandoahPhaseTimings::conc_update_refs_prepare); EventMark em("%s", msg); heap->try_inject_pin(); @@ -118,7 +118,7 @@ void ShenandoahConcurrentGC::entry_update_card_table() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent update cards", ""); - ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_update_card_table); + ShenandoahConcurrentSubphase gc_phase(msg, ShenandoahPhaseTimings::conc_update_card_table); EventMark em("%s", msg); ShenandoahWorkerScope scope(heap->workers(), @@ -137,6 +137,9 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { _generation->ref_processor()->set_soft_reference_policy( GCCause::should_clear_all_soft_refs(cause)); + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent GC", ""); + ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_gc, /* log_heap_usage = */ true); + ShenandoahBreakpointGCScope breakpoint_gc_scope(cause); // Reset for upcoming marking @@ -295,7 +298,7 @@ void ShenandoahConcurrentGC::entry_complete_abbreviated_cycle() { TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent complete abbreviated cycle", ""); - ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::complete_abbreviated); + ShenandoahConcurrentSubphase gc_phase(msg, ShenandoahPhaseTimings::complete_abbreviated); EventMark em("%s", msg); ShenandoahWorkerScope scope(heap->workers(), @@ -379,7 +382,7 @@ void ShenandoahConcurrentGC::entry_init_mark() { assert(!heap->has_forwarded_objects(), "Should not have forwarded objects here"); SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Init Mark", ""); - ShenandoahPausePhase gc_phase(msg, ShenandoahPhaseTimings::init_mark); + ShenandoahPauseSubphase gc_phase(msg, ShenandoahPhaseTimings::init_mark); EventMark em("%s", msg); ShenandoahWorkerScope scope(ShenandoahHeap::heap()->workers(), @@ -395,7 +398,7 @@ void ShenandoahConcurrentGC::entry_final_mark() { "Should not have forwarded objects during final mark, unless old gen concurrent mark is running"); SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Final Mark", ""); - ShenandoahPausePhase gc_phase(msg, ShenandoahPhaseTimings::final_mark); + ShenandoahPauseSubphase gc_phase(msg, ShenandoahPhaseTimings::final_mark); EventMark em("%s", msg); ShenandoahWorkerScope scope(ShenandoahHeap::heap()->workers(), @@ -407,7 +410,7 @@ void ShenandoahConcurrentGC::entry_final_mark() { void ShenandoahConcurrentGC::entry_init_update_refs() { SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Init Update Refs", ""); - ShenandoahPausePhase gc_phase(msg, ShenandoahPhaseTimings::init_update_refs); + ShenandoahPauseSubphase gc_phase(msg, ShenandoahPhaseTimings::init_update_refs); EventMark em("%s", msg); // No workers used in this phase, no setup required @@ -416,7 +419,7 @@ void ShenandoahConcurrentGC::entry_init_update_refs() { void ShenandoahConcurrentGC::entry_final_update_refs() { SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Final Update Refs", ""); - ShenandoahPausePhase gc_phase(msg, ShenandoahPhaseTimings::final_update_refs); + ShenandoahPauseSubphase gc_phase(msg, ShenandoahPhaseTimings::final_update_refs); EventMark em("%s", msg); ShenandoahWorkerScope scope(ShenandoahHeap::heap()->workers(), @@ -428,7 +431,7 @@ void ShenandoahConcurrentGC::entry_final_update_refs() { void ShenandoahConcurrentGC::entry_final_verify() { SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Verify Final", ""); - ShenandoahPausePhase gc_phase(msg, ShenandoahPhaseTimings::final_verify); + ShenandoahPauseSubphase gc_phase(msg, ShenandoahPhaseTimings::final_verify); EventMark em("%s", msg); op_verify_final(); @@ -442,7 +445,7 @@ void ShenandoahConcurrentGC::entry_reset() { TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); { SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent reset", ""); - ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_reset); + ShenandoahConcurrentSubphase gc_phase(msg, ShenandoahPhaseTimings::conc_reset); EventMark em("%s", msg); ShenandoahWorkerScope scope(heap->workers(), @@ -458,7 +461,7 @@ void ShenandoahConcurrentGC::entry_scan_remembered_set() { TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent remembered set scanning", ""); - ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::init_scan_rset); + ShenandoahConcurrentSubphase gc_phase(msg, ShenandoahPhaseTimings::init_scan_rset); EventMark em("%s", msg); ShenandoahWorkerScope scope(heap->workers(), @@ -474,7 +477,7 @@ void ShenandoahConcurrentGC::entry_mark_roots() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent marking roots", ""); - ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_mark_roots); + ShenandoahConcurrentSubphase gc_phase(msg, ShenandoahPhaseTimings::conc_mark_roots); EventMark em("%s", msg); ShenandoahWorkerScope scope(heap->workers(), @@ -492,7 +495,7 @@ void ShenandoahConcurrentGC::entry_mark() { TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent marking", ""); - ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_mark); + ShenandoahConcurrentSubphase gc_phase(msg, ShenandoahPhaseTimings::conc_mark); EventMark em("%s", msg); ShenandoahWorkerScope scope(heap->workers(), @@ -507,7 +510,7 @@ void ShenandoahConcurrentGC::entry_mark() { void ShenandoahConcurrentGC::entry_thread_roots() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent thread roots", ""); - ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_thread_roots); + ShenandoahConcurrentSubphase gc_phase(msg, ShenandoahPhaseTimings::conc_thread_roots); EventMark em("%s", msg); ShenandoahWorkerScope scope(heap->workers(), @@ -522,7 +525,7 @@ void ShenandoahConcurrentGC::entry_thread_roots() { void ShenandoahConcurrentGC::entry_weak_refs() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent weak references", ""); - ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_weak_refs); + ShenandoahConcurrentSubphase gc_phase(msg, ShenandoahPhaseTimings::conc_weak_refs); EventMark em("%s", msg); ShenandoahWorkerScope scope(heap->workers(), @@ -538,7 +541,7 @@ void ShenandoahConcurrentGC::entry_weak_roots() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent weak roots", ""); - ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_weak_roots); + ShenandoahConcurrentSubphase gc_phase(msg, ShenandoahPhaseTimings::conc_weak_roots); EventMark em("%s", msg); ShenandoahWorkerScope scope(heap->workers(), @@ -554,7 +557,7 @@ void ShenandoahConcurrentGC::entry_class_unloading() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent class unloading", ""); - ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_class_unload); + ShenandoahConcurrentSubphase gc_phase(msg, ShenandoahPhaseTimings::conc_class_unload); EventMark em("%s", msg); ShenandoahWorkerScope scope(heap->workers(), @@ -570,7 +573,7 @@ void ShenandoahConcurrentGC::entry_strong_roots() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent strong roots", ""); - ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_strong_roots); + ShenandoahConcurrentSubphase gc_phase(msg, ShenandoahPhaseTimings::conc_strong_roots); EventMark em("%s", msg); ShenandoahGCWorkerPhase worker_phase(ShenandoahPhaseTimings::conc_strong_roots); @@ -588,7 +591,7 @@ void ShenandoahConcurrentGC::entry_cleanup_early() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent cleanup", ""); - ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_cleanup_early, true /* log_heap_usage */); + ShenandoahConcurrentSubphase gc_phase(msg, ShenandoahPhaseTimings::conc_cleanup_early, false /* log_heap_usage */); EventMark em("%s", msg); // This phase does not use workers, no need for setup @@ -607,7 +610,7 @@ void ShenandoahConcurrentGC::entry_evacuate() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent evacuation", ""); - ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_evac); + ShenandoahConcurrentSubphase gc_phase(msg, ShenandoahPhaseTimings::conc_evac); EventMark em("%s", msg); ShenandoahWorkerScope scope(heap->workers(), @@ -623,7 +626,7 @@ void ShenandoahConcurrentGC::entry_update_thread_roots() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent update thread roots", ""); - ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_update_thread_roots); + ShenandoahConcurrentSubphase gc_phase(msg, ShenandoahPhaseTimings::conc_update_thread_roots); EventMark em("%s", msg); // No workers used in this phase, no setup required @@ -636,7 +639,7 @@ void ShenandoahConcurrentGC::entry_update_refs() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent update references", ""); - ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_update_refs); + ShenandoahConcurrentSubphase gc_phase(msg, ShenandoahPhaseTimings::conc_update_refs); EventMark em("%s", msg); ShenandoahWorkerScope scope(heap->workers(), @@ -652,7 +655,7 @@ void ShenandoahConcurrentGC::entry_cleanup_complete() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent cleanup", ""); - ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_cleanup_complete, true /* log_heap_usage */); + ShenandoahConcurrentSubphase gc_phase(msg, ShenandoahPhaseTimings::conc_cleanup_complete, false /* log_heap_usage */); EventMark em("%s", msg); // This phase does not use workers, no need for setup @@ -664,7 +667,7 @@ void ShenandoahConcurrentGC::entry_reset_after_collect() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent reset after collect", ""); - ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_reset_after_collect); + ShenandoahConcurrentSubphase gc_phase(msg, ShenandoahPhaseTimings::conc_reset_after_collect); EventMark em("%s", msg); op_reset_after_collect(); @@ -1259,7 +1262,7 @@ void ShenandoahConcurrentGC::entry_final_roots() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent final roots", ""); - ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_final_roots); + ShenandoahConcurrentSubphase gc_phase(msg, ShenandoahPhaseTimings::conc_final_roots); EventMark em("%s", msg); heap->concurrent_final_roots(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp index 7549fe023168..2c82271f07f8 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp @@ -282,7 +282,7 @@ void ShenandoahControlThread::service_concurrent_normal_cycle(GCCause::Cause cau // ShenandoahHeap* heap = ShenandoahHeap::heap(); if (check_cancellation_or_degen(ShenandoahGC::_degenerated_outside_cycle)) { - log_info(gc)("Cancelled"); + log_info(gc, phases)("Cancelled"); return; } heap->increment_total_collections(false); @@ -371,7 +371,7 @@ void ShenandoahControlThread::notify_control_thread(GCCause::Cause cause) { void ShenandoahControlThread::handle_requested_gc(GCCause::Cause cause) { if (should_terminate()) { - log_info(gc)("Control thread is terminating, no more GCs"); + log_info(gc, phases)("Control thread is terminating, no more GCs"); return; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp index 439e23af2812..824f7bb432af 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp @@ -498,7 +498,7 @@ const char* ShenandoahDegenGC::degen_event_message(ShenandoahDegenPoint point) c } void ShenandoahDegenGC::upgrade_to_full() { - log_info(gc)("Degenerated GC upgrading to Full GC"); + log_info(gc, phases)("Degenerated GC upgrading to Full GC"); ShenandoahHeap* heap = ShenandoahHeap::heap(); heap->cancel_gc(GCCause::_shenandoah_upgrade_to_full_gc); heap->increment_total_collections(true); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp index 493736f4194e..139406246693 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp @@ -350,7 +350,7 @@ ShenandoahMarkingContext* ShenandoahGeneration::complete_marking_context() { } void ShenandoahGeneration::cancel_marking() { - log_info(gc)("Cancel marking: %s", name()); + log_info(gc, phases)("Cancel marking: %s", name()); if (is_concurrent_mark_in_progress()) { set_mark_incomplete(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp index 5d2e58559eee..c59f4992a7e2 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -425,7 +425,7 @@ void ShenandoahGenerationalControlThread::service_concurrent_old_cycle(const She if (_heap->cancelled_gc()) { // Young generation bootstrap cycle has failed. Concurrent mark for old generation // is going to resume after degenerated bootstrap cycle completes. - log_info(gc)("Bootstrap cycle for old generation was cancelled"); + log_info(gc, phases)("Bootstrap cycle for old generation was cancelled"); return; } @@ -656,7 +656,7 @@ bool ShenandoahGenerationalControlThread::request_concurrent_gc(ShenandoahGenera } // Cancel the old GC and wait for the control thread to start servicing the new request. - log_info(gc)("Preempting old generation mark to allow %s GC", generation->name()); + log_info(gc, phases)("Preempting old generation mark to allow %s GC", generation->name()); while (gc_mode() == servicing_old) { _heap->cancel_gc(GCCause::_shenandoah_concurrent_gc); notify_control_thread(ml, GCCause::_shenandoah_concurrent_gc, generation); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp index 31129182380e..ef4ee13a7d41 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp @@ -1023,7 +1023,7 @@ void ShenandoahGenerationalHeap::complete_concurrent_cycle() { void ShenandoahGenerationalHeap::entry_global_coalesce_and_fill() { const char* msg = "Coalescing and filling old regions"; - ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_coalesce_and_fill); + ShenandoahConcurrentSubphase gc_phase(msg, ShenandoahPhaseTimings::conc_coalesce_and_fill); TraceCollectorStats tcs(monitoring_support()->concurrent_collection_counters()); EventMark em("%s", msg); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp index c377ef8b0edd..98fc9040df96 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp @@ -514,6 +514,7 @@ class ShenandoahHeapRegion { } void reset_age() { + assert(get_top_before_promote() == nullptr, "Cannot reset age on region (%zu) scheduled for promotion", index()); CENSUS_NOISE(_youth += _age;) _age = 0; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.cpp b/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.cpp index ffa087ac3c0e..4a3c2b9f2e30 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.cpp @@ -39,7 +39,6 @@ ShenandoahInPlacePromotionPlanner::ShenandoahInPlacePromotionPlanner(const Shena , _marking_context(_heap->marking_context()) , _mutator_regions(_free_set) , _collector_regions(_free_set) - , _pip_padding_bytes(0) { } @@ -105,7 +104,6 @@ void ShenandoahInPlacePromotionPlanner::prepare(ShenandoahHeapRegion* r) { remnant_bytes = 0; } - _pip_padding_bytes += remnant_bytes; _free_set->prepare_to_promote_in_place(i, remnant_bytes); } else { // Since the remnant is so small that this region has already been retired, we don't have to worry about any @@ -117,7 +115,6 @@ void ShenandoahInPlacePromotionPlanner::prepare(ShenandoahHeapRegion* r) { } void ShenandoahInPlacePromotionPlanner::complete_planning() const { - _heap->old_generation()->set_pad_for_promote_in_place(_pip_padding_bytes); _heap->old_generation()->set_expected_humongous_region_promotions(_pip_humongous_stats.count); _heap->old_generation()->set_expected_regular_region_promotions(_pip_regular_stats.count); log_info(gc, ergo)("Planning to promote in place %zu humongous regions and %zu" @@ -136,30 +133,31 @@ void ShenandoahInPlacePromotionPlanner::complete_planning() const { } void ShenandoahInPlacePromoter::maybe_promote_region(ShenandoahHeapRegion* r) const { - if (r->is_young() && r->is_active() && _heap->is_tenurable(r)) { - if (r->is_humongous_start()) { - // We promote humongous_start regions along with their affiliated continuations during evacuation rather than - // doing this work during a safepoint. We cannot put humongous regions into the collection set because that - // triggers the load-reference barrier (LRB) to copy on reference fetch. - // - // Aged humongous continuation regions are handled with their start region. If an aged regular region has - // more garbage than ShenandoahOldGarbageThreshold, we'll promote by evacuation. If there is room for evacuation - // in this cycle, the region will be in the collection set. If there is no room, the region will be promoted - // by evacuation in some future GC cycle. - - // We do not promote primitive arrays because there's no performance penalty keeping them in young. When/if they - // become garbage, reclaiming the memory from young is much quicker and more efficient than reclaiming them from old. - oop obj = cast_to_oop(r->bottom()); - if (!obj->is_typeArray()) { - promote_humongous(r); - } - } else if (r->is_regular_or_regular_pinned() && (r->get_top_before_promote() != nullptr)) { - // Likewise, we cannot put promote-in-place regions into the collection set because that would also trigger - // the LRB to copy on reference fetch. - // - // If an aged regular region has received allocations during the current cycle, we do not promote because the - // newly allocated objects do not have appropriate age; this region's age will be reset to zero at end of cycle. - promote(r); + if (r->is_regular_or_regular_pinned() && (r->get_top_before_promote() != nullptr)) { + // This region was scheduled for promotion. The promotion must be completed. + // The 'always_tenure' override flag set by WB.fullGC() is not carried over + // into the degenerated cycle so we cannot rely on is_tenurable again. We checked + // it when we made the plan for this region, that plan is authoritative. + assert(r->is_young() && r->is_active(), "Region scheduled for promotion must still be young and active"); + promote(r); + return; + } + + if (r->is_young() && r->is_active() && r->is_humongous_start() && _heap->is_tenurable(r)) { + // We promote humongous_start regions along with their affiliated continuations during evacuation rather than + // doing this work during a safepoint. We cannot put humongous regions into the collection set because that + // triggers the load-reference barrier (LRB) to copy on reference fetch. + // + // Aged humongous continuation regions are handled with their start region. If an aged regular region has + // more garbage than ShenandoahOldGarbageThreshold, we'll promote by evacuation. If there is room for evacuation + // in this cycle, the region will be in the collection set. If there is no room, the region will be promoted + // by evacuation in some future GC cycle. + + // We do not promote primitive arrays because there's no performance penalty keeping them in young. When/if they + // become garbage, reclaiming the memory from young is much quicker and more efficient than reclaiming them from old. + oop obj = cast_to_oop(r->bottom()); + if (!obj->is_typeArray()) { + promote_humongous(r); } } } @@ -182,7 +180,6 @@ void ShenandoahInPlacePromoter::promote(ShenandoahHeapRegion* region) const { "Region %zu has too much garbage for promotion", region->index()); assert(region->is_young(), "Only young regions can be promoted"); assert(region->is_regular_or_regular_pinned(), "Use different service to promote humongous regions"); - assert(_heap->is_tenurable(region), "Only promote regions that are sufficiently aged"); assert(region->get_top_before_promote() == tams, "Region %zu has been used for allocations before promotion", region->index()); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.hpp b/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.hpp index d2cb644a59eb..4777c1893e3f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.hpp @@ -84,7 +84,7 @@ class ShenandoahInPlacePromotionPlanner { RegionPromotionStats() : count(0), usage(0), free(0), garbage(0) {} void update(ShenandoahHeapRegion* region) { count++; - usage += region->used(); + usage += region->get_live_data_bytes(); free += region->free(); garbage += region->garbage(); } @@ -101,9 +101,6 @@ class ShenandoahInPlacePromotionPlanner { RegionPromotions _mutator_regions; RegionPromotions _collector_regions; - // Tracks the padding of space above top in regions eligible for promotion in place - size_t _pip_padding_bytes; - // Tracks stats for in place promotions RegionPromotionStats _pip_regular_stats; RegionPromotionStats _pip_humongous_stats; @@ -124,6 +121,11 @@ class ShenandoahInPlacePromotionPlanner { const RegionPromotionStats& humongous_region_stats() const { return _pip_humongous_stats; } size_t old_garbage_threshold() const { return _old_garbage_threshold; } + + // Return the total amount of live bytes that will be promoted in place + size_t live_bytes() const { + return regular_region_stats().usage + humongous_region_stats().usage; + } }; // For regions that have been selected and prepared for promotion, this class diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp index c98b96c689b2..1ae080d31c64 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp @@ -86,6 +86,9 @@ bool ShenandoahOldGC::collect(GCCause::Cause cause) { assert(!_old_generation->is_preparing_for_mark(), "Old regions need to be parsable during concurrent mark."); heap->release_injected_pins(); + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent GC", ""); + ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_gc, /* log_heap_usage = */ true); + // Enable preemption of old generation mark. _allow_preemption.set(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp index 0f01795bb4ce..8d34937218ec 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp @@ -109,7 +109,6 @@ ShenandoahOldGeneration::ShenandoahOldGeneration(uint max_queues) _promoted_reserve(0), _promoted_expended(0), _promotion_potential(0), - _pad_for_promote_in_place(0), _promotable_humongous_regions(0), _promotable_regular_regions(0), _is_parsable(true), @@ -363,7 +362,7 @@ void ShenandoahOldGeneration::cancel_gc() { validate_idle(); #endif } else { - log_info(gc)("Terminating old gc cycle."); + log_info(gc, phases)("Terminating old gc cycle."); // Stop marking cancel_marking(); // Stop tracking old regions diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp index 61a3114f906b..3519303c3037 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp @@ -74,11 +74,6 @@ class ShenandoahOldGeneration : public ShenandoahGeneration { // It is also used when computing the optimum size for the old generation. size_t _promotion_potential; - // When a region is selected to be promoted in place, the remaining free memory is filled - // in to prevent additional allocations (preventing premature promotion of newly allocated - // objects). This field records the total amount of padding used for such regions. - size_t _pad_for_promote_in_place; - // During construction of the collection set, we keep track of regions that are eligible // for promotion in place. These fields track the count of those humongous and regular regions. // This data is used to force the evacuation phase even when the collection set is otherwise @@ -153,10 +148,6 @@ class ShenandoahOldGeneration : public ShenandoahGeneration { void set_promotion_potential(size_t val) { _promotion_potential = val; } size_t get_promotion_potential() const { return _promotion_potential; } - // See description in field declaration - void set_pad_for_promote_in_place(size_t pad) { _pad_for_promote_in_place = pad; } - size_t get_pad_for_promote_in_place() const { return _pad_for_promote_in_place; } - // See description in field declaration void set_expected_humongous_region_promotions(size_t region_count) { _promotable_humongous_regions = region_count; } void set_expected_regular_region_promotions(size_t region_count) { _promotable_regular_regions = region_count; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp index dfb42e0b76f0..25ff982a9e4d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp @@ -51,6 +51,7 @@ class outputStream; SHENANDOAH_WORKER_PHASE_DO(NAME_PREFIX, DESC_PREFIX, f) #define SHENANDOAH_PHASE_DO(f) \ + SHENANDOAH_SIMPLE_PHASE_DEF(f, conc_gc, "Concurrent GC") \ SHENANDOAH_SIMPLE_PHASE_DEF(f, conc_reset, "Concurrent Reset") \ SHENANDOAH_SIMPLE_PHASE_DEF(f, init_mark_gross, "Pause Init Mark (G)") \ SHENANDOAH_SIMPLE_PHASE_DEF(f, init_mark, "Pause Init Mark (N)") \ diff --git a/src/hotspot/share/gc/shenandoah/shenandoahTrace.cpp b/src/hotspot/share/gc/shenandoah/shenandoahTrace.cpp index c28e572dd6bd..40b9c94737e1 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahTrace.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahTrace.cpp @@ -23,6 +23,7 @@ */ #include "gc/shenandoah/shenandoahCollectionSet.inline.hpp" +#include "gc/shenandoah/shenandoahInPlacePromoter.hpp" #include "gc/shenandoah/shenandoahTrace.hpp" #include "jfr/jfrEvents.hpp" @@ -43,22 +44,19 @@ void ShenandoahTracer::report_evacuation_info(const ShenandoahCollectionSet* cse } } -void ShenandoahTracer::report_promotion_info(const ShenandoahCollectionSet* cset, - size_t regions_promoted_humongous, size_t humongous_promoted_garbage, size_t humongous_promoted_free, - size_t regions_promoted_regular, size_t regular_promoted_garbage, size_t regular_promoted_free) { - +void ShenandoahTracer::report_promotion_info(const ShenandoahCollectionSet* cset, const ShenandoahInPlacePromotionPlanner& planner) { EventShenandoahPromotionInformation e; if (e.should_commit()) { e.set_gcId(GCId::current()); e.set_collectedOld(cset->get_live_bytes_in_old_regions()); e.set_collectedPromoted(cset->get_live_bytes_in_tenurable_regions()); e.set_collectedYoung(cset->get_live_bytes_in_untenurable_regions()); - e.set_regionsPromotedHumongous(regions_promoted_humongous); - e.set_humongousPromotedGarbage(humongous_promoted_garbage); - e.set_humongousPromotedFree(humongous_promoted_free); - e.set_regionsPromotedRegular(regions_promoted_regular); - e.set_regularPromotedGarbage(regular_promoted_garbage); - e.set_regularPromotedFree(regular_promoted_free); + e.set_regionsPromotedHumongous(planner.humongous_region_stats().count); + e.set_humongousPromotedGarbage(planner.humongous_region_stats().garbage); + e.set_humongousPromotedFree(planner.humongous_region_stats().free); + e.set_regionsPromotedRegular(planner.regular_region_stats().count); + e.set_regularPromotedGarbage(planner.regular_region_stats().garbage); + e.set_regularPromotedFree(planner.regular_region_stats().free); e.commit(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahTrace.hpp b/src/hotspot/share/gc/shenandoah/shenandoahTrace.hpp index e5c80e0705fe..b37e8d669959 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahTrace.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahTrace.hpp @@ -29,6 +29,7 @@ #include "memory/allocation.hpp" class ShenandoahCollectionSet; +class ShenandoahInPlacePromotionPlanner; class ShenandoahTracer : public GCTracer, public CHeapObj { public: @@ -39,9 +40,7 @@ class ShenandoahTracer : public GCTracer, public CHeapObj { size_t free_regions, size_t regions_immediate, size_t immediate_size); // Sends a JFR event summarizing in-place promotion activity (generational mode only) - static void report_promotion_info(const ShenandoahCollectionSet* cset, - size_t regions_promoted_humongous, size_t humongous_promoted_garbage, size_t humongous_promoted_free, - size_t regions_promoted_regular, size_t regular_promoted_garbage, size_t regular_promoted_free); + static void report_promotion_info(const ShenandoahCollectionSet* cset, const ShenandoahInPlacePromotionPlanner& planner); }; #endif diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp b/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp index c4204d852d58..fb6a77b48ae9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp @@ -100,6 +100,28 @@ ShenandoahGCPauseMark::ShenandoahGCPauseMark(uint gc_id, const char* notificatio ); } +ShenandoahPauseSubphase::ShenandoahPauseSubphase(const char* title, ShenandoahPhaseTimings::Phase phase, bool log_heap_usage) : + ShenandoahTimingsTracker(phase), + _tracer(title, nullptr, GCCause::_no_gc, log_heap_usage), + _timer(ShenandoahHeap::heap()->gc_timer()) { + _timer->register_gc_phase_start(title, Ticks::now()); +} + +ShenandoahPauseSubphase::~ShenandoahPauseSubphase() { + _timer->register_gc_phase_end(Ticks::now()); +} + +ShenandoahConcurrentSubphase::ShenandoahConcurrentSubphase(const char* title, ShenandoahPhaseTimings::Phase phase, bool log_heap_usage) : + ShenandoahTimingsTracker(phase), + _tracer(title, nullptr, GCCause::_no_gc, log_heap_usage), + _timer(ShenandoahHeap::heap()->gc_timer()) { + _timer->register_gc_phase_start(title, Ticks::now()); +} + +ShenandoahConcurrentSubphase::~ShenandoahConcurrentSubphase() { + _timer->register_gc_phase_end(Ticks::now()); +} + ShenandoahPausePhase::ShenandoahPausePhase(const char* title, ShenandoahPhaseTimings::Phase phase, bool log_heap_usage) : ShenandoahTimingsTracker(phase), _tracer(title, nullptr, GCCause::_no_gc, log_heap_usage), diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp b/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp index e9761ebeb863..e16801871c58 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp @@ -30,16 +30,17 @@ #include "gc/shared/gcTraceTime.inline.hpp" #include "gc/shared/gcVMOperations.hpp" #include "gc/shared/isGCActiveMark.hpp" -#include "gc/shared/suspendibleThreadSet.hpp" #include "gc/shared/workerThread.hpp" +#include "gc/shenandoah/shenandoahGenerationType.hpp" +#include "gc/shenandoah/shenandoahHeap.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" -#include "gc/shenandoah/shenandoahThreadLocalData.hpp" #include "jfr/jfrEvents.hpp" #include "memory/allocation.hpp" #include "runtime/safepoint.hpp" #include "runtime/vmOperations.hpp" #include "runtime/vmThread.hpp" #include "services/memoryService.hpp" +#include "utilities/globalDefinitions.hpp" #include #include @@ -107,7 +108,35 @@ class ShenandoahTimingsTracker : public StackObj { }; /* - * ShenandoahPausePhase tracks a STW pause and emits Shenandoah timing and + * ShenandoahPauseSubphase tracks a STW pause and emits Shenandoah timing and + * a corresponding JFR event + */ +class ShenandoahPauseSubphase : public ShenandoahTimingsTracker { +private: + GCTraceTimeWrapper _tracer; + ConcurrentGCTimer* const _timer; + +public: + ShenandoahPauseSubphase(const char* title, ShenandoahPhaseTimings::Phase phase, bool log_heap_usage = false); + ~ShenandoahPauseSubphase(); +}; + +/* + * ShenandoahConcurrentSubphase tracks a concurrent GC phase and emits Shenandoah timing and + * a corresponding JFR event + */ +class ShenandoahConcurrentSubphase : public ShenandoahTimingsTracker { +private: + GCTraceTimeWrapper _tracer; + ConcurrentGCTimer* const _timer; + +public: + ShenandoahConcurrentSubphase(const char* title, ShenandoahPhaseTimings::Phase phase, bool log_heap_usage = false); + ~ShenandoahConcurrentSubphase(); +}; + +/* + * ShenandoahPausePhase tracks a pause GC phase and emits Shenandoah timing and * a corresponding JFR event */ class ShenandoahPausePhase : public ShenandoahTimingsTracker { @@ -257,6 +286,32 @@ inline size_t shenandoah_safe_size_cast(const double d) { return static_cast(d); } +// Convert a possibly signed double into a smaller number with appropriate engineering units. +struct ShenandoahSignedSize { + const double value; + const char* unit; + + static ShenandoahSignedSize get(double v) { + if (!std::isfinite(v)) { + return { v, "B" }; + } + const double magnitude = fabsd(v); + + if (magnitude >= 100.0 * G) { + return { std::copysign(magnitude / G, v), "G" }; + } + + if (magnitude >= 100.0 * M) { + return { std::copysign(magnitude / M, v), "M" }; + } + + if (magnitude >= 100.0 * K) { + return { std::copysign(magnitude / K, v), "K" }; + } + + return { std::copysign(magnitude, v), "B" }; + } +}; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHUTILS_HPP diff --git a/src/hotspot/share/interpreter/bytecodeUtils.cpp b/src/hotspot/share/interpreter/bytecodeUtils.cpp index 469aa0d5dcc6..8dd1f2288e29 100644 --- a/src/hotspot/share/interpreter/bytecodeUtils.cpp +++ b/src/hotspot/share/interpreter/bytecodeUtils.cpp @@ -305,7 +305,7 @@ static char const* get_field_name(Method* method, int cp_index, Bytecodes::Code return name->as_C_string(); } -static bool is_null_restricted_field(Method* method, address code_base, int bci) { +static bool might_be_null_restricted_field(Method* method, address code_base, int bci) { ConstantPool* cp = method->constants(); int cp_index = Bytes::get_native_u2(code_base + bci + 1); ResolvedFieldEntry* field = cp->resolved_field_entry_at(cp_index); @@ -315,18 +315,10 @@ static bool is_null_restricted_field(Method* method, address code_base, int bci) if (is_resolved) { return field->is_null_free_inline_type(); } else { - // This is more expensive but rare. C1 might not have resolved the field - // in the interpreter first. - fieldDescriptor fd; - Klass* klass = cp->resolved_klass_ref_at(cp_index, bc); - assert (klass != nullptr, "must be resolved if we got an NPE here"); - - Symbol* name = cp->name_ref_at(cp_index, bc); - Symbol* sig = cp->signature_ref_at(cp_index, bc); - Klass* owner = InstanceKlass::cast(klass)->find_field(name, sig, false, &fd); - return owner != nullptr && fd.is_null_free_inline_type(); + // This is rare. The compiler might not have resolved the field or the klass + // in the interpreter first. Just say it might be null restricted because we don't know. + return true; } - return false; } static void print_local_var(outputStream *os, unsigned int bci, Method* method, int slot, bool is_parameter) { @@ -1205,7 +1197,7 @@ bool ExceptionMessageBuilder::print_NPE_cause(outputStream* os, int bci, int slo if (print_NPE_cause0(os, bci, slot, _max_cause_detail, false, " because \"")) { if (code == Bytecodes::_aastore) { os->print("\" is null or is a null-free array and there's an attempt to store null in it"); - } else if (code == Bytecodes::_putfield && is_null_restricted_field(_method, code_base, bci)) { + } else if (code == Bytecodes::_putfield && might_be_null_restricted_field(_method, code_base, bci)) { int cp_index = Bytes::get_native_u2(code_base + bci + 1); os->print("\" is null or \"%s\" is a null restricted field and there's an attempt to store null in it", get_field_name(_method, cp_index, code)); diff --git a/src/hotspot/share/jfr/jfrEvents.hpp b/src/hotspot/share/jfr/jfrEvents.hpp index ba0d9f5b61e8..d4437f7d64a2 100644 --- a/src/hotspot/share/jfr/jfrEvents.hpp +++ b/src/hotspot/share/jfr/jfrEvents.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp index c6c1b4cad600..c1eded10f92b 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -56,6 +56,7 @@ #include "runtime/interfaceSupport.inline.hpp" #include "runtime/mutex.hpp" #include "runtime/safepoint.hpp" +#include "runtime/thread.inline.hpp" typedef JfrCheckpointManager::BufferPtr BufferPtr; typedef JfrCheckpointManager::ConstBufferPtr ConstBufferPtr; @@ -257,6 +258,7 @@ BufferPtr JfrCheckpointManager::acquire_virtual_thread_local(Thread* thread, siz BufferPtr JfrCheckpointManager::renew(ConstBufferPtr old, Thread* thread, size_t size, JfrCheckpointBufferKind kind /* JFR_THREADLOCAL */) { assert(old != nullptr, "invariant"); assert(old->acquired_by_self(), "invariant"); + assert(!old->retired(), "invariant"); if (kind == JFR_GLOBAL) { return lease_global(thread, instance()._global_mspace->in_previous_epoch_list(old), size); } @@ -493,9 +495,10 @@ class VirtualThreadLocalCheckpointWriteOp { }; typedef CheckpointWriteOp WriteOperation; +typedef ExclusiveOp ExclusiveWriteOperation; typedef MutexedWriteOp MutexedWriteOperation; typedef ReleaseWithExcisionOp ReleaseOperation; -typedef CompositeOperation WriteReleaseOperation; +typedef CompositeOperation WriteReleaseOperation; typedef VirtualThreadLocalCheckpointWriteOp VirtualThreadLocalCheckpointOperation; typedef MutexedWriteOp VirtualThreadLocalWriteOperation; @@ -510,11 +513,12 @@ void JfrCheckpointManager::shift_epoch() { size_t JfrCheckpointManager::write() { DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(JavaThread::current())); WriteOperation wo(chunkwriter()); - MutexedWriteOperation mwo(wo); - _thread_local_mspace->iterate(mwo, true); // previous epoch list + // Must take exclusive ownership before writing, because non-Java threads can still have active usage. + ExclusiveWriteOperation ewo(wo); + _thread_local_mspace->iterate(ewo, true); // previous epoch list assert(_global_mspace->free_list_is_empty(), "invariant"); ReleaseOperation ro(_global_mspace, _global_mspace->live_list(true)); // previous epoch list - WriteReleaseOperation wro(&mwo, &ro); + WriteReleaseOperation wro(&ewo, &ro); process_live_list(wro, _global_mspace, true); // previous epoch list // Do virtual thread local list last. Careful, the vtlco destructor writes to chunk. VirtualThreadLocalCheckpointOperation vtlco(chunkwriter()); @@ -524,19 +528,22 @@ size_t JfrCheckpointManager::write() { } typedef DiscardOp > DiscardOperation; -typedef CompositeOperation DiscardReleaseOperation; +typedef ExclusiveDiscardOp > ExclusiveDiscardOperation; +typedef CompositeOperation DiscardReleaseOperation; size_t JfrCheckpointManager::clear() { JfrTraceIdLoadBarrier::clear(); clear_type_set(); - DiscardOperation dop(mutexed); // mutexed discard mode - _thread_local_mspace->iterate(dop, true); // previous epoch list + // Must take exclusive ownership before discarding, because non-Java threads can still have active usage. + ExclusiveDiscardOperation edo(mutexed); + _thread_local_mspace->iterate(edo, true); // previous epoch list + DiscardOperation dop(mutexed); // already has exclusive ownership discard mode _virtual_thread_local_mspace->iterate(dop, true); // previous epoch list ReleaseOperation ro(_global_mspace, _global_mspace->live_list(true)); // previous epoch list - DiscardReleaseOperation dro(&dop, &ro); + DiscardReleaseOperation dro(&edo, &ro); assert(_global_mspace->free_list_is_empty(), "invariant"); process_live_list(dro, _global_mspace, true); // previous epoch list - return dop.elements(); + return edo.elements() + dop.elements(); } size_t JfrCheckpointManager::write_static_type_set(Thread* thread) { @@ -623,7 +630,11 @@ void JfrCheckpointManager::write_type_set() { void JfrCheckpointManager::on_unloading_classes() { assert_locked_or_safepoint(ClassLoaderDataGraph_lock); - JfrCheckpointWriter writer(Thread::current()); + Thread* const current = Thread::current(); + // Take the epoch shift lock to ensure no epoch shift + // occurs during artifact serialization (for concurrent GCs). + ConditionalMutexLocker lock(current, JfrEpochShift_lock, UseShenandoahGC || UseZGC, Mutex::_no_safepoint_check_flag); + JfrCheckpointWriter writer(current); JfrTypeSet::on_unloading_classes(&writer); JfrAddRefCountedBlob add_blob(writer, false /* move */, false /* reset */); } diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp index 17d945af65e6..ded5a42ea82d 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -111,18 +111,22 @@ void JfrCheckpointThreadClosure::do_thread(Thread* t) { _writer.write(false); // isVirtual } +static inline void invoke(JfrCheckpointThreadClosure& tc, Thread* t) { + assert(t != nullptr, "invariant"); + if (t->jfr_thread_local()->should_write()) { + tc.do_thread(t); + } +} + void JfrThreadConstantSet::serialize(JfrCheckpointWriter& writer) { JfrCheckpointThreadClosure tc(writer); JfrJavaThreadIterator javathreads; while (javathreads.has_next()) { - JavaThread* const jt = javathreads.next(); - if (jt->jfr_thread_local()->should_write()) { - tc.do_thread(jt); - } + invoke(tc, javathreads.next()); } JfrNonJavaThreadIterator nonjavathreads; while (nonjavathreads.has_next()) { - tc.do_thread(nonjavathreads.next()); + invoke(tc, nonjavathreads.next()); } } diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.cpp index 58d12f709809..ddbfebb15984 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,7 @@ #include "memory/resourceArea.hpp" #include "nmt/memTracker.hpp" #include "runtime/javaThread.hpp" +#include "runtime/safepoint.hpp" #include "runtime/semaphore.hpp" #include "runtime/thread.inline.hpp" #include "utilities/macros.hpp" @@ -59,6 +60,7 @@ class JfrSerializerRegistration : public JfrCHeapObj { } void on_rotation() const { + assert(SafepointSynchronize::is_at_safepoint(), "invariant"); _serializer->on_rotation(); } @@ -160,11 +162,16 @@ Semaphore SerializerRegistrationGuard::_mutex_semaphore(1); typedef JfrLinkedList List; static List types; +template +static inline void iterate(Processor& p) { + SerializerRegistrationGuard guard; + types.iterate(p); +} + void JfrTypeManager::destroy() { SerializerRegistrationGuard guard; - JfrSerializerRegistration* registration; while (types.is_nonempty()) { - registration = types.remove(); + JfrSerializerRegistration* registration = types.remove(); assert(registration != nullptr, "invariant"); delete registration; } @@ -180,8 +187,9 @@ class InvokeOnRotation { }; void JfrTypeManager::on_rotation() { + assert(SafepointSynchronize::is_at_safepoint(), "invariant"); InvokeOnRotation ior; - types.iterate(ior); + iterate(ior); } #ifdef ASSERT @@ -211,12 +219,13 @@ static bool register_static_type(JfrTypeId id, bool permit_cache, JfrSerializer* delete serializer; return false; } - assert(!types.in_list(registration), "invariant"); - DEBUG_ONLY(assert_not_registered_twice(id, types);) if (JfrRecorder::is_recording()) { - JfrCheckpointWriter writer(Thread::current(), true, STATICS); + JfrCheckpointWriter writer(Thread::current(), true, STATICS, JFR_THREADLOCAL); registration->invoke(writer); } + SerializerRegistrationGuard guard; + assert(!types.in_list(registration), "invariant"); + DEBUG_ONLY(assert_not_registered_twice(id, types);) types.add(registration); return true; } @@ -232,7 +241,6 @@ static bool load_thread_constants(TRAPS) { } bool JfrTypeManager::initialize() { - SerializerRegistrationGuard guard; register_static_type(TYPE_FLAGVALUEORIGIN, true, new FlagValueOriginConstant()); register_static_type(TYPE_INFLATECAUSE, true, new MonitorInflateCauseConstant()); register_static_type(TYPE_GCCAUSE, true, new GCCauseConstant()); @@ -256,7 +264,6 @@ bool JfrTypeManager::initialize() { // implementation for the static registration function exposed in the JfrSerializer api bool JfrSerializer::register_serializer(JfrTypeId id, bool permit_cache, JfrSerializer* serializer) { - SerializerRegistrationGuard guard; return register_static_type(id, permit_cache, serializer); } @@ -274,6 +281,5 @@ class InvokeSerializer { void JfrTypeManager::write_static_types(JfrCheckpointWriter& writer) { InvokeSerializer is(writer); - SerializerRegistrationGuard guard; - types.iterate(is); + iterate(is); } diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp index 3dd9ea41d3d6..3141c5ab1abe 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,6 +47,8 @@ #include "oops/instanceKlass.inline.hpp" #include "oops/objArrayKlass.hpp" #include "oops/oop.inline.hpp" +#include "runtime/mutex.hpp" +#include "runtime/thread.inline.hpp" #include "utilities/accessFlags.hpp" #include "utilities/bitMap.inline.hpp" #include "utilities/stack.inline.hpp" @@ -87,7 +89,13 @@ static inline bool previous_epoch() { template static inline bool used(const T* ptr) { assert(ptr != nullptr, "invariant"); - return current_epoch() ? USED_THIS_EPOCH(ptr) : USED_PREVIOUS_EPOCH(ptr); + if (flushpoint()) { + return USED_THIS_EPOCH(ptr); + } + if (unloading()) { + return USED_THIS_EPOCH(ptr) || USED_PREVIOUS_EPOCH(ptr); + } + return USED_PREVIOUS_EPOCH(ptr); } template @@ -257,7 +265,7 @@ class ModuleFieldSelector { if (pkg == nullptr) { return nullptr; } - assert(current_epoch() ? IS_SERIALIZED(pkg) : true, "invariant"); + assert(IS_SERIALIZED(pkg), "invariant"); return pkg->module(); } }; @@ -280,7 +288,7 @@ class ModuleCldFieldSelector { if (mod == nullptr) { return nullptr; } - assert(current_epoch() ? IS_SERIALIZED(mod) : true, "invariant"); + assert(IS_SERIALIZED(mod), "invariant"); return mod->loader_data(); } }; @@ -307,6 +315,16 @@ class SerializePredicate { } }; +#ifdef ASSERT +template +static bool test_is_previous_epoch_cleared_bit_set(const T* ptr) { + assert(ptr != nullptr, "invariant"); + const Thread* const current = Thread::current(); + assert(current != nullptr, "invariant"); + return !current->is_JfrRecorder_thread() || IS_PREVIOUS_EPOCH_CLEARED_BIT_SET(ptr); +} +#endif + template static void set_serialized(const T* ptr) { assert(ptr != nullptr, "invariant"); @@ -314,7 +332,7 @@ static void set_serialized(const T* ptr) { CLEAR_THIS_EPOCH_CLEARED_BIT(ptr); assert(!IS_THIS_EPOCH_CLEARED_BIT_SET(ptr), "invariant"); } - assert(IS_PREVIOUS_EPOCH_CLEARED_BIT_SET(ptr), "invariant"); + assert(test_is_previous_epoch_cleared_bit_set(ptr), "invariant"); SET_SERIALIZED(ptr); assert(IS_SERIALIZED(ptr), "invariant"); } @@ -359,12 +377,12 @@ static void do_write_klass(JfrCheckpointWriter* writer, CldPtr cld, KlassPtr kla return; } assert(used(klass), "invariant"); - assert(unloading() ? true : IS_NOT_SERIALIZED(klass), "invariant"); + assert(unloading() || IS_NOT_SERIALIZED(klass), "invariant"); set_serialized(klass); } static inline bool should_write_cld_klass(KlassPtr klass, bool leakp) { - return klass != nullptr && (leakp ? IS_LEAKP(klass) : unloading() ? true : IS_NOT_SERIALIZED(klass)); + return klass != nullptr && (leakp ? IS_LEAKP(klass) : unloading() || IS_NOT_SERIALIZED(klass)); } static void write_klass(JfrCheckpointWriter* writer, KlassPtr klass, bool leakp, int& elements) { @@ -539,6 +557,7 @@ static void do_unloading_klass(Klass* klass) { assert(used(klass), "invariant"); } if (JfrKlassUnloading::on_unload(klass)) { + assert(used(klass), "invariant"); if (JfrTraceId::has_sticky_bit(klass)) { JfrMethodTracer::add_to_unloaded_set(klass); } @@ -996,6 +1015,15 @@ static void write_clds_on_clear() { /***** Methods *****/ +#ifdef ASSERT +static bool test_is_previous_epoch_method_cleared_bit_set(MethodPtr method) { + assert(method != nullptr, "invariant"); + const Thread* const current = Thread::current(); + assert(current != nullptr, "invariant"); + return !current->is_JfrRecorder_thread() || IS_PREVIOUS_EPOCH_METHOD_CLEARED_BIT_SET(method); +} +#endif + template <> void set_serialized(MethodPtr method) { assert(method != nullptr, "invariant"); @@ -1003,9 +1031,9 @@ void set_serialized(MethodPtr method) { CLEAR_THIS_EPOCH_METHOD_CLEARED_BIT(method); assert(!IS_THIS_EPOCH_METHOD_CLEARED_BIT_SET(method), "invariant"); } - assert(unloading() ? true : METHOD_IS_NOT_SERIALIZED(method), "invariant"); + assert(test_is_previous_epoch_method_cleared_bit_set(method), "invariant"); + assert(unloading() || METHOD_IS_NOT_SERIALIZED(method), "invariant"); SET_METHOD_SERIALIZED(method); - assert(IS_PREVIOUS_EPOCH_METHOD_CLEARED_BIT_SET(method), "invariant"); assert(METHOD_IS_SERIALIZED(method), "invariant"); } @@ -1054,7 +1082,7 @@ class MethodIteratorHost { MethodIteratorHost(JfrCheckpointWriter* writer) : _method_cb(writer, unloading(), false), _klass_cb(writer, unloading(), false), - _method_flag_predicate(current_epoch()) {} + _method_flag_predicate(previous_epoch(), unloading()) {} bool operator()(KlassPtr klass) { if (klass->is_instance_klass()) { @@ -1320,6 +1348,7 @@ void JfrTypeSet::clear(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_w } size_t JfrTypeSet::on_unloading_classes(JfrCheckpointWriter* writer) { + assert(!(UseShenandoahGC || UseZGC) || JfrEpochShift_lock->owned_by_self(), "invariant"); // JfrTraceIdEpoch::has_changed_tag_state_no_reset() is a load-acquire we issue to see side-effects (i.e. tags). // The JfrRecorderThread does this as part of normal processing, but with concurrent class unloading, which can // happen in arbitrary threads, we invoke it explicitly. diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp index cc5ebb2be39f..3f11ea54924a 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp @@ -1,5 +1,5 @@ -/* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + /* + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -135,14 +135,22 @@ class SymbolPredicate { template class MethodFlagPredicate { - bool _current_epoch; + const bool _previous_epoch; + const bool _class_unload; public: - MethodFlagPredicate(bool current_epoch) : _current_epoch(current_epoch) {} + MethodFlagPredicate(bool previous_epoch, bool class_unload) : _previous_epoch(previous_epoch), _class_unload(class_unload) {} bool operator()(const Method* method) { - if (_current_epoch) { - return leakp ? METHOD_IS_LEAKP(method) : METHOD_FLAG_USED_THIS_EPOCH(method); + if (leakp) { + return METHOD_IS_LEAKP(method); } - return leakp ? METHOD_IS_LEAKP(method) : METHOD_FLAG_USED_PREVIOUS_EPOCH(method); + if (_previous_epoch) { + assert(!_class_unload, "invariant"); + return METHOD_FLAG_USED_PREVIOUS_EPOCH(method); + } + if (_class_unload) { + return METHOD_FLAG_USED_THIS_EPOCH(method) || METHOD_FLAG_USED_PREVIOUS_EPOCH(method); + } + return METHOD_FLAG_USED_THIS_EPOCH(method); } }; diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.cpp index bc7156adc4da..8a6e4f4c952f 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.cpp @@ -47,6 +47,7 @@ static constexpr const u2 epoch_generation_overflow = excluded_bit; void JfrTraceIdEpoch::shift_epoch() { assert(SafepointSynchronize::is_at_safepoint(), "invariant"); + assert(!(UseShenandoahGC || UseZGC) || JfrEpochShift_lock->owned_by_self(), "invariant"); _epoch_state = !_epoch_state; if (++_generation == epoch_generation_overflow) { _generation = 1; diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdLoadBarrier.inline.hpp b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdLoadBarrier.inline.hpp index c2b638401075..1eb876f4315b 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdLoadBarrier.inline.hpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdLoadBarrier.inline.hpp @@ -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 @@ -150,12 +150,11 @@ inline traceid JfrTraceIdLoadBarrier::load(const ClassLoaderData* cld) { if (cld->has_class_mirror_holder()) { return 0; } - const traceid id = set_used_and_get(cld); const Klass* const class_loader_klass = cld->class_loader_klass(); if (class_loader_klass != nullptr) { load(class_loader_klass); } - return id; + return set_used_and_get(cld); } inline traceid JfrTraceIdLoadBarrier::load(const ModuleEntry* module) { diff --git a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp index 6f8d44fb1a45..84c37e91df40 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp +++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp @@ -464,11 +464,14 @@ void JfrRecorderService::invoke_safepoint_clear() { void JfrRecorderService::safepoint_clear() { assert(SafepointSynchronize::is_at_safepoint(), "invariant"); - _storage.clear(); _checkpoint_manager.notify_threads(true); - _chunkwriter.set_time_stamp(); JfrDeprecationManager::on_safepoint_clear(); JfrStackTraceRepository::clear(); + // Ensure that non-Java threads cannot perform tagging, enqueuing, + // or event writing that interleaves with the epoch shift. + ConditionalMutexLocker lock(JfrEpochShift_lock, UseShenandoahGC || UseZGC, Mutex::_no_safepoint_check_flag); + _storage.clear(); + _chunkwriter.set_time_stamp(); _checkpoint_manager.shift_epoch(); } @@ -576,10 +579,13 @@ void JfrRecorderService::safepoint_write() { assert(SafepointSynchronize::is_at_safepoint(), "invariant"); JfrStackTraceRepository::clear_leak_profiler(); _checkpoint_manager.on_rotation(); - _storage.write_at_safepoint(); - _chunkwriter.set_time_stamp(); JfrDeprecationManager::on_safepoint_write(); write_stacktrace(_stack_trace_repository, _chunkwriter, true); + // Ensure that non-Java threads cannot perform tagging, enqueuing, + // or event writing that interleaves with the epoch shift. + ConditionalMutexLocker lock(JfrEpochShift_lock, UseShenandoahGC || UseZGC, Mutex::_no_safepoint_check_flag); + _storage.write_at_safepoint(); + _chunkwriter.set_time_stamp(); _checkpoint_manager.shift_epoch(); } diff --git a/src/hotspot/share/jfr/recorder/storage/jfrEpochStorage.hpp b/src/hotspot/share/jfr/recorder/storage/jfrEpochStorage.hpp index 02f6a0ca7748..bb83c7fafbc7 100644 --- a/src/hotspot/share/jfr/recorder/storage/jfrEpochStorage.hpp +++ b/src/hotspot/share/jfr/recorder/storage/jfrEpochStorage.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,7 +34,7 @@ /* * Provides storage as a function of an epoch, with iteration capabilities for the current and previous epoch. * - * When iterating the previous epoch, where exclusive access to buffers is assumed, + * When iterating the previous epoch, where exclusive access to buffers is assumed (see disclaimer below), * all buffers will be reinitialized post-callback, with retired buffers reclaimed * and moved onto the free list and non-retired buffers left in-place. * @@ -47,6 +47,12 @@ * The design caters to use cases having multiple incremental iterations over the current epoch, * and a single iteration over the previous epoch. * + * DISCLAIMER: Exclusive access to the previous epoch is only guaranteed for uses that respect the safepoint protocol. + * This is because the JFR epoch evolves under a safepoint. If non-Java threads use the storage, + * observe that they do NOT respect when the epoch evolves. As such, they could still have active uses + * towards the previous epoch, although JavaThreads do not. In effect, this becomes another instance + * of concurrent access, as described for the current epoch. Be extra careful about who is using this kind of storage. + * * The JfrEpochStorage can be specialized by the following policies: * * NodeType the type of the Node to be managed by the JfrMemorySpace. diff --git a/src/hotspot/share/jfr/recorder/storage/jfrMemorySpace.inline.hpp b/src/hotspot/share/jfr/recorder/storage/jfrMemorySpace.inline.hpp index 1b6273b78d2b..05fda370d12f 100644 --- a/src/hotspot/share/jfr/recorder/storage/jfrMemorySpace.inline.hpp +++ b/src/hotspot/share/jfr/recorder/storage/jfrMemorySpace.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -641,9 +641,13 @@ inline bool ReinitializeAllReleaseRetiredOp::process(typename template inline void assert_migration_state(const Node* old, const Node* new_node, size_t used, size_t requested) { assert(old != nullptr, "invariant"); - assert(new_node != nullptr, "invariant"); + assert(old->acquired_by_self(), "invariant"); + assert(!old->retired(), "invariant"); assert(old->pos() >= old->start(), "invariant"); assert(old->pos() + used <= old->end(), "invariant"); + assert(new_node != nullptr, "invariant"); + assert(new_node->acquired_by_self(), "invariant"); + assert(!new_node->retired(), "invariant"); assert(new_node->free_size() >= (used + requested), "invariant"); } #endif // ASSERT diff --git a/src/hotspot/share/jfr/recorder/storage/jfrStorageUtils.inline.hpp b/src/hotspot/share/jfr/recorder/storage/jfrStorageUtils.inline.hpp index 5a7cb7e7a788..ac61b05c4ac7 100644 --- a/src/hotspot/share/jfr/recorder/storage/jfrStorageUtils.inline.hpp +++ b/src/hotspot/share/jfr/recorder/storage/jfrStorageUtils.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -60,9 +60,7 @@ inline bool ConcurrentWriteOp::process(typename Operation::Type* t) { const size_t unflushed_size = get_unflushed_size(top, t); assert((intptr_t)unflushed_size >= 0, "invariant"); if (unflushed_size == 0) { - if (is_retired) { - t->set_top(top); - } else { + if (!is_retired) { t->release_critical_section_top(top); } return true; @@ -91,7 +89,7 @@ inline bool MutexedWriteOp::process(typename Operation::Type* t) { } template -static void retired_sensitive_acquire(Type* t, Thread* thread) { +inline void retired_sensitive_acquire(Type* t, Thread* thread) { assert(t != nullptr, "invariant"); assert(thread != nullptr, "invariant"); assert(thread == Thread::current(), "invariant"); diff --git a/src/hotspot/share/jfr/support/jfrJdkJfrEvent.cpp b/src/hotspot/share/jfr/support/jfrJdkJfrEvent.cpp index ff1f4cdd3984..0e7f706b210b 100644 --- a/src/hotspot/share/jfr/support/jfrJdkJfrEvent.cpp +++ b/src/hotspot/share/jfr/support/jfrJdkJfrEvent.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -49,11 +49,6 @@ static oop new_java_util_arraylist(TRAPS) { static const int initial_array_size = 64; -template -static GrowableArray* c_heap_allocate_array(int size = initial_array_size) { - return new (mtTracing) GrowableArray(size, mtTracing); -} - static bool initialize(TRAPS) { static bool initialized = false; if (!initialized) { diff --git a/src/hotspot/share/jfr/support/jfrKlassUnloading.cpp b/src/hotspot/share/jfr/support/jfrKlassUnloading.cpp index d136eeab53ad..bf285c3f41e9 100644 --- a/src/hotspot/share/jfr/support/jfrKlassUnloading.cpp +++ b/src/hotspot/share/jfr/support/jfrKlassUnloading.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -100,7 +100,7 @@ bool JfrKlassUnloading::on_unload(const Klass* k) { ++event_klass_unloaded_count; } add_to_unloaded_klass_set(JfrTraceId::load_raw(k)); - return USED_THIS_EPOCH(k); + return USED_THIS_EPOCH(k) || USED_PREVIOUS_EPOCH(k); } static inline bool is_unloaded(const JfrCHeapTraceIdSet* set, const traceid& id) { diff --git a/src/hotspot/share/oops/accessBackend.hpp b/src/hotspot/share/oops/accessBackend.hpp index 368abc7ef25c..a32cf48948f5 100644 --- a/src/hotspot/share/oops/accessBackend.hpp +++ b/src/hotspot/share/oops/accessBackend.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1133,7 +1133,7 @@ namespace AccessInternal { // that the passed in types make sense. template - static void verify_types(){ + inline void verify_types(){ // If this fails to compile, then you have sent in something that is // not recognized as a valid primitive type to a primitive Access function. STATIC_ASSERT((HasDecorator::value || // oops have already been validated diff --git a/src/hotspot/share/oops/arrayKlass.hpp b/src/hotspot/share/oops/arrayKlass.hpp index 7144818246e4..a9fa993e1b68 100644 --- a/src/hotspot/share/oops/arrayKlass.hpp +++ b/src/hotspot/share/oops/arrayKlass.hpp @@ -152,6 +152,20 @@ class ArrayKlass: public Klass { }; class ArrayDescription : public StackObj { + // Layout for uint32_t encoding + // + // 31 24 23 16 15 0 + // +----------------+----------------+-----------------------------+ + // | KlassKind | LayoutKind | ArrayProperties | + // +----------------+----------------+-----------------------------+ + // 8 bits 8 bits 16 bits + static constexpr uint32_t _layout_kind_shift = 16; + static constexpr uint32_t _kind_shift = 24; + + static constexpr uint32_t _properties_mask = (1u << _layout_kind_shift) - 1; + static constexpr uint32_t _layout_kind_mask = (1u << (_kind_shift - _layout_kind_shift)) - 1; + static constexpr uint32_t _kind_mask = (1u << (32 - _kind_shift)) - 1; + public: Klass::KlassKind _kind; ArrayProperties _properties; @@ -168,6 +182,30 @@ class ArrayDescription : public StackObj { const bool non_atomic = lk != LayoutKind::REFERENCE && !LayoutKindHelper::is_atomic_flat(lk); _properties = p.with_non_atomic(non_atomic); } - }; + + uint32_t value() const { + assert((_properties.value() & ~_properties_mask) == 0, "array properties do not fit into encoding"); + + uint32_t layout_kind_value = static_cast(_layout_kind); + assert((layout_kind_value & ~_layout_kind_mask) == 0, "layout kind does not fit into encoding"); + + uint32_t kind_value = static_cast(_kind); + assert((kind_value & ~_kind_mask) == 0, "klass kind does not fit into encoding"); + + return _properties.value() | (layout_kind_value << _layout_kind_shift) | (kind_value << _kind_shift); + } + + static ArrayDescription from_value(uint32_t value) { + ArrayProperties properties(value & _properties_mask); + + uint32_t layout_kind_value = (value >> _layout_kind_shift) & _layout_kind_mask; + LayoutKind layout_kind = static_cast(layout_kind_value); + + uint32_t kind_value = (value >> _kind_shift) & _kind_mask; + Klass::KlassKind kind = static_cast(kind_value); + + return ArrayDescription(kind, properties, layout_kind); + } +}; #endif // SHARE_OOPS_ARRAYKLASS_HPP diff --git a/src/hotspot/share/oops/constantPool.hpp b/src/hotspot/share/oops/constantPool.hpp index 8da04079eba4..824ec6409eba 100644 --- a/src/hotspot/share/oops/constantPool.hpp +++ b/src/hotspot/share/oops/constantPool.hpp @@ -580,12 +580,6 @@ class ConstantPool : public Metadata { return symbol_at(signature_index); } - // Returns a resolved klass or nullptr if not resolved. Does not try to resolve the class. - Klass* resolved_klass_ref_at(int which, Bytecodes::Code code) { - jint ref_index = klass_ref_index_at(which, code); - return resolved_klass_at(ref_index); - } - u2 klass_ref_index_at(int which, Bytecodes::Code code); u2 name_and_type_ref_index_at(int which, Bytecodes::Code code); diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index 010510c2f3fd..e4bff1dbf70c 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -1075,11 +1075,24 @@ static void load_classes_from_loadable_descriptors_attribute(InstanceKlass *ik, TempNewSymbol class_name = Signature::strip_envelope(sig); if (class_name == ik->name()) continue; log_info(class, preload)("Preloading of class %s during linking of class %s " - "because of the class is listed in the LoadableDescriptors attribute", + "because the class is listed in the LoadableDescriptors attribute", sig->as_C_string(), ik->name()->as_C_string()); oop loader = ik->class_loader(); - Klass* klass = SystemDictionary::resolve_or_null(class_name, - Handle(THREAD, loader), THREAD); + Klass* klass = nullptr; + + if (CDSConfig::is_using_aot_linked_classes() && ik->in_aot_cache() && !ik->defined_by_other_loaders()) { + // + We come to here during AOTLinkedClassBulkLoader::link_classes() and it's too early to + // execute any Java code. + // + All loadable descriptors that can be resolved would have been resolved during the AOT assembly + // phase, and would have been loaded earlier by AOTLinkedClassBulkLoader, so they can be found in + // the system dictionary. + // + If no class of the given name have been loaded yet, it's most likely because the class + // is missing from JAR files. Just ignore it. + klass = SystemDictionary::find_instance_or_array_klass(THREAD, class_name, Handle(THREAD, loader)); + } else { + klass = SystemDictionary::resolve_or_null(class_name, + Handle(THREAD, loader), THREAD); + } if (HAS_PENDING_EXCEPTION) { CLEAR_PENDING_EXCEPTION; } @@ -3331,14 +3344,21 @@ void InstanceKlass::unload_class(InstanceKlass* ik) { log_info(class, unload)("unloading class %s " PTR_FORMAT, ik->external_name(), p2i(ik)); } - Events::log_class_unloading(Thread::current(), ik); + Thread* const current = Thread::current(); + Events::log_class_unloading(current, ik); #if INCLUDE_JFR assert(ik != nullptr, "invariant"); - EventClassUnload event; - event.set_unloadedClass(ik); - event.set_definingClassLoader(ik->class_loader_data()); - event.commit(); + // For concurrent unloading, we need the ik, the cld, and the event + // to end up in the correct JFR epoch, hence the acquisition of the + // epoch shift lock before the event.commit(). + if (EventClassUnload::is_enabled()) { + EventClassUnload event; + event.set_unloadedClass(ik); + event.set_definingClassLoader(ik->class_loader_data()); + ConditionalMutexLocker ml(JfrEpochShift_lock, UseShenandoahGC || UseZGC, Mutex::_no_safepoint_check_flag); + event.commit(); + } #endif } diff --git a/src/hotspot/share/oops/metadata.hpp b/src/hotspot/share/oops/metadata.hpp index bd3f17fa3f4f..819e23f97a29 100644 --- a/src/hotspot/share/oops/metadata.hpp +++ b/src/hotspot/share/oops/metadata.hpp @@ -82,7 +82,7 @@ class Metadata : public MetaspaceObj { }; template -static void print_on_maybe_null(outputStream* st, const char* str, const M* m) { +inline void print_on_maybe_null(outputStream* st, const char* str, const M* m) { if (nullptr != m) { st->print_raw(str); m->print_value_on(st); diff --git a/src/hotspot/share/oops/objArrayKlass.hpp b/src/hotspot/share/oops/objArrayKlass.hpp index 16b3170fdda2..934eedcbf6a6 100644 --- a/src/hotspot/share/oops/objArrayKlass.hpp +++ b/src/hotspot/share/oops/objArrayKlass.hpp @@ -50,7 +50,6 @@ class ObjArrayKlass : public ArrayKlass { static ArrayDescription array_layout_selection(Klass* element, ArrayProperties properties); ObjArrayKlass* allocate_klass_from_description(ArrayDescription ad, TRAPS); - ObjArrayKlass* klass_from_description(ArrayDescription adesc, TRAPS); inline ObjArrayKlass* next_refined_array_klass_acquire() const; inline void release_set_next_refined_klass(ObjArrayKlass* ak); @@ -74,6 +73,7 @@ class ObjArrayKlass : public ArrayKlass { Klass* element_klass() const { return _element_klass; } ObjArrayKlass* klass_with_properties(ArrayProperties props, TRAPS); + ObjArrayKlass* klass_from_description(ArrayDescription adesc, TRAPS); ObjArrayKlass* next_refined_array_klass() const { return _next_refined_array_klass; } bool find_refined_array_klass(ObjArrayKlass* k); diff --git a/src/hotspot/share/oops/oopsHierarchy.hpp b/src/hotspot/share/oops/oopsHierarchy.hpp index 03c6850f00b0..83f2ad866138 100644 --- a/src/hotspot/share/oops/oopsHierarchy.hpp +++ b/src/hotspot/share/oops/oopsHierarchy.hpp @@ -82,6 +82,10 @@ using CheckOopFunctionPointer = void(*)(oopDesc*); extern CheckOopFunctionPointer check_oop_function; class oop { +public: + using DescType = oopDesc; + +private: oopDesc* _o; void register_oop(); @@ -125,41 +129,54 @@ struct PrimitiveConversions::Translate : public std::true_type { static Value recover(Decayed x) { return oop(x); } }; -#define DEF_OOP(type) \ - class type##OopDesc; \ - class type##Oop : public oop { \ - public: \ - type##Oop() : oop() {} \ - type##Oop(const type##Oop& o) : oop(o) {} \ - type##Oop(const oop& o) : oop(o) {} \ - type##Oop(type##OopDesc* o) : oop((oopDesc*)o) {} \ - operator type##OopDesc* () const { return (type##OopDesc*)obj(); } \ - type##OopDesc* operator->() const { \ - return (type##OopDesc*)obj(); \ - } \ - type##Oop& operator=(const type##Oop& o) { \ - oop::operator=(o); \ - return *this; \ - } \ - }; \ +#define DEF_OOP_IMPL(OopType, OopDescType, BaseOopType) \ + class OopDescType; \ + class OopType : public BaseOopType { \ + public: \ + using DescType = OopDescType; \ + OopType() : BaseOopType() {} \ + OopType(std::nullptr_t) : BaseOopType() {} \ + OopType(const OopType& o) : BaseOopType(o) {} \ + explicit OopType(const oop& o) : BaseOopType(o) {} \ + OopType(DescType* o) : BaseOopType((BaseOopType::DescType*)o) {} \ + operator DescType*() const { return (DescType*)obj(); } \ + DescType* operator->() const { return (DescType*)obj(); } \ + OopType& operator=(std::nullptr_t) { \ + BaseOopType::operator=(nullptr); \ + return *this; \ + } \ + OopType& operator=(const OopType& o) { \ + BaseOopType::operator=(o); \ + return *this; \ + } \ + OopType& operator=(const oop& o) = delete; \ + }; \ \ - template<> \ - struct PrimitiveConversions::Translate : public std::true_type { \ - typedef type##Oop Value; \ - typedef type##OopDesc* Decayed; \ + template <> \ + struct PrimitiveConversions::Translate : public std::true_type { \ + typedef OopType Value; \ + typedef OopType::DescType* Decayed; \ \ - static Decayed decay(Value x) { return (type##OopDesc*)x.obj(); } \ - static Value recover(Decayed x) { return type##Oop(x); } \ - }; + static Decayed decay(Value x) { return (OopType::DescType*)x.obj(); } \ + static Value recover(Decayed x) { return OopType(x); } \ + }; + +#define DEF_OOP_BASE(type, base) \ + DEF_OOP_IMPL(type##Oop, type##OopDesc, base##Oop) +#define DEF_OOP(type) DEF_OOP_IMPL(type##Oop, type##OopDesc, oop) DEF_OOP(instance); -DEF_OOP(inline); -DEF_OOP(stackChunk); +DEF_OOP_BASE(inline, instance); +DEF_OOP_BASE(stackChunk, instance); DEF_OOP(array); -DEF_OOP(objArray); -DEF_OOP(typeArray); -DEF_OOP(flatArray); -DEF_OOP(refArray); +DEF_OOP_BASE(objArray, array); +DEF_OOP_BASE(typeArray, array); +DEF_OOP_BASE(flatArray, objArray); +DEF_OOP_BASE(refArray, objArray); + +#undef DEF_OOP_IMPL +#undef DEF_OOP_BASE +#undef DEF_OOP #endif // CHECK_UNHANDLED_OOPS diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 4ac91427eef8..31fcc46e0cb4 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -697,6 +697,13 @@ const Type *AddLNode::add_ring( const Type *t0, const Type *t1 ) const { } +//============================================================================= +//------------------------------Ideal------------------------------------------ +Node* AddFPNode::Ideal(PhaseGVN* phase, bool can_reshape) { + // Floating-point addition is commutative but not associative. + return commute(phase, this) ? this : nullptr; +} + //============================================================================= //------------------------------add_of_identity-------------------------------- // Check for addition of the identity @@ -724,12 +731,6 @@ const Type *AddFNode::add_ring( const Type *t0, const Type *t1 ) const { return TypeF::make( t0->getf() + t1->getf() ); } -//------------------------------Ideal------------------------------------------ -Node *AddFNode::Ideal(PhaseGVN *phase, bool can_reshape) { - // Floating point additions are not associative because of boundary conditions (infinity) - return commute(phase, this) ? this : nullptr; -} - //============================================================================= //------------------------------add_of_identity-------------------------------- // Check for addition of the identity @@ -773,12 +774,6 @@ const Type *AddDNode::add_ring( const Type *t0, const Type *t1 ) const { return TypeD::make( t0->getd() + t1->getd() ); } -//------------------------------Ideal------------------------------------------ -Node *AddDNode::Ideal(PhaseGVN *phase, bool can_reshape) { - // Floating point additions are not associative because of boundary conditions (infinity) - return commute(phase, this) ? this : nullptr; -} - //============================================================================= //------------------------------Identity--------------------------------------- @@ -857,11 +852,14 @@ const Type *AddPNode::bottom_type() const { if (in(Address) == nullptr) return TypePtr::BOTTOM; const TypePtr *tp = in(Address)->bottom_type()->isa_ptr(); if( !tp ) return Type::TOP; // TOP input means TOP output - assert( in(Offset)->Opcode() != Op_ConP, "" ); - const Type *t = in(Offset)->bottom_type(); - if( t == Type::TOP ) - return tp->add_offset(Type::OffsetTop); - const TypeX *tx = t->is_intptr_t(); + + assert(in(Offset)->Opcode() != Op_ConP, ""); + const Type* t = in(Offset)->bottom_type(); + if (t == Type::TOP) { + return Type::TOP; + } + + const TypeX* tx = t->is_intptr_t(); intptr_t txoffset = Type::OffsetBot; if (tx->is_con()) { // Left input is an add of a constant? txoffset = tx->get_con(); diff --git a/src/hotspot/share/opto/addnode.hpp b/src/hotspot/share/opto/addnode.hpp index 793eff8dd5d1..1d78a3e5a672 100644 --- a/src/hotspot/share/opto/addnode.hpp +++ b/src/hotspot/share/opto/addnode.hpp @@ -166,45 +166,51 @@ class AddLNode : public AddNode { virtual uint ideal_reg() const { return Op_RegL; } }; +//------------------------------AddFPNode-------------------------------------- +// Add 2 floats, doubles, or half-precision floats +class AddFPNode : public AddNode { +protected: + AddFPNode(Node* in1, Node* in2) : AddNode(in1, in2) {} +public: + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); + virtual Node* Identity(PhaseGVN* phase) { return this; } +}; + //------------------------------AddFNode--------------------------------------- // Add 2 floats -class AddFNode : public AddNode { +class AddFNode : public AddFPNode { public: - AddFNode( Node *in1, Node *in2 ) : AddNode(in1,in2) {} + AddFNode( Node *in1, Node *in2 ) : AddFPNode(in1,in2) {} virtual int Opcode() const; - virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual const Type *add_of_identity( const Type *t1, const Type *t2 ) const; virtual const Type *add_ring( const Type *, const Type * ) const; virtual const Type *add_id() const { return TypeF::ZERO; } virtual const Type *bottom_type() const { return Type::FLOAT; } int max_opcode() const { return Op_MaxF; } int min_opcode() const { return Op_MinF; } - virtual Node* Identity(PhaseGVN* phase) { return this; } virtual uint ideal_reg() const { return Op_RegF; } }; //------------------------------AddDNode--------------------------------------- // Add 2 doubles -class AddDNode : public AddNode { +class AddDNode : public AddFPNode { public: - AddDNode( Node *in1, Node *in2 ) : AddNode(in1,in2) {} + AddDNode( Node *in1, Node *in2 ) : AddFPNode(in1,in2) {} virtual int Opcode() const; - virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual const Type *add_of_identity( const Type *t1, const Type *t2 ) const; virtual const Type *add_ring( const Type *, const Type * ) const; virtual const Type *add_id() const { return TypeD::ZERO; } virtual const Type *bottom_type() const { return Type::DOUBLE; } int max_opcode() const { return Op_MaxD; } int min_opcode() const { return Op_MinD; } - virtual Node* Identity(PhaseGVN* phase) { return this; } virtual uint ideal_reg() const { return Op_RegD; } }; //------------------------------AddHFNode--------------------------------------- // Add 2 half-precision floats -class AddHFNode : public AddNode { +class AddHFNode : public AddFPNode { public: - AddHFNode(Node* in1, Node* in2) : AddNode(in1,in2) {} + AddHFNode(Node* in1, Node* in2) : AddFPNode(in1, in2) {} virtual int Opcode() const; virtual const Type* add_of_identity(const Type* t1, const Type* t2) const; virtual const Type* add_ring(const Type*, const Type*) const; @@ -212,7 +218,6 @@ class AddHFNode : public AddNode { virtual const Type* bottom_type() const { return Type::HALF_FLOAT; } int max_opcode() const { return Op_MaxHF; } int min_opcode() const { return Op_MinHF; } - virtual Node* Identity(PhaseGVN* phase) { return this; } virtual uint ideal_reg() const { return Op_RegF; } }; diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index e1216b1e9efc..33799f25d4f4 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -726,11 +726,14 @@ "Re-process nodes that could benefit from a deep revisit after " \ "the IGVN worklist drains") \ \ + product(uint, MacroExpansionCleanupCount, 16, DIAGNOSTIC, \ + "Run IGVN to clean the graph after this many macro nodes are " \ + "expanded or when we approach the max live node limit.") \ + range(1, 100) \ + \ develop(uint, VerifyIterativeGVN, 0, \ - "Verify Iterative Global Value Numbering =GFEDCBA, with:" \ - " G: verify Node::Identity return an existing node" \ - " F: verify Node::Ideal does not return nullptr if the node" \ - "hash has changed" \ + "Verify Iterative Global Value Numbering =FEDCBA, with:" \ + " F: verify IGVN method return invariants" \ " E: verify node specific invariants" \ " D: verify Node::Identity did not miss opportunities" \ " C: verify Node::Ideal did not miss opportunities" \ diff --git a/src/hotspot/share/opto/castnode.cpp b/src/hotspot/share/opto/castnode.cpp index c10f5b2eb300..472780bb96dd 100644 --- a/src/hotspot/share/opto/castnode.cpp +++ b/src/hotspot/share/opto/castnode.cpp @@ -204,6 +204,11 @@ TypeNode* ConstraintCastNode::dominating_cast(PhaseGVN* gvn, PhaseTransform* pt) bool ConstraintCastNode::higher_equal_types(PhaseGVN* phase, const Node* other) const { const Type* t = phase->type(other); + if ((t->isa_rawptr() && (type()->isa_oopptr() || type()->isa_klassptr())) || + ((t->isa_oopptr() || t->isa_klassptr()) && type()->isa_rawptr())) { + assert(is_CheckCastPP(), "unrelated types from %s", Name()); + return false; + } if (!t->higher_equal_speculative(type())) { return false; } @@ -224,6 +229,10 @@ Node* ConstraintCastNode::pin_node_under_control_impl() const { Node* ConstraintCastNode::ideal_cast_of_inline_type_node(PhaseGVN* phase) { InlineTypeNode* vt = in(1)->as_InlineType(); + if (type()->isa_rawptr() != nullptr) { + return nullptr; + } + const Type* join = vt->type()->filter(type()); if (join == Type::TOP) { // Do not push a dead Cast since its type can be unrelated diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index e12eec300899..fbecaba01a50 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -1442,7 +1442,7 @@ const TypePtr *Compile::flatten_alias_type( const TypePtr *tj ) const { } // Remove size and stability - const TypeAry* normalized_ary = TypeAry::make(ta->elem(), TypeInt::POS, false, ta->is_flat(), ta->is_not_flat(), ta->is_not_null_free(), ta->is_atomic()); + const TypeAry* normalized_ary = TypeAry::make(ta->elem(), TypeInt::POS, false, ta->is_flat(), ta->is_not_flat(), ta->is_null_free(), ta->is_not_null_free(), ta->is_atomic()); // Remove ptr, const_oop, and offset if (ta->elem() == Type::BOTTOM) { // Bottom array (meet of int[] and byte[] for example), accesses to it will be done with @@ -1465,7 +1465,7 @@ const TypePtr *Compile::flatten_alias_type( const TypePtr *tj ) const { // All arrays of references share the same slice if (!ta->is_flat() && ta->elem()->make_oopptr() != nullptr) { - const TypeAry* tary = TypeAry::make(TypeInstPtr::BOTTOM, TypeInt::POS, false, false, true, true, true); + const TypeAry* tary = TypeAry::make(TypeInstPtr::BOTTOM, TypeInt::POS, false, false, true, false, true, true); tj = ta = TypeAryPtr::make(TypePtr::BotPTR, nullptr, tary, nullptr, false, Type::Offset::bottom); } @@ -1726,7 +1726,7 @@ Compile::AliasType* Compile::find_alias_type(const TypePtr* adr_type, bool no_cr Type::str(adr_type), Type::str(flat), Type::str(flatten_alias_type(flat))); assert(flat != TypePtr::BOTTOM, "cannot alias-analyze an untyped ptr: adr_type = %s", Type::str(adr_type)); - if (flat->isa_oopptr() && !flat->isa_klassptr()) { + if (flat->isa_instptr()) { const TypeOopPtr* foop = flat->is_oopptr(); // Scalarizable allocations have exact klass always. bool exact = !foop->klass_is_exact() || foop->is_known_instance(); @@ -2307,15 +2307,17 @@ void Compile::adjust_flat_array_access_aliases(PhaseIterGVN& igvn) { } } -#ifdef ASSERT + int start_alias = num_alias_types(); // Start of new aliases for (uint i = 0; i < memnodes.size(); i++) { Node* m = memnodes.at(i); const TypePtr* adr_type = m->adr_type(); +#ifdef ASSERT m->as_Mem()->set_adr_type(adr_type); - } #endif // ASSERT + // This has the side effect of allocating new aliases for flat array accesses + get_alias_index(adr_type); + } - int start_alias = num_alias_types(); // Start of new aliases Node_Stack stack(0); #ifdef ASSERT VectorSet seen(Thread::current()->resource_area()); @@ -3320,7 +3322,7 @@ void Compile::Optimize() { return; } print_method(PHASE_AFTER_MACRO_ELIMINATION, 2); - if (mex.expand_macro_nodes()) { + if (!mex.expand_macro_nodes()) { assert(failing(), "must bail out w/ explicit message"); return; } @@ -3425,8 +3427,12 @@ bool Compile::has_vbox_nodes() { //---------------------------- Bitwise operation packing optimization --------------------------- static bool is_vector_unary_bitwise_op(Node* n) { - return n->Opcode() == Op_XorV && - VectorNode::is_vector_bitwise_not_pattern(n); + // A masked XorV not-pattern is NOT unary: on inactive lanes the masked + // operation keeps its first operand, so both inputs must be preserved (and + // their order matters, since in(1) becomes the masked MacroLogicV + // passthrough). Only an unmasked not-pattern is genuinely unary. +return VectorNode::is_vector_bitwise_not_pattern(n) && + !n->is_predicated_vector(); } static bool is_vector_binary_bitwise_op(Node* n) { @@ -3469,7 +3475,7 @@ static uint collect_unique_inputs(Node* n, Unique_Node_List& inputs) { uint cnt = 0; if (is_vector_bitwise_op(n)) { uint inp_cnt = n->is_predicated_vector() ? n->req()-1 : n->req(); - if (VectorNode::is_vector_bitwise_not_pattern(n)) { + if (is_vector_unary_bitwise_op(n)) { assert(n->req() == (n->is_predicated_vector() ? 4 : 3), "must have 2 data inputs"); Node* opnd = VectorNode::is_all_ones_vector(n->in(1)) ? n->in(2) : n->in(1); if (!inputs.member(opnd)) { @@ -3630,7 +3636,7 @@ uint Compile::compute_truth_table(Unique_Node_List& partition, Unique_Node_List& res = func1 & func2; break; case Op_XorV: - if (VectorNode::is_vector_bitwise_not_pattern(n)) { + if (is_vector_unary_bitwise_op(n)) { assert(func2 == 0 && func3 == 0, "not unary"); res = (~func1) & 0xFF; } else { @@ -6066,10 +6072,10 @@ void Compile::igv_print_graph_to_network(const char* name, GrowableArraytype(value)->higher_equal(type)) { + if (type->base() == Type::Int && phase->type(value)->higher_equal(type)) { return value; } + Node* result = nullptr; if (bt == T_BYTE) { result = phase->transform(new LShiftINode(value, phase->intcon(24))); diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index f3189b6ba40a..f931deaad344 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -104,7 +104,7 @@ class InlineTypeNode; class nmethod; class Node_Stack; struct Final_Reshape_Counts; -class VerifyMeetResult; +class VerifyMeetJoinResult; enum LoopOptsMode { LoopOptsDefault, @@ -1392,7 +1392,7 @@ class Compile : public Phase { bool needs_clinit_barrier(ciInstanceKlass* ik, ciMethod* accessing_method); #ifdef ASSERT - VerifyMeetResult* _type_verify; + VerifyMeetJoinResult* _type_verify; void set_exception_backedge() { _exception_backedge = true; } bool has_exception_backedge() const { return _exception_backedge; } #endif diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 957b79caf1fe..20c50a074442 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -169,10 +169,11 @@ bool ConnectionGraph::compute_escape() { java_objects_worklist.append(phantom_obj); for( uint next = 0; next < ideal_nodes.size(); ++next ) { Node* n = ideal_nodes.at(next); - if ((n->Opcode() == Op_LoadX || n->Opcode() == Op_StoreX) && + if (n->is_Mem() && !n->in(MemNode::Address)->is_AddP() && _igvn->type(n->in(MemNode::Address))->isa_oopptr()) { - // Load/Store at mark work address is at offset 0 so has no AddP which confuses EA + // EA expects on-heap memory addresses to be represented by an AddP. An AddP with a zero + // offset can be optimized to its oop base, so recreate it. Node* addp = AddPNode::make_with_base(n->in(MemNode::Address), n->in(MemNode::Address), _igvn->MakeConX(0)); _igvn->register_new_node_with_optimizer(addp); _igvn->replace_input_of(n, MemNode::Address, addp); @@ -4517,10 +4518,8 @@ void ConnectionGraph::move_inst_mem(Node* n, Unique_Node_List& orig_phis) { if (n != mmem->memory_at(general_idx) || alias_idx == general_idx) { continue; // Nothing to do } - // Replace previous general reference to mem node. - uint orig_uniq = C->unique(); - Node* m = find_inst_mem(n, general_idx, orig_phis); - assert(orig_uniq == C->unique(), "no new nodes"); + // Replace previous general reference to mem node and assert no new node is created. + Node* m = find_inst_mem_assert_no_new_node(n, general_idx, orig_phis); mmem->set_memory_at(general_idx, m); --imax; --i; @@ -4537,15 +4536,26 @@ void ConnectionGraph::move_inst_mem(Node* n, Unique_Node_List& orig_phis) { alias_idx == general_idx) { continue; // Nothing to do } - // Move to general memory slice. - uint orig_uniq = C->unique(); - Node* m = find_inst_mem(n, general_idx, orig_phis); - assert(orig_uniq == C->unique(), "no new nodes"); + // Move to general memory slice and assert no new node is created. + Node* m = find_inst_mem_assert_no_new_node(n, general_idx, orig_phis); igvn->hash_delete(use); imax -= use->replace_edge(n, m, igvn); igvn->hash_insert(use); record_for_optimizer(use); --i; + } else if (use->is_memory_access_intrinsic()) { + if (alias_idx == general_idx) { + continue; + } + if (use->in(MemNode::Memory) == n) { + // Move to general memory slice and assert no new node is created. + Node* m = find_inst_mem_assert_no_new_node(n, general_idx, orig_phis); + igvn->hash_delete(use); + imax -= use->replace_edge(n, m, igvn); + igvn->hash_insert(use); + record_for_optimizer(use); + --i; + } #ifdef ASSERT } else if (use->is_Mem()) { // Memory nodes should have new memory input. @@ -4789,6 +4799,13 @@ Node* ConnectionGraph::find_inst_mem(Node* orig_mem, int alias_idx, Unique_Node_ return result; } +Node* ConnectionGraph::find_inst_mem_assert_no_new_node(Node* orig_mem, int alias_idx, Unique_Node_List& orig_phis) { + uint orig_uniq = _compile->unique(); + Node* result = find_inst_mem(orig_mem, alias_idx, orig_phis); + assert(orig_uniq == _compile->unique(), "no new nodes"); + return result; +} + // // Convert the types of non-escaped object to instance types where possible, // propagate the new type information through the graph, and update memory @@ -5193,12 +5210,8 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, // They overwrite memory edge corresponding to destination array, memnode_worklist.push(use); } else if (!(op == Op_CmpP || op == Op_Conv2B || - op == Op_CastP2X || - op == Op_FastLock || op == Op_AryEq || - op == Op_StrComp || op == Op_CountPositives || - op == Op_StrCompressedCopy || op == Op_StrInflatedCopy || - op == Op_StrEquals || op == Op_VectorizedHashCode || - op == Op_StrIndexOf || op == Op_StrIndexOfChar || + op == Op_CastP2X || op == Op_FastLock || + use->is_memory_access_intrinsic() || op == Op_SubTypeCheck || op == Op_InlineType || op == Op_FlatArrayCheck || op == Op_ReinterpretS2HF || op == Op_ReachabilityFence || @@ -5389,9 +5402,7 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, // They overwrite memory edge corresponding to destination array, memnode_worklist.push(use); } else if (!(BarrierSet::barrier_set()->barrier_set_c2()->is_gc_barrier_node(use) || - op == Op_AryEq || op == Op_StrComp || op == Op_CountPositives || - op == Op_StrCompressedCopy || op == Op_StrInflatedCopy || op == Op_VectorizedHashCode || - op == Op_StrEquals || op == Op_StrIndexOf || op == Op_StrIndexOfChar || op == Op_FlatArrayCheck)) { + use->is_memory_access_intrinsic() || op == Op_FlatArrayCheck)) { n->dump(); use->dump(); assert(false, "EA: missing memory path"); diff --git a/src/hotspot/share/opto/escape.hpp b/src/hotspot/share/opto/escape.hpp index 7303e20195e8..44f2d6aaa694 100644 --- a/src/hotspot/share/opto/escape.hpp +++ b/src/hotspot/share/opto/escape.hpp @@ -566,6 +566,7 @@ class ConnectionGraph: public ArenaObj { void move_inst_mem(Node* n, Unique_Node_List& orig_phis); bool flat_access_aliases_with(Node* flat_access, const TypeOopPtr *toop); Node* find_inst_mem(Node* mem, int alias_idx, Unique_Node_List& orig_phi_worklist, uint rec_depth = 0); + Node* find_inst_mem_assert_no_new_node(Node* mem, int alias_idx, Unique_Node_List& orig_phi_worklist); Node* step_through_mergemem(MergeMemNode *mmem, int alias_idx, const TypeOopPtr *toop); Node_Array _node_map; // used for bookkeeping during type splitting diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index a2da4efa2a1b..81b9fa501665 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -59,6 +59,7 @@ #include "runtime/sharedRuntime.hpp" #include "runtime/stubRoutines.hpp" #include "utilities/bitMap.inline.hpp" +#include "utilities/globalDefinitions.hpp" #include "utilities/growableArray.hpp" #include "utilities/powerOfTwo.hpp" @@ -3976,6 +3977,15 @@ Node* GraphKit::flat_array_test(Node* array_or_klass, bool flat) { } Node* GraphKit::null_free_array_test(Node* array, bool null_free) { + const TypeAryPtr* array_type = gvn().type(array)->isa_aryptr(); + if (array_type != nullptr) { + if (array_type->is_null_free()) { + return intcon(null_free); + } else if (array_type->is_not_null_free()) { + return intcon(!null_free); + } + } + return mark_word_test(array, markWord::null_free_array_bit_in_place, null_free); } @@ -3988,6 +3998,15 @@ Node* GraphKit::null_free_atomic_array_test(Node* array, ciInlineKlass* vk) { return intcon(0); // Never atomic } + const TypeAryPtr* array_type = gvn().type(array)->isa_aryptr(); + if (array_type != nullptr) { + if (array_type->is_atomic()) { + return intcon(1); + } else if (array_type->klass_is_exact() && !array_type->is_atomic()) { + return intcon(0); + } + } + Node* array_klass = load_object_klass(array); int layout_kind_offset = in_bytes(FlatArrayKlass::layout_kind_offset()); Node* layout_kind_addr = basic_plus_adr(top(), array_klass, layout_kind_offset); @@ -4230,33 +4249,19 @@ void GraphKit::shared_unlock(Node* box, Node* obj) { Node* GraphKit::get_layout_helper(Node* klass_node, jint& constant_value) { const TypeKlassPtr* klass_t = _gvn.type(klass_node)->isa_klassptr(); if (!StressReflectiveCode && klass_t != nullptr) { - bool xklass = klass_t->klass_is_exact(); - bool can_be_flat = false; - const TypeAryPtr* ary_type = klass_t->as_exact_instance_type()->isa_aryptr(); - if (UseArrayFlattening && !xklass && ary_type != nullptr) { - // Don't constant fold if the runtime type might be a flat array but the static type is not. - const TypeOopPtr* elem = ary_type->elem()->make_oopptr(); - can_be_flat = ary_type->can_be_inline_array() && (!elem->is_inlinetypeptr() || elem->inline_klass()->maybe_flat_in_array()); + if (klass_t->klass_is_exact()) { + constant_value = klass_t->exact_klass()->layout_helper(); + return nullptr; } - if (!can_be_flat && (xklass || (klass_t->isa_aryklassptr() && klass_t->is_aryklassptr()->elem() != Type::BOTTOM))) { - jint lhelper; - if (klass_t->is_flat()) { - lhelper = ary_type->flat_layout_helper(); - } else if (klass_t->isa_aryklassptr()) { - BasicType elem = ary_type->elem()->array_element_basic_type(); - if (is_reference_type(elem, true)) { - elem = T_OBJECT; - } - lhelper = Klass::array_layout_helper(elem); - } else { - lhelper = klass_t->is_instklassptr()->exact_klass()->layout_helper(); - } - if (lhelper != Klass::_lh_neutral_value) { - constant_value = lhelper; - return (Node*) nullptr; - } + + const TypeAryKlassPtr* aryklass_t = klass_t->isa_aryklassptr(); + if (aryklass_t != nullptr && aryklass_t->elem()->isa_klassptr() != nullptr && aryklass_t->is_not_flat()) { + // If we know that the array cannot be flat, then the layout_helper value is known + constant_value = Klass::array_layout_helper(T_OBJECT); + return nullptr; } } + constant_value = Klass::_lh_neutral_value; // put in a known value Node* lhp = off_heap_plus_addr(klass_node, in_bytes(Klass::layout_helper_offset())); return make_load(nullptr, lhp, TypeInt::INT, T_INT, MemNode::unordered); @@ -4436,13 +4441,6 @@ Node* GraphKit::new_instance(Node* klass_node, (*return_size_val) = size; } - // This is a precise notnull oop of the klass. - // (Actually, it need not be precise if this is a reflective allocation.) - // It's what we cast the result to. - const TypeKlassPtr* tklass = _gvn.type(klass_node)->isa_klassptr(); - if (!tklass) tklass = TypeInstKlassPtr::OBJECT; - const TypeOopPtr* oop_type = tklass->as_exact_instance_type(); - // Now generate allocation code // The entire memory state is needed for slow path of the allocation @@ -4455,6 +4453,20 @@ Node* GraphKit::new_instance(Node* klass_node, size, klass_node, initial_slow_test, inline_type_node); + // This is a precise notnull oop of the klass. + // (Actually, it need not be precise if this is a reflective allocation.) + // It's what we cast the result to. + const TypeInstKlassPtr* tklass = _gvn.type(klass_node)->isa_instklassptr(); + const TypeOopPtr* oop_type; + if (tklass == nullptr) { + oop_type = TypeInstPtr::BOTTOM; + } else if (tklass->klass_is_exact() && (tklass->instance_klass()->is_abstract() || !tklass->interfaces()->eq(tklass->instance_klass()))) { + // tklass may be an abstract class or an interface, for which we cannot make a TypeOopPtr + oop_type = TypeInstPtr::BOTTOM; + } else { + oop_type = tklass->as_exact_instance_type(); + } + return set_output_for_allocation(alloc, oop_type, deoptimize_on_exception); } @@ -4816,7 +4828,7 @@ Node* GraphKit::load_String_value(Node* str, bool set_ctrl) { false, nullptr, Type::Offset(0)); const TypePtr* value_field_type = string_type->add_offset(value_offset); const TypeAryPtr* value_type = TypeAryPtr::make(TypePtr::BotPTR, - TypeAry::make(TypeInt::BYTE, TypeInt::POS, false, false, true, true, true), + TypeAry::make(TypeInt::BYTE, TypeInt::POS, false, false, true, false, true, true), ciTypeArrayKlass::make(T_BYTE), true, Type::Offset(0)); Node* p = basic_plus_adr(str, str, value_offset); Node* load = access_load_at(str, p, value_field_type, value_type, T_OBJECT, diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 36d69f18041b..bbe3d184c4de 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -2976,10 +2976,17 @@ bool LibraryCallKit::inline_unsafe_load_store(const BasicType type, const LoadSt Compile::AliasType* alias_type = C->alias_type(adr_type); BasicType bt = alias_type->basic_type(); - if (bt != T_ILLEGAL && - (is_reference_type(bt) != (type == T_OBJECT))) { - // Don't intrinsify mismatched object accesses. - return false; + if (bt != T_ILLEGAL) { + if (adr_type->isa_aryptr() && adr_type->is_flat()) { + // mismatched access to a flat array element: + // type=T_OBJECT doesn't make sense (and breaks Compile::adjust_flat_array_access_aliases()). + // Some other type may need to be supported so this may need to be relaxed. + return false; + } + if (is_reference_type(bt) != (type == T_OBJECT)) { + // Don't intrinsify mismatched object accesses. + return false; + } } old_state.discard(); @@ -4240,7 +4247,7 @@ bool LibraryCallKit::inline_native_setCurrentThread() { const Type* LibraryCallKit::scopedValueCache_type() { ciKlass* objects_klass = ciObjArrayKlass::make(env()->Object_klass()); const TypeOopPtr* etype = TypeOopPtr::make_from_klass(env()->Object_klass()); - const TypeAry* arr0 = TypeAry::make(etype, TypeInt::POS, /* stable= */ false, /* flat= */ false, /* not_flat= */ true, /* not_null_free= */ true, true); + const TypeAry* arr0 = TypeAry::make(etype, TypeInt::POS, /* stable= */ false, /* flat= */ false, /* not_flat= */ true, /*null_free=*/ false, /* not_null_free= */ true, true); // Because we create the scopedValue cache lazily we have to make the // type of the result BotPTR. diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index b5329c861769..ccf2cb0382ff 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -3199,6 +3199,17 @@ void PhaseMacroExpand::refine_strip_mined_loop_macro_nodes() { } } +// Clean up the graph so we're less likely to hit the maximum node limit +static bool cleanup_graph(PhaseIterGVN& igvn) { + igvn.set_delay_transform(false); + igvn.optimize(); + if (igvn.C->failing()) { + return false; + } + igvn.set_delay_transform(true); + return true; +} + //---------------------------eliminate_macro_nodes---------------------- // Eliminate scalar replaced allocations and associated locks. void PhaseMacroExpand::eliminate_macro_nodes(bool eliminate_locks) { @@ -3312,12 +3323,9 @@ void PhaseMacroExpand::eliminate_macro_nodes(bool eliminate_locks) { // other macro nodes can remove all these safepoints, allowing the allocation to be removed. // Hence after igvn we retry removing macro nodes if some progress that has been made in this // iteration. - _igvn.set_delay_transform(false); - _igvn.optimize(); - if (C->failing()) { - return; + if (!cleanup_graph(_igvn)) { + return; // failing } - _igvn.set_delay_transform(true); if (!progress) { break; @@ -3407,21 +3415,18 @@ void PhaseMacroExpand::eliminate_opaque_looplimit_macro_nodes() { } //------------------------------expand_macro_nodes---------------------- -// Returns true if a failure occurred. +// Returns false if a failure occurred. bool PhaseMacroExpand::expand_macro_nodes() { if (StressMacroExpansion) { C->shuffle_macro_nodes(); } - // Clean up the graph so we're less likely to hit the maximum node - // limit - _igvn.set_delay_transform(false); - _igvn.optimize(); - if (C->failing()) return true; - _igvn.set_delay_transform(true); - + // Clean up after eliminate_opaque_looplimit_macro_nodes() + if (!cleanup_graph(_igvn)) { + return false; // failing + } - // Because we run IGVN after each expansion, some macro nodes may go + // Because we run IGVN after set of expansions, some macro nodes may go // dead and be removed from the list as we iterate over it. Move // Allocate nodes (processed in a second pass) at the beginning of // the list and then iterate from the last element of the list until @@ -3429,26 +3434,34 @@ bool PhaseMacroExpand::expand_macro_nodes() { // the list due to nodes going dead. C->sort_macro_nodes(); - // expand arraycopy "macro" nodes first // For ReduceBulkZeroing, we must first process all arraycopy nodes - // before the allocate nodes are expanded. + // before the allocate nodes are expanded. Sorting macro nodes list + // enforces it. + + // Worst case is a macro node gets expanded into about 200 nodes. + // Allow 50% more for optimization. + static const uint macro_expansion_estimate = 300; + const uint macro_expansion_margin = macro_expansion_estimate * MacroExpansionCleanupCount; + const uint macro_expansion_limit = C->max_node_limit() > macro_expansion_margin ? + C->max_node_limit() - macro_expansion_margin : 0; + uint pending_expansions_to_cleanup = 0; + while (C->macro_count() > 0) { int macro_count = C->macro_count(); - Node * n = C->macro_node(macro_count-1); + Node* n = C->macro_node(macro_count-1); assert(n->is_macro(), "only macro nodes expected here"); if (_igvn.type(n) == Type::TOP || (n->in(0) != nullptr && n->in(0)->is_top())) { // node is unreachable, so don't try to expand it C->remove_macro_node(n); continue; } + // Reached Allocate nodes - jump to second pass to process them. if (n->is_Allocate()) { break; } // Make sure expansion will not cause node limit to be exceeded. - // Worst case is a macro node gets expanded into about 200 nodes. - // Allow 50% more for optimization. - if (C->check_node_count(300, "out of nodes before macro expansion")) { - return true; + if (C->check_node_count(macro_expansion_estimate, "out of nodes before macro expansion")) { + return false; } DEBUG_ONLY(int old_macro_count = C->macro_count();) @@ -3488,22 +3501,35 @@ bool PhaseMacroExpand::expand_macro_nodes() { } } assert(C->macro_count() == (old_macro_count - 1), "expansion must have deleted one node from macro list"); - if (C->failing()) return true; + if (C->failing()) { + return false; + } C->print_method(PHASE_AFTER_MACRO_EXPANSION_STEP, 5, n); - // Clean up the graph so we're less likely to hit the maximum node - // limit - _igvn.set_delay_transform(false); - _igvn.optimize(); - if (C->failing()) return true; - _igvn.set_delay_transform(true); + pending_expansions_to_cleanup++; + if (pending_expansions_to_cleanup < MacroExpansionCleanupCount && + C->live_nodes() < macro_expansion_limit) { + // skip clean up + continue; + } + if (!cleanup_graph(_igvn)) { + return false; // failing + } + pending_expansions_to_cleanup = 0; + } + + // Cleanup graph before expanding Allocate nodes. + if (pending_expansions_to_cleanup > 0) { + if (!cleanup_graph(_igvn)) { + return false; // failing + } + pending_expansions_to_cleanup = 0; } // All nodes except Allocate nodes are expanded now. There could be // new optimization opportunities (such as folding newly created // load from a just allocated object). Run IGVN. - // expand "macro" nodes // nodes are removed from the macro list as they are processed while (C->macro_count() > 0) { int macro_count = C->macro_count(); @@ -3515,10 +3541,8 @@ bool PhaseMacroExpand::expand_macro_nodes() { continue; } // Make sure expansion will not cause node limit to be exceeded. - // Worst case is a macro node gets expanded into about 200 nodes. - // Allow 50% more for optimization. - if (C->check_node_count(300, "out of nodes before macro expansion")) { - return true; + if (C->check_node_count(macro_expansion_estimate, "out of nodes before macro expansion")) { + return false; } switch (n->class_id()) { case Node::Class_Allocate: @@ -3531,19 +3555,29 @@ bool PhaseMacroExpand::expand_macro_nodes() { assert(false, "unknown node type in macro list"); } assert(C->macro_count() < macro_count, "must have deleted a node from macro list"); - if (C->failing()) return true; + if (C->failing()) { + return false; + } C->print_method(PHASE_AFTER_MACRO_EXPANSION_STEP, 5, n); - // Clean up the graph so we're less likely to hit the maximum node - // limit - _igvn.set_delay_transform(false); - _igvn.optimize(); - if (C->failing()) return true; - _igvn.set_delay_transform(true); + pending_expansions_to_cleanup++; + if (pending_expansions_to_cleanup < MacroExpansionCleanupCount && + C->live_nodes() < macro_expansion_limit) { + // skip clean up + continue; + } + if (!cleanup_graph(_igvn)) { + return false; // failing + } + pending_expansions_to_cleanup = 0; + } + if (pending_expansions_to_cleanup > 0) { + if (!cleanup_graph(_igvn)) { + return false; // failing + } } - _igvn.set_delay_transform(false); - return false; + return true; } #ifndef PRODUCT diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index d90d01175f75..dbd975200844 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -1249,10 +1249,21 @@ Node* LoadNode::can_see_arraycopy_value(Node* st, PhaseGVN* phase) const { Node* ctl = ac->in(0); Node* src = ac->in(ArrayCopyNode::Src); - if (!ac->as_ArrayCopy()->is_clonebasic() && !phase->type(src)->isa_aryptr()) { + const TypeAryPtr* dst_arytype = phase->type(ac->in(ArrayCopyNode::Dest))->isa_aryptr(); + const TypeAryPtr* src_arytype = phase->type(src)->isa_aryptr(); + if (src_arytype == nullptr && !ac->as_ArrayCopy()->is_clonebasic()) { return nullptr; } + if (src_arytype != nullptr && dst_arytype != nullptr) { + // Cannot transform if the layouts are different + bool may_transform = (src_arytype->is_not_flat() && dst_arytype->is_not_flat()) || + (src_arytype->klass_is_exact() && dst_arytype->klass_is_exact() && src_arytype->exact_klass() == dst_arytype->exact_klass()); + if (!may_transform) { + return nullptr; + } + } + // load depends on the tests that validate the arraycopy LoadNode* ld = clone_pinned(); Node* addp = in(MemNode::Address)->clone(); @@ -2337,6 +2348,10 @@ const Type* LoadNode::Value(PhaseGVN* phase) const { assert(off != Type::OffsetTop, "case covered by TypePtr::empty"); Compile* C = phase->C; + if (is_mismatched_access()) { + return _type; + } + // If load can see a previous constant store, use that. Node* value = can_see_stored_value_through_membars(mem, phase); if (value != nullptr && value->is_Con()) { diff --git a/src/hotspot/share/opto/output.cpp b/src/hotspot/share/opto/output.cpp index 8abe61f698fd..460c0c466779 100644 --- a/src/hotspot/share/opto/output.cpp +++ b/src/hotspot/share/opto/output.cpp @@ -23,6 +23,7 @@ */ #include "asm/assembler.inline.hpp" +#include "ci/ciFlatArrayKlass.hpp" #include "code/aotCodeCache.hpp" #include "code/compiledIC.hpp" #include "code/debugInfo.hpp" @@ -35,6 +36,7 @@ #include "gc/shared/c2/barrierSetC2.hpp" #include "gc/shared/gc_globals.hpp" #include "memory/allocation.hpp" +#include "oops/arrayKlass.hpp" #include "opto/ad.hpp" #include "opto/block.hpp" #include "opto/c2_MacroAssembler.hpp" @@ -745,6 +747,22 @@ void PhaseOutput::set_sv_for_object_node(GrowableArray *objs, objs->append(sv); } +static jint array_description_value(const TypeAryPtr* ary_type) { + ciArrayKlass* array_klass = ary_type->exact_klass()->as_array_klass(); + const bool is_element_inline = array_klass->element_klass()->is_inlinetype(); + ArrayProperties properties = ArrayProperties::Default() + .with_null_restricted(is_element_inline && array_klass->is_elem_null_free()) + .with_non_atomic(is_element_inline && !array_klass->is_elem_atomic()); + + LayoutKind layout_kind = LayoutKind::REFERENCE; + Klass::KlassKind kind = Klass::RefArrayKlassKind; + if (ary_type->is_flat()) { + layout_kind = array_klass->as_flat_array_klass()->layout_kind(); + kind = Klass::FlatArrayKlassKind; + } + return (jint)ArrayDescription(kind, properties, layout_kind).value(); +} + void PhaseOutput::FillLocArray( int idx, MachSafePointNode* sfpt, Node *local, GrowableArray *array, @@ -792,14 +810,7 @@ void PhaseOutput::FillLocArray( int idx, MachSafePointNode* sfpt, Node *local, } } if (cik->is_array_klass() && !cik->is_type_array_klass()) { - ciArrayKlass* ciak = cik->as_array_klass(); - const bool is_element_inline = ciak->element_klass()->is_inlinetype(); - - const ArrayProperties props = ArrayProperties::Default() - .with_null_restricted(is_element_inline && ciak->is_elem_null_free()) - .with_non_atomic(is_element_inline && !ciak->is_elem_atomic()); - - properties = new ConstantIntValue((jint)props.value()); + properties = new ConstantIntValue(array_description_value(t->is_aryptr())); } sv = new ObjectValue(spobj->_idx, new ConstantOopWriteValue(cik->java_mirror()->constant_encoding()), true, properties); @@ -1143,14 +1154,7 @@ void PhaseOutput::Process_OopMap_Node(MachNode *mach, int current_offset) { assert(!cik->is_inlinetype(), "Synchronization on value object?"); ScopeValue* properties = nullptr; if (cik->is_array_klass() && !cik->is_type_array_klass()) { - ciArrayKlass* ciak = cik->as_array_klass(); - const bool is_element_inline = ciak->element_klass()->is_inlinetype(); - - const ArrayProperties props = ArrayProperties::Default() - .with_null_restricted(is_element_inline && ciak->is_elem_null_free()) - .with_non_atomic(is_element_inline && !ciak->is_elem_atomic()); - - properties = new ConstantIntValue((jint)props.value()); + properties = new ConstantIntValue(array_description_value(t->is_aryptr())); } ObjectValue* sv = new ObjectValue(spobj->_idx, new ConstantOopWriteValue(cik->java_mirror()->constant_encoding()), true, properties); @@ -2885,6 +2889,7 @@ void Scheduling::anti_do_def( Block *b, Node *def, OptoReg::Name def_reg, int is // Yes, found a use/kill pinch-point pinch->set_req(0,nullptr); // pinch->replace_by(kill); // Move anti-dep edges up + _pinch_free_list.push(pinch); pinch = kill; _reg_node.map(def_reg,pinch); return; diff --git a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp index 9c194774421a..133744ee8bca 100644 --- a/src/hotspot/share/opto/parse2.cpp +++ b/src/hotspot/share/opto/parse2.cpp @@ -125,8 +125,16 @@ void Parse::array_load(BasicType bt) { if (element_ptr->is_inlinetypeptr()) { ciInlineKlass* vk = element_ptr->inline_klass(); Node* flat_array = cast_to_flat_array(array, vk); - Node* vt = InlineTypeNode::make_from_flat_array(this, vk, flat_array, array_index); - ideal.set(res, vt); + + // It may be the case that array is only known to be not flat when we try to cast it to a + // flat array. For example, array is a not-null-free array and vk does not have a + // nullable layout. + if (!flat_array->is_top()) { + Node* vt = InlineTypeNode::make_from_flat_array(this, vk, flat_array, array_index); + ideal.set(res, vt); + } else { + ideal.set(res, InlineTypeNode::make_null(gvn(), vk)); + } } else { // Element type is unknown, and thus we cannot statically determine the exact flat array layout. Emit a // runtime call to correctly load the inline type element from the flat array. @@ -290,17 +298,22 @@ void Parse::array_store(BasicType bt) { // Element type is known, cast and store to flat array layout. Node* flat_array = cast_to_flat_array(array, vk); - // Re-execute flat array store if buffering triggers deoptimization - PreserveReexecuteState preexecs(this); - jvms()->set_should_reexecute(true); - inc_sp(3); - - if (!stored_value_casted->is_InlineType()) { - assert(_gvn.type(stored_value_casted) == TypePtr::NULL_PTR, "Unexpected value"); - stored_value_casted = InlineTypeNode::make_null(_gvn, vk); + // It may be the case that array is only known to be not flat when we try to cast it to + // a flat array. For example, array is a not-null-free array and vk does not have a + // nullable layout. + if (!flat_array->is_top()) { + // Re-execute flat array store if buffering triggers deoptimization + PreserveReexecuteState preexecs(this); + jvms()->set_should_reexecute(true); + inc_sp(3); + + if (!stored_value_casted->is_InlineType()) { + assert(_gvn.type(stored_value_casted) == TypePtr::NULL_PTR, "Unexpected value"); + stored_value_casted = InlineTypeNode::make_null(_gvn, vk); + } + + stored_value_casted->as_InlineType()->store_flat_array(this, flat_array, array_index); } - - stored_value_casted->as_InlineType()->store_flat_array(this, flat_array, array_index); } else { // Element type is unknown, emit a runtime call since the flat array layout is not statically known. store_to_unknown_flat_array(array, array_index, stored_value_casted); diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 9ec0af0e1701..77c2fd27a50e 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -686,9 +686,9 @@ Node* PhaseGVN::apply_ideal(Node* k, bool can_reshape) { } Node* PhaseGVN::apply_identity(Node* n) { - DEBUG_ONLY(uint old_unique = is_verify_Identity_return() ? C->unique() : 0;) + DEBUG_ONLY(uint old_unique = is_verify_IGVN_method_return() ? C->unique() : 0;) Node* const i = n->Identity(this); - assert(!is_verify_Identity_return() || i->_idx < old_unique, + assert(!is_verify_IGVN_method_return() || i->_idx < old_unique, "Identity() must return an existing node"); return i; } @@ -1431,7 +1431,7 @@ void PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape, bool deep_revisit // } // break; // keep verifying - // AddFNode::Ideal calls "commute", which can reorder the inputs for this: + // AddFPNode::Ideal calls "commute", which can reorder the inputs for this: // Check for tight loop increments: Loop-phi of Add of loop-phi // It wants to take the phi into in(1): // 471 Phi === 435 38 390 @@ -2235,18 +2235,12 @@ Node *PhaseIterGVN::transform_old(Node* n) { DEBUG_ONLY(dead_loop_check(k);) DEBUG_ONLY(bool is_new = (k->outcnt() == 0);) C->remove_modified_node(k); -#ifndef PRODUCT - uint hash_before = is_verify_Ideal_return() ? k->hash() : 0; -#endif + DEBUG_ONLY(uint hash_before = is_verify_IGVN_method_return() ? k->hash() : 0;) Node* i = apply_ideal(k, /*can_reshape=*/true); assert(i != k || is_new || i->outcnt() > 0, "don't return dead nodes"); -#ifndef PRODUCT - if (is_verify_Ideal_return()) { - assert(k->outcnt() == 0 || i != nullptr || hash_before == k->hash(), "hash changed after Ideal returned nullptr for %s", k->Name()); - } - verify_step(k); -#endif - + assert(!is_verify_IGVN_method_return() || k->outcnt() == 0 || + i != nullptr || hash_before == k->hash(), "hash changed after Ideal returned nullptr for %s", k->Name()); + NOT_PRODUCT(verify_step(k);) DEBUG_ONLY(uint loop_count = 1;) if (i != nullptr) { set_progress(); @@ -2270,17 +2264,12 @@ Node *PhaseIterGVN::transform_old(Node* n) { // Try idealizing again DEBUG_ONLY(is_new = (k->outcnt() == 0);) C->remove_modified_node(k); -#ifndef PRODUCT - uint hash_before = is_verify_Ideal_return() ? k->hash() : 0; -#endif + DEBUG_ONLY(uint hash_before = is_verify_IGVN_method_return() ? k->hash() : 0;) i = apply_ideal(k, /*can_reshape=*/true); assert(i != k || is_new || (i->outcnt() > 0), "don't return dead nodes"); -#ifndef PRODUCT - if (is_verify_Ideal_return()) { - assert(k->outcnt() == 0 || i != nullptr || hash_before == k->hash(), "hash changed after Ideal returned nullptr for %s", k->Name()); - } - verify_step(k); -#endif + assert(!is_verify_IGVN_method_return() || k->outcnt() == 0 || + i != nullptr || hash_before == k->hash(), "hash changed after Ideal returned nullptr for %s", k->Name()); + NOT_PRODUCT(verify_step(k);) DEBUG_ONLY(loop_count++;) } diff --git a/src/hotspot/share/opto/phaseX.hpp b/src/hotspot/share/opto/phaseX.hpp index c6fb61ec5c96..4e706f8c4783 100644 --- a/src/hotspot/share/opto/phaseX.hpp +++ b/src/hotspot/share/opto/phaseX.hpp @@ -458,12 +458,12 @@ class PhaseGVN : public PhaseValues { void dump_infinite_loop_info(Node* n, const char* where); // Check for a simple dead loop when a data node references itself. void dead_loop_check(Node *n); -#endif - -#ifndef PRODUCT - static bool is_verify_Identity_return() { - // '-XX:VerifyIterativeGVN=1000000' - return ((VerifyIterativeGVN % 10'000'000) / 1'000'000) == 1; + // This checks that: + // - Identity() returns only existing nodes (GVN and IGVN). + // - Ideal() does not return nullptr if the node's hash has changed (IGVN). + static bool is_verify_IGVN_method_return() { + // '-XX:VerifyIterativeGVN=100000' + return ((VerifyIterativeGVN % 1'000'000) / 100'000) == 1; } #endif }; @@ -678,19 +678,15 @@ class PhaseIterGVN : public PhaseGVN { } static bool is_verify_Ideal() { // '-XX:VerifyIterativeGVN=100' - return ((VerifyIterativeGVN % 1000) / 100) == 1; + return ((VerifyIterativeGVN % 1'000) / 100) == 1; } static bool is_verify_Identity() { // '-XX:VerifyIterativeGVN=1000' - return ((VerifyIterativeGVN % 10000) / 1000) == 1; + return ((VerifyIterativeGVN % 10'000) / 1'000) == 1; } static bool is_verify_invariants() { // '-XX:VerifyIterativeGVN=10000' - return ((VerifyIterativeGVN % 100000) / 10000) == 1; - } - static bool is_verify_Ideal_return() { - // '-XX:VerifyIterativeGVN=100000' - return ((VerifyIterativeGVN % 1000000) / 100000) == 1; + return ((VerifyIterativeGVN % 100'000) / 10'000) == 1; } protected: // Sub-quadratic implementation of '-XX:VerifyIterativeGVN=1' (Use-Def verification). diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index cb26e68ef588..89c74e888b66 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -690,69 +690,24 @@ template class TypeIntPrototype, uintn_t<4>>; template class TypeIntPrototype, uintn_t<5>>; template class TypeIntPrototype, uintn_t<6>>; -// Compute the meet of 2 types. When dual is true, the subset relation in CT is -// reversed. This means that the result of 2 CTs would be the intersection of -// them if dual is true, and be the union of them if dual is false. The subset -// relation in the Type hierarchy is still the same, however. E.g. the result -// of 1 CT and Type::BOTTOM would always be Type::BOTTOM, and the result of 1 -// CT and Type::TOP would always be the CT instance itself. template -const Type* TypeIntHelper::int_type_xmeet(const CT* i1, const Type* t2) { - // Perform a fast test for common case; meeting the same types together. - if (i1 == t2 || t2 == Type::TOP) { - return i1; - } - const CT* i2 = t2->try_cast(); - if (i2 != nullptr) { - assert(i1->_is_dual == i2->_is_dual, "must have the same duality"); - using S = std::remove_const_t; - using U = std::remove_const_t; - - if (!i1->_is_dual) { - // meet (a.k.a union) - return int_type_union(i1, i2); - } else { - // join (a.k.a intersection) - return CT::make_or_top(TypeIntPrototype{{MAX2(i1->_lo, i2->_lo), MIN2(i1->_hi, i2->_hi)}, - {MAX2(i1->_ulo, i2->_ulo), MIN2(i1->_uhi, i2->_uhi)}, - {i1->_bits._zeros | i2->_bits._zeros, i1->_bits._ones | i2->_bits._ones}}, - MIN2(i1->_widen, i2->_widen), true); - } - } +const Type* TypeIntHelper::int_type_xmeet(const CT* t1, const CT* t2) { + return int_type_union(t1, t2); +} +template const Type* TypeIntHelper::int_type_xmeet(const TypeInt* i1, const TypeInt* t2); +template const Type* TypeIntHelper::int_type_xmeet(const TypeLong* i1, const TypeLong* t2); - assert(t2->base() != i1->base(), ""); - switch (t2->base()) { // Switch on original type - case Type::AnyPtr: // Mixing with oops happens when javac - case Type::RawPtr: // reuses local variables - case Type::OopPtr: - case Type::InstPtr: - case Type::AryPtr: - case Type::MetadataPtr: - case Type::KlassPtr: - case Type::InstKlassPtr: - case Type::AryKlassPtr: - case Type::NarrowOop: - case Type::NarrowKlass: - case Type::Int: - case Type::Long: - case Type::HalfFloatTop: - case Type::HalfFloatCon: - case Type::HalfFloatBot: - case Type::FloatTop: - case Type::FloatCon: - case Type::FloatBot: - case Type::DoubleTop: - case Type::DoubleCon: - case Type::DoubleBot: - case Type::Bottom: // Ye Olde Default - return Type::BOTTOM; - default: // All else is a mistake - i1->typerr(t2); - return nullptr; - } +template +const Type* TypeIntHelper::int_type_xjoin(const CT* t1, const CT* t2) { + using S = std::remove_const_t; + using U = std::remove_const_t; + return CT::make_or_top(TypeIntPrototype{{MAX2(t1->_lo, t2->_lo), MIN2(t1->_hi, t2->_hi)}, + {MAX2(t1->_ulo, t2->_ulo), MIN2(t1->_uhi, t2->_uhi)}, + {t1->_bits._zeros | t2->_bits._zeros, t1->_bits._ones | t2->_bits._ones}}, + MIN2(t1->_widen, t2->_widen)); } -template const Type* TypeIntHelper::int_type_xmeet(const TypeInt* i1, const Type* t2); -template const Type* TypeIntHelper::int_type_xmeet(const TypeLong* i1, const Type* t2); +template const Type* TypeIntHelper::int_type_xjoin(const TypeInt* i1, const TypeInt* t2); +template const Type* TypeIntHelper::int_type_xjoin(const TypeLong* i1, const TypeLong* t2); // Called in PhiNode::Value during CCP, monotically widen the value set, do so rigorously // first, after WidenMax attempts, if the type has still not converged we speed up the diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index e5e34051587e..09eb5750d6d9 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -145,7 +145,10 @@ class TypeIntHelper { } template - static const Type* int_type_xmeet(const CT* i1, const Type* t2); + static const Type* int_type_xmeet(const CT* i1, const CT* t2); + + template + static const Type* int_type_xjoin(const CT* i1, const CT* t2); template static auto int_type_union(CTP t1, CTP t2) { diff --git a/src/hotspot/share/opto/subnode.cpp b/src/hotspot/share/opto/subnode.cpp index a58e1cf5e2a1..d45bfa235686 100644 --- a/src/hotspot/share/opto/subnode.cpp +++ b/src/hotspot/share/opto/subnode.cpp @@ -2118,8 +2118,9 @@ const Type* AbsNode::Value(PhaseGVN* phase) const { Node* AbsNode::Identity(PhaseGVN* phase) { Node* in1 = in(1); // No need to do abs for non-negative values - if (phase->type(in1)->higher_equal(TypeInt::POS) || - phase->type(in1)->higher_equal(TypeLong::POS)) { + const Type* in_type = phase->type(in1); + if ((in_type->isa_int() && in_type->is_int()->_lo >= 0) || + (in_type->isa_long() && in_type->is_long()->_lo >= 0)) { return in1; } // Convert "abs(abs(x))" into "abs(x)" diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index 09a4237ab0bc..7f39d1de985b 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -27,35 +27,34 @@ #include "ci/ciFlatArrayKlass.hpp" #include "ci/ciInlineKlass.hpp" #include "ci/ciInstanceKlass.hpp" +#include "ci/ciMetadata.hpp" #include "ci/ciMethodData.hpp" #include "ci/ciObjArrayKlass.hpp" +#include "ci/ciObject.hpp" #include "ci/ciTypeFlow.hpp" #include "classfile/javaClasses.hpp" #include "classfile/symbolTable.hpp" -#include "classfile/vmSymbols.hpp" #include "compiler/compileLog.hpp" #include "libadt/dict.hpp" -#include "memory/oopFactory.hpp" #include "memory/resourceArea.hpp" -#include "oops/instanceKlass.hpp" #include "oops/instanceMirrorKlass.hpp" -#include "oops/objArrayKlass.hpp" -#include "oops/typeArrayKlass.hpp" #include "opto/arraycopynode.hpp" #include "opto/callnode.hpp" +#include "opto/compile.hpp" #include "opto/matcher.hpp" #include "opto/node.hpp" #include "opto/opcodes.hpp" #include "opto/rangeinference.hpp" #include "opto/runtime.hpp" #include "opto/type.hpp" +#include "opto/typejavaptr.hpp" #include "runtime/globals.hpp" #include "runtime/stubRoutines.hpp" #include "utilities/checkedCast.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/growableArray.hpp" #include "utilities/ostream.hpp" -#include "utilities/powerOfTwo.hpp" #include "utilities/stringUtils.hpp" // Portions of code courtesy of Clifford Click @@ -64,8 +63,6 @@ // Dictionary of types shared among compilations. Dict* Type::_shared_type_dict = nullptr; -const Type::Offset Type::Offset::top(Type::OffsetTop); -const Type::Offset Type::Offset::bottom(Type::OffsetBot); const Type::Offset Type::Offset::meet(const Type::Offset other) const { // Either is 'TOP' offset? Return the other offset! @@ -76,10 +73,17 @@ const Type::Offset Type::Offset::meet(const Type::Offset other) const { return Offset(_offset); } -const Type::Offset Type::Offset::dual() const { - if (_offset == OffsetTop) return bottom;// Map 'TOP' into 'BOTTOM' - if (_offset == OffsetBot) return top;// Map 'BOTTOM' into 'TOP' - return Offset(_offset); // Map everything else into self +const Type::Offset Type::Offset::join(const Type::Offset other) const { + if (*this == bottom) { + return other; + } else if (other == bottom) { + return *this; + } + if (*this != other) { + return top; + } else { + return *this; + } } const Type::Offset Type::Offset::add(intptr_t offset) const { @@ -690,10 +694,10 @@ void Type::Initialize_shared(Compile* current) { TypeAryPtr::_array_interfaces = TypeInterfaces::make(&array_interfaces); TypeAryKlassPtr::_array_interfaces = TypeAryPtr::_array_interfaces; - TypeAryPtr::BOTTOM = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(Type::BOTTOM, TypeInt::POS, false, false, false, false, false), nullptr, false, Offset::bottom); - TypeAryPtr::RANGE = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(Type::BOTTOM,TypeInt::POS, false, false, false, false, false), nullptr /* current->env()->Object_klass() */, false, Offset(arrayOopDesc::length_offset_in_bytes())); + TypeAryPtr::BOTTOM = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(Type::BOTTOM, TypeInt::POS, false, false, false, false, false, false), nullptr, false, Offset::bottom); + TypeAryPtr::RANGE = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(Type::BOTTOM,TypeInt::POS, false, false, false, false, false, false), nullptr /* current->env()->Object_klass() */, false, Offset(arrayOopDesc::length_offset_in_bytes())); - TypeAryPtr::NARROWOOPS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeNarrowOop::BOTTOM, TypeInt::POS, false, false, false, false, false), nullptr /*ciArrayKlass::make(o)*/, false, Offset::bottom); + TypeAryPtr::NARROWOOPS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeNarrowOop::BOTTOM, TypeInt::POS, false, false, false, false, false, false), nullptr /*ciArrayKlass::make(o)*/, false, Offset::bottom); #ifdef _LP64 if (UseCompressedOops) { @@ -703,16 +707,16 @@ void Type::Initialize_shared(Compile* current) { #endif { // There is no shared klass for Object[]. See note in TypeAryPtr::klass(). - TypeAryPtr::OOPS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInstPtr::BOTTOM,TypeInt::POS, false, false, false, false, false), nullptr /*ciArrayKlass::make(o)*/, false, Offset::bottom); + TypeAryPtr::OOPS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInstPtr::BOTTOM,TypeInt::POS, false, false, false, false, false, false), nullptr /*ciArrayKlass::make(o)*/, false, Offset::bottom); } - TypeAryPtr::BYTES = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInt::BYTE ,TypeInt::POS, false, false, true, true, true), ciTypeArrayKlass::make(T_BYTE), true, Offset::bottom); - TypeAryPtr::SHORTS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInt::SHORT ,TypeInt::POS, false, false, true, true, true), ciTypeArrayKlass::make(T_SHORT), true, Offset::bottom); - TypeAryPtr::CHARS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInt::CHAR ,TypeInt::POS, false, false, true, true, true), ciTypeArrayKlass::make(T_CHAR), true, Offset::bottom); - TypeAryPtr::INTS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInt::INT ,TypeInt::POS, false, false, true, true, true), ciTypeArrayKlass::make(T_INT), true, Offset::bottom); - TypeAryPtr::LONGS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeLong::LONG ,TypeInt::POS, false, false, true, true, true), ciTypeArrayKlass::make(T_LONG), true, Offset::bottom); - TypeAryPtr::FLOATS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(Type::FLOAT ,TypeInt::POS, false, false, true, true, true), ciTypeArrayKlass::make(T_FLOAT), true, Offset::bottom); - TypeAryPtr::DOUBLES = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(Type::DOUBLE ,TypeInt::POS, false, false, true, true, true), ciTypeArrayKlass::make(T_DOUBLE), true, Offset::bottom); - TypeAryPtr::INLINES = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInstPtr::BOTTOM,TypeInt::POS, /* stable= */ false, /* flat= */ true, false, false, false), nullptr, false, Offset::bottom); + TypeAryPtr::BYTES = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInt::BYTE ,TypeInt::POS, false, false, true, false, true, true), ciTypeArrayKlass::make(T_BYTE), true, Offset::bottom); + TypeAryPtr::SHORTS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInt::SHORT ,TypeInt::POS, false, false, true, false, true, true), ciTypeArrayKlass::make(T_SHORT), true, Offset::bottom); + TypeAryPtr::CHARS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInt::CHAR ,TypeInt::POS, false, false, true, false, true, true), ciTypeArrayKlass::make(T_CHAR), true, Offset::bottom); + TypeAryPtr::INTS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInt::INT ,TypeInt::POS, false, false, true, false, true, true), ciTypeArrayKlass::make(T_INT), true, Offset::bottom); + TypeAryPtr::LONGS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeLong::LONG ,TypeInt::POS, false, false, true, false, true, true), ciTypeArrayKlass::make(T_LONG), true, Offset::bottom); + TypeAryPtr::FLOATS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(Type::FLOAT ,TypeInt::POS, false, false, true, false, true, true), ciTypeArrayKlass::make(T_FLOAT), true, Offset::bottom); + TypeAryPtr::DOUBLES = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(Type::DOUBLE ,TypeInt::POS, false, false, true, false, true, true), ciTypeArrayKlass::make(T_DOUBLE), true, Offset::bottom); + TypeAryPtr::INLINES = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInstPtr::BOTTOM,TypeInt::POS, /* stable= */ false, /* flat= */ true, false, false, false, false), nullptr, false, Offset::bottom); // Nobody should ask _array_body_type[T_NARROWOOP]. Use null as assert. TypeAryPtr::_array_body_type[T_NARROWOOP] = nullptr; @@ -855,40 +859,18 @@ void Type::Initialize(Compile* current) { // Do the hash-cons trick. If the Type already exists in the type table, // delete the current Type and return the existing Type. Otherwise stick the // current Type in the Type table. -const Type *Type::hashcons(void) { +const Type* Type::hashcons() { DEBUG_ONLY(base()); // Check the assertion in Type::base(). // Look up the Type in the Type dictionary - Dict *tdic = type_dict(); + Dict* tdic = type_dict(); Type* old = (Type*)(tdic->Insert(this, this, false)); - if( old ) { // Pre-existing Type? - if( old != this ) // Yes, this guy is not the pre-existing? - delete this; // Yes, Nuke this guy - assert( old->_dual, "" ); - return old; // Return pre-existing - } - - // Every type has a dual (to make my lattice symmetric). - // Since we just discovered a new Type, compute its dual right now. - assert( !_dual, "" ); // No dual yet - _dual = xdual(); // Compute the dual - if (equals(this, _dual)) { // Handle self-symmetric - if (_dual != this) { - delete _dual; - _dual = this; - } + if (old == nullptr) { return this; } - assert( !_dual->_dual, "" ); // No reverse dual yet - assert( !(*tdic)[_dual], "" ); // Dual not in type system either - // New Type, insert into Type table - tdic->Insert((void*)_dual,(void*)_dual); - ((Type*)_dual)->_dual = this; // Finish up being symmetric -#ifdef ASSERT - Type *dual_dual = (Type*)_dual->xdual(); - assert( eq(dual_dual), "xdual(xdual()) should be identity" ); - delete dual_dual; -#endif - return this; // Return new Type + if (old != this) { + delete this; + } + return old; } //------------------------------eq--------------------------------------------- @@ -916,35 +898,35 @@ bool Type::is_nan() const { } #ifdef ASSERT -class VerifyMeet; -class VerifyMeetResult : public ArenaObj { - friend class VerifyMeet; +class VerifyMeetJoin; +class VerifyMeetJoinResult : public ArenaObj { + friend class VerifyMeetJoin; friend class Type; private: - class VerifyMeetResultEntry { + class CacheEntry { private: const Type* _in1; const Type* _in2; const Type* _res; public: - VerifyMeetResultEntry(const Type* in1, const Type* in2, const Type* res): + CacheEntry(const Type* in1, const Type* in2, const Type* res): _in1(in1), _in2(in2), _res(res) { } - VerifyMeetResultEntry(): + CacheEntry(): _in1(nullptr), _in2(nullptr), _res(nullptr) { } - bool operator==(const VerifyMeetResultEntry& rhs) const { + bool operator==(const CacheEntry& rhs) const { return _in1 == rhs._in1 && _in2 == rhs._in2 && _res == rhs._res; } - bool operator!=(const VerifyMeetResultEntry& rhs) const { + bool operator!=(const CacheEntry& rhs) const { return !(rhs == *this); } - static int compare(const VerifyMeetResultEntry& v1, const VerifyMeetResultEntry& v2) { + static int compare(const CacheEntry& v1, const CacheEntry& v2) { if ((intptr_t) v1._in1 < (intptr_t) v2._in1) { return -1; } else if (v1._in1 == v2._in1) { @@ -960,302 +942,410 @@ class VerifyMeetResult : public ArenaObj { } const Type* res() const { return _res; } }; + uint _depth; - GrowableArray _cache; - - // With verification code, the meet of A and B causes the computation of: - // 1- meet(A, B) - // 2- meet(B, A) - // 3- meet(dual(meet(A, B)), dual(A)) - // 4- meet(dual(meet(A, B)), dual(B)) - // 5- meet(dual(A), dual(B)) - // 6- meet(dual(B), dual(A)) - // 7- meet(dual(meet(dual(A), dual(B))), A) - // 8- meet(dual(meet(dual(A), dual(B))), B) + GrowableArray _meet_cache; + GrowableArray _join_cache; + + // With verification code, the meet/join of A and B causes the computation of: + // 1- meet(A, B) + // 2- meet(B, A) + // 3- join(A, B) + // 4- join(B, A) + // 5- meet(A, meet(A, B)) + // 6- meet(B, meet(A, B)) + // 7- join(A, meet(A, B)) + // 8- join(B, meet(A, B)) + // 9- meet(A, join(A, B)) + // 10- meet(B, join(A, B)) + // 11- join(A, join(A, B)) + // 12- join(B, join(A, B)) // // In addition the meet of A[] and B[] requires the computation of the meet of A and B. // // The meet of A[] and B[] triggers the computation of: - // 1- meet(A[], B[][) - // 1.1- meet(A, B) - // 1.2- meet(B, A) - // 1.3- meet(dual(meet(A, B)), dual(A)) - // 1.4- meet(dual(meet(A, B)), dual(B)) - // 1.5- meet(dual(A), dual(B)) - // 1.6- meet(dual(B), dual(A)) - // 1.7- meet(dual(meet(dual(A), dual(B))), A) - // 1.8- meet(dual(meet(dual(A), dual(B))), B) + // 1- meet(A[], B[]) + // 1.1- meet(A, B) + // 1.2- meet(B, A) + // 1.3- join(A, B) + // 1.4- join(B, A) + // 1.5- meet(A, meet(A, B)) + // 1.6- meet(B, meet(A, B)) + // 1.7- join(A, meet(A, B)) + // 1.8- join(B, meet(A, B)) + // 1.9- meet(A, join(A, B)) + // 1.10- meet(B, join(A, B)) + // 1.11- join(A, join(A, B)) + // 1.12- join(B, join(A, B)) // 2- meet(B[], A[]) - // 2.1- meet(B, A) = 1.2 - // 2.2- meet(A, B) = 1.1 - // 2.3- meet(dual(meet(B, A)), dual(B)) = 1.4 - // 2.4- meet(dual(meet(B, A)), dual(A)) = 1.3 - // 2.5- meet(dual(B), dual(A)) = 1.6 - // 2.6- meet(dual(A), dual(B)) = 1.5 - // 2.7- meet(dual(meet(dual(B), dual(A))), B) = 1.8 - // 2.8- meet(dual(meet(dual(B), dual(A))), B) = 1.7 + // 2.1- meet(B, A) = 1.2 + // 2.2- meet(A, B) = 1.1 + // 2.3- join(B, A) = 1.4 + // 2.4- join(A, B) = 1.3 + // 2.5- meet(B, meet(B, A)) = 1.6 + // 2.6- meet(A, meet(B, A)) = 1.5 + // 2.7- join(B, meet(B, A)) = 1.8 + // 2.8- join(A, meet(B, A)) = 1.7 + // 2.9- meet(B, join(B, A)) = 1.10 + // 2.10- meet(A, join(B, A)) = 1.9 + // 2.11- join(B, join(B, A)) = 1.12 + // 2.12- join(A, join(B, A)) = 1.11 // etc. - // The number of meet operations performed grows exponentially with the number of dimensions of the arrays but the number - // of different meet operations is linear in the number of dimensions. The function below caches meet results for the - // duration of the meet at the root of the recursive calls. // - const Type* meet(const Type* t1, const Type* t2) { + // When the dimensions of the arrays increase, the number of performed operations grows + // exponentially but the number of distinct operations grows linearly. The function below caches + // the results for the duration of the operation at the root of the recursive calls. + template + static const Type* meet_join(F op, GrowableArray& cache, const Type* t1, const Type* t2) { bool found = false; - const VerifyMeetResultEntry meet(t1, t2, nullptr); - int pos = _cache.find_sorted(meet, found); + const CacheEntry entry(t1, t2, nullptr); + int pos = cache.find_sorted(entry, found); const Type* res = nullptr; if (found) { - res = _cache.at(pos).res(); + res = cache.at(pos).res(); } else { - res = t1->xmeet(t2); - _cache.insert_sorted(VerifyMeetResultEntry(t1, t2, res)); + res = op(t1, t2); + cache.insert_sorted(CacheEntry(t1, t2, res)); found = false; - _cache.find_sorted(meet, found); + cache.find_sorted(entry, found); assert(found, "should be in table after it's added"); } return res; } - void add(const Type* t1, const Type* t2, const Type* res) { - _cache.insert_sorted(VerifyMeetResultEntry(t1, t2, res)); + const Type* meet(const Type* t1, const Type* t2) { + auto op = [](const Type* t1, const Type* t2) { + return Type::xmeet(t1, t2); + }; + return meet_join(op, _meet_cache, t1, t2); + } + + const Type* join(const Type* t1, const Type* t2) { + auto op = [](const Type* t1, const Type* t2) { + return Type::xjoin(t1, t2); + }; + return meet_join(op, _join_cache, t1, t2); } - bool empty_cache() const { - return _cache.length() == 0; + void empty_cache() { + _meet_cache.trunc_to(0); + _join_cache.trunc_to(0); } public: - VerifyMeetResult(Compile* C) : - _depth(0), _cache(C->comp_arena(), 2, 0, VerifyMeetResultEntry()) { + VerifyMeetJoinResult(Compile* C) : _depth(0), + _meet_cache(C->comp_arena(), 2, 0, CacheEntry()), + _join_cache(C->comp_arena(), 2, 0, CacheEntry()) { } }; -void Type::assert_type_verify_empty() const { - assert(Compile::current()->_type_verify == nullptr || Compile::current()->_type_verify->empty_cache(), "cache should have been discarded"); -} - -class VerifyMeet { +class VerifyMeetJoin { private: Compile* _C; public: - VerifyMeet(Compile* C) : _C(C) { + VerifyMeetJoin(Compile* C) : _C(C) { if (C->_type_verify == nullptr) { - C->_type_verify = new (C->comp_arena())VerifyMeetResult(C); + C->_type_verify = new (C->comp_arena())VerifyMeetJoinResult(C); } _C->_type_verify->_depth++; } - ~VerifyMeet() { + ~VerifyMeetJoin() { assert(_C->_type_verify->_depth != 0, ""); _C->_type_verify->_depth--; if (_C->_type_verify->_depth == 0) { - _C->_type_verify->_cache.trunc_to(0); + _C->_type_verify->empty_cache(); } } - const Type* meet(const Type* t1, const Type* t2) const { + const Type* meet(const Type* t1, const Type* t2) { return _C->_type_verify->meet(t1, t2); } - void add(const Type* t1, const Type* t2, const Type* res) const { - _C->_type_verify->add(t1, t2, res); + const Type* join(const Type* t1, const Type* t2) { + return _C->_type_verify->join(t1, t2); } }; -void Type::check_symmetrical(const Type* t, const Type* mt, const VerifyMeet& verify) const { - Compile* C = Compile::current(); - const Type* mt2 = verify.meet(t, this); - - // Verify that: - // this meet t == t meet this - if (mt != mt2) { - tty->print_cr("=== Meet Not Commutative ==="); - tty->print("t = "); t->dump(); tty->cr(); - tty->print("this = "); dump(); tty->cr(); - tty->print("t meet this = "); mt2->dump(); tty->cr(); - tty->print("this meet t = "); mt->dump(); tty->cr(); +void Type::check_fundamental_laws(const Type* t1, const Type* t2, VerifyMeetJoin& verify) { + const Type* mt1 = verify.meet(t1, t2); + const Type* mt2 = verify.meet(t2, t1); + if (mt1 != mt2) { + stringStream ss; + ss.print_cr("=== Meet Not Commutative ==="); + ss.print("t1 = "); t1->dump_on(&ss); ss.cr(); + ss.print("t2 = "); t2->dump_on(&ss); ss.cr(); + ss.print("t1 meets t2 = "); mt1->dump_on(&ss); ss.cr(); + ss.print("t2 meets t1 = "); mt2->dump_on(&ss); ss.cr(); + tty->print("%s", ss.as_string()); fatal("meet not commutative"); } - const Type* dual_join = mt->_dual; - const Type* t2t = verify.meet(dual_join,t->_dual); - const Type* t2this = verify.meet(dual_join,this->_dual); - - // Interface meet Oop is Not Symmetric: - // Interface:AnyNull meet Oop:AnyNull == Interface:AnyNull - // Interface:NotNull meet Oop:NotNull == java/lang/Object:NotNull - - // Verify that: - // 1) mt_dual meet t_dual == t_dual - // which corresponds to - // !(t meet this) meet !t == - // (!t join !this) meet !t == !t - // 2) mt_dual meet this_dual == this_dual - // which corresponds to - // !(t meet this) meet !this == - // (!t join !this) meet !this == !this - if (t2t != t->_dual || t2this != this->_dual) { - tty->print_cr("=== Meet Not Symmetric ==="); - tty->print("t = "); t->dump(); tty->cr(); - tty->print("this= "); dump(); tty->cr(); - tty->print("mt=(t meet this)= "); mt->dump(); tty->cr(); - - tty->print("t_dual= "); t->_dual->dump(); tty->cr(); - tty->print("this_dual= "); _dual->dump(); tty->cr(); - tty->print("mt_dual= "); mt->_dual->dump(); tty->cr(); - - // 1) - tty->print("mt_dual meet t_dual= "); t2t ->dump(); tty->cr(); - // 2) - tty->print("mt_dual meet this_dual= "); t2this ->dump(); tty->cr(); - tty->cr(); - tty->print_cr("Fail: "); - if (t2t != t->_dual) { - tty->print_cr("- mt_dual meet t_dual != t_dual"); + + const Type* jt1 = verify.join(t1, t2); + const Type* jt2 = verify.join(t2, t1); + if (jt1 != jt2) { + stringStream ss; + ss.print_cr("=== Join Not Commutative ==="); + ss.print("t1 = "); t1->dump_on(&ss); ss.cr(); + ss.print("t2 = "); t2->dump_on(&ss); ss.cr(); + ss.print("t1 joins t2 = "); jt1->dump_on(&ss); ss.cr(); + ss.print("t2 joins t1 = "); jt2->dump_on(&ss); ss.cr(); + tty->print("%s", ss.as_string()); + fatal("join not commutative"); + } + + const Type* mt = mt1; + const Type* jt = jt1; + + const Type* t1mmt = verify.meet(t1, mt); + const Type* t2mmt = verify.meet(t2, mt); + const Type* t1jmt = verify.join(t1, mt); + const Type* t2jmt = verify.join(t2, mt); + const Type* t1mjt = verify.meet(t1, jt); + const Type* t2mjt = verify.meet(t2, jt); + const Type* t1jjt = verify.join(t1, jt); + const Type* t2jjt = verify.join(t2, jt); + + if (t1mmt != mt || t2mmt != mt || t1jmt != t1 || t2jmt != t2 || + t1mjt != t1 || t2mjt != t2 || t1jjt != jt || t2jjt != jt) { + stringStream ss; + ss.print_cr("=== Fundamental Laws Violation ==="); + ss.print("t1 = "); t1->dump_on(&ss); ss.cr(); + ss.print("t2 = "); t2->dump_on(&ss); ss.cr(); + ss.print("mt = t1 meets t2 = "); mt->dump_on(&ss); ss.cr(); + ss.print("jt = t1 joins t2 = "); jt->dump_on(&ss); ss.cr(); + + ss.print("t1 meets mt = "); t1mmt->dump_on(&ss); ss.cr(); + ss.print("t2 meets mt = "); t2mmt->dump_on(&ss); ss.cr(); + ss.print("t1 joins mt = "); t1jmt->dump_on(&ss); ss.cr(); + ss.print("t2 joins mt = "); t2jmt->dump_on(&ss); ss.cr(); + ss.print("t1 meets jt = "); t1mjt->dump_on(&ss); ss.cr(); + ss.print("t2 meets jt = "); t2mjt->dump_on(&ss); ss.cr(); + ss.print("t1 joins jt = "); t1jjt->dump_on(&ss); ss.cr(); + ss.print("t2 joins jt = "); t2jjt->dump_on(&ss); ss.cr(); + ss.cr(); + + ss.print_cr("Failed laws:"); + if (t1mmt != mt) { + ss.print_cr("t1 meets mt == mt"); + } + if (t2mmt != mt) { + ss.print_cr("t2 meets mt == mt"); } - if (t2this != this->_dual) { - tty->print_cr("- mt_dual meet this_dual != this_dual"); + if (t1jmt != t1) { + ss.print_cr("t1 joins mt == t1"); + } + if (t2jmt != t2) { + ss.print_cr("t2 joins mt == t2"); + } + if (t1mjt != t1) { + ss.print_cr("t1 meets jt == t1"); + } + if (t2mjt != t2) { + ss.print_cr("t2 meets jt == t2"); + } + if (t1jjt != jt) { + ss.print_cr("t1 joins jt == jt"); + } + if (t2jjt != jt) { + ss.print_cr("t2 joins jt == jt"); } - tty->cr(); - fatal("meet not symmetric"); + tty->print("%s", ss.as_string()); + fatal("Fundamental Laws Violation"); } } #endif -//------------------------------meet------------------------------------------- -// Compute the MEET of two types. NOT virtual. It enforces that meet is -// commutative and the lattice is symmetric. -const Type *Type::meet_helper(const Type *t, bool include_speculative) const { - if (isa_narrowoop() && t->isa_narrowoop()) { - const Type* result = make_ptr()->meet_helper(t->make_ptr(), include_speculative); +template +const Type* Type::meet_join_helper(F op, const Type* t1, const Type* t2, bool include_speculative) { + if (t1->isa_narrowoop() && t2->isa_narrowoop()) { + const Type* result = meet_join_helper(op, t1->make_ptr(), t2->make_ptr(), include_speculative); return result->make_narrowoop(); } - if (isa_narrowklass() && t->isa_narrowklass()) { - const Type* result = make_ptr()->meet_helper(t->make_ptr(), include_speculative); + if (t1->isa_narrowklass() && t2->isa_narrowklass()) { + const Type* result = meet_join_helper(op, t1->make_ptr(), t2->make_ptr(), include_speculative); return result->make_narrowklass(); } + t1 = t1->maybe_remove_speculative(include_speculative); + t2 = t2->maybe_remove_speculative(include_speculative); + const Type* rt = op(t1, t2); + #ifdef ASSERT Compile* C = Compile::current(); - VerifyMeet verify(C); + VerifyMeetJoin verify(C); + check_fundamental_laws(t1, t2, verify); #endif - const Type *this_t = maybe_remove_speculative(include_speculative); - t = t->maybe_remove_speculative(include_speculative); - - const Type *mt = this_t->xmeet(t); -#ifdef ASSERT - verify.add(this_t, t, mt); - if (isa_narrowoop() || t->isa_narrowoop()) { - return mt; - } - if (isa_narrowklass() || t->isa_narrowklass()) { - return mt; - } - // TODO 8387653 This currently triggers a verification failure, the code around "// Even though MyValue is final" needs adjustments - if ((this_t->isa_ptr() && this_t->is_ptr()->is_not_flat()) || - (this_t->_dual->isa_ptr() && this_t->_dual->is_ptr()->is_not_flat())) return mt; - this_t->check_symmetrical(t, mt, verify); - const Type *mt_dual = verify.meet(this_t->_dual, t->_dual); - this_t->_dual->check_symmetrical(t->_dual, mt_dual, verify); -#endif - return mt; + return rt; } -//------------------------------xmeet------------------------------------------ -// Compute the MEET of two types. It returns a new Type object. -const Type *Type::xmeet( const Type *t ) const { - // Perform a fast test for common case; meeting the same types together. - if( this == t ) return this; // Meeting same type-rep? - - // Meeting TOP with anything? - if( _base == Top ) return t; - - // Meeting BOTTOM with anything? - if( _base == Bottom ) return BOTTOM; +// These methods compute the meet and join of two types. They perform additional verification that +// ensures the sanity of the implementation. +const Type* Type::meet_helper(const Type* t, bool include_speculative) const { + auto op = [](const Type* t1, const Type* t2) { + return xmeet(t1, t2); + }; + return meet_join_helper(op, this, t, include_speculative); +} - // Current "this->_base" is one of: Bad, Multi, Control, Top, - // Abio, Abstore, Floatxxx, Doublexxx, Bottom, lastype. - switch (t->base()) { // Switch on original type +const Type* Type::join_helper(const Type* t, bool include_speculative) const { + auto op = [](const Type* t1, const Type* t2) { + return xjoin(t1, t2); + }; + return meet_join_helper(op, this, t, include_speculative); +} - // Cut in half the number of cases I must handle. Only need cases for when - // the given enum "t->type" is less than or equal to the local enum "type". - case HalfFloatCon: - case FloatCon: - case DoubleCon: - case Int: - case Long: - return t->xmeet(this); +const Type* Type::xmeet(const Type* t1, const Type* t2) { + if (t1 == t2) { + return t1; + } + if (t1 == Type::TOP) { + return t2; + } else if (t2 == Type::TOP) { + return t1; + } else if (t1 == Type::BOTTOM || t2 == Type::BOTTOM) { + return Type::BOTTOM; + } - case OopPtr: - return t->xmeet(this); + return t1->xmeet(t2); +} - case InstPtr: - return t->xmeet(this); +const Type* Type::xjoin(const Type* t1, const Type* t2) { + if (t1 == t2) { + return t1; + } + if (t1 == Type::TOP || t2 == Type::TOP) { + return Type::TOP; + } else if (t1 == Type::BOTTOM) { + return t2; + } else if (t2 == Type::BOTTOM) { + return t1; + } - case MetadataPtr: - case KlassPtr: - case InstKlassPtr: - case AryKlassPtr: - return t->xmeet(this); + return t1->xjoin(t2); +} - case AryPtr: - return t->xmeet(this); +//------------------------------xmeet------------------------------------------ +// Compute the MEET of two types. It returns a new Type object. +const Type* Type::xmeet(const Type* t) const { + // Current "this->_base" is one of: Floatxxx, Doublexxx. For Bad, Multi, Control, Abio, Abstore, + // should have been handled by Type::xmeet(const Type* t1, const Type* t2) since their types are + // singleton and t1 must always be equal to t2. + switch (base()) { + default: + typerr(t); + + case HalfFloatTop: + case HalfFloatBot: + case FloatTop: + case FloatBot: + case DoubleTop: + case DoubleBot: + break; + } - case NarrowOop: - return t->xmeet(this); + switch (t->base()) { // Switch on original type + case HalfFloatCon: + case FloatCon: + case DoubleCon: + return t->xmeet(this); + + case HalfFloatTop: + if (base() == HalfFloatTop || base() == HalfFloatBot) { + return this; + } + typerr(t); + case HalfFloatBot: + if (base() == HalfFloatTop || base() == HalfFloatBot) { + return t; + } + typerr(t); - case NarrowKlass: - return t->xmeet(this); + case FloatTop: + if (base() == FloatTop || base() == FloatBot) { + return this; + } + typerr(t); + case FloatBot: + if (base() == FloatTop || base() == FloatBot) { + return t; + } + typerr(t); - case Bad: // Type check - default: // Bogus type not in lattice - typerr(t); - return Type::BOTTOM; + case DoubleTop: + if (base() == DoubleTop || base() == DoubleBot) { + return this; + } + typerr(t); + case DoubleBot: + if (base() == DoubleTop || base() == DoubleBot) { + return t; + } + typerr(t); - case Bottom: // Ye Olde Default - return t; + default: + typerr(t); + } +} - case HalfFloatTop: - if (_base == HalfFloatTop) { return this; } - case HalfFloatBot: // Half Float - if (_base == HalfFloatBot || _base == HalfFloatTop) { return HALF_FLOAT; } - if (_base == FloatBot || _base == FloatTop) { return Type::BOTTOM; } - if (_base == DoubleTop || _base == DoubleBot) { return Type::BOTTOM; } - typerr(t); - return Type::BOTTOM; +// Compute the JOIN of two types. This is similar to xmeet above. +const Type* Type::xjoin(const Type* t) const { + switch (base()) { + default: + typerr(t); + + case HalfFloatTop: + case HalfFloatBot: + case FloatTop: + case FloatBot: + case DoubleTop: + case DoubleBot: + break; + } - case FloatTop: - if (_base == FloatTop ) { return this; } - case FloatBot: // Float - if (_base == FloatBot || _base == FloatTop) { return FLOAT; } - if (_base == HalfFloatTop || _base == HalfFloatBot) { return Type::BOTTOM; } - if (_base == DoubleTop || _base == DoubleBot) { return Type::BOTTOM; } - typerr(t); - return Type::BOTTOM; + switch (t->base()) { // Switch on original type + case HalfFloatCon: + case FloatCon: + case DoubleCon: + return t->xjoin(this); + + case HalfFloatTop: + if (base() == HalfFloatTop || base() == HalfFloatBot) { + return t; + } + typerr(t); + case HalfFloatBot: + if (base() == HalfFloatTop || base() == HalfFloatBot) { + return this; + } + typerr(t); - case DoubleTop: - if (_base == DoubleTop) { return this; } - case DoubleBot: // Double - if (_base == DoubleBot || _base == DoubleTop) { return DOUBLE; } - if (_base == HalfFloatTop || _base == HalfFloatBot) { return Type::BOTTOM; } - if (_base == FloatTop || _base == FloatBot) { return Type::BOTTOM; } - typerr(t); - return Type::BOTTOM; + case FloatTop: + if (base() == FloatTop || base() == FloatBot) { + return t; + } + typerr(t); + case FloatBot: + if (base() == FloatTop || base() == FloatBot) { + return this; + } + typerr(t); - // These next few cases must match exactly or it is a compile-time error. - case Control: // Control of code - case Abio: // State of world outside of program - case Memory: - if (_base == t->_base) { return this; } - typerr(t); - return Type::BOTTOM; + case DoubleTop: + if (base() == DoubleTop || base() == DoubleBot) { + return t; + } + typerr(t); + case DoubleBot: + if (base() == DoubleTop || base() == DoubleBot) { + return this; + } + typerr(t); - case Top: // Top of the lattice - return this; + default: + typerr(t); } - - // The type is unchanged - return this; } //-----------------------------filter------------------------------------------ @@ -1266,13 +1356,6 @@ const Type *Type::filter_helper(const Type *kills, bool include_speculative) con return ft; } -//------------------------------xdual------------------------------------------ -const Type *Type::xdual() const { - // Note: the base() accessor asserts the sanity of _base. - assert(_type_info[base()].dual_type != Bad, "implement with v-call"); - return new Type(_type_info[_base].dual_type); -} - //------------------------------has_memory------------------------------------- bool Type::has_memory() const { Type::TYPES tx = base(); @@ -1470,59 +1553,34 @@ const TypeF *TypeF::make(float f) { return (TypeF*)(new TypeF(f))->hashcons(); } -//------------------------------meet------------------------------------------- -// Compute the MEET of two types. It returns a new Type object. -const Type *TypeF::xmeet( const Type *t ) const { - // Perform a fast test for common case; meeting the same types together. - if( this == t ) return this; // Meeting same type-rep? - +const Type* TypeF::xmeet(const Type* t) const { // Current "this->_base" is FloatCon - switch (t->base()) { // Switch on original type - case AnyPtr: // Mixing with oops happens when javac - case RawPtr: // reuses local variables - case OopPtr: - case InstPtr: - case AryPtr: - case MetadataPtr: - case KlassPtr: - case InstKlassPtr: - case AryKlassPtr: - case NarrowOop: - case NarrowKlass: - case Int: - case Long: - case HalfFloatTop: - case HalfFloatCon: - case HalfFloatBot: - case DoubleTop: - case DoubleCon: - case DoubleBot: - case Bottom: // Ye Olde Default - return Type::BOTTOM; - - case FloatBot: - return t; - - default: // All else is a mistake - typerr(t); - - case FloatCon: // Float-constant vs Float-constant? - if( jint_cast(_f) != jint_cast(t->getf()) ) // unequal constants? - // must compare bitwise as positive zero, negative zero and NaN have - // all the same representation in C++ - return FLOAT; // Return generic float - // Equal constants - case Top: - case FloatTop: - break; // Return the float constant + switch (t->base()) { + case FloatTop: + return this; + case FloatCon: + assert(jint_cast(_f) != jint_cast(t->getf()), "Equivalent instances should not appear here"); + return Type::FLOAT; + case FloatBot: + return t; + default: + typerr(t); } - return this; // Return the float constant } -//------------------------------xdual------------------------------------------ -// Dual: symmetric -const Type *TypeF::xdual() const { - return this; +const Type* TypeF::xjoin(const Type* t) const { + // Current "this->_base" is FloatCon + switch (t->base()) { + case FloatTop: + return t; + case FloatCon: + assert(jint_cast(_f) != jint_cast(t->getf()), "Equivalent instances should not appear here"); + return Type::make(FloatTop); + case FloatBot: + return this; + default: + typerr(t); + } } //------------------------------eq--------------------------------------------- @@ -1593,59 +1651,34 @@ const TypeH* TypeH::make(float f) { return (TypeH*)(new TypeH(hf))->hashcons(); } -//------------------------------xmeet------------------------------------------- -// Compute the MEET of two types. It returns a new Type object. const Type* TypeH::xmeet(const Type* t) const { - // Perform a fast test for common case; meeting the same types together. - if (this == t) return this; // Meeting same type-rep? - // Current "this->_base" is FloatCon - switch (t->base()) { // Switch on original type - case AnyPtr: // Mixing with oops happens when javac - case RawPtr: // reuses local variables - case OopPtr: - case InstPtr: - case AryPtr: - case MetadataPtr: - case KlassPtr: - case InstKlassPtr: - case AryKlassPtr: - case NarrowOop: - case NarrowKlass: - case Int: - case Long: - case FloatTop: - case FloatCon: - case FloatBot: - case DoubleTop: - case DoubleCon: - case DoubleBot: - case Bottom: // Ye Olde Default - return Type::BOTTOM; - - case HalfFloatBot: - return t; - - default: // All else is a mistake - typerr(t); - - case HalfFloatCon: // Half float-constant vs Half float-constant? - if (_f != t->geth()) { // unequal constants? - // must compare bitwise as positive zero, negative zero and NaN have - // all the same representation in C++ - return HALF_FLOAT; // Return generic float - } // Equal constants - case Top: - case HalfFloatTop: - break; // Return the Half float constant + switch (t->base()) { + case HalfFloatTop: + return this; + case HalfFloatCon: + assert(_f != t->is_half_float_constant()->_f, "Equivalent instances should not appear here"); + return Type::HALF_FLOAT; + case HalfFloatBot: + return t; + default: + typerr(t); } - return this; // Return the Half float constant } -//------------------------------xdual------------------------------------------ -// Dual: symmetric -const Type* TypeH::xdual() const { - return this; +const Type* TypeH::xjoin(const Type* t) const { + // Current "this->_base" is FloatCon + switch (t->base()) { + case HalfFloatTop: + return t; + case HalfFloatCon: + assert(_f != t->is_half_float_constant()->_f, "Equivalent instances should not appear here"); + return Type::make(HalfFloatTop); + case HalfFloatBot: + return this; + default: + typerr(t); + } } //------------------------------eq--------------------------------------------- @@ -1718,56 +1751,34 @@ const TypeD *TypeD::make(double d) { return (TypeD*)(new TypeD(d))->hashcons(); } -//------------------------------meet------------------------------------------- -// Compute the MEET of two types. It returns a new Type object. -const Type *TypeD::xmeet( const Type *t ) const { - // Perform a fast test for common case; meeting the same types together. - if( this == t ) return this; // Meeting same type-rep? - +const Type* TypeD::xmeet(const Type* t) const { // Current "this->_base" is DoubleCon - switch (t->base()) { // Switch on original type - case AnyPtr: // Mixing with oops happens when javac - case RawPtr: // reuses local variables - case OopPtr: - case InstPtr: - case AryPtr: - case MetadataPtr: - case KlassPtr: - case InstKlassPtr: - case AryKlassPtr: - case NarrowOop: - case NarrowKlass: - case Int: - case Long: - case HalfFloatTop: - case HalfFloatCon: - case HalfFloatBot: - case FloatTop: - case FloatCon: - case FloatBot: - case Bottom: // Ye Olde Default - return Type::BOTTOM; - - case DoubleBot: - return t; - - default: // All else is a mistake - typerr(t); - - case DoubleCon: // Double-constant vs Double-constant? - if( jlong_cast(_d) != jlong_cast(t->getd()) ) // unequal constants? (see comment in TypeF::xmeet) - return DOUBLE; // Return generic double - case Top: - case DoubleTop: - break; + switch (t->base()) { + case DoubleTop: + return this; + case DoubleCon: + assert(jlong_cast(_d) != jlong_cast(t->getd()), "Equivalent instances should not appear here"); + return Type::DOUBLE; + case DoubleBot: + return t; + default: + typerr(t); } - return this; // Return the double constant } -//------------------------------xdual------------------------------------------ -// Dual: symmetric -const Type *TypeD::xdual() const { - return this; +const Type* TypeD::xjoin(const Type* t) const { + // Current "this->_base" is DoubleCon + switch (t->base()) { + case DoubleTop: + return t; + case DoubleCon: + assert(jlong_cast(_d) != jlong_cast(t->getd()), "Equivalent instances should not appear here"); + return Type::make(DoubleTop); + case DoubleBot: + return this; + default: + typerr(t); + } } //------------------------------eq--------------------------------------------- @@ -1895,24 +1906,16 @@ const TypeInt* TypeInt::INT; // 32-bit integers const TypeInt* TypeInt::SYMINT; // symmetric range [-max_jint..max_jint] const TypeInt* TypeInt::TYPE_DOMAIN; // alias for TypeInt::INT -TypeInt::TypeInt(const TypeIntPrototype& t, int widen, bool dual) - : TypeInteger(Int, t.normalize_widen(widen), dual), _lo(t._srange._lo), _hi(t._srange._hi), +TypeInt::TypeInt(const TypeIntPrototype& t, int widen) + : TypeInteger(Int, t.normalize_widen(widen)), _lo(t._srange._lo), _hi(t._srange._hi), _ulo(t._urange._lo), _uhi(t._urange._hi), _bits(t._bits) { DEBUG_ONLY(t.verify_constraints()); } -const Type* TypeInt::make_or_top(const TypeIntPrototype& t, int widen, bool dual) { - auto canonicalized_t = t.canonicalize_constraints(); - if (canonicalized_t.empty()) { - return dual ? Type::BOTTOM : Type::TOP; - } - return (new TypeInt(canonicalized_t._data, widen, dual))->hashcons()->is_int(); -} - const TypeInt* TypeInt::make(jint con) { juint ucon = con; return (new TypeInt(TypeIntPrototype{{con, con}, {ucon, ucon}, {~ucon, ucon}}, - WidenMin, false))->hashcons()->is_int(); + WidenMin))->hashcons()->is_int(); } const TypeInt* TypeInt::make(jint lo, jint hi, int widen) { @@ -1927,11 +1930,14 @@ const TypeInt* TypeInt::make_unsigned(juint ulo, juint uhi, int widen) { } const Type* TypeInt::make_or_top(const TypeIntPrototype& t, int widen) { - return make_or_top(t, widen, false); + auto canonicalized_t = t.canonicalize_constraints(); + if (canonicalized_t.empty()) { + return Type::TOP; + } + return (new TypeInt(canonicalized_t._data, widen))->hashcons()->is_int(); } bool TypeInt::contains(jint i) const { - assert(!_is_dual, "dual types should only be used for join calculation"); juint u = i; return i >= _lo && i <= _hi && u >= _ulo && u <= _uhi && @@ -1939,33 +1945,34 @@ bool TypeInt::contains(jint i) const { } bool TypeInt::contains(const TypeInt* t) const { - assert(!_is_dual && !t->_is_dual, "dual types should only be used for join calculation"); return TypeIntHelper::int_type_is_subset(this, t); } #ifdef ASSERT bool TypeInt::strictly_contains(const TypeInt* t) const { - assert(!_is_dual && !t->_is_dual, "dual types should only be used for join calculation"); return TypeIntHelper::int_type_is_subset(this, t) && !TypeIntHelper::int_type_is_equal(this, t); } #endif // ASSERT const Type* TypeInt::xmeet(const Type* t) const { - return TypeIntHelper::int_type_xmeet(this, t); + if (base() != Int || t->base() != Int) { + typerr(t); + } + return TypeIntHelper::int_type_xmeet(this, t->is_int()); } -const Type* TypeInt::xdual() const { - return new TypeInt(TypeIntPrototype{{_lo, _hi}, {_ulo, _uhi}, _bits}, - _widen, !_is_dual); +const Type* TypeInt::xjoin(const Type* t) const { + if (base() != Int || t->base() != Int) { + typerr(t); + } + return TypeIntHelper::int_type_xjoin(this, t->is_int()); } const Type* TypeInt::widen(const Type* old, const Type* limit) const { - assert(!_is_dual, "dual types should only be used for join calculation"); return TypeIntHelper::int_type_widen(this, old->isa_int(), limit->isa_int()); } const Type* TypeInt::narrow(const Type* old) const { - assert(!_is_dual, "dual types should only be used for join calculation"); if (old == nullptr) { return this; } @@ -1975,17 +1982,15 @@ const Type* TypeInt::narrow(const Type* old) const { //-----------------------------filter------------------------------------------ const Type* TypeInt::filter_helper(const Type* kills, bool include_speculative) const { - assert(!_is_dual, "dual types should only be used for join calculation"); const TypeInt* ft = join_helper(kills, include_speculative)->isa_int(); if (ft == nullptr) { return Type::TOP; // Canonical empty value } - assert(!ft->_is_dual, "dual types should only be used for join calculation"); if (ft->_widen < this->_widen) { // Do not allow the value of kill->_widen to affect the outcome. // The widen bits must be allowed to run freely through the graph. return (new TypeInt(TypeIntPrototype{{ft->_lo, ft->_hi}, {ft->_ulo, ft->_uhi}, ft->_bits}, - this->_widen, false))->hashcons(); + this->_widen))->hashcons(); } return ft; } @@ -1994,14 +1999,14 @@ const Type* TypeInt::filter_helper(const Type* kills, bool include_speculative) // Structural equality check for Type representations bool TypeInt::eq(const Type* t) const { const TypeInt* r = t->is_int(); - return TypeIntHelper::int_type_is_equal(this, r) && _widen == r->_widen && _is_dual == r->_is_dual; + return TypeIntHelper::int_type_is_equal(this, r) && _widen == r->_widen; } //------------------------------hash------------------------------------------- // Type-specific hashing function. uint TypeInt::hash(void) const { return (uint)_lo + (uint)_hi + (uint)_ulo + (uint)_uhi + - (uint)_bits._zeros + (uint)_bits._ones + (uint)_widen + (uint)_is_dual + (uint)Type::Int; + (uint)_bits._zeros + (uint)_bits._ones + (uint)_widen + (uint)Type::Int; } //------------------------------is_finite-------------------------------------- @@ -2036,24 +2041,16 @@ const TypeLong* TypeLong::INT; // 32-bit subrange const TypeLong* TypeLong::UINT; // 32-bit unsigned subrange const TypeLong* TypeLong::TYPE_DOMAIN; // alias for TypeLong::LONG -TypeLong::TypeLong(const TypeIntPrototype& t, int widen, bool dual) - : TypeInteger(Long, t.normalize_widen(widen), dual), _lo(t._srange._lo), _hi(t._srange._hi), +TypeLong::TypeLong(const TypeIntPrototype& t, int widen) + : TypeInteger(Long, t.normalize_widen(widen)), _lo(t._srange._lo), _hi(t._srange._hi), _ulo(t._urange._lo), _uhi(t._urange._hi), _bits(t._bits) { DEBUG_ONLY(t.verify_constraints()); } -const Type* TypeLong::make_or_top(const TypeIntPrototype& t, int widen, bool dual) { - auto canonicalized_t = t.canonicalize_constraints(); - if (canonicalized_t.empty()) { - return dual ? Type::BOTTOM : Type::TOP; - } - return (new TypeLong(canonicalized_t._data, widen, dual))->hashcons()->is_long(); -} - const TypeLong* TypeLong::make(jlong con) { julong ucon = con; return (new TypeLong(TypeIntPrototype{{con, con}, {ucon, ucon}, {~ucon, ucon}}, - WidenMin, false))->hashcons()->is_long(); + WidenMin))->hashcons()->is_long(); } const TypeLong* TypeLong::make(jlong lo, jlong hi, int widen) { @@ -2068,11 +2065,14 @@ const TypeLong* TypeLong::make_unsigned(julong ulo, julong uhi, int widen) { } const Type* TypeLong::make_or_top(const TypeIntPrototype& t, int widen) { - return make_or_top(t, widen, false); + auto canonicalized_t = t.canonicalize_constraints(); + if (canonicalized_t.empty()) { + return Type::TOP; + } + return (new TypeLong(canonicalized_t._data, widen))->hashcons()->is_long(); } bool TypeLong::contains(jlong i) const { - assert(!_is_dual, "dual types should only be used for join calculation"); julong u = i; return i >= _lo && i <= _hi && u >= _ulo && u <= _uhi && @@ -2080,33 +2080,34 @@ bool TypeLong::contains(jlong i) const { } bool TypeLong::contains(const TypeLong* t) const { - assert(!_is_dual && !t->_is_dual, "dual types should only be used for join calculation"); return TypeIntHelper::int_type_is_subset(this, t); } #ifdef ASSERT bool TypeLong::strictly_contains(const TypeLong* t) const { - assert(!_is_dual && !t->_is_dual, "dual types should only be used for join calculation"); return TypeIntHelper::int_type_is_subset(this, t) && !TypeIntHelper::int_type_is_equal(this, t); } #endif // ASSERT const Type* TypeLong::xmeet(const Type* t) const { - return TypeIntHelper::int_type_xmeet(this, t); + if (base() != Long || t->base() != Long) { + typerr(t); + } + return TypeIntHelper::int_type_xmeet(this, t->is_long()); } -const Type* TypeLong::xdual() const { - return new TypeLong(TypeIntPrototype{{_lo, _hi}, {_ulo, _uhi}, _bits}, - _widen, !_is_dual); +const Type* TypeLong::xjoin(const Type* t) const { + if (base() != Long || t->base() != Long) { + typerr(t); + } + return TypeIntHelper::int_type_xjoin(this, t->is_long()); } const Type* TypeLong::widen(const Type* old, const Type* limit) const { - assert(!_is_dual, "dual types should only be used for join calculation"); return TypeIntHelper::int_type_widen(this, old->isa_long(), limit->isa_long()); } const Type* TypeLong::narrow(const Type* old) const { - assert(!_is_dual, "dual types should only be used for join calculation"); if (old == nullptr) { return this; } @@ -2116,17 +2117,15 @@ const Type* TypeLong::narrow(const Type* old) const { //-----------------------------filter------------------------------------------ const Type* TypeLong::filter_helper(const Type* kills, bool include_speculative) const { - assert(!_is_dual, "dual types should only be used for join calculation"); const TypeLong* ft = join_helper(kills, include_speculative)->isa_long(); if (ft == nullptr) { return Type::TOP; // Canonical empty value } - assert(!ft->_is_dual, "dual types should only be used for join calculation"); if (ft->_widen < this->_widen) { // Do not allow the value of kill->_widen to affect the outcome. // The widen bits must be allowed to run freely through the graph. return (new TypeLong(TypeIntPrototype{{ft->_lo, ft->_hi}, {ft->_ulo, ft->_uhi}, ft->_bits}, - this->_widen, false))->hashcons(); + this->_widen))->hashcons(); } return ft; } @@ -2135,14 +2134,14 @@ const Type* TypeLong::filter_helper(const Type* kills, bool include_speculative) // Structural equality check for Type representations bool TypeLong::eq(const Type* t) const { const TypeLong* r = t->is_long(); - return TypeIntHelper::int_type_is_equal(this, r) && _widen == r->_widen && _is_dual == r->_is_dual; + return TypeIntHelper::int_type_is_equal(this, r) && _widen == r->_widen; } //------------------------------hash------------------------------------------- // Type-specific hashing function. uint TypeLong::hash(void) const { return (uint)_lo + (uint)_hi + (uint)_ulo + (uint)_uhi + - (uint)_bits._zeros + (uint)_bits._ones + (uint)_widen + (uint)_is_dual + (uint)Type::Long; + (uint)_bits._zeros + (uint)_bits._ones + (uint)_widen + (uint)Type::Long; } //------------------------------is_finite-------------------------------------- @@ -2360,40 +2359,24 @@ const Type **TypeTuple::fields( uint arg_cnt ) { //------------------------------meet------------------------------------------- // Compute the MEET of two types. It returns a new Type object. -const Type *TypeTuple::xmeet( const Type *t ) const { - // Perform a fast test for common case; meeting the same types together. - if( this == t ) return this; // Meeting same type-rep? - - // Current "this->_base" is Tuple - switch (t->base()) { // switch on original type - - case Bottom: // Ye Olde Default - return t; - - default: // All else is a mistake - typerr(t); - - case Tuple: { // Meeting 2 signatures? - const TypeTuple *x = t->is_tuple(); - assert( _cnt == x->_cnt, "" ); - const Type **fields = (const Type **)(Compile::current()->type_arena()->AmallocWords( _cnt*sizeof(Type*) )); - for( uint i=0; i<_cnt; i++ ) - fields[i] = field_at(i)->xmeet( x->field_at(i) ); - return TypeTuple::make(_cnt,fields); +const Type* TypeTuple::xmeet(const Type* t) const { + const TypeTuple* x = t->is_tuple(); + assert(_cnt == x->_cnt, "mismatched shape: %d != %d", _cnt, x->_cnt); + const Type** fields = static_cast(Compile::current()->type_arena()->AmallocWords(_cnt * sizeof(Type*))); + for (uint i = 0; i < _cnt; i++) { + fields[i] = field_at(i)->meet_speculative(x->field_at(i)); } - case Top: - break; - } - return this; // Return the double constant + return TypeTuple::make(_cnt, fields); } -//------------------------------xdual------------------------------------------ -// Dual: compute field-by-field dual -const Type *TypeTuple::xdual() const { - const Type **fields = (const Type **)(Compile::current()->type_arena()->AmallocWords( _cnt*sizeof(Type*) )); - for( uint i=0; i<_cnt; i++ ) - fields[i] = _fields[i]->dual(); - return new TypeTuple(_cnt,fields); +const Type* TypeTuple::xjoin(const Type* t) const { + const TypeTuple* x = t->is_tuple(); + assert(_cnt == x->_cnt, "mismatched shape: %d != %d", _cnt, x->_cnt); + const Type** fields = static_cast(Compile::current()->type_arena()->AmallocWords(_cnt * sizeof(Type*))); + for (uint i = 0; i < _cnt; i++) { + fields[i] = field_at(i)->join_speculative(x->field_at(i)); + } + return TypeTuple::make(_cnt, fields); } //------------------------------eq--------------------------------------------- @@ -2471,56 +2454,12 @@ inline const TypeInt* normalize_array_size(const TypeInt* size) { //------------------------------make------------------------------------------- const TypeAry* TypeAry::make(const Type* elem, const TypeInt* size, bool stable, - bool flat, bool not_flat, bool not_null_free, bool atomic) { + bool flat, bool not_flat, bool null_free, bool not_null_free, bool atomic) { if (UseCompressedOops && elem->isa_oopptr()) { elem = elem->make_narrowoop(); } size = normalize_array_size(size); - return (TypeAry*)(new TypeAry(elem, size, stable, flat, not_flat, not_null_free, atomic))->hashcons(); -} - -//------------------------------meet------------------------------------------- -// Compute the MEET of two types. It returns a new Type object. -const Type *TypeAry::xmeet( const Type *t ) const { - // Perform a fast test for common case; meeting the same types together. - if( this == t ) return this; // Meeting same type-rep? - - // Current "this->_base" is Ary - switch (t->base()) { // switch on original type - - case Bottom: // Ye Olde Default - return t; - - default: // All else is a mistake - typerr(t); - - case Array: { // Meeting 2 arrays? - const TypeAry* a = t->is_ary(); - const Type* size = _size->xmeet(a->_size); - const TypeInt* isize = size->isa_int(); - if (isize == nullptr) { - assert(size == Type::TOP || size == Type::BOTTOM, ""); - return size; - } - return TypeAry::make(_elem->meet_speculative(a->_elem), - isize, _stable && a->_stable, - _flat && a->_flat, - _not_flat && a->_not_flat, - _not_null_free && a->_not_null_free, - _atomic && a->_atomic); - } - case Top: - break; - } - return this; // Return the double constant -} - -//------------------------------xdual------------------------------------------ -// Dual: compute field-by-field dual -const Type *TypeAry::xdual() const { - const TypeInt* size_dual = _size->dual()->is_int(); - size_dual = normalize_array_size(size_dual); - return new TypeAry(_elem->dual(), size_dual, !_stable, !_flat, !_not_flat, !_not_null_free, !_atomic); + return (TypeAry*)(new TypeAry(elem, size, stable, flat, not_flat, null_free, not_null_free, atomic))->hashcons(); } //------------------------------eq--------------------------------------------- @@ -2532,6 +2471,7 @@ bool TypeAry::eq( const Type *t ) const { _size == a->_size && _flat == a->_flat && _not_flat == a->_not_flat && + _null_free == a->_null_free && _not_null_free == a->_not_null_free && _atomic == a->_atomic; @@ -2541,21 +2481,21 @@ bool TypeAry::eq( const Type *t ) const { // Type-specific hashing function. uint TypeAry::hash(void) const { return (uint)(uintptr_t)_elem + (uint)(uintptr_t)_size + (uint)(_stable ? 43 : 0) + - (uint)(_flat ? 44 : 0) + (uint)(_not_flat ? 45 : 0) + (uint)(_not_null_free ? 46 : 0) + (uint)(_atomic ? 47 : 0); + (uint)(_flat ? 44 : 0) + (uint)(_not_flat ? 45 : 0) + (uint)(_null_free ? 46 : 0) + (uint)(_not_null_free ? 47 : 0) + (uint)(_atomic ? 48 : 0); } /** * Return same type without a speculative part in the element */ const TypeAry* TypeAry::remove_speculative() const { - return make(_elem->remove_speculative(), _size, _stable, _flat, _not_flat, _not_null_free, _atomic); + return make(_elem->remove_speculative(), _size, _stable, _flat, _not_flat, _null_free, _not_null_free, _atomic); } /** * Return same type with cleaned up speculative part of element */ const Type* TypeAry::cleanup_speculative() const { - return make(_elem->cleanup_speculative(), _size, _stable, _flat, _not_flat, _not_null_free, _atomic); + return make(_elem->cleanup_speculative(), _size, _stable, _flat, _not_flat, _null_free, _not_null_free, _atomic); } /** @@ -2597,15 +2537,7 @@ bool TypeAry::singleton(void) const { bool TypeAry::empty(void) const { assert(!_size->empty(), "TypeInt is never empty"); - // TODO 8385426 This should be simplified at construction time once we get rid of dual - // Doing it with the dual-based join is annoying. TypeAry::empty tests whether the - // element type is empty. When computing the dual of an array that can be flat or not, - // we will get an element type that is empty, and doesn't need more. We even shouldn't - // do more otherwise, we can't make the dual involutive. But if we compute the - // intersection of a flat and a non-flat array, we could change the element type to an - // empty type to reduce the abstract value. And we must be careful not to do that in - // the dual world. - return _elem->empty() || (_flat && _not_flat); + return _elem->empty() || (_flat && _not_flat) || (_null_free && _not_null_free); } //--------------------------ary_must_be_exact---------------------------------- @@ -2613,41 +2545,39 @@ bool TypeAry::ary_must_be_exact() const { // This logic looks at the element type of an array, and returns true // if the element type is either a primitive or a final instance class. // In such cases, an array built on this ary must have no subclasses. - if (_elem == BOTTOM) return false; // general array not exact - if (_elem == TOP ) return false; // inverted general array not exact - const TypeOopPtr* toop = nullptr; - if (UseCompressedOops && _elem->isa_narrowoop()) { - toop = _elem->make_ptr()->isa_oopptr(); - } else { - toop = _elem->isa_oopptr(); + if (_elem == BOTTOM) { + // general array not exact + return false; + } else if (_elem == TOP) { + // inverted general array not exact + return false; } - if (!toop) return true; // a primitive type, like int - if (!toop->is_loaded()) return false; // unloaded class - const TypeInstPtr* tinst; - if (_elem->isa_narrowoop()) - tinst = _elem->make_ptr()->isa_instptr(); - else - tinst = _elem->isa_instptr(); - if (tinst) { - if (tinst->instance_klass()->is_final()) { - // Even though MyValue is final, [LMyValue is only exact if the array - // is (not) null-free due to null-free [LMyValue <: null-able [LMyValue. - // TODO 8387653 If we know that the array can't be null-free, it's allowed to be exact, right? - // If so, we should add '&& !_not_null_free' - if (tinst->is_inlinetypeptr() && (tinst->ptr() != TypePtr::NotNull)) { - return false; - } + + const TypeOopPtr* toop = _elem->make_oopptr(); + if (toop == nullptr) { + // a primitive type, like int + return true; + } else if (!toop->is_loaded()) { + // unloaded class + return false; + } + + if (const TypeInstPtr* tinst = toop->isa_instptr(); tinst != nullptr) { + if (tinst->instance_klass()->is_final()) { + // Even though MyValue is final, MyValue[] is only exact if MyValue is not a value class, + // because there may be many refined type for MyValue[] + if (tinst->is_inlinetypeptr()) { + return false; + } return true; } return false; } - const TypeAryPtr* tap; - if (_elem->isa_narrowoop()) - tap = _elem->make_ptr()->isa_aryptr(); - else - tap = _elem->isa_aryptr(); - if (tap) + + if (const TypeAryPtr* tap = toop->isa_aryptr(); tap != nullptr) { return tap->ary()->ary_must_be_exact(); + } + return false; } @@ -2704,48 +2634,6 @@ const TypeVect* TypeVect::makemask(BasicType elem_bt, uint length) { } } -//------------------------------meet------------------------------------------- -// Compute the MEET of two types. Since each TypeVect is the only instance of -// its species, meeting often returns itself -const Type* TypeVect::xmeet(const Type* t) const { - // Perform a fast test for common case; meeting the same types together. - if (this == t) { - return this; - } - - // Current "this->_base" is Vector - switch (t->base()) { // switch on original type - - case Bottom: // Ye Olde Default - return t; - - default: // All else is a mistake - typerr(t); - case VectorMask: - case VectorA: - case VectorS: - case VectorD: - case VectorX: - case VectorY: - case VectorZ: { // Meeting 2 vectors? - const TypeVect* v = t->is_vect(); - assert(base() == v->base(), ""); - assert(length() == v->length(), ""); - assert(element_basic_type() == v->element_basic_type(), ""); - return this; - } - case Top: - break; - } - return this; -} - -//------------------------------xdual------------------------------------------ -// Since each TypeVect is the only instance of its species, it is self-dual -const Type* TypeVect::xdual() const { - return this; -} - //------------------------------eq--------------------------------------------- // Structural equality check for Type representations bool TypeVect::eq(const Type* t) const { @@ -2864,50 +2752,82 @@ const Type *TypePtr::xmeet(const Type *t) const { return res; } -const Type *TypePtr::xmeet_helper(const Type *t) const { - // Perform a fast test for common case; meeting the same types together. - if( this == t ) return this; // Meeting same type-rep? +const Type* TypePtr::xmeet_helper(const Type* t) const { + if (base() != AnyPtr) { + typerr(t); + } - // Current "this->_base" is AnyPtr - switch (t->base()) { // switch on original type - case Int: // Mixing ints & oops happens when javac - case Long: // reuses local variables - case HalfFloatTop: - case HalfFloatCon: - case HalfFloatBot: - case FloatTop: - case FloatCon: - case FloatBot: - case DoubleTop: - case DoubleCon: - case DoubleBot: - case NarrowOop: - case NarrowKlass: - case Bottom: // Ye Olde Default - return Type::BOTTOM; - case Top: - return this; + switch (t->base()) { + case AnyPtr: { + const TypePtr* tp = t->is_ptr(); + const TypePtr* speculative = xmeet_speculative(tp); + int depth = meet_inline_depth(tp->inline_depth()); + return make(AnyPtr, meet_ptr(tp->ptr()), meet_offset(tp->offset()), speculative, depth); + } + case RawPtr: + case OopPtr: + case InstPtr: + case AryPtr: + case MetadataPtr: + case KlassPtr: + case InstKlassPtr: + case AryKlassPtr: + // Call in reverse direction, delegate to the subtypes to implement the meet + return t->is_ptr()->xmeet(this); + default: + typerr(t); + } +} - case AnyPtr: { // Meeting to AnyPtrs - const TypePtr *tp = t->is_ptr(); - const TypePtr* speculative = xmeet_speculative(tp); - int depth = meet_inline_depth(tp->inline_depth()); - return make(AnyPtr, meet_ptr(tp->ptr()), meet_offset(tp->offset()), speculative, depth); +const Type* TypePtr::xjoin(const Type* t) const { + const Type* res = xjoin_helper(t); + if (res->isa_ptr() == nullptr) { + return res; } - case RawPtr: // For these, flip the call around to cut down - case OopPtr: - case InstPtr: // on the cases I have to handle. - case AryPtr: - case MetadataPtr: - case KlassPtr: - case InstKlassPtr: - case AryKlassPtr: - return t->xmeet(this); // Call in reverse direction - default: // All else is a mistake + + const TypePtr* res_ptr = res->is_ptr(); + if (res_ptr->speculative() != nullptr) { + // type->speculative() is null means that speculation is no better + // than type, i.e. type->speculative() == type. So there are 2 + // ways to represent the fact that we have no useful speculative + // data and we should use a single one to be able to test for + // equality between types. Check whether type->speculative() == + // type and set speculative to null if it is the case. + if (res_ptr->remove_speculative() == res_ptr->speculative()) { + return res_ptr->remove_speculative(); + } + } + + return res; +} + +const Type* TypePtr::xjoin_helper(const Type* t) const { + if (base() != AnyPtr) { typerr(t); + } + switch (t->base()) { + case AnyPtr: { + const TypePtr* tp = t->is_ptr(); + Offset offset = join_offset(tp->offset()); + PTR ptr = offset == Offset::top ? TopPTR : join_ptr(tp->ptr()); + const TypePtr* speculative = xjoin_speculative(tp); + int depth = join_inline_depth(tp->inline_depth()); + return make(AnyPtr, ptr, offset, speculative, depth); + } + case RawPtr: + case OopPtr: + case InstPtr: + case AryPtr: + case MetadataPtr: + case KlassPtr: + case InstKlassPtr: + case AryKlassPtr: + // Call in reverse direction + return t->is_ptr()->xjoin(this); + default: + typerr(t); } - return this; } //------------------------------meet_offset------------------------------------ @@ -2915,32 +2835,20 @@ Type::Offset TypePtr::meet_offset(int offset) const { return _offset.meet(Offset(offset)); } -//------------------------------dual_offset------------------------------------ -Type::Offset TypePtr::dual_offset() const { - return _offset.dual(); +Type::Offset TypePtr::join_offset(int offset) const { + return _offset.join(Offset(offset)); } +const char* const TypePtr::flat_in_array_msg[Uninitialized] = { + "TOP flat in array", "flat in array", "not flat in array", "maybe flat in array" +}; + //------------------------------xdual------------------------------------------ // Dual: compute field-by-field dual const TypePtr::PTR TypePtr::ptr_dual[TypePtr::lastPTR] = { BotPTR, NotNull, Constant, Null, AnyNull, TopPTR }; -const TypePtr::FlatInArray TypePtr::flat_in_array_dual[Uninitialized] = { - /* TopFlat -> */ MaybeFlat, - /* Flat -> */ NotFlat, - /* NotFlat -> */ Flat, - /* MaybeFlat -> */ TopFlat -}; - -const char* const TypePtr::flat_in_array_msg[Uninitialized] = { - "TOP flat in array", "flat in array", "not flat in array", "maybe flat in array" -}; - -const Type *TypePtr::xdual() const { - return new TypePtr(AnyPtr, dual_ptr(), dual_offset(), relocInfo::none, dual_speculative(), dual_inline_depth()); -} - //------------------------------xadd_offset------------------------------------ Type::Offset TypePtr::xadd_offset(intptr_t offset) const { return _offset.add(offset); @@ -3008,54 +2916,55 @@ const Type* TypePtr::cleanup_speculative() const { return this; } -/** - * dual of the speculative part of the type - */ -const TypePtr* TypePtr::dual_speculative() const { - if (_speculative == nullptr) { - return nullptr; - } - return _speculative->dual()->is_ptr(); -} - /** * meet of the speculative parts of 2 types * * @param other type to meet with */ const TypePtr* TypePtr::xmeet_speculative(const TypePtr* other) const { - bool this_has_spec = (_speculative != nullptr); - bool other_has_spec = (other->speculative() != nullptr); + bool this_no_spec = speculative() == nullptr; + bool other_no_spec = other->speculative() == nullptr; - if (!this_has_spec && !other_has_spec) { + if (this_no_spec && other_no_spec) { return nullptr; } - // If we are at a point where control flow meets and one branch has - // a speculative type and the other has not, we meet the speculative - // type of one branch with the actual type of the other. If the - // actual type is exact and the speculative is as well, then the - // result is a speculative type which is exact and we can continue - // speculation further. - const TypePtr* this_spec = _speculative; + // Use the static type if speculative() is nullptr + const TypePtr* this_spec = speculative(); const TypePtr* other_spec = other->speculative(); - if (!this_has_spec) { + if (this_no_spec) { this_spec = this; } - if (!other_has_spec) { + if (other_no_spec) { other_spec = other; } return this_spec->meet(other_spec)->is_ptr(); } -/** - * dual of the inline depth for this type (used for speculation) - */ -int TypePtr::dual_inline_depth() const { - return -inline_depth(); +const TypePtr* TypePtr::xjoin_speculative(const TypePtr* other) const { + bool this_no_spec = speculative() == nullptr; + bool other_no_spec = other->speculative() == nullptr; + + if (this_no_spec && other_no_spec) { + return nullptr; + } + + // Use the static type if speculative() is nullptr + const TypePtr* this_spec = speculative(); + const TypePtr* other_spec = other->speculative(); + + if (this_no_spec) { + this_spec = this; + } + + if (other_no_spec) { + other_spec = other; + } + + return this_spec->join(other_spec)->is_ptr(); } /** @@ -3067,6 +2976,10 @@ int TypePtr::meet_inline_depth(int depth) const { return MAX2(inline_depth(), depth); } +int TypePtr::join_inline_depth(int depth) const { + return MIN2(inline_depth(), depth); +} + /** * Are the speculative parts of 2 types equal? * @@ -3357,22 +3270,19 @@ intptr_t TypeRawPtr::get_con() const { //------------------------------meet------------------------------------------- // Compute the MEET of two types. It returns a new Type object. -const Type *TypeRawPtr::xmeet( const Type *t ) const { - // Perform a fast test for common case; meeting the same types together. - if( this == t ) return this; // Meeting same type-rep? - - // Current "this->_base" is RawPtr - switch( t->base() ) { // switch on original type - case Bottom: // Ye Olde Default - return t; - case Top: - return this; - case AnyPtr: // Meeting to AnyPtrs +const Type* TypeRawPtr::xmeet(const Type* t) const { + if (base() != RawPtr) { + typerr(t); + } + + switch (t->base()) { + case AnyPtr: break; - case RawPtr: { // might be top, bot, any/not or constant - enum PTR tptr = t->is_ptr()->ptr(); - enum PTR ptr = meet_ptr( tptr ); - if( ptr == Constant ) { // Cannot be equal constants, so... + case RawPtr: { + PTR tptr = t->is_ptr()->ptr(); + PTR ptr = meet_ptr(tptr); + if (ptr == Constant) { + // Same constant cases have been handled in Type::xmeet(const Type*, const Type*) if( tptr == Constant && _ptr != Constant) return t; if( _ptr == Constant && tptr != Constant) return this; ptr = NotNull; // Fall down in lattice @@ -3380,14 +3290,6 @@ const Type *TypeRawPtr::xmeet( const Type *t ) const { return make( ptr ); } - case OopPtr: - case InstPtr: - case AryPtr: - case MetadataPtr: - case KlassPtr: - case InstKlassPtr: - case AryKlassPtr: - return TypePtr::BOTTOM; // Oop meet raw is not well defined default: // All else is a mistake typerr(t); } @@ -3409,10 +3311,51 @@ const Type *TypeRawPtr::xmeet( const Type *t ) const { return this; } -//------------------------------xdual------------------------------------------ -// Dual: compute field-by-field dual -const Type *TypeRawPtr::xdual() const { - return new TypeRawPtr(dual_ptr(), _bits, _reloc); +const Type* TypeRawPtr::xjoin(const Type* t) const { + if (base() != RawPtr) { + typerr(t); + } + + switch (t->base()) { + case AnyPtr: { + const TypePtr* tp = t->is_ptr(); + PTR ptr = join_ptr(tp->ptr()); + switch (tp->ptr()) { + case TopPTR: + case Null: + return TypePtr::make(AnyPtr, ptr, Offset(tp->offset()), tp->speculative(), tp->inline_depth()); + case NotNull: + case BotPTR: + return this->ptr() == Constant ? this : make(ptr); + default: + typerr(t); + } + } + + case RawPtr: { + const TypeRawPtr* tp = t->is_rawptr(); + PTR ptr = join_ptr(tp->ptr()); + // this->ptr() can only be Constant, NotNull, BotPTR + if (ptr != Constant) { + // Neither is a constant + return make(ptr); + } + + // At least 1 is a constant + if (this->ptr() == Constant && tp->ptr() == Constant) { + assert(this->_bits != tp->_bits, "should have been handled in Type::xjoin"); + return TypePtr::make(AnyPtr, TopPTR, Offset(0)); + } else if (this->ptr() == Constant) { + return this; + } else { + assert(tp->ptr() == Constant, ""); + return tp; + } + } + + default: + typerr(t); + } } //------------------------------add_offset------------------------------------- @@ -3562,10 +3505,6 @@ uint TypeInterfaces::hash() const { return _hash; } -const Type* TypeInterfaces::xdual() const { - return this; -} - void TypeInterfaces::compute_hash() { uint hash = 0; for (int i = 0; i < _interfaces.length(); i++) { @@ -3721,12 +3660,6 @@ void TypeInterfaces::verify_is_loaded() const { } #endif -// Can't be implemented because there's no way to know if the type is above or below the center line. -const Type* TypeInterfaces::xmeet(const Type* t) const { - ShouldNotReachHere(); - return Type::xmeet(t); -} - bool TypeInterfaces::singleton(void) const { ShouldNotReachHere(); return Type::singleton(); @@ -3751,9 +3684,13 @@ TypeOopPtr::TypeOopPtr(TYPES t, PTR ptr, ciKlass* k, const TypeInterfaces* inter _is_ptr_to_strict_final_field(false), _instance_id(instance_id) { #ifdef ASSERT + assert((o != nullptr) == (ptr == TypePtr::Constant), "inconsistent constant status"); + assert(xk || o == nullptr, "constant must have an exact klass"); if (klass() != nullptr && klass()->is_loaded()) { interfaces->verify_is_loaded(); } + assert(instance_id != InstanceTop, "must not have top instance_id"); + assert(ptr != Constant || instance_id == InstanceBot, "a constant cannot have an instance_id"); #endif if (Compile::current()->eliminate_boxing() && (t == InstPtr) && (offset.get() > 0) && xk && (k != nullptr) && k->is_instance_klass()) { @@ -3886,41 +3823,11 @@ const TypeKlassPtr* TypeOopPtr::as_klass_type(bool try_for_exact) const { //------------------------------meet------------------------------------------- // Compute the MEET of two types. It returns a new Type object. -const Type *TypeOopPtr::xmeet_helper(const Type *t) const { - // Perform a fast test for common case; meeting the same types together. - if( this == t ) return this; // Meeting same type-rep? - - // Current "this->_base" is OopPtr - switch (t->base()) { // switch on original type - - case Int: // Mixing ints & oops happens when javac - case Long: // reuses local variables - case HalfFloatTop: - case HalfFloatCon: - case HalfFloatBot: - case FloatTop: - case FloatCon: - case FloatBot: - case DoubleTop: - case DoubleCon: - case DoubleBot: - case NarrowOop: - case NarrowKlass: - case Bottom: // Ye Olde Default - return Type::BOTTOM; - case Top: - return this; - - default: // All else is a mistake +const Type* TypeOopPtr::xmeet_helper(const Type *t) const { + if (base() != OopPtr) { typerr(t); - - case RawPtr: - case MetadataPtr: - case KlassPtr: - case InstKlassPtr: - case AryKlassPtr: - return TypePtr::BOTTOM; // Oop meet raw is not well defined - + } + switch (t->base()) { // switch on original type case AnyPtr: { // Found an AnyPtr type vs self-OopPtr type const TypePtr *tp = t->is_ptr(); @@ -3954,19 +3861,56 @@ const Type *TypeOopPtr::xmeet_helper(const Type *t) const { case InstPtr: // For these, flip the call around to cut down case AryPtr: - return t->xmeet(this); // Call in reverse direction + return t->is_oopptr()->xmeet_helper(this); // Call in reverse direction - } // End of switch - return this; // Return the double constant + default: + typerr(t); + } } +const Type* TypeOopPtr::xjoin_helper(const Type* t) const { + if (base() != OopPtr) { + typerr(t); + } + + switch (t->base()) { + case AnyPtr: { + const TypePtr* tp = t->is_ptr(); + Offset offset = join_offset(tp->offset()); + PTR other_ptr = offset == Offset::top ? TopPTR : tp->ptr(); + PTR ptr = join_ptr(other_ptr); + const TypePtr* speculative = xjoin_speculative(tp); + int depth = join_inline_depth(tp->inline_depth()); + + switch (other_ptr) { + case Null: + case TopPTR: + return TypePtr::make(AnyPtr, ptr, offset, speculative, depth); + case BotPTR: + case NotNull: { + int instance_id = join_instance_id(InstanceBot); + return make(ptr, offset, instance_id, speculative, depth); + } + default: + typerr(t); + } + } + + case OopPtr: { + const TypeOopPtr* tp = t->is_oopptr(); + int instance_id = join_instance_id(tp->instance_id()); + const TypePtr* speculative = xjoin_speculative(tp); + int depth = join_inline_depth(tp->inline_depth()); + return make(join_ptr(tp->ptr()), join_offset(tp->offset()), instance_id, speculative, depth); + } + + case InstPtr: + case AryPtr: + return t->is_oopptr()->xjoin_helper(this); -//------------------------------xdual------------------------------------------ -// Dual of a pure heap pointer. No relevant klass or oop information. -const Type *TypeOopPtr::xdual() const { - assert(klass() == Compile::current()->env()->Object_klass(), "no klasses here"); - assert(const_oop() == nullptr, "no constants here"); - return new TypeOopPtr(_base, dual_ptr(), klass(), _interfaces, klass_is_exact(), const_oop(), dual_offset(), Offset::bottom, dual_instance_id(), dual_speculative(), dual_inline_depth()); + default: + typerr(t); + } } //--------------------------make_from_klass_common----------------------------- @@ -4010,16 +3954,17 @@ const TypeOopPtr* TypeOopPtr::make_from_klass_common(ciKlass *klass, bool klass_ // Determine null-free/flat properties bool flat; bool not_flat; + bool null_free; bool not_null_free; bool atomic; if (xk) { flat = array_klass->is_flat_array_klass(); not_flat = !flat; - bool is_null_free = array_klass->is_elem_null_free(); - not_null_free = !is_null_free; + null_free = array_klass->is_elem_null_free(); + not_null_free = !null_free; atomic = array_klass->is_elem_atomic(); - if (is_null_free) { + if (null_free) { etype = etype->join_speculative(NOTNULL)->is_oopptr(); } } else { @@ -4031,12 +3976,13 @@ const TypeOopPtr* TypeOopPtr::make_from_klass_common(ciKlass *klass, bool klass_ flat = false; bool not_inline = !exact_etype->can_be_inline_type(); + null_free = false; not_null_free = not_inline; not_flat = !UseArrayFlattening || not_inline || (exact_etype->is_inlinetypeptr() && !exact_etype->inline_klass()->maybe_flat_in_array()); atomic = not_flat; } - const TypeAry* arr0 = TypeAry::make(etype, TypeInt::POS, /* stable= */ false, flat, not_flat, not_null_free, atomic); + const TypeAry* arr0 = TypeAry::make(etype, TypeInt::POS, /* stable= */ false, flat, not_flat, null_free, not_null_free, atomic); // We used to pass NotNull in here, asserting that the sub-arrays // are all not-null. This is not true in generally, as code can // slam nullptrs down in the subarrays. @@ -4046,7 +3992,7 @@ const TypeOopPtr* TypeOopPtr::make_from_klass_common(ciKlass *klass, bool klass_ // Element is an typeArray const Type* etype = get_const_basic_type(klass->as_type_array_klass()->element_type()); const TypeAry* arr0 = TypeAry::make(etype, TypeInt::POS, - /* stable= */ false, /* flat= */ false, /* not_flat= */ true, /* not_null_free= */ true, true); + /* stable= */ false, /* flat= */ false, /* not_flat= */ true, /*null_free=*/ false, /* not_null_free= */ true, true); // We used to pass NotNull in here, asserting that the array pointer // is not-null. That was not true in general. const TypeAryPtr* arr = TypeAryPtr::make(TypePtr::BotPTR, arr0, klass, true, Offset(0)); @@ -4082,7 +4028,7 @@ const TypeOopPtr* TypeOopPtr::make_from_constant(ciObject* o, bool require_const } bool is_atomic = o->as_array()->is_atomic(); const TypeAry* arr0 = TypeAry::make(etype, TypeInt::make(o->as_array()->length()), /* stable= */ false, /* flat= */ is_flat, - /* not_flat= */ !is_flat, /* not_null_free= */ !is_null_free, /* atomic= */ is_atomic); + /* not_flat= */ !is_flat, /*null_free=*/ is_null_free, /* not_null_free= */ !is_null_free, /* atomic= */ is_atomic); // We used to pass NotNull in here, asserting that the sub-arrays // are all not-null. This is not true in generally, as code can // slam nulls down in the subarrays. @@ -4095,7 +4041,7 @@ const TypeOopPtr* TypeOopPtr::make_from_constant(ciObject* o, bool require_const // Element is an typeArray const Type* etype = (Type*)get_const_basic_type(klass->as_type_array_klass()->element_type()); const TypeAry* arr0 = TypeAry::make(etype, TypeInt::make(o->as_array()->length()), /* stable= */ false, /* flat= */ false, - /* not_flat= */ true, /* not_null_free= */ true, true); + /* not_flat= */ true, /*null_free=*/ false, /* not_null_free= */ true, true); // We used to pass NotNull in here, asserting that the array pointer // is not-null. That was not true in general. if (make_constant) { @@ -4264,23 +4210,31 @@ int TypeOopPtr::meet_instance_id( int instance_id ) const { return _instance_id; } -//------------------------------dual_instance_id-------------------------------- -int TypeOopPtr::dual_instance_id( ) const { - if( _instance_id == InstanceTop ) return InstanceBot; // Map TOP into BOTTOM - if( _instance_id == InstanceBot ) return InstanceTop; // Map BOTTOM into TOP - return _instance_id; // Map everything else into self +int TypeOopPtr::join_instance_id(int uid) const { + if (_instance_id == InstanceBot) { + return uid; + } else if (uid == InstanceBot) { + return _instance_id; + } + if (_instance_id != uid) { + return InstanceTop; + } else { + return _instance_id; + } } - const TypeInterfaces* TypeOopPtr::meet_interfaces(const TypeOopPtr* other) const { - if (above_centerline(_ptr) && above_centerline(other->_ptr)) { - return _interfaces->union_with(other->_interfaces); - } else if (above_centerline(_ptr) && !above_centerline(other->_ptr)) { - return other->_interfaces; - } else if (above_centerline(other->_ptr) && !above_centerline(_ptr)) { - return _interfaces; + if (above_centerline(ptr()) || above_centerline(other->ptr())) { + typerr(other); + } + return interfaces()->intersection_with(other->interfaces()); +} + +const TypeInterfaces* TypeOopPtr::join_interfaces(const TypeOopPtr* other) const { + if (above_centerline(ptr()) || above_centerline(other->ptr())) { + typerr(other); } - return _interfaces->intersection_with(other->_interfaces); + return interfaces()->union_with(other->interfaces()); } /** @@ -4332,6 +4286,8 @@ TypeInstPtr::TypeInstPtr(PTR ptr, ciKlass* k, const TypeInterfaces* interfaces, assert(k != nullptr && (k->is_loaded() || o == nullptr), "cannot have constants with non-loaded klass"); + assert(!xk || k->is_loaded(), "pointer to an oop of an exact type must be loaded"); + assert(!xk || interfaces->eq(k->as_instance_klass()), "inconsistency between k and interfaces"); }; //------------------------------make------------------------------------------- @@ -4432,114 +4388,24 @@ const TypeInstPtr* TypeInstPtr::cast_to_instance_id(int instance_id) const { return make(_ptr, klass(), _interfaces, _klass_is_exact, const_oop(), _offset, _flat_in_array, instance_id, _speculative, _inline_depth); } -//------------------------------xmeet_unloaded--------------------------------- -// Compute the MEET of two InstPtrs when at least one is unloaded. -// Assume classes are different since called after check for same name/class-loader -const TypeInstPtr *TypeInstPtr::xmeet_unloaded(const TypeInstPtr *tinst, const TypeInterfaces* interfaces) const { - Offset off = meet_offset(tinst->offset()); - PTR ptr = meet_ptr(tinst->ptr()); - int instance_id = meet_instance_id(tinst->instance_id()); - const TypePtr* speculative = xmeet_speculative(tinst); - int depth = meet_inline_depth(tinst->inline_depth()); - - const TypeInstPtr *loaded = is_loaded() ? this : tinst; - const TypeInstPtr *unloaded = is_loaded() ? tinst : this; - if( loaded->klass()->equals(ciEnv::current()->Object_klass()) ) { - // - // Meet unloaded class with java/lang/Object - // - // Meet - // | Unloaded Class - // Object | TOP | AnyNull | Constant | NotNull | BOTTOM | - // =================================================================== - // TOP | ..........................Unloaded......................| - // AnyNull | U-AN |................Unloaded......................| - // Constant | ... O-NN .................................. | O-BOT | - // NotNull | ... O-NN .................................. | O-BOT | - // BOTTOM | ........................Object-BOTTOM ..................| - // - assert(loaded->ptr() != TypePtr::Null, "insanity check"); - // - if (loaded->ptr() == TypePtr::TopPTR) { return unloaded->with_speculative(speculative); } - else if (loaded->ptr() == TypePtr::AnyNull) { - FlatInArray flat_in_array = meet_flat_in_array(_flat_in_array, tinst->flat_in_array()); - return make(ptr, unloaded->klass(), interfaces, false, nullptr, off, flat_in_array, instance_id, - speculative, depth); - } - else if (loaded->ptr() == TypePtr::BotPTR) { return TypeInstPtr::BOTTOM->with_speculative(speculative); } - else if (loaded->ptr() == TypePtr::Constant || loaded->ptr() == TypePtr::NotNull) { - if (unloaded->ptr() == TypePtr::BotPTR) { return TypeInstPtr::BOTTOM->with_speculative(speculative); } - else { return TypeInstPtr::NOTNULL->with_speculative(speculative); } - } - else if (unloaded->ptr() == TypePtr::TopPTR) { return unloaded->with_speculative(speculative); } - - return unloaded->cast_to_ptr_type(TypePtr::AnyNull)->is_instptr()->with_speculative(speculative); - } - - // Both are unloaded, not the same class, not Object - // Or meet unloaded with a different loaded class, not java/lang/Object - if (ptr != TypePtr::BotPTR) { - return TypeInstPtr::NOTNULL->with_speculative(speculative); - } - return TypeInstPtr::BOTTOM->with_speculative(speculative); -} - - //------------------------------meet------------------------------------------- // Compute the MEET of two types. It returns a new Type object. -const Type *TypeInstPtr::xmeet_helper(const Type *t) const { - // Perform a fast test for common case; meeting the same types together. - if( this == t ) return this; // Meeting same type-rep? +const Type* TypeInstPtr::xmeet_helper(const Type* t) const { + if (base() != InstPtr) { + typerr(t); + } // Current "this->_base" is Pointer switch (t->base()) { // switch on original type - - case Int: // Mixing ints & oops happens when javac - case Long: // reuses local variables - case HalfFloatTop: - case HalfFloatCon: - case HalfFloatBot: - case FloatTop: - case FloatCon: - case FloatBot: - case DoubleTop: - case DoubleCon: - case DoubleBot: - case NarrowOop: - case NarrowKlass: - case Bottom: // Ye Olde Default - return Type::BOTTOM; - case Top: - return this; - default: // All else is a mistake typerr(t); - case MetadataPtr: - case KlassPtr: - case InstKlassPtr: - case AryKlassPtr: - case RawPtr: return TypePtr::BOTTOM; - - case AryPtr: { // All arrays inherit from Object class - // Call in reverse direction to avoid duplication - return t->is_aryptr()->xmeet_helper(this); - } - case OopPtr: { // Meeting to OopPtrs // Found a OopPtr type vs self-InstPtr type const TypeOopPtr *tp = t->is_oopptr(); Offset offset = meet_offset(tp->offset()); PTR ptr = meet_ptr(tp->ptr()); switch (tp->ptr()) { - case TopPTR: - case AnyNull: { - int instance_id = meet_instance_id(InstanceTop); - const TypePtr* speculative = xmeet_speculative(tp); - int depth = meet_inline_depth(tp->inline_depth()); - return make(ptr, klass(), _interfaces, klass_is_exact(), - (ptr == Constant ? const_oop() : nullptr), offset, flat_in_array(), instance_id, speculative, depth); - } case NotNull: case BotPTR: { int instance_id = meet_instance_id(tp->instance_id()); @@ -4575,230 +4441,47 @@ const Type *TypeInstPtr::xmeet_helper(const Type *t) const { } } - /* - A-top } - / | \ } Tops - B-top A-any C-top } - | / | \ | } Any-nulls - B-any | C-any } - | | | - B-con A-con C-con } constants; not comparable across classes - | | | - B-not | C-not } - | \ | / | } not-nulls - B-bot A-not C-bot } - \ | / } Bottoms - A-bot } - */ - - case InstPtr: { // Meeting 2 Oops? - // Found an InstPtr sub-type vs self-InstPtr type - const TypeInstPtr *tinst = t->is_instptr(); - Offset off = meet_offset(tinst->offset()); - PTR ptr = meet_ptr(tinst->ptr()); - int instance_id = meet_instance_id(tinst->instance_id()); - const TypePtr* speculative = xmeet_speculative(tinst); - int depth = meet_inline_depth(tinst->inline_depth()); - const TypeInterfaces* interfaces = meet_interfaces(tinst); - - ciKlass* tinst_klass = tinst->klass(); - ciKlass* this_klass = klass(); - - ciKlass* res_klass = nullptr; - bool res_xk = false; - const Type* res; - MeetResult kind = meet_instptr(ptr, interfaces, this, tinst, res_klass, res_xk); - - if (kind == UNLOADED) { - // One of these classes has not been loaded - const TypeInstPtr* unloaded_meet = xmeet_unloaded(tinst, interfaces); -#ifndef PRODUCT - if (PrintOpto && Verbose) { - tty->print("meet of unloaded classes resulted in: "); - unloaded_meet->dump(); - tty->cr(); - tty->print(" this == "); - dump(); - tty->cr(); - tty->print(" tinst == "); - tinst->dump(); - tty->cr(); - } -#endif - res = unloaded_meet; - } else { - FlatInArray flat_in_array = meet_flat_in_array(_flat_in_array, tinst->flat_in_array()); - if (kind == NOT_SUBTYPE && instance_id > 0) { - instance_id = InstanceBot; - } else if (kind == LCA) { - instance_id = InstanceBot; - } - ciObject* o = nullptr; // Assume not constant when done - ciObject* this_oop = const_oop(); - ciObject* tinst_oop = tinst->const_oop(); - if (ptr == Constant) { - if (this_oop != nullptr && tinst_oop != nullptr && - this_oop->equals(tinst_oop)) - o = this_oop; - else if (above_centerline(_ptr)) { - assert(!tinst_klass->is_interface(), ""); - o = tinst_oop; - } else if (above_centerline(tinst->_ptr)) { - assert(!this_klass->is_interface(), ""); - o = this_oop; - } else - ptr = NotNull; - } - res = make(ptr, res_klass, interfaces, res_xk, o, off, flat_in_array, instance_id, speculative, depth); - } - - return res; - - } // End of case InstPtr - + case InstPtr: + case AryPtr: + return TypeJavaPtrMeetHelper::javaptr_type_xmeet(this, t->is_oopptr()); } // End of switch - return this; // Return the double constant } -template TypePtr::MeetResult TypePtr::meet_instptr(PTR& ptr, const TypeInterfaces*& interfaces, const T* this_type, const T* other_type, - ciKlass*& res_klass, bool& res_xk) { - ciKlass* this_klass = this_type->klass(); - ciKlass* other_klass = other_type->klass(); - - bool this_xk = this_type->klass_is_exact(); - bool other_xk = other_type->klass_is_exact(); - PTR this_ptr = this_type->ptr(); - PTR other_ptr = other_type->ptr(); - const TypeInterfaces* this_interfaces = this_type->interfaces(); - const TypeInterfaces* other_interfaces = other_type->interfaces(); - // Check for easy case; klasses are equal (and perhaps not loaded!) - // If we have constants, then we created oops so classes are loaded - // and we can handle the constants further down. This case handles - // both-not-loaded or both-loaded classes - if (ptr != Constant && this_klass->equals(other_klass) && this_xk == other_xk) { - res_klass = this_klass; - res_xk = this_xk; - return QUICK; - } - - // Classes require inspection in the Java klass hierarchy. Must be loaded. - if (!other_klass->is_loaded() || !this_klass->is_loaded()) { - return UNLOADED; - } - - // !!! Here's how the symmetry requirement breaks down into invariants: - // If we split one up & one down AND they subtype, take the down man. - // If we split one up & one down AND they do NOT subtype, "fall hard". - // If both are up and they subtype, take the subtype class. - // If both are up and they do NOT subtype, "fall hard". - // If both are down and they subtype, take the supertype class. - // If both are down and they do NOT subtype, "fall hard". - // Constants treated as down. - - // Now, reorder the above list; observe that both-down+subtype is also - // "fall hard"; "fall hard" becomes the default case: - // If we split one up & one down AND they subtype, take the down man. - // If both are up and they subtype, take the subtype class. - - // If both are down and they subtype, "fall hard". - // If both are down and they do NOT subtype, "fall hard". - // If both are up and they do NOT subtype, "fall hard". - // If we split one up & one down AND they do NOT subtype, "fall hard". - - // If a proper subtype is exact, and we return it, we return it exactly. - // If a proper supertype is exact, there can be no subtyping relationship! - // If both types are equal to the subtype, exactness is and-ed below the - // centerline and or-ed above it. (N.B. Constants are always exact.) - - const T* subtype = nullptr; - bool subtype_exact = false; - if (this_type->is_same_java_type_as(other_type)) { - // Same klass - subtype = this_type; - subtype_exact = below_centerline(ptr) ? (this_xk && other_xk) : (this_xk || other_xk); - } else if (!other_xk && this_type->is_meet_subtype_of(other_type)) { - subtype = this_type; // Pick subtyping class - subtype_exact = this_xk; - } else if (!this_xk && other_type->is_meet_subtype_of(this_type)) { - subtype = other_type; // Pick subtyping class - subtype_exact = other_xk; - } - - if (subtype != nullptr) { - if (above_centerline(ptr)) { - // Both types are empty. - this_type = other_type = subtype; - this_xk = other_xk = subtype_exact; - } else if (above_centerline(this_ptr) && !above_centerline(other_ptr)) { - // this_type is empty while other_type is not. Take other_type. - this_type = other_type; - this_xk = other_xk; - } else if (above_centerline(other_ptr) && !above_centerline(this_ptr)) { - // other_type is empty while this_type is not. Take this_type. - other_type = this_type; // this is down; keep down man - } else { - // this_type and other_type are both non-empty. - this_xk = subtype_exact; // either they are equal, or we'll do an LCA - } - } - - // Check for classes now being equal - if (this_type->is_same_java_type_as(other_type)) { - // If the klasses are equal, the constants may still differ. Fall to - // NotNull if they do (neither constant is null; that is a special case - // handled elsewhere). - res_klass = this_type->klass(); - res_xk = this_xk; - return SUBTYPE; - } // Else classes are not equal - - // Since klasses are different, we require a LCA in the Java - // class hierarchy - which means we have to fall to at least NotNull. - if (ptr == TopPTR || ptr == AnyNull || ptr == Constant) { - ptr = NotNull; +const Type* TypeInstPtr::xjoin_helper(const Type* t) const { + if (base() != InstPtr) { + typerr(t); } - interfaces = this_interfaces->intersection_with(other_interfaces); - - // Now we find the LCA of Java classes - ciKlass* k = this_klass->least_common_ancestor(other_klass); - - res_klass = k; - res_xk = false; - return LCA; -} + switch (t->base()) { + case AnyPtr: + case OopPtr: { + const TypePtr* tp = t->is_ptr(); + Offset offset = join_offset(tp->offset()); + PTR other_ptr = offset == Offset::top ? TopPTR : tp->ptr(); + PTR ptr = join_ptr(other_ptr); + const TypePtr* speculative = xjoin_speculative(tp); + int depth = join_inline_depth(tp->inline_depth()); + + switch (other_ptr) { + case TopPTR: + case Null: + return TypePtr::make(AnyPtr, ptr, offset, speculative, depth); + case NotNull: + case BotPTR: { + int instance_id = join_instance_id(InstanceBot); + return make(ptr, klass(), interfaces(), klass_is_exact(), const_oop(), offset, flat_in_array(), instance_id, speculative, depth); + } + default: + typerr(t); + } + } -// Top-Flat Flat Not-Flat Maybe-Flat -// ------------------------------------------------------------- -// Top-Flat Top-Flat Flat Not-Flat Maybe-Flat -// Flat Flat Flat Maybe-Flat Maybe-Flat -// Not-Flat Not-Flat Maybe-Flat Not-Flat Maybe-Flat -// Maybe-Flat Maybe-Flat Maybe-Flat Maybe-Flat Maybe-flat -TypePtr::FlatInArray TypePtr::meet_flat_in_array(const FlatInArray left, const FlatInArray right) { - if (left == TopFlat) { - return right; - } - if (right == TopFlat) { - return left; - } - if (left == MaybeFlat || right == MaybeFlat) { - return MaybeFlat; - } + case InstPtr: + case AryPtr: + return TypeJavaPtrJoinHelper::javaptr_type_xjoin(this, t->is_oopptr()); - switch (left) { - case Flat: - if (right == Flat) { - return Flat; - } - return MaybeFlat; - case NotFlat: - if (right == NotFlat) { - return NotFlat; - } - return MaybeFlat; default: - ShouldNotReachHere(); - return Uninitialized; + typerr(t); } } @@ -4812,15 +4495,6 @@ ciType* TypeInstPtr::java_mirror_type() const { return const_oop()->as_instance()->java_mirror_type(); } - -//------------------------------xdual------------------------------------------ -// Dual: do NOT dual on klasses. This means I do NOT understand the Java -// inheritance mechanism. -const Type* TypeInstPtr::xdual() const { - return new TypeInstPtr(dual_ptr(), klass(), _interfaces, klass_is_exact(), const_oop(), dual_offset(), - dual_flat_in_array(), dual_instance_id(), dual_speculative(), dual_inline_depth()); -} - //------------------------------eq--------------------------------------------- // Structural equality check for Type representations bool TypeInstPtr::eq( const Type *t ) const { @@ -4955,67 +4629,6 @@ const TypeKlassPtr* TypeInstPtr::as_klass_type(bool try_for_exact) const { return TypeInstKlassPtr::make(xk ? TypePtr::Constant : TypePtr::NotNull, klass(), _interfaces, Offset(0), flat_in_array); } -template bool TypePtr::is_meet_subtype_of_helper_for_instance(const T1* this_one, const T2* other, bool this_xk, bool other_xk) { - static_assert(std::is_base_of::value, ""); - - if (!this_one->is_instance_type(other)) { - return false; - } - - if (other->klass() == ciEnv::current()->Object_klass() && other->_interfaces->empty()) { - return true; - } - - return this_one->klass()->is_subtype_of(other->klass()) && - (!this_xk || this_one->_interfaces->contains(other->_interfaces)); -} - - -bool TypeInstPtr::is_meet_subtype_of_helper(const TypeOopPtr *other, bool this_xk, bool other_xk) const { - return TypePtr::is_meet_subtype_of_helper_for_instance(this, other, this_xk, other_xk); -} - -template bool TypePtr::is_meet_subtype_of_helper_for_array(const T1* this_one, const T2* other, bool this_xk, bool other_xk) { - static_assert(std::is_base_of::value, ""); - if (other->klass() == ciEnv::current()->Object_klass() && other->_interfaces->empty()) { - return true; - } - - if (this_one->is_instance_type(other)) { - return other->klass() == ciEnv::current()->Object_klass() && this_one->_interfaces->contains(other->_interfaces); - } - - int dummy; - bool this_top_or_bottom = (this_one->base_element_type(dummy) == Type::TOP || this_one->base_element_type(dummy) == Type::BOTTOM); - if (this_top_or_bottom) { - return false; - } - - const T1* other_ary = this_one->is_array_type(other); - const TypePtr* other_elem = other_ary->elem()->make_ptr(); - const TypePtr* this_elem = this_one->elem()->make_ptr(); - if (other_elem != nullptr && this_elem != nullptr) { - return this_one->is_reference_type(this_elem)->is_meet_subtype_of_helper(this_one->is_reference_type(other_elem), this_xk, other_xk); - } - if (other_elem == nullptr && this_elem == nullptr) { - return this_one->klass()->is_subtype_of(other->klass()); - } - - return false; -} - -bool TypeAryPtr::is_meet_subtype_of_helper(const TypeOopPtr *other, bool this_xk, bool other_xk) const { - return TypePtr::is_meet_subtype_of_helper_for_array(this, other, this_xk, other_xk); -} - -bool TypeInstKlassPtr::is_meet_subtype_of_helper(const TypeKlassPtr *other, bool this_xk, bool other_xk) const { - return TypePtr::is_meet_subtype_of_helper_for_instance(this, other, this_xk, other_xk); -} - -bool TypeAryKlassPtr::is_meet_subtype_of_helper(const TypeKlassPtr *other, bool this_xk, bool other_xk) const { - return TypePtr::is_meet_subtype_of_helper_for_array(this, other, this_xk, other_xk); -} - //============================================================================= // Convenience common pre-built types. const TypeAryPtr* TypeAryPtr::BOTTOM; @@ -5136,7 +4749,7 @@ const TypeAryPtr* TypeAryPtr::cast_to_size(const TypeInt* new_size) const { assert(new_size != nullptr, ""); new_size = narrow_size_type(new_size); if (new_size == size()) return this; - const TypeAry* new_ary = TypeAry::make(elem(), new_size, is_stable(), is_flat(), is_not_flat(), is_not_null_free(), is_atomic()); + const TypeAry* new_ary = TypeAry::make(elem(), new_size, is_stable(), is_flat(), is_not_flat(), is_null_free(), is_not_null_free(), is_atomic()); return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _field_offset, _instance_id, _speculative, _inline_depth, _is_autobox_cache); } @@ -5145,7 +4758,7 @@ const TypeAryPtr* TypeAryPtr::cast_to_flat(bool flat) const { return this; } assert(!flat || !is_not_flat(), "inconsistency"); - const TypeAry* new_ary = TypeAry::make(elem(), size(), is_stable(), flat, is_not_flat(), is_not_null_free(), is_atomic()); + const TypeAry* new_ary = TypeAry::make(elem(), size(), is_stable(), flat, is_not_flat(), is_null_free(), is_not_null_free(), is_atomic()); const TypeAryPtr* res = make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _field_offset, _instance_id, _speculative, _inline_depth, _is_autobox_cache); if (res->speculative() == res->remove_speculative()) { return res->remove_speculative(); @@ -5159,7 +4772,7 @@ const TypeAryPtr* TypeAryPtr::cast_to_not_flat(bool not_flat) const { return this; } assert(!not_flat || !is_flat(), "inconsistency"); - const TypeAry* new_ary = TypeAry::make(elem(), size(), is_stable(), is_flat(), not_flat, is_not_null_free(), is_atomic()); + const TypeAry* new_ary = TypeAry::make(elem(), size(), is_stable(), is_flat(), not_flat, is_null_free(), is_not_null_free(), is_atomic()); const TypeAryPtr* res = make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _field_offset, _instance_id, _speculative, _inline_depth, _is_autobox_cache); // We keep the speculative part if it contains information about flat-/nullability. // Make sure it's removed if it's not better than the non-speculative type anymore. @@ -5182,7 +4795,7 @@ const TypeAryPtr* TypeAryPtr::cast_to_null_free(bool null_free) const { new_elem = new_elem->meet_speculative(TypePtr::NULL_PTR); } new_elem = elem->isa_narrowoop() ? new_elem->make_narrowoop() : new_elem; - const TypeAry* new_ary = TypeAry::make(new_elem, size(), is_stable(), is_flat(), is_not_flat(), is_not_null_free(), is_atomic()); + const TypeAry* new_ary = TypeAry::make(new_elem, size(), is_stable(), is_flat(), is_not_flat(), null_free, is_not_null_free(), is_atomic()); const TypeAryPtr* res = make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _field_offset, _instance_id, _speculative, _inline_depth, _is_autobox_cache); if (res->speculative() == res->remove_speculative()) { return res->remove_speculative(); @@ -5198,7 +4811,7 @@ const TypeAryPtr* TypeAryPtr::cast_to_not_null_free(bool not_null_free) const { return this; } assert(!not_null_free || !is_null_free(), "inconsistency"); - const TypeAry* new_ary = TypeAry::make(elem(), size(), is_stable(), is_flat(), is_not_flat(), not_null_free, is_atomic()); + const TypeAry* new_ary = TypeAry::make(elem(), size(), is_stable(), is_flat(), is_not_flat(), is_null_free(), not_null_free, is_atomic()); const TypePtr* new_spec = _speculative; if (new_spec != nullptr) { // Could be 'null free' from profiling, which would contradict the cast. @@ -5263,7 +4876,7 @@ const TypeAryPtr* TypeAryPtr::cast_to_stable(bool stable, int stable_dimension) elem = elem_ptr = elem_ptr->is_aryptr()->cast_to_stable(stable, stable_dimension - 1); } - const TypeAry* new_ary = TypeAry::make(elem, size(), stable, is_flat(), is_not_flat(), is_not_null_free(), is_atomic()); + const TypeAry* new_ary = TypeAry::make(elem, size(), stable, is_flat(), is_not_flat(), is_null_free(), is_not_null_free(), is_atomic()); return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _field_offset, _instance_id, _speculative, _inline_depth, _is_autobox_cache); } @@ -5285,7 +4898,7 @@ const TypeAryPtr* TypeAryPtr::cast_to_autobox_cache() const { if (etype == nullptr) return this; // The pointers in the autobox arrays are always non-null. etype = etype->cast_to_ptr_type(TypePtr::NotNull)->is_oopptr(); - const TypeAry* new_ary = TypeAry::make(etype, size(), is_stable(), is_flat(), is_not_flat(), is_not_null_free(), is_atomic()); + const TypeAry* new_ary = TypeAry::make(etype, size(), is_stable(), is_flat(), is_not_flat(), is_null_free(), is_not_null_free(), is_atomic()); return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _field_offset, _instance_id, _speculative, _inline_depth, /*is_autobox_cache=*/true); } @@ -5318,31 +4931,13 @@ bool TypeAryPtr::maybe_java_subtype_of_helper(const TypeOopPtr* other, bool this } //------------------------------meet------------------------------------------- // Compute the MEET of two types. It returns a new Type object. -const Type *TypeAryPtr::xmeet_helper(const Type *t) const { - // Perform a fast test for common case; meeting the same types together. - if( this == t ) return this; // Meeting same type-rep? +const Type* TypeAryPtr::xmeet_helper(const Type* t) const { + if (base() != AryPtr) { + typerr(t); + } + // Current "this->_base" is Pointer switch (t->base()) { // switch on original type - - // Mixing ints & oops happens when javac reuses local variables - case Int: - case Long: - case HalfFloatTop: - case HalfFloatCon: - case HalfFloatBot: - case FloatTop: - case FloatCon: - case FloatBot: - case DoubleTop: - case DoubleCon: - case DoubleBot: - case NarrowOop: - case NarrowKlass: - case Bottom: // Ye Olde Default - return Type::BOTTOM; - case Top: - return this; - default: // All else is a mistake typerr(t); @@ -5354,12 +4949,6 @@ const Type *TypeAryPtr::xmeet_helper(const Type *t) const { int depth = meet_inline_depth(tp->inline_depth()); const TypePtr* speculative = xmeet_speculative(tp); switch (tp->ptr()) { - case TopPTR: - case AnyNull: { - int instance_id = meet_instance_id(InstanceTop); - return make(ptr, (ptr == Constant ? const_oop() : nullptr), - _ary, _klass, _klass_is_exact, offset, _field_offset, instance_id, speculative, depth); - } case BotPTR: case NotNull: { int instance_id = meet_instance_id(tp->instance_id()); @@ -5394,293 +4983,48 @@ const Type *TypeAryPtr::xmeet_helper(const Type *t) const { } } - case MetadataPtr: - case KlassPtr: - case InstKlassPtr: - case AryKlassPtr: - case RawPtr: return TypePtr::BOTTOM; - - case AryPtr: { // Meeting 2 references? - const TypeAryPtr *tap = t->is_aryptr(); - Offset off = meet_offset(tap->offset()); - Offset field_off = meet_field_offset(tap->field_offset()); - const Type* tm = _ary->meet_speculative(tap->_ary); - const TypeAry* tary = tm->isa_ary(); - if (tary == nullptr) { - assert(tm == Type::TOP || tm == Type::BOTTOM, ""); - return tm; - } - PTR ptr = meet_ptr(tap->ptr()); - int instance_id = meet_instance_id(tap->instance_id()); - const TypePtr* speculative = xmeet_speculative(tap); - int depth = meet_inline_depth(tap->inline_depth()); - - ciKlass* res_klass = nullptr; - bool res_xk = false; - bool res_flat = false; - bool res_not_flat = false; - bool res_not_null_free = false; - bool res_atomic = false; - const Type* elem = tary->_elem; - if (meet_aryptr(ptr, elem, this, tap, res_klass, res_xk, res_flat, res_not_flat, res_not_null_free, res_atomic) == NOT_SUBTYPE) { - instance_id = InstanceBot; - } else if (this->is_flat() != tap->is_flat()) { - // Meeting flat inline type array with non-flat array. Adjust (field) offset accordingly. - if (tary->_flat) { - // Result is in a flat representation - off = Offset(is_flat() ? offset() : tap->offset()); - field_off = is_flat() ? field_offset() : tap->field_offset(); - } else if (below_centerline(ptr)) { - // Result is in a non-flat representation - off = Offset(flat_offset()).meet(Offset(tap->flat_offset())); - field_off = (field_off == Offset::top) ? Offset::top : Offset::bottom; - } else if (flat_offset() == tap->flat_offset()) { - off = Offset(!is_flat() ? offset() : tap->offset()); - field_off = !is_flat() ? field_offset() : tap->field_offset(); - } - } - - ciObject* o = nullptr; // Assume not constant when done - ciObject* this_oop = const_oop(); - ciObject* tap_oop = tap->const_oop(); - if (ptr == Constant) { - if (this_oop != nullptr && tap_oop != nullptr && - this_oop->equals(tap_oop)) { - o = tap_oop; - } else if (above_centerline(_ptr)) { - o = tap_oop; - } else if (above_centerline(tap->_ptr)) { - o = this_oop; - } else { - ptr = NotNull; - } - } - return make(ptr, o, TypeAry::make(elem, tary->_size, tary->_stable, res_flat, res_not_flat, res_not_null_free, res_atomic), res_klass, res_xk, off, field_off, instance_id, speculative, depth); - } - - // All arrays inherit from Object class - case InstPtr: { - const TypeInstPtr *tp = t->is_instptr(); - Offset offset = meet_offset(tp->offset()); - PTR ptr = meet_ptr(tp->ptr()); - int instance_id = meet_instance_id(tp->instance_id()); - const TypePtr* speculative = xmeet_speculative(tp); - int depth = meet_inline_depth(tp->inline_depth()); - const TypeInterfaces* interfaces = meet_interfaces(tp); - const TypeInterfaces* tp_interfaces = tp->_interfaces; - const TypeInterfaces* this_interfaces = _interfaces; - - switch (ptr) { - case TopPTR: - case AnyNull: // Fall 'down' to dual of object klass - // For instances when a subclass meets a superclass we fall - // below the centerline when the superclass is exact. We need to - // do the same here. - // - // Flat in array: - // We do - // dual(TypeAryPtr) MEET dual(TypeInstPtr) - // If TypeInstPtr is anything else than Object, then the result of the meet is bottom Object (i.e. we could have - // instances or arrays). - // If TypeInstPtr is an Object and either - // - exact - // - inexact AND flat in array == dual(not flat in array) (i.e. not an array type) - // then the result of the meet is bottom Object (i.e. we could have instances or arrays). - // Otherwise, we meet two array pointers and create a new TypeAryPtr. - if (tp->klass()->equals(ciEnv::current()->Object_klass()) && this_interfaces->contains(tp_interfaces) && - !tp->klass_is_exact() && !tp->is_not_flat_in_array()) { - return TypeAryPtr::make(ptr, _ary, _klass, _klass_is_exact, offset, _field_offset, instance_id, speculative, depth); - } else { - // cannot subclass, so the meet has to fall badly below the centerline - ptr = NotNull; - instance_id = InstanceBot; - interfaces = this_interfaces->intersection_with(tp_interfaces); - FlatInArray flat_in_array = meet_flat_in_array(NotFlat, tp->flat_in_array()); - return TypeInstPtr::make(ptr, ciEnv::current()->Object_klass(), interfaces, false, nullptr, offset, flat_in_array, instance_id, speculative, depth); - } - case Constant: - case NotNull: - case BotPTR: { // Fall down to object klass - // LCA is object_klass, but if we subclass from the top we can do better - if (above_centerline(tp->ptr())) { - // If 'tp' is above the centerline and it is Object class - // then we can subclass in the Java class hierarchy. - // For instances when a subclass meets a superclass we fall - // below the centerline when the superclass is exact. We need - // to do the same here. - - // Flat in array: We do TypeAryPtr MEET dual(TypeInstPtr), same applies as above in TopPTR/AnyNull case. - if (tp->klass()->equals(ciEnv::current()->Object_klass()) && this_interfaces->contains(tp_interfaces) && - !tp->klass_is_exact() && !tp->is_not_flat_in_array()) { - // that is, my array type is a subtype of 'tp' klass - return make(ptr, (ptr == Constant ? const_oop() : nullptr), - _ary, _klass, _klass_is_exact, offset, _field_offset, instance_id, speculative, depth); - } - } - // The other case cannot happen, since t cannot be a subtype of an array. - // The meet falls down to Object class below centerline. - if (ptr == Constant) { - ptr = NotNull; - } - if (instance_id > 0) { - instance_id = InstanceBot; - } - - FlatInArray flat_in_array = meet_flat_in_array(NotFlat, tp->flat_in_array()); - interfaces = this_interfaces->intersection_with(tp_interfaces); - return TypeInstPtr::make(ptr, ciEnv::current()->Object_klass(), interfaces, false, nullptr, offset, - flat_in_array, instance_id, speculative, depth); - } - default: typerr(t); - } - } + case AryPtr: + case InstPtr: + return TypeJavaPtrMeetHelper::javaptr_type_xmeet(this, t->is_oopptr()); } - return this; // Lint noise } - -template TypePtr::MeetResult TypePtr::meet_aryptr(PTR& ptr, const Type*& elem, const T* this_ary, const T* other_ary, - ciKlass*& res_klass, bool& res_xk, bool &res_flat, bool& res_not_flat, bool& res_not_null_free, bool &res_atomic) { - int dummy; - bool this_top_or_bottom = (this_ary->base_element_type(dummy) == Type::TOP || this_ary->base_element_type(dummy) == Type::BOTTOM); - bool other_top_or_bottom = (other_ary->base_element_type(dummy) == Type::TOP || other_ary->base_element_type(dummy) == Type::BOTTOM); - ciKlass* this_klass = this_ary->klass(); - ciKlass* other_klass = other_ary->klass(); - bool this_xk = this_ary->klass_is_exact(); - bool other_xk = other_ary->klass_is_exact(); - PTR this_ptr = this_ary->ptr(); - PTR other_ptr = other_ary->ptr(); - bool this_flat = this_ary->is_flat(); - bool this_not_flat = this_ary->is_not_flat(); - bool other_flat = other_ary->is_flat(); - bool other_not_flat = other_ary->is_not_flat(); - bool this_not_null_free = this_ary->is_not_null_free(); - bool other_not_null_free = other_ary->is_not_null_free(); - bool this_atomic = this_ary->is_atomic(); - bool other_atomic = other_ary->is_atomic(); - const bool same_nullness = this_ary->is_null_free() == other_ary->is_null_free(); - res_klass = nullptr; - MeetResult result = SUBTYPE; - res_flat = this_flat && other_flat; - bool res_null_free = this_ary->is_null_free() && other_ary->is_null_free(); - res_not_flat = this_not_flat && other_not_flat; - res_not_null_free = this_not_null_free && other_not_null_free; - res_atomic = this_atomic && other_atomic; - - if (elem->isa_int()) { - // Integral array element types have irrelevant lattice relations. - // It is the klass that determines array layout, not the element type. - if (this_top_or_bottom) { - res_klass = other_klass; - } else if (other_top_or_bottom || other_klass == this_klass) { - res_klass = this_klass; - } else { - // Something like byte[int+] meets char[int+]. - // This must fall to bottom, not (int[-128..65535])[int+]. - // instance_id = InstanceBot; - elem = Type::BOTTOM; - result = NOT_SUBTYPE; - if (above_centerline(ptr) || ptr == Constant) { - ptr = NotNull; - res_xk = false; - return NOT_SUBTYPE; - } - } - } else {// Non integral arrays. - // Must fall to bottom if exact klasses in upper lattice - // are not equal or super klass is exact. - if ((above_centerline(ptr) || ptr == Constant) && !this_ary->is_same_java_type_as(other_ary) && - // meet with top[] and bottom[] are processed further down: - !this_top_or_bottom && !other_top_or_bottom && - // both are exact and not equal: - ((other_xk && this_xk) || - // 'tap' is exact and super or unrelated: - (other_xk && !other_ary->is_meet_subtype_of(this_ary)) || - // 'this' is exact and super or unrelated: - (this_xk && !this_ary->is_meet_subtype_of(other_ary)))) { - if (above_centerline(ptr) || (elem->make_ptr() && above_centerline(elem->make_ptr()->_ptr))) { - elem = Type::BOTTOM; - } - ptr = NotNull; - res_xk = false; - return NOT_SUBTYPE; - } +const Type* TypeAryPtr::xjoin_helper(const Type* t) const { + if (base() != AryPtr) { + typerr(t); } - res_xk = false; - switch (other_ptr) { - case AnyNull: - case TopPTR: - // Compute new klass on demand, do not use tap->_klass - if (below_centerline(this_ptr)) { - res_xk = this_xk; - if (this_ary->is_flat()) { - elem = this_ary->elem(); + switch (t->base()) { + case AnyPtr: + case OopPtr: { + const TypePtr* tp = t->is_ptr(); + Offset offset = join_offset(tp->offset()); + PTR other_ptr = offset == Offset::top ? TopPTR : tp->ptr(); + PTR ptr = join_ptr(other_ptr); + const TypePtr* speculative = xjoin_speculative(tp); + int depth = join_inline_depth(tp->inline_depth()); + + switch (other_ptr) { + case TopPTR: + case Null: + return TypePtr::make(AnyPtr, ptr, offset, speculative, depth); + case NotNull: + case BotPTR: { + int instance_id = join_instance_id(InstanceBot); + return make(ptr, const_oop(), _ary, klass(), klass_is_exact(), offset, field_offset(), instance_id, speculative, depth, is_autobox_cache()); } - } else { - res_xk = (other_xk || this_xk); + default: + typerr(t); } - break; - case Constant: { - if (this_ptr == Constant && same_nullness) { - // Only exact if same nullness since: - // null-free [LMyValue <: nullable [LMyValue. - res_xk = true; - } else if (above_centerline(this_ptr)) { - res_xk = true; - } else { - // Only precise for identical arrays - res_xk = this_xk && (this_ary->is_same_java_type_as(other_ary) || (this_top_or_bottom && other_top_or_bottom)); - // Even though MyValue is final, [LMyValue is only exact if the array - // is (not) null-free due to null-free [LMyValue <: null-able [LMyValue. - if (res_xk && !res_null_free && !res_not_null_free) { - ptr = NotNull; - res_xk = false; - } - } - break; - } - case NotNull: - case BotPTR: - // Compute new klass on demand, do not use tap->_klass - if (above_centerline(this_ptr)) { - res_xk = other_xk; - if (other_ary->is_flat()) { - elem = other_ary->elem(); - } - } else { - res_xk = (other_xk && this_xk) && - (this_ary->is_same_java_type_as(other_ary) || (this_top_or_bottom && other_top_or_bottom)); // Only precise for identical arrays - // Even though MyValue is final, [LMyValue is only exact if the array - // is (not) null-free due to null-free [LMyValue <: null-able [LMyValue. - if (res_xk && !res_null_free && !res_not_null_free) { - res_xk = false; - } - } - break; - default: { - ShouldNotReachHere(); - return result; } - } - return result; -} + case InstPtr: + case AryPtr: + return TypeJavaPtrJoinHelper::javaptr_type_xjoin(this, t->is_oopptr()); -//------------------------------xdual------------------------------------------ -// Dual: compute field-by-field dual -const Type *TypeAryPtr::xdual() const { - bool xk = _klass_is_exact; - return new TypeAryPtr(dual_ptr(), _const_oop, _ary->dual()->is_ary(), _klass, xk, dual_offset(), dual_field_offset(), dual_instance_id(), is_autobox_cache(), dual_speculative(), dual_inline_depth()); -} - -Type::Offset TypeAryPtr::meet_field_offset(const Type::Offset offset) const { - return _field_offset.meet(offset); -} - -//------------------------------dual_offset------------------------------------ -Type::Offset TypeAryPtr::dual_field_offset() const { - return _field_offset.dual(); + default: + typerr(t); + } } //------------------------------dump2------------------------------------------ @@ -5918,12 +5262,6 @@ bool TypeNarrowPtr::eq( const Type *t ) const { return false; } -const Type *TypeNarrowPtr::xdual() const { // Compute dual right now. - const TypePtr* odual = _ptrtype->dual()->is_ptr(); - return make_same_narrowptr(odual); -} - - const Type *TypeNarrowPtr::filter_helper(const Type *kills, bool include_speculative) const { if (isa_same_narrowptr(kills)) { const Type* ft =_ptrtype->filter_helper(is_same_narrowptr(kills)->_ptrtype, include_speculative); @@ -5943,58 +5281,6 @@ const Type *TypeNarrowPtr::filter_helper(const Type *kills, bool include_specula } } -//------------------------------xmeet------------------------------------------ -// Compute the MEET of two types. It returns a new Type object. -const Type *TypeNarrowPtr::xmeet( const Type *t ) const { - // Perform a fast test for common case; meeting the same types together. - if( this == t ) return this; // Meeting same type-rep? - - if (t->base() == base()) { - const Type* result = _ptrtype->xmeet(t->make_ptr()); - if (result->isa_ptr()) { - return make_hash_same_narrowptr(result->is_ptr()); - } - return result; - } - - // Current "this->_base" is NarrowKlass or NarrowOop - switch (t->base()) { // switch on original type - - case Int: // Mixing ints & oops happens when javac - case Long: // reuses local variables - case HalfFloatTop: - case HalfFloatCon: - case HalfFloatBot: - case FloatTop: - case FloatCon: - case FloatBot: - case DoubleTop: - case DoubleCon: - case DoubleBot: - case AnyPtr: - case RawPtr: - case OopPtr: - case InstPtr: - case AryPtr: - case MetadataPtr: - case KlassPtr: - case InstKlassPtr: - case AryKlassPtr: - case NarrowOop: - case NarrowKlass: - case Bottom: // Ye Olde Default - return Type::BOTTOM; - case Top: - return this; - - default: // All else is a mistake - typerr(t); - - } // End of switch - - return this; -} - #ifndef PRODUCT void TypeNarrowPtr::dump2( Dict & d, uint depth, outputStream *st ) const { _ptrtype->dump2(d, depth, st); @@ -6111,30 +5397,11 @@ const TypeMetadataPtr* TypeMetadataPtr::cast_to_ptr_type(PTR ptr) const { //------------------------------meet------------------------------------------- // Compute the MEET of two types. It returns a new Type object. const Type *TypeMetadataPtr::xmeet( const Type *t ) const { - // Perform a fast test for common case; meeting the same types together. - if( this == t ) return this; // Meeting same type-rep? + if (base() != MetadataPtr) { + typerr(t); + } - // Current "this->_base" is OopPtr switch (t->base()) { // switch on original type - - case Int: // Mixing ints & oops happens when javac - case Long: // reuses local variables - case HalfFloatTop: - case HalfFloatCon: - case HalfFloatBot: - case FloatTop: - case FloatCon: - case FloatBot: - case DoubleTop: - case DoubleCon: - case DoubleBot: - case NarrowOop: - case NarrowKlass: - case Bottom: // Ye Olde Default - return Type::BOTTOM; - case Top: - return this; - default: // All else is a mistake typerr(t); @@ -6158,15 +5425,6 @@ const Type *TypeMetadataPtr::xmeet( const Type *t ) const { } } - case RawPtr: - case KlassPtr: - case InstKlassPtr: - case AryKlassPtr: - case OopPtr: - case InstPtr: - case AryPtr: - return TypePtr::BOTTOM; // Oop meet raw is not well defined - case MetadataPtr: { const TypeMetadataPtr *tp = t->is_metadataptr(); Offset offset = meet_offset(tp->offset()); @@ -6187,14 +5445,53 @@ const Type *TypeMetadataPtr::xmeet( const Type *t ) const { break; } } // End of switch - return this; // Return the double constant } +const Type* TypeMetadataPtr::xjoin(const Type* t) const { + if (base() != MetadataPtr) { + typerr(t); + } -//------------------------------xdual------------------------------------------ -// Dual of a pure metadata pointer. -const Type *TypeMetadataPtr::xdual() const { - return new TypeMetadataPtr(dual_ptr(), metadata(), dual_offset()); + switch (t->base()) { + case AnyPtr: { + const TypePtr* tp = t->is_ptr(); + PTR ptr = join_ptr(tp->ptr()); + Offset offset = join_offset(tp->offset()); + switch (tp->ptr()) { + case TopPTR: + case Null: + return TypePtr::make(AnyPtr, ptr, offset, tp->speculative(), tp->inline_depth()); + case NotNull: + case BotPTR: + return make(ptr, _metadata, offset); + default: + typerr(t); + } + } + + case MetadataPtr: { + const TypeMetadataPtr* tp = t->is_metadataptr(); + PTR ptr = join_ptr(tp->ptr()); + Offset offset = join_offset(tp->offset()); + if (offset == Offset::top) { + return TypePtr::make(AnyPtr, TopPTR, offset); + } + + ciMetadata* metadata = this->metadata(); + if (ciMetadata* meta2 = tp->metadata(); meta2 != nullptr) { + if (metadata == nullptr) { + metadata = meta2; + } else if (!metadata->equals(meta2)) { + return TypePtr::make(AnyPtr, ptr == TypePtr::BotPTR ? Null : TopPTR, offset); + } + } + + return make(ptr, metadata, offset); + } + + default: + typerr(t); + } } //------------------------------dump2------------------------------------------ @@ -6321,14 +5618,17 @@ const Type *TypeKlassPtr::filter_helper(const Type *kills, bool include_speculat } const TypeInterfaces* TypeKlassPtr::meet_interfaces(const TypeKlassPtr* other) const { - if (above_centerline(_ptr) && above_centerline(other->_ptr)) { - return _interfaces->union_with(other->_interfaces); - } else if (above_centerline(_ptr) && !above_centerline(other->_ptr)) { - return other->_interfaces; - } else if (above_centerline(other->_ptr) && !above_centerline(_ptr)) { - return _interfaces; + if (above_centerline(ptr()) || above_centerline(other->ptr())) { + typerr(other); + } + return interfaces()->intersection_with(other->interfaces()); +} + +const TypeInterfaces* TypeKlassPtr::join_interfaces(const TypeKlassPtr* other) const { + if (above_centerline(ptr()) || above_centerline(other->ptr())) { + typerr(other); } - return _interfaces->intersection_with(other->_interfaces); + return interfaces()->union_with(other->interfaces()); } //------------------------------get_con---------------------------------------- @@ -6463,31 +5763,13 @@ const TypeInstPtr* TypeInstKlassPtr::as_subtype_instance_type(bool klass_change) //------------------------------xmeet------------------------------------------ // Compute the MEET of two types, return a new Type object. -const Type *TypeInstKlassPtr::xmeet( const Type *t ) const { - // Perform a fast test for common case; meeting the same types together. - if( this == t ) return this; // Meeting same type-rep? +const Type* TypeInstKlassPtr::xmeet(const Type* t) const { + if (base() != InstKlassPtr) { + typerr(t); + } // Current "this->_base" is Pointer switch (t->base()) { // switch on original type - - case Int: // Mixing ints & oops happens when javac - case Long: // reuses local variables - case HalfFloatTop: - case HalfFloatCon: - case HalfFloatBot: - case FloatTop: - case FloatCon: - case FloatBot: - case DoubleTop: - case DoubleCon: - case DoubleBot: - case NarrowOop: - case NarrowKlass: - case Bottom: // Ye Olde Default - return Type::BOTTOM; - case Top: - return this; - default: // All else is a mistake typerr(t); @@ -6510,117 +5792,43 @@ const Type *TypeInstKlassPtr::xmeet( const Type *t ) const { } } - case RawPtr: - case MetadataPtr: - case OopPtr: - case AryPtr: // Meet with AryPtr - case InstPtr: // Meet with InstPtr - return TypePtr::BOTTOM; + case InstKlassPtr: + case AryKlassPtr: + return TypeJavaPtrMeetHelper::javaptr_type_xmeet(this, t->is_klassptr()); - // - // A-top } - // / | \ } Tops - // B-top A-any C-top } - // | / | \ | } Any-nulls - // B-any | C-any } - // | | | - // B-con A-con C-con } constants; not comparable across classes - // | | | - // B-not | C-not } - // | \ | / | } not-nulls - // B-bot A-not C-bot } - // \ | / } Bottoms - // A-bot } - // + } // End of switch + return this; // Return the double constant +} - case InstKlassPtr: { // Meet two KlassPtr types - const TypeInstKlassPtr *tkls = t->is_instklassptr(); - Offset off = meet_offset(tkls->offset()); - PTR ptr = meet_ptr(tkls->ptr()); - const TypeInterfaces* interfaces = meet_interfaces(tkls); - - ciKlass* res_klass = nullptr; - bool res_xk = false; - const FlatInArray flat_in_array = meet_flat_in_array(_flat_in_array, tkls->flat_in_array()); - switch (meet_instptr(ptr, interfaces, this, tkls, res_klass, res_xk)) { - case UNLOADED: - ShouldNotReachHere(); - case SUBTYPE: - case NOT_SUBTYPE: - case LCA: - case QUICK: { - assert(res_xk == (ptr == Constant), ""); - const Type* res = make(ptr, res_klass, interfaces, off, flat_in_array); - return res; - } - default: - ShouldNotReachHere(); - } - } // End of case KlassPtr - case AryKlassPtr: { // All arrays inherit from Object class - const TypeAryKlassPtr *tp = t->is_aryklassptr(); - Offset offset = meet_offset(tp->offset()); - PTR ptr = meet_ptr(tp->ptr()); - const TypeInterfaces* interfaces = meet_interfaces(tp); - const TypeInterfaces* tp_interfaces = tp->_interfaces; - const TypeInterfaces* this_interfaces = _interfaces; +const Type* TypeInstKlassPtr::xjoin(const Type* t) const { + if (base() != InstKlassPtr) { + typerr(t); + } - switch (ptr) { - case TopPTR: - case AnyNull: // Fall 'down' to dual of object klass - // For instances when a subclass meets a superclass we fall - // below the centerline when the superclass is exact. We need to - // do the same here. - // - // Flat in array: See explanation for meet with TypeInstPtr in TypeAryPtr::xmeet_helper(). - if (klass()->equals(ciEnv::current()->Object_klass()) && tp_interfaces->contains(this_interfaces) && - !klass_is_exact() && !is_not_flat_in_array()) { - return TypeAryKlassPtr::make(ptr, tp->elem(), tp->klass(), offset, tp->is_not_flat(), tp->is_not_null_free(), tp->is_flat(), tp->is_null_free(), tp->is_atomic(), tp->is_refined_type()); - } else { - // cannot subclass, so the meet has to fall badly below the centerline - ptr = NotNull; - interfaces = _interfaces->intersection_with(tp->_interfaces); - FlatInArray flat_in_array = meet_flat_in_array(_flat_in_array, NotFlat); - return make(ptr, ciEnv::current()->Object_klass(), interfaces, offset, flat_in_array); + switch (t->base()) { + case AnyPtr: { + const TypePtr* tp = t->is_ptr(); + PTR ptr = join_ptr(tp->ptr()); + Offset offset = join_offset(tp->offset()); + switch (tp->ptr()) { + case TopPTR: + case Null: + return TypePtr::make(AnyPtr, ptr, offset, tp->speculative(), tp->inline_depth()); + case NotNull: + case BotPTR: + return make(ptr, klass(), interfaces(), offset); + default: + typerr(t); } - case Constant: - case NotNull: - case BotPTR: { // Fall down to object klass - // LCA is object_klass, but if we subclass from the top we can do better - if( above_centerline(_ptr) ) { // if( _ptr == TopPTR || _ptr == AnyNull ) - // If 'this' (InstPtr) is above the centerline and it is Object class - // then we can subclass in the Java class hierarchy. - // For instances when a subclass meets a superclass we fall - // below the centerline when the superclass is exact. We need - // to do the same here. - // - // Flat in array: See explanation for meet with TypeInstPtr in TypeAryPtr::xmeet_helper(). - if (klass()->equals(ciEnv::current()->Object_klass()) && tp_interfaces->contains(this_interfaces) && - !klass_is_exact() && !is_not_flat_in_array()) { - // that is, tp's array type is a subtype of my klass - return TypeAryKlassPtr::make(ptr, tp->elem(), tp->klass(), offset, tp->is_not_flat(), tp->is_not_null_free(), tp->is_flat(), tp->is_null_free(), tp->is_atomic(), tp->is_refined_type()); - } - } - // The other case cannot happen, since I cannot be a subtype of an array. - // The meet falls down to Object class below centerline. - if( ptr == Constant ) - ptr = NotNull; - interfaces = this_interfaces->intersection_with(tp_interfaces); - FlatInArray flat_in_array = meet_flat_in_array(_flat_in_array, NotFlat); - return make(ptr, ciEnv::current()->Object_klass(), interfaces, offset, flat_in_array); - } - default: typerr(t); } - } - } // End of switch - return this; // Return the double constant -} + case InstKlassPtr: + case AryKlassPtr: + return TypeJavaPtrJoinHelper::javaptr_type_xjoin(this, t->is_klassptr()); -//------------------------------xdual------------------------------------------ -// Dual: compute field-by-field dual -const Type* TypeInstKlassPtr::xdual() const { - return new TypeInstKlassPtr(dual_ptr(), klass(), _interfaces, dual_offset(), dual_flat_in_array()); + default: + typerr(t); + } } template bool TypePtr::is_java_subtype_of_helper_for_instance(const T1* this_one, const T2* other, bool this_exact, bool other_exact) { @@ -6765,6 +5973,10 @@ bool TypeAryPtr::can_be_inline_array() const { } const TypeAryKlassPtr *TypeAryKlassPtr::make(PTR ptr, const Type* elem, ciKlass* k, Offset offset, bool not_flat, bool not_null_free, bool flat, bool null_free, bool atomic, bool refined_type) { + if (ptr == TypePtr::Constant && elem->isa_klassptr() != nullptr) { + // If an array klass ptr is a constant, its element is also a constant + elem = elem->is_klassptr()->cast_to_exactness(true); + } return (TypeAryKlassPtr*)(new TypeAryKlassPtr(ptr, elem, k, offset, not_flat, not_null_free, flat, null_free, atomic, refined_type))->hashcons(); } @@ -6876,7 +6088,7 @@ ciKlass* TypeAryPtr::klass() const { // Oops, need to compute _klass and cache it ciKlass* k_ary = compute_klass(); - if( this != TypeAryPtr::OOPS && this->dual() != TypeAryPtr::OOPS ) { + if (this != TypeAryPtr::OOPS) { // The _klass field acts as a cache of the underlying // ciKlass for this array type. In order to set the field, // we need to cast away const-ness. @@ -7001,33 +6213,37 @@ const TypeAryPtr* TypeAryKlassPtr::as_exact_instance_type(bool klass_change) con } else { el = elem(); } - bool flat, not_flat, not_null_free, atomic; + bool flat, not_flat, null_free, not_null_free, atomic; if (_refined_type) { if (_null_free && el->isa_ptr()) { el = el->is_ptr()->join_speculative(TypePtr::NOTNULL); } flat = is_flat(); not_flat = is_not_flat(); + null_free = is_null_free(); not_null_free = is_not_null_free(); atomic = is_atomic(); } else { // Unrefined types aren't trustworthy! Let's not mistake their ignorance for information. // We can always have arrays of references. Flatness is not guaranteed. flat = false; + null_free = false; // There are asserts that expect us to not be entirely naive about properties. // Only arrays of value classes can be null free. Otherwise, not_null_free == true. That is if the element type // is not an instance class, or this instance class cannot be an inline type, it's surely not null-restricted. - not_null_free = !elem()->isa_instklassptr() || !elem()->is_instklassptr()->can_be_inline_type(); + not_null_free = is_java_primitive(elem()->basic_type()) || + elem()->isa_aryklassptr() != nullptr || + (elem()->isa_instklassptr() != nullptr && !elem()->is_instklassptr()->can_be_inline_type()); bool array_can_be_flat; if (elem()->isa_instklassptr()) { FlatInArray elem_flat_in_array = elem()->is_instklassptr()->flat_in_array(); array_can_be_flat = elem_flat_in_array == MaybeFlat || elem_flat_in_array == Flat; } else { - array_can_be_flat = false; + array_can_be_flat = elem() == Type::BOTTOM; } not_flat = !array_can_be_flat; atomic = !array_can_be_flat; } - return TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(el, TypeInt::POS, false, flat, not_flat, not_null_free, atomic), k, xk, Offset(0)); + return TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(el, TypeInt::POS, false, flat, not_flat, null_free, not_null_free, atomic), k, xk, Offset(0)); } // Corresponding type for instances that subtype the given class @@ -7037,31 +6253,12 @@ const TypeAryPtr* TypeAryKlassPtr::as_subtype_instance_type(bool klass_change) c //------------------------------xmeet------------------------------------------ // Compute the MEET of two types, return a new Type object. -const Type *TypeAryKlassPtr::xmeet( const Type *t ) const { - // Perform a fast test for common case; meeting the same types together. - if( this == t ) return this; // Meeting same type-rep? +const Type* TypeAryKlassPtr::xmeet(const Type* t) const { + if (base() != AryKlassPtr) { + typerr(t); + } - // Current "this->_base" is Pointer switch (t->base()) { // switch on original type - - case Int: // Mixing ints & oops happens when javac - case Long: // reuses local variables - case HalfFloatTop: - case HalfFloatCon: - case HalfFloatBot: - case FloatTop: - case FloatCon: - case FloatBot: - case DoubleTop: - case DoubleCon: - case DoubleBot: - case NarrowOop: - case NarrowKlass: - case Bottom: // Ye Olde Default - return Type::BOTTOM; - case Top: - return this; - default: // All else is a mistake typerr(t); @@ -7084,135 +6281,43 @@ const Type *TypeAryKlassPtr::xmeet( const Type *t ) const { } } - case RawPtr: - case MetadataPtr: - case OopPtr: - case AryPtr: // Meet with AryPtr - case InstPtr: // Meet with InstPtr - return TypePtr::BOTTOM; + case AryKlassPtr: + case InstKlassPtr: + return TypeJavaPtrMeetHelper::javaptr_type_xmeet(this, t->is_klassptr()); - // - // A-top } - // / | \ } Tops - // B-top A-any C-top } - // | / | \ | } Any-nulls - // B-any | C-any } - // | | | - // B-con A-con C-con } constants; not comparable across classes - // | | | - // B-not | C-not } - // | \ | / | } not-nulls - // B-bot A-not C-bot } - // \ | / } Bottoms - // A-bot } - // + } // End of switch + return this; // Return the double constant +} - case AryKlassPtr: { // Meet two KlassPtr types - const TypeAryKlassPtr *tap = t->is_aryklassptr(); - Offset off = meet_offset(tap->offset()); - const Type* elem = _elem->meet(tap->_elem); - PTR ptr = meet_ptr(tap->ptr()); - ciKlass* res_klass = nullptr; - bool res_xk = false; - bool res_flat = false; - bool res_not_flat = false; - bool res_not_null_free = false; - bool res_atomic = false; - MeetResult res = meet_aryptr(ptr, elem, this, tap, - res_klass, res_xk, res_flat, res_not_flat, res_not_null_free, res_atomic); - assert(res_xk == (ptr == Constant), ""); - bool flat = meet_flat(tap->_flat); - bool null_free = meet_null_free(tap->_null_free); - bool atomic = meet_atomic(tap->_atomic); - bool refined_type = _refined_type && tap->_refined_type; - if (res == NOT_SUBTYPE) { - flat = false; - null_free = false; - atomic = false; - refined_type = false; - } else if (res == SUBTYPE) { - if (above_centerline(tap->ptr()) && !above_centerline(this->ptr())) { - flat = _flat; - null_free = _null_free; - atomic = _atomic; - refined_type = _refined_type; - } else if (above_centerline(this->ptr()) && !above_centerline(tap->ptr())) { - flat = tap->_flat; - null_free = tap->_null_free; - atomic = tap->_atomic; - refined_type = tap->_refined_type; - } else if (above_centerline(this->ptr()) && above_centerline(tap->ptr())) { - flat = _flat || tap->_flat; - null_free = _null_free || tap->_null_free; - atomic = _atomic || tap->_atomic; - refined_type = _refined_type || tap->_refined_type; - } else if (res_xk && _refined_type != tap->_refined_type) { - // This can happen if the phi emitted by LibraryCallKit::load_default_refined_array_klass/load_non_refined_array_klass - // is processed before the typeArray guard is folded. Both inputs are constant but the input corresponding to the - // typeArray will go away. Don't constant fold it yet but wait for the control input to collapse. - ptr = PTR::NotNull; - } - } - return make(ptr, elem, res_klass, off, res_not_flat, res_not_null_free, flat, null_free, atomic, refined_type); - } // End of case KlassPtr - case InstKlassPtr: { - const TypeInstKlassPtr *tp = t->is_instklassptr(); - Offset offset = meet_offset(tp->offset()); - PTR ptr = meet_ptr(tp->ptr()); - const TypeInterfaces* interfaces = meet_interfaces(tp); - const TypeInterfaces* tp_interfaces = tp->_interfaces; - const TypeInterfaces* this_interfaces = _interfaces; +const Type* TypeAryKlassPtr::xjoin(const Type* t) const { + if (base() != AryKlassPtr) { + typerr(t); + } - switch (ptr) { - case TopPTR: - case AnyNull: // Fall 'down' to dual of object klass - // For instances when a subclass meets a superclass we fall - // below the centerline when the superclass is exact. We need to - // do the same here. - // - // Flat in array: See explanation for meet with TypeInstPtr in TypeAryPtr::xmeet_helper(). - if (tp->klass()->equals(ciEnv::current()->Object_klass()) && this_interfaces->contains(tp_interfaces) && - !tp->klass_is_exact() && !tp->is_not_flat_in_array()) { - return TypeAryKlassPtr::make(ptr, _elem, _klass, offset, is_not_flat(), is_not_null_free(), is_flat(), is_null_free(), is_atomic(), is_refined_type()); - } else { - // cannot subclass, so the meet has to fall badly below the centerline - ptr = NotNull; - interfaces = this_interfaces->intersection_with(tp->_interfaces); - FlatInArray flat_in_array = meet_flat_in_array(NotFlat, tp->flat_in_array()); - return TypeInstKlassPtr::make(ptr, ciEnv::current()->Object_klass(), interfaces, offset, flat_in_array); + switch (t->base()) { + case AnyPtr: { + const TypePtr* tp = t->is_ptr(); + PTR ptr = join_ptr(tp->ptr()); + Offset offset = join_offset(tp->offset()); + switch (tp->ptr()) { + case TopPTR: + case Null: + return TypePtr::make(AnyPtr, ptr, offset, tp->speculative(), tp->inline_depth()); + case NotNull: + case BotPTR: + return make(ptr, elem(), klass(), offset, is_not_flat(), is_not_null_free(), is_flat(), is_null_free(), is_atomic(), is_refined_type()); + default: + typerr(t); } - case Constant: - case NotNull: - case BotPTR: { // Fall down to object klass - // LCA is object_klass, but if we subclass from the top we can do better - if (above_centerline(tp->ptr())) { - // If 'tp' is above the centerline and it is Object class - // then we can subclass in the Java class hierarchy. - // For instances when a subclass meets a superclass we fall - // below the centerline when the superclass is exact. We need - // to do the same here. - // - // Flat in array: See explanation for meet with TypeInstPtr in TypeAryPtr::xmeet_helper(). - if (tp->klass()->equals(ciEnv::current()->Object_klass()) && this_interfaces->contains(tp_interfaces) && - !tp->klass_is_exact() && !tp->is_not_flat_in_array()) { - // that is, my array type is a subtype of 'tp' klass - return make(ptr, _elem, _klass, offset, is_not_flat(), is_not_null_free(), is_flat(), is_null_free(), is_atomic(), is_refined_type()); - } - } - // The other case cannot happen, since t cannot be a subtype of an array. - // The meet falls down to Object class below centerline. - if (ptr == Constant) - ptr = NotNull; - interfaces = this_interfaces->intersection_with(tp_interfaces); - FlatInArray flat_in_array = meet_flat_in_array(NotFlat, tp->flat_in_array()); - return TypeInstKlassPtr::make(ptr, ciEnv::current()->Object_klass(), interfaces, offset, flat_in_array); } - default: typerr(t); - } - } - } // End of switch - return this; // Return the double constant + case InstKlassPtr: + case AryKlassPtr: + return TypeJavaPtrJoinHelper::javaptr_type_xjoin(this, t->is_klassptr()); + + default: + typerr(t); + } } template bool TypePtr::is_java_subtype_of_helper_for_array(const T1* this_one, const T2* other, bool this_exact, bool other_exact) { @@ -7336,12 +6441,6 @@ bool TypeAryKlassPtr::maybe_java_subtype_of_helper(const TypeKlassPtr* other, bo return TypePtr::maybe_java_subtype_of_helper_for_array(this, other, this_exact, other_exact); } -//------------------------------xdual------------------------------------------ -// Dual: compute field-by-field dual -const Type *TypeAryKlassPtr::xdual() const { - return new TypeAryKlassPtr(dual_ptr(), elem()->dual(), klass(), dual_offset(), !is_not_flat(), !is_not_null_free(), dual_flat(), dual_null_free(), dual_atomic(), _refined_type); -} - // Is there a single ciKlass* that can represent that type? ciKlass* TypeAryKlassPtr::exact_klass_helper() const { if (elem()->isa_klassptr()) { @@ -7350,7 +6449,7 @@ ciKlass* TypeAryKlassPtr::exact_klass_helper() const { return nullptr; } assert(!k->is_array_klass() || !k->as_array_klass()->is_refined(), "no mechanism to create an array of refined arrays %s", k->name()->as_utf8()); - k = ciArrayKlass::make(k, is_null_free(), is_atomic(), _refined_type); + k = ciObjArrayKlass::make(k, _refined_type, is_null_free(), is_atomic(), is_not_flat()); return k; } @@ -7462,33 +6561,6 @@ const TypeFunc* TypeFunc::make(ciMethod* method, bool is_call, bool is_osr_compi return tf; } -//------------------------------meet------------------------------------------- -// Compute the MEET of two types. It returns a new Type object. -const Type *TypeFunc::xmeet( const Type *t ) const { - // Perform a fast test for common case; meeting the same types together. - if( this == t ) return this; // Meeting same type-rep? - - // Current "this->_base" is Func - switch (t->base()) { // switch on original type - - case Bottom: // Ye Olde Default - return t; - - default: // All else is a mistake - typerr(t); - - case Top: - break; - } - return this; // Return the double constant -} - -//------------------------------xdual------------------------------------------ -// Dual: compute field-by-field dual -const Type *TypeFunc::xdual() const { - return this; -} - //------------------------------eq--------------------------------------------- // Structural equality check for Type representations bool TypeFunc::eq( const Type *t ) const { diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 03a641bee870..15587dc740e7 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -29,6 +29,7 @@ #include "opto/adlcVMDeps.hpp" #include "opto/compile.hpp" #include "opto/rangeinference.hpp" +#include "utilities/debug.hpp" // Portions of code courtesy of Clifford Click @@ -73,7 +74,7 @@ class TypeKlassPtr; class TypeInstKlassPtr; class TypeAryKlassPtr; class TypeMetadataPtr; -class VerifyMeet; +class VerifyMeetJoin; template class TypeIntPrototype; @@ -84,6 +85,7 @@ class TypeIntPrototype; // different kind of Type exists. Types are never modified after creation, so // all their interesting fields are constant. class Type { + friend class VerifyMeetJoinResult; public: enum TYPES { @@ -149,18 +151,18 @@ class Type { int _offset; public: - explicit Offset(int offset) : _offset(offset) {} + constexpr explicit Offset(int offset) : _offset(offset) {} const Offset meet(const Offset other) const; - const Offset dual() const; + const Offset join(const Offset other) const; const Offset add(intptr_t offset) const; - bool operator==(const Offset& other) const { + constexpr bool operator==(const Offset& other) const { return _offset == other._offset; } - bool operator!=(const Offset& other) const { + constexpr bool operator!=(const Offset& other) const { return _offset != other._offset; } - int get() const { return _offset; } + constexpr int get() const { return _offset; } void dump2(outputStream *st) const; @@ -197,29 +199,28 @@ class Type { return Compile::current()->type_dict(); } - // DUAL operation: reflect around lattice centerline. Used instead of - // join to ensure my lattice is symmetric up and down. Dual is computed - // lazily, on demand, and cached in _dual. - const Type *_dual; // Cached dual value + template + static const Type* meet_join_helper(F op, const Type* t1, const Type* t2, bool include_speculative); + const Type* meet_helper(const Type* t, bool include_speculative) const; - const Type *meet_helper(const Type *t, bool include_speculative) const; - void check_symmetrical(const Type* t, const Type* mt, const VerifyMeet& verify) const NOT_DEBUG_RETURN; + static void check_fundamental_laws(const Type* t1, const Type* t2, VerifyMeetJoin& verify) NOT_DEBUG_RETURN; + static const Type* xmeet(const Type* t1, const Type* t2); + static const Type* xjoin(const Type* t1, const Type* t2); + + // Compute meet and join, overriden by subclasses + virtual const Type* xmeet(const Type* t) const; + virtual const Type* xjoin(const Type* t) const; protected: // Each class of type is also identified by its base. - const TYPES _base; // Enum of Types type - - Type( TYPES t ) : _dual(nullptr), _base(t) {} // Simple types - // ~Type(); // Use fast deallocation - const Type *hashcons(); // Hash-cons the type - virtual const Type *filter_helper(const Type *kills, bool include_speculative) const; - const Type *join_helper(const Type *t, bool include_speculative) const { - assert_type_verify_empty(); - return dual()->meet_helper(t->dual(), include_speculative)->dual(); - } + const TYPES _base; // Enum of Types type - void assert_type_verify_empty() const NOT_DEBUG_RETURN; + Type(TYPES t) : _base(t) {} // Simple types + // ~Type(); // Use fast deallocation + const Type* hashcons(); // Hash-cons the type + virtual const Type* filter_helper(const Type* kills, bool include_speculative) const; + const Type* join_helper(const Type* t, bool include_speculative) const; public: @@ -258,7 +259,7 @@ class Type { return equals(meet_speculative(t), t); } - // MEET operation; lower in lattice. + // MEET operations // Variant that drops the speculative part of the types const Type *meet(const Type *t) const { return meet_helper(t, false); @@ -272,16 +273,7 @@ class Type { // NARROW: complement for widen, used by pessimistic phases virtual const Type *narrow( const Type *old ) const { return this; } - // DUAL operation: reflect around lattice centerline. Used instead of - // join to ensure my lattice is symmetric up and down. - const Type *dual() const { return _dual; } - - // Compute meet dependent on base type - virtual const Type *xmeet( const Type *t ) const; - virtual const Type *xdual() const; // Compute dual right now. - - // JOIN operation; higher in lattice. Done by finding the dual of the - // meet of the dual of the 2 inputs. + // JOIN operations // Variant that drops the speculative part of the types const Type *join(const Type *t) const { return join_helper(t, false); @@ -292,7 +284,7 @@ class Type { } // Modified version of JOIN adapted to the needs Node::Value. - // Normalizes all empty values to TOP. Does not kill _widen bits. + // Normalizes all empty values to TOP. Does not kill _widen bits. // Variant that drops the speculative part of the types const Type *filter(const Type *kills) const { return filter_helper(kills, false); @@ -429,7 +421,7 @@ class Type { static const char* str(const Type* t); #endif // !PRODUCT - void typerr(const Type *t) const; // Mixing types error + [[noreturn]] void typerr(const Type *t) const; // Mixing types error // Create basic type static const Type* get_const_basic_type(BasicType type) { @@ -533,6 +525,9 @@ class Type { static const Type* _const_basic_type[T_CONFLICT+1]; }; +inline constexpr Type::Offset Type::Offset::top(Type::OffsetTop); +inline constexpr Type::Offset Type::Offset::bottom(Type::OffsetBot); + //------------------------------TypeF------------------------------------------ // Class of Float-Constant Types. class TypeF : public Type { @@ -550,8 +545,8 @@ class TypeF : public Type { virtual bool is_finite() const; // Has a finite value virtual bool is_nan() const; // Is not a number (NaN) - virtual const Type *xmeet( const Type *t ) const; - virtual const Type *xdual() const; // Compute dual right now. + virtual const Type* xmeet(const Type* t) const; + virtual const Type* xjoin(const Type* t) const; // Convenience common pre-built types. static const TypeF *MAX; static const TypeF *MIN; @@ -583,7 +578,7 @@ class TypeH : public Type { virtual float getf() const; virtual const Type* xmeet(const Type* t) const; - virtual const Type* xdual() const; // Compute dual right now. + virtual const Type* xjoin(const Type* t) const; // Convenience common pre-built types. static const TypeH* MAX; static const TypeH* MIN; @@ -613,8 +608,8 @@ class TypeD : public Type { virtual bool is_finite() const; // Has a finite value virtual bool is_nan() const; // Is not a number (NaN) - virtual const Type *xmeet( const Type *t ) const; - virtual const Type *xdual() const; // Compute dual right now. + virtual const Type* xmeet(const Type* t) const; + virtual const Type* xjoin(const Type* t) const; // Convenience common pre-built types. static const TypeD *MAX; static const TypeD *MIN; @@ -629,12 +624,7 @@ class TypeD : public Type { class TypeInteger : public Type { protected: - TypeInteger(TYPES t, int w, bool dual) : Type(t), _is_dual(dual), _widen(w) {} - - // Denote that a set is a dual set. - // Dual sets are only used to compute the join of 2 sets, and not used - // outside. - const bool _is_dual; + TypeInteger(TYPES t, int w) : Type(t), _widen(w) {} public: const short _widen; // Limit on times we widen this sucker @@ -800,8 +790,7 @@ class TypeInteger : public Type { */ class TypeInt : public TypeInteger { private: - TypeInt(const TypeIntPrototype& t, int w, bool dual); - static const Type* make_or_top(const TypeIntPrototype& t, int widen, bool dual); + TypeInt(const TypeIntPrototype& t, int w); friend class TypeIntHelper; @@ -828,7 +817,7 @@ class TypeInt : public TypeInteger { static const Type* make_or_top(const TypeIntPrototype& t, int widen); static const TypeInt* make(const TypeIntPrototype& t, int widen) { return make_or_top(t, widen)->is_int(); } static const TypeInt* make(const TypeIntMirror& t, int widen) { - return (new TypeInt(TypeIntPrototype{{t._lo, t._hi}, {t._ulo, t._uhi}, t._bits}, widen, false))->hashcons()->is_int(); + return (new TypeInt(TypeIntPrototype{{t._lo, t._hi}, {t._ulo, t._uhi}, t._bits}, widen))->hashcons()->is_int(); } // Check for single integer @@ -848,7 +837,7 @@ class TypeInt : public TypeInteger { virtual bool is_finite() const; // Has a finite value virtual const Type* xmeet(const Type* t) const; - virtual const Type* xdual() const; // Compute dual right now. + virtual const Type* xjoin(const Type* t) const; virtual const Type* widen(const Type* t, const Type* limit_type) const; virtual const Type* narrow(const Type* t) const; @@ -891,8 +880,7 @@ class TypeInt : public TypeInteger { // Similar to TypeInt class TypeLong : public TypeInteger { private: - TypeLong(const TypeIntPrototype& t, int w, bool dual); - static const Type* make_or_top(const TypeIntPrototype& t, int widen, bool dual); + TypeLong(const TypeIntPrototype& t, int w); friend class TypeIntHelper; @@ -920,7 +908,7 @@ class TypeLong : public TypeInteger { static const Type* make_or_top(const TypeIntPrototype& t, int widen); static const TypeLong* make(const TypeIntPrototype& t, int widen) { return make_or_top(t, widen)->is_long(); } static const TypeLong* make(const TypeIntMirror& t, int widen) { - return (new TypeLong(TypeIntPrototype{{t._lo, t._hi}, {t._ulo, t._uhi}, t._bits}, widen, false))->hashcons()->is_long(); + return (new TypeLong(TypeIntPrototype{{t._lo, t._hi}, {t._ulo, t._uhi}, t._bits}, widen))->hashcons()->is_long(); } // Check for single integer @@ -946,7 +934,7 @@ class TypeLong : public TypeInteger { virtual jlong lo_as_long() const { return _lo; } virtual const Type* xmeet(const Type* t) const; - virtual const Type* xdual() const; // Compute dual right now. + virtual const Type* xjoin(const Type* t) const; virtual const Type* widen(const Type* t, const Type* limit_type) const; virtual const Type* narrow(const Type* t) const; // Convenience common pre-built types. @@ -1007,8 +995,8 @@ class TypeTuple : public Type { // Memory for Control, I_O, Memory, FramePtr, and ReturnAdr is allocated implicitly static const Type **fields( uint arg_cnt ); - virtual const Type *xmeet( const Type *t ) const; - virtual const Type *xdual() const; // Compute dual right now. + virtual const Type* xmeet(const Type* t) const; + virtual const Type* xjoin(const Type* t) const; // Convenience common pre-built types. static const TypeTuple *IFBOTH; static const TypeTuple *IFFALSE; @@ -1030,8 +1018,8 @@ class TypeTuple : public Type { //------------------------------TypeAry---------------------------------------- // Class of Array Types class TypeAry : public Type { - TypeAry(const Type* elem, const TypeInt* size, bool stable, bool flat, bool not_flat, bool not_null_free, bool atomic) : Type(Array), - _elem(elem), _size(size), _stable(stable), _flat(flat), _not_flat(not_flat), _not_null_free(not_null_free), _atomic(atomic) {} + TypeAry(const Type* elem, const TypeInt* size, bool stable, bool flat, bool not_flat, bool null_free, bool not_null_free, bool atomic) : Type(Array), + _elem(elem), _size(size), _stable(stable), _flat(flat), _not_flat(not_flat), _null_free(null_free), _not_null_free(not_null_free), _atomic(atomic) {} public: virtual bool eq( const Type *t ) const; virtual uint hash() const; // Type specific hashing @@ -1046,6 +1034,7 @@ class TypeAry : public Type { // Inline type array properties const bool _flat; // Array is flat const bool _not_flat; // Array is never flat + const bool _null_free; // Array is null-free const bool _not_null_free; // Array is never null-free const bool _atomic; // Array is atomic @@ -1053,10 +1042,10 @@ class TypeAry : public Type { public: static const TypeAry* make(const Type* elem, const TypeInt* size, bool stable, - bool flat, bool not_flat, bool not_null_free, bool atomic); + bool flat, bool not_flat, bool null_free, bool not_null_free, bool atomic); - virtual const Type *xmeet( const Type *t ) const; - virtual const Type *xdual() const; // Compute dual right now. + virtual const Type* xmeet(const Type* t) const { ShouldNotReachHere(); } + virtual const Type* xjoin(const Type* t) const { ShouldNotReachHere(); } bool ary_must_be_exact() const; // true if arrays of such are never generic virtual const TypeAry* remove_speculative() const; virtual const Type* cleanup_speculative() const; @@ -1090,9 +1079,6 @@ class TypeVect : public Type { static const TypeVect* make(const BasicType elem_bt, uint length, bool is_mask = false); static const TypeVect* makemask(const BasicType elem_bt, uint length); - virtual const Type* xmeet( const Type *t) const; - virtual const Type* xdual() const; // Compute dual right now. - static const TypeVect* VECTA; static const TypeVect* VECTS; static const TypeVect* VECTD; @@ -1179,7 +1165,6 @@ class TypeInterfaces : public Type { bool eq(ciInstanceKlass* k) const; bool is_subset(ciInstanceKlass* k) const; uint hash() const; - const Type *xdual() const; void dump(outputStream* st) const; const TypeInterfaces* union_with(const TypeInterfaces* other) const; const TypeInterfaces* intersection_with(const TypeInterfaces* other) const; @@ -1194,8 +1179,6 @@ class TypeInterfaces : public Type { static int compare(ciInstanceKlass* const& k1, ciInstanceKlass* const& k2); static int compare(ciInstanceKlass** k1, ciInstanceKlass** k2); - const Type* xmeet(const Type* t) const; - bool singleton(void) const; bool has_non_array_interface() const; }; @@ -1214,19 +1197,8 @@ class TypePtr : public Type { public: enum PTR { TopPTR, AnyNull, Constant, Null, NotNull, BotPTR, lastPTR }; - // Only applies to TypeInstPtr and TypeInstKlassPtr. Since the common super class is TypePtr, it is defined here. - // - // FlatInArray defines the following Boolean Lattice structure - // - // TopFlat - // / \ - // Flat NotFlat - // \ / - // MaybeFlat - // - // with meet (see TypePtr::meet_flat_in_array()) and join (implemented over dual, see TypePtr::flat_in_array_dual) enum FlatInArray { - TopFlat, // Dedicated top element and dual of MaybeFlat. Result when joining Flat and NotFlat. + TopFlat, // Dedicated top element. Result when joining Flat and NotFlat. Flat, // An instance is always flat in an array. NotFlat, // An instance is never flat in an array. MaybeFlat, // We don't know whether an instance is flat in an array. @@ -1237,13 +1209,16 @@ class TypePtr : public Type { relocInfo::relocType reloc, const TypePtr* speculative = nullptr, int inline_depth = InlineDepthBottom) : - Type(t), _speculative(speculative), _inline_depth(inline_depth), _offset(offset), - _ptr(ptr), _reloc(reloc) {} + Type(t), _speculative(speculative), _inline_depth(inline_depth), _offset(offset), _ptr(ptr), _reloc(reloc) { + assert(t == AnyPtr || (ptr != TopPTR && ptr != Null), "Top and Null must be AnyPtr"); + assert(ptr != AnyNull, "Nonsensical PTR"); + assert(ptr == TopPTR || offset != Offset::top, "only TopPTR can have top offset"); + assert(static_cast(ptr) < static_cast(lastPTR), "out of bounds"); + } static const PTR ptr_meet[lastPTR][lastPTR]; static const PTR ptr_dual[lastPTR]; static const char * const ptr_msg[lastPTR]; - static const FlatInArray flat_in_array_dual[Uninitialized]; static const char* const flat_in_array_msg[Uninitialized]; enum { @@ -1263,51 +1238,29 @@ class TypePtr : public Type { int _inline_depth; // utility methods to work on the speculative part of the type - const TypePtr* dual_speculative() const; const TypePtr* xmeet_speculative(const TypePtr* other) const; + const TypePtr* xjoin_speculative(const TypePtr* other) const; bool eq_speculative(const TypePtr* other) const; int hash_speculative() const; const TypePtr* add_offset_speculative(intptr_t offset) const; const TypePtr* with_offset_speculative(intptr_t offset) const; // utility methods to work on the inline depth of the type - int dual_inline_depth() const; int meet_inline_depth(int depth) const; - + int join_inline_depth(int depth) const; #ifndef PRODUCT void dump_speculative(outputStream* st) const; void dump_inline_depth(outputStream* st) const; void dump_offset(outputStream* st) const; #endif - // TypeInstPtr (TypeAryPtr resp.) and TypeInstKlassPtr (TypeAryKlassPtr resp.) implement very similar meet logic. - // The logic for meeting 2 instances (2 arrays resp.) is shared in the 2 utility methods below. However the logic for - // the oop and klass versions can be slightly different and extra logic may have to be executed depending on what - // exact case the meet falls into. The MeetResult struct is used by the utility methods to communicate what case was - // encountered so the right logic specific to klasses or oops can be executed., - enum MeetResult { - QUICK, - UNLOADED, - SUBTYPE, - NOT_SUBTYPE, - LCA - }; - template static TypePtr::MeetResult meet_instptr(PTR& ptr, const TypeInterfaces*& interfaces, const T* this_type, - const T* other_type, ciKlass*& res_klass, bool& res_xk); - protected: - static FlatInArray meet_flat_in_array(FlatInArray left, FlatInArray other); - - template static MeetResult meet_aryptr(PTR& ptr, const Type*& elem, const T* this_ary, const T* other_ary, - ciKlass*& res_klass, bool& res_xk, bool &res_flat, bool &res_not_flat, bool &res_not_null_free, bool &res_atomic); - template static bool is_java_subtype_of_helper_for_instance(const T1* this_one, const T2* other, bool this_exact, bool other_exact); template static bool is_same_java_type_as_helper_for_instance(const T1* this_one, const T2* other); template static bool maybe_java_subtype_of_helper_for_instance(const T1* this_one, const T2* other, bool this_exact, bool other_exact); template static bool is_java_subtype_of_helper_for_array(const T1* this_one, const T2* other, bool this_exact, bool other_exact); template static bool is_same_java_type_as_helper_for_array(const T1* this_one, const T2* other); template static bool maybe_java_subtype_of_helper_for_array(const T1* this_one, const T2* other, bool this_exact, bool other_exact); - template static bool is_meet_subtype_of_helper_for_instance(const T1* this_one, const T2* other, bool this_xk, bool other_xk); - template static bool is_meet_subtype_of_helper_for_array(const T1* this_one, const T2* other, bool this_xk, bool other_xk); + public: const Offset _offset; // Offset into oop, with TOP & BOT const PTR _ptr; // Pointer equivalence class @@ -1336,13 +1289,14 @@ class TypePtr : public Type { virtual bool singleton(void) const; // TRUE if type is a singleton virtual bool empty(void) const; // TRUE if type is vacuous - virtual const Type *xmeet( const Type *t ) const; - virtual const Type *xmeet_helper( const Type *t ) const; + virtual const Type* xmeet(const Type* t) const; + virtual const Type* xmeet_helper(const Type* t) const; + virtual const Type* xjoin(const Type* t) const; + virtual const Type* xjoin_helper(const Type* t) const; Offset meet_offset(int offset) const; - Offset dual_offset() const; - virtual const Type *xdual() const; // Compute dual right now. + Offset join_offset(int offset) const; - // meet, dual and join over pointer equivalence sets + // meet and join over pointer equivalence sets PTR meet_ptr( const PTR in_ptr ) const { return ptr_meet[in_ptr][ptr()]; } PTR dual_ptr() const { return ptr_dual[ptr()]; } @@ -1419,8 +1373,8 @@ class TypeRawPtr : public TypePtr { virtual const TypePtr* add_offset(intptr_t offset) const; virtual const TypeRawPtr* with_offset(intptr_t offset) const { ShouldNotReachHere(); return nullptr;} - virtual const Type *xmeet( const Type *t ) const; - virtual const Type *xdual() const; // Compute dual right now. + virtual const Type* xmeet(const Type* t) const; + virtual const Type* xjoin(const Type* t) const; // Convenience common pre-built types. static const TypeRawPtr *BOTTOM; static const TypeRawPtr *NOTNULL; @@ -1447,6 +1401,13 @@ class TypeOopPtr : public TypePtr { InstanceTop = -1, // undefined instance InstanceBot = 0 // any possible instance }; + + static constexpr bool is_oopptr_type = true; + using ciEnv = ::ciEnv; + using PtrType = TypePtr; + using InstType = TypeInstPtr; + using AryType = TypeAryPtr; + protected: // Oop is null, unless this is a constant oop. @@ -1470,10 +1431,11 @@ class TypeOopPtr : public TypePtr { static const TypeOopPtr* make_from_klass_common(ciKlass* klass, bool klass_change, bool try_for_exact, InterfaceHandling interface_handling); - int dual_instance_id() const; int meet_instance_id(int uid) const; + int join_instance_id(int uid) const; const TypeInterfaces* meet_interfaces(const TypeOopPtr* other) const; + const TypeInterfaces* join_interfaces(const TypeOopPtr* other) const; // Do not allow interface-vs.-noninterface joins to collapse to top. virtual const Type *filter_helper(const Type *kills, bool include_speculative) const; @@ -1539,8 +1501,9 @@ class TypeOopPtr : public TypePtr { ciKlass* exact_klass(bool maybe_null = false) const { assert(klass_is_exact(), ""); ciKlass* k = exact_klass_helper(); assert(k != nullptr || maybe_null, ""); return k; } ciKlass* unloaded_klass() const { assert(!is_loaded(), "only for unloaded types"); return klass(); } - virtual bool is_loaded() const { return klass()->is_loaded(); } - virtual bool klass_is_exact() const { return _klass_is_exact; } + virtual bool is_loaded() const { return klass()->is_loaded(); } + virtual bool klass_is_exact() const { return _klass_is_exact; } + const TypeInterfaces* interfaces() const { return _interfaces; } // Returns true if this pointer points at memory which contains a // compressed oop references. @@ -1586,9 +1549,9 @@ class TypeOopPtr : public TypePtr { virtual const TypePtr* with_instance_id(int instance_id) const; - virtual const Type *xdual() const; // Compute dual right now. // the core of the computation of the meet for TypeOopPtr and for its subclasses - virtual const Type *xmeet_helper(const Type *t) const; + virtual const Type* xmeet_helper(const Type* t) const; + virtual const Type* xjoin_helper(const Type* t) const; // Convenience common pre-built type. static const TypeOopPtr *BOTTOM; @@ -1596,18 +1559,6 @@ class TypeOopPtr : public TypePtr { virtual void dump2( Dict &d, uint depth, outputStream *st ) const; #endif private: - virtual bool is_meet_subtype_of(const TypePtr* other) const { - return is_meet_subtype_of_helper(other->is_oopptr(), klass_is_exact(), other->is_oopptr()->klass_is_exact()); - } - - virtual bool is_meet_subtype_of_helper(const TypeOopPtr* other, bool this_xk, bool other_xk) const { - ShouldNotReachHere(); return false; - } - - virtual const TypeInterfaces* interfaces() const { - return _interfaces; - }; - const TypeOopPtr* is_reference_type(const Type* other) const { return other->isa_oopptr(); } @@ -1716,14 +1667,9 @@ class TypeInstPtr : public TypeOopPtr { virtual const TypeInstPtr* cast_to_maybe_flat_in_array() const; virtual FlatInArray flat_in_array() const { return _flat_in_array; } - FlatInArray dual_flat_in_array() const { - return flat_in_array_dual[_flat_in_array]; - } - // the core of the computation of the meet of 2 types - virtual const Type *xmeet_helper(const Type *t) const; - virtual const TypeInstPtr *xmeet_unloaded(const TypeInstPtr *tinst, const TypeInterfaces* interfaces) const; - virtual const Type *xdual() const; // Compute dual right now. + virtual const Type* xmeet_helper(const Type* t) const; + virtual const Type* xjoin_helper(const Type* t) const; const TypeKlassPtr* as_klass_type(bool try_for_exact = false) const; @@ -1740,8 +1686,6 @@ class TypeInstPtr : public TypeOopPtr { #endif private: - virtual bool is_meet_subtype_of_helper(const TypeOopPtr* other, bool this_xk, bool other_xk) const; - virtual bool is_meet_same_type_as(const TypePtr* other) const { return _klass->equals(other->is_instptr()->_klass) && _interfaces->eq(other->is_instptr()->_interfaces); } @@ -1755,6 +1699,8 @@ class TypeAryPtr : public TypeOopPtr { friend class TypePtr; friend class TypeInstPtr; friend class TypeInterfaces; + friend class TypeJavaPtrMeetHelper; + friend class TypeJavaPtrJoinHelper; TypeAryPtr(PTR ptr, ciObject* o, const TypeAry *ary, ciKlass* k, bool xk, Offset offset, Offset field_offset, int instance_id, bool is_autobox_cache, @@ -1782,8 +1728,6 @@ class TypeAryPtr : public TypeOopPtr { // the array has its own memory slice so we need to keep track of // which field is accessed const Offset _field_offset; - Offset meet_field_offset(const Type::Offset offset) const; - Offset dual_field_offset() const; ciKlass* compute_klass() const; @@ -1814,7 +1758,7 @@ class TypeAryPtr : public TypeOopPtr { // Inline type array properties bool is_flat() const { return _ary->_flat; } bool is_not_flat() const { return _ary->_not_flat; } - bool is_null_free() const { return _ary->_elem->make_ptr() != nullptr && (_ary->_elem->make_ptr()->ptr() == NotNull || _ary->_elem->make_ptr()->ptr() == AnyNull); } + bool is_null_free() const { return _ary->_null_free; } bool is_not_null_free() const { return _ary->_not_null_free; } bool is_atomic() const { return _ary->_atomic; } @@ -1855,8 +1799,8 @@ class TypeAryPtr : public TypeOopPtr { virtual const TypePtr* with_instance_id(int instance_id) const; // the core of the computation of the meet of 2 types - virtual const Type *xmeet_helper(const Type *t) const; - virtual const Type *xdual() const; // Compute dual right now. + virtual const Type* xmeet_helper(const Type* t) const; + virtual const Type* xjoin_helper(const Type* t) const; // Inline type array properties const TypeAryPtr* cast_to_flat(bool flat) const; @@ -1909,8 +1853,6 @@ class TypeAryPtr : public TypeOopPtr { #ifndef PRODUCT virtual void dump2( Dict &d, uint depth, outputStream *st ) const; // Specialized per-Type dumping #endif -private: - virtual bool is_meet_subtype_of_helper(const TypeOopPtr* other, bool this_xk, bool other_xk) const; }; //------------------------------TypeMetadataPtr------------------------------------- @@ -1940,8 +1882,8 @@ class TypeMetadataPtr : public TypePtr { virtual const TypePtr *add_offset( intptr_t offset ) const; - virtual const Type *xmeet( const Type *t ) const; - virtual const Type *xdual() const; // Compute dual right now. + virtual const Type* xmeet(const Type* t) const; + virtual const Type* xjoin(const Type* t) const; virtual intptr_t get_con() const; @@ -1969,11 +1911,18 @@ class TypeKlassPtr : public TypePtr { virtual uint hash() const; virtual bool singleton(void) const; // TRUE if type is a singleton + static constexpr bool is_oopptr_type = false; + using ciEnv = ::ciEnv; + using PtrType = TypePtr; + using InstType = TypeInstKlassPtr; + using AryType = TypeAryKlassPtr; + protected: ciKlass* _klass; const TypeInterfaces* _interfaces; const TypeInterfaces* meet_interfaces(const TypeKlassPtr* other) const; + const TypeInterfaces* join_interfaces(const TypeKlassPtr* other) const; virtual bool must_be_exact() const { ShouldNotReachHere(); return false; } virtual ciKlass* exact_klass_helper() const; virtual ciKlass* klass() const { return _klass; } @@ -2011,9 +1960,9 @@ class TypeKlassPtr : public TypePtr { // corresponding pointer to instances which subtype a given class virtual const TypeOopPtr* as_subtype_instance_type(bool klass_change = true) const = 0; - virtual const TypePtr *add_offset( intptr_t offset ) const { ShouldNotReachHere(); return nullptr; } - virtual const Type *xmeet( const Type *t ) const { ShouldNotReachHere(); return nullptr; } - virtual const Type *xdual() const { ShouldNotReachHere(); return nullptr; } + virtual const TypePtr* add_offset(intptr_t offset) const { ShouldNotReachHere(); return nullptr; } + virtual const Type* xmeet(const Type* t) const { ShouldNotReachHere(); return nullptr; } + virtual const Type* xjoin(const Type* t) const { ShouldNotReachHere(); return nullptr; } virtual intptr_t get_con() const; @@ -2023,19 +1972,11 @@ class TypeKlassPtr : public TypePtr { virtual const TypeKlassPtr* try_improve() const { return this; } -private: - virtual bool is_meet_subtype_of(const TypePtr* other) const { - return is_meet_subtype_of_helper(other->is_klassptr(), klass_is_exact(), other->is_klassptr()->klass_is_exact()); - } - - virtual bool is_meet_subtype_of_helper(const TypeKlassPtr* other, bool this_xk, bool other_xk) const { - ShouldNotReachHere(); return false; - } - - virtual const TypeInterfaces* interfaces() const { + const TypeInterfaces* interfaces() const { return _interfaces; }; +private: const TypeKlassPtr* is_reference_type(const Type* other) const { return other->isa_klassptr(); } @@ -2103,18 +2044,14 @@ class TypeInstKlassPtr : public TypeKlassPtr { virtual bool empty() const; virtual const TypePtr *add_offset( intptr_t offset ) const; - virtual const Type *xmeet( const Type *t ) const; - virtual const Type *xdual() const; + virtual const Type* xmeet(const Type* t) const; + virtual const Type* xjoin(const Type* t) const; virtual const TypeInstKlassPtr* with_offset(intptr_t offset) const; virtual const TypeKlassPtr* try_improve() const; virtual FlatInArray flat_in_array() const { return _flat_in_array; } - FlatInArray dual_flat_in_array() const { - return flat_in_array_dual[_flat_in_array]; - } - virtual bool can_be_inline_array() const; // Convenience common pre-built types. @@ -2124,9 +2061,6 @@ class TypeInstKlassPtr : public TypeKlassPtr { #ifndef PRODUCT virtual void dump2(Dict& d, uint depth, outputStream* st) const; #endif // PRODUCT - -private: - virtual bool is_meet_subtype_of_helper(const TypeKlassPtr* other, bool this_xk, bool other_xk) const; }; // Array klass pointer, mirrors TypeAryPtr @@ -2134,6 +2068,8 @@ class TypeAryKlassPtr : public TypeKlassPtr { friend class TypeInstKlassPtr; friend class Type; friend class TypePtr; + friend class TypeJavaPtrMeetHelper; + friend class TypeJavaPtrJoinHelper; const Type *_elem; const bool _not_flat; // Array is never flat @@ -2155,30 +2091,6 @@ class TypeAryKlassPtr : public TypeKlassPtr { virtual bool must_be_exact() const; - bool dual_flat() const { - return _flat; - } - - bool meet_flat(bool other) const { - return _flat && other; - } - - bool dual_null_free() const { - return _null_free; - } - - bool meet_null_free(bool other) const { - return _null_free && other; - } - - bool dual_atomic() const { - return _atomic; - } - - bool meet_atomic(bool other) const { - return _atomic && other; - } - public: // returns base element type, an instance klass (and not interface) for object arrays @@ -2212,9 +2124,9 @@ class TypeAryKlassPtr : public TypeKlassPtr { virtual const TypeAryPtr* as_exact_instance_type(bool klass_change = true) const; virtual const TypeAryPtr* as_subtype_instance_type(bool klass_change = true) const; - virtual const TypePtr *add_offset( intptr_t offset ) const; - virtual const Type *xmeet( const Type *t ) const; - virtual const Type *xdual() const; // Compute dual right now. + virtual const TypePtr* add_offset(intptr_t offset) const; + virtual const Type* xmeet(const Type* t) const; + virtual const Type* xjoin(const Type* t) const; virtual const TypeAryKlassPtr* with_offset(intptr_t offset) const; @@ -2237,8 +2149,6 @@ class TypeAryKlassPtr : public TypeKlassPtr { #ifndef PRODUCT virtual void dump2( Dict &d, uint depth, outputStream *st ) const; // Specialized per-Type dumping #endif -private: - virtual bool is_meet_subtype_of_helper(const TypeKlassPtr* other, bool this_xk, bool other_xk) const; }; class TypeNarrowPtr : public Type { @@ -2263,9 +2173,6 @@ class TypeNarrowPtr : public Type { virtual uint hash() const; // Type specific hashing virtual bool singleton(void) const; // TRUE if type is a singleton - virtual const Type *xmeet( const Type *t ) const; - virtual const Type *xdual() const; // Compute dual right now. - virtual intptr_t get_con() const; virtual bool empty(void) const; // TRUE if type is vacuous @@ -2414,9 +2321,6 @@ class TypeFunc : public Type { bool scalarized_return = false); static const TypeFunc *make(const TypeTuple* domain, const TypeTuple* range); - virtual const Type *xmeet( const Type *t ) const; - virtual const Type *xdual() const; // Compute dual right now. - BasicType return_type() const; bool returns_inline_type_as_fields() const { diff --git a/src/hotspot/share/opto/typejavaptr.hpp b/src/hotspot/share/opto/typejavaptr.hpp new file mode 100644 index 000000000000..8c964cd77757 --- /dev/null +++ b/src/hotspot/share/opto/typejavaptr.hpp @@ -0,0 +1,818 @@ +/* + * 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. + * + */ + +#ifndef SHARE_OPTO_TYPEJAVAPTR_HPP +#define SHARE_OPTO_TYPEJAVAPTR_HPP + +#include "cppstdlib/type_traits.hpp" +#include "opto/type.hpp" + +class TypeJavaPtrMeetHelper { +private: + friend class TypeJavaPtrJoinHelper; + +public: + template + static const PtrType* javaptr_type_xmeet(const PtrType* t1, const PtrType* t2) { + if (t1 == t2) { + return t1; + } + + TypePtr::PTR ptr1 = t1->ptr(); + TypePtr::PTR ptr2 = t2->ptr(); + assert(ptr1 == TypePtr::Constant || ptr1 == TypePtr::NotNull || ptr1 == TypePtr::BotPTR, "unexpected ptr: %d", int(ptr1)); + assert(ptr2 == TypePtr::Constant || ptr2 == TypePtr::NotNull || ptr2 == TypePtr::BotPTR, "unexpected ptr: %d", int(ptr2)); + + if constexpr (PtrType::is_oopptr_type) { + return oopptr_type_xmeet(t1, t2); + } else { + return klassptr_type_xmeet(t1, t2); + } + } + +private: + template + static const OopType* oopptr_type_xmeet(const OopType* t1, const OopType* t2) { + assert(t1 != t2, "must have been handled"); + Type::TYPES base1 = t1->base(); + Type::TYPES base2 = t2->base(); + assert(base1 == Type::InstPtr || base1 == Type::AryPtr, "must be an oopptr: %d", int(base1)); + assert(base2 == Type::InstPtr || base2 == Type::AryPtr, "must be an oopptr: %d", int(base2)); + + Type::Offset offset = meet_offset(t1, t2); + auto interfaces = meet_interfaces(t1, t2); + auto flat_in_array = meet_flat_in_array(t1, t2); + int instance_id = meet_instance_id(t1, t2); + auto speculative = meet_speculative(t1, t2); + int inline_depth = meet_inline_depth(t1, t2); + + if (base1 != base2) { + TypePtr::PTR ptr = t1->ptr() == TypePtr::BotPTR || t2->ptr() == TypePtr::BotPTR ? TypePtr::BotPTR : TypePtr::NotNull; + return OopType::InstType::make(ptr, OopType::ciEnv::current()->Object_klass(), interfaces, false, nullptr, offset, + flat_in_array, instance_id, speculative, inline_depth); + } else if (base1 == Type::InstPtr) { + return instptr_type_xmeet(t1->is_instptr(), t2->is_instptr(), offset, interfaces, flat_in_array, instance_id, speculative, inline_depth); + } else { + return aryptr_type_xmeet(t1->is_aryptr(), t2->is_aryptr(), offset, instance_id, speculative, inline_depth); + } + } + + template + static const InstOopType* instptr_type_xmeet(const InstOopType* t1, const InstOopType* t2, Type::Offset offset, InterfacesType interfaces, + TypePtr::FlatInArray flat_in_array, int instance_id, const PtrType* speculative, int inline_depth) { + using ConstOopType = decltype(t1->const_oop()); + + auto k1 = t1->instance_klass(); + auto k2 = t2->instance_klass(); + TypePtr::PTR ptr; + ConstOopType const_oop = nullptr; + meet_ptr_and_const_oop(ptr, const_oop, t1, t2); + bool xk = t1->klass_is_exact() && t2->klass_is_exact() && k1 == k2; + + // Consider an unloaded class to be a direct child of j.l.O and not have any subclass + decltype(k1) k; + if (k1 == k2) { + k = k1; + } else if (k1->is_java_lang_Object() || k2->is_java_lang_Object() || !k1->is_loaded() || !k2->is_loaded()) { + k = InstOopType::ciEnv::current()->Object_klass(); + } else { + k = k1->least_common_ancestor(k2)->as_instance_klass(); + } + + return InstOopType::make(ptr, k, interfaces, xk, const_oop, offset, flat_in_array, instance_id, speculative, inline_depth); + } + + template + static const AryOopType* aryptr_type_xmeet(const AryOopType* t1, const AryOopType* t2, Type::Offset offset, int instance_id, + const PtrType* speculative, int inline_depth) { + using AryType = std::remove_pointer_tary())>; + using ConstOopType = decltype(t1->const_oop()); + using ElemType = decltype(t1->elem()); + using KlassType = decltype(t1->klass()); + + TypePtr::PTR ptr; + ConstOopType const_oop = nullptr; + meet_ptr_and_const_oop(ptr, const_oop, t1, t2); + + ElemType elem; + KlassType klass = nullptr; + meet_ary_elem(elem, klass, t1, t2); + + auto size = t1->size()->meet(t2->size())->is_int(); + bool stable = t1->is_stable() && t2->is_stable(); + bool flat = t1->is_flat() && t2->is_flat(); + bool not_flat = t1->is_not_flat() && t2->is_not_flat(); + bool null_free = t1->is_null_free() && t2->is_null_free(); + bool not_null_free = t1->is_not_null_free() && t2->is_not_null_free(); + bool atomic = t1->is_atomic() && t2->is_atomic(); + auto ary = AryType::make(elem, size, stable, flat, not_flat, null_free, not_null_free, atomic); + bool xk = t1->klass_is_exact() && t2->klass_is_exact() && !aryptr_klass_disjoint(t1, t2); + auto field_offset = t1->field_offset().meet(t2->field_offset()); + bool autobox_cache = t1->is_autobox_cache() && t2->is_autobox_cache(); + return AryOopType::make(ptr, const_oop, ary, klass, xk, offset, field_offset, instance_id, speculative, inline_depth, autobox_cache); + } + + template + static const KlassType* klassptr_type_xmeet(const KlassType* t1, const KlassType* t2) { + assert(t1 != t2, "must have been handled"); + Type::TYPES base1 = t1->base(); + Type::TYPES base2 = t2->base(); + assert(base1 == Type::InstKlassPtr || base1 == Type::AryKlassPtr, "must be a klassptr: %d", int(base1)); + assert(base2 == Type::InstKlassPtr || base2 == Type::AryKlassPtr, "must be a klassptr: %d", int(base2)); + + Type::Offset offset = meet_offset(t1, t2); + auto interfaces = meet_interfaces(t1, t2); + auto flat_in_array = meet_flat_in_array(t1, t2); + + if (base1 != base2) { + TypePtr::PTR ptr = t1->ptr() == TypePtr::BotPTR || t2->ptr() == TypePtr::BotPTR ? TypePtr::BotPTR : TypePtr::NotNull; + return KlassType::InstType::make(ptr, KlassType::ciEnv::current()->Object_klass(), interfaces, offset, flat_in_array); + } else if (base1 == Type::InstKlassPtr) { + return instklassptr_type_xmeet(t1->is_instklassptr(), t2->is_instklassptr(), offset, interfaces, flat_in_array); + } else { + return aryklassptr_type_xmeet(t1->is_aryklassptr(), t2->is_aryklassptr(), offset); + } + } + + template + static const InstKlassType* instklassptr_type_xmeet(const InstKlassType* t1, const InstKlassType* t2, Type::Offset offset, InterfacesType interfaces, + TypePtr::FlatInArray flat_in_array) { + TypePtr::PTR ptr = meet_inst_klass_ptr(t1, t2); + auto klass = t1->instance_klass()->least_common_ancestor(t2->instance_klass())->as_instance_klass(); + return InstKlassType::make(ptr, klass, interfaces, offset, flat_in_array); + } + + template + static const AryKlassType* aryklassptr_type_xmeet(const AryKlassType* t1, const AryKlassType* t2, Type::Offset offset) { + using ElemType = decltype(t1->elem()); + using KlassType = decltype(t1->klass()); + + TypePtr::PTR ptr = meet_ary_klass_ptr(t1, t2); + ElemType elem; + KlassType klass = nullptr; + meet_ary_elem(elem, klass, t1, t2); + bool not_flat = t1->is_not_flat() && t2->is_not_flat(); + bool not_null_free = t1->is_not_null_free() && t2->is_not_null_free(); + bool flat = t1->is_flat() && t2->is_flat(); + bool null_free = t1->is_null_free() && t2->is_null_free(); + bool atomic = t1->is_atomic() && t2->is_atomic(); + bool refined = t1->is_refined_type() && t2->is_refined_type(); + return AryKlassType::make(ptr, elem, klass, offset, not_flat, not_null_free, flat, null_free, atomic, refined); + } + + template + static Type::Offset meet_offset(const PtrType* t1, const PtrType* t2) { + return Type::Offset(t1->offset()).meet(Type::Offset(t2->offset())); + } + + template > + static InterfacesType meet_interfaces(const PtrType* t1, const PtrType* t2) { + return t1->interfaces()->intersection_with(t2->interfaces()); + } + + template + static int meet_instance_id(const OopType* t1, const OopType* t2) { + int id1 = t1->instance_id(); + int id2 = t2->instance_id(); + assert(id1 != TypeOopPtr::InstanceTop && id2 != TypeOopPtr::InstanceTop, "InstanceTop must be normalized to TypePtr::TopPTR"); + return id1 == id2 ? id1 : TypeOopPtr::InstanceBot; + } + + template + static auto meet_speculative(const OopType* t1, const OopType* t2) { + auto s1 = t1->speculative(); + auto s2 = t2->speculative(); + if (s1 == nullptr && s2 == nullptr) { + return s1; + } + + if (s1 == nullptr) { + s1 = t1; + } else if (s2 == nullptr) { + s2 = t2; + } + return s1->meet(s2)->is_ptr(); + } + + template + static int meet_inline_depth(const OopType* t1, const OopType* t2) { + return MAX2(t1->inline_depth(), t2->inline_depth()); + } + + template + static void meet_ptr_and_const_oop(TypePtr::PTR& ptr, ConstOopType& const_oop, const OopType* t1, const OopType* t2) { + if (t1->ptr() == TypePtr::Constant && t2->ptr() == TypePtr::Constant && t1->const_oop() == t2->const_oop()) { + ptr = TypePtr::Constant; + const_oop = t1->const_oop(); + } else if (t1->ptr() != TypePtr::BotPTR && t2->ptr() != TypePtr::BotPTR) { + ptr = TypePtr::NotNull; + } else { + ptr = TypePtr::BotPTR; + } + } + + template + static TypePtr::PTR meet_inst_klass_ptr(const InstKlassType* t1, const InstKlassType* t2) { + if (t1->ptr() == TypePtr::Constant && t2->ptr() == TypePtr::Constant && + t1->instance_klass() == t2->instance_klass() && t1->interfaces() == t2->interfaces()) { + return TypePtr::Constant; + } else if (t1->ptr() != TypePtr::BotPTR && t2->ptr() != TypePtr::BotPTR) { + return TypePtr::NotNull; + } else { + return TypePtr::BotPTR; + } + } + + template + static TypePtr::PTR meet_ary_klass_ptr(const AryKlassType* t1, const AryKlassType* t2) { + if (t1->ptr() == TypePtr::Constant && t2->ptr() == TypePtr::Constant && + t1->elem() == t2->elem() && t1->klass() == t2->klass() && + t1->is_not_flat() == t2->is_not_flat() && t1->is_not_null_free() == t2->is_not_null_free() && + t1->is_flat() == t2->is_flat() && t1->is_null_free() == t2->is_null_free() && + t1->is_atomic() == t2->is_atomic() && t1->is_refined_type() == t2->is_refined_type()) { + return TypePtr::Constant; + } else if (t1->ptr() != TypePtr::BotPTR && t2->ptr() != TypePtr::BotPTR) { + return TypePtr::NotNull; + } else { + return TypePtr::BotPTR; + } + } + + template + static TypePtr::FlatInArray meet_flat_in_array(const PtrType* t1, const PtrType* t2) { + auto v1 = t1->flat_in_array(); + auto v2 = t2->flat_in_array(); + assert(v1 != TypePtr::TopFlat && v2 != TypePtr::TopFlat, "TopFlat must be normalized to TypePtr::TopPTR"); + return v1 == v2 ? v1 : TypePtr::MaybeFlat; + } + + template + static void meet_ary_elem(const ElemType*& elem, CIKlassType& klass, const AryType* t1, const AryType* t2) { + const ElemType* elem1 = t1->elem(); + const ElemType* elem2 = t2->elem(); + assert(!elem1->empty() && !elem2->empty(), "cannot be top"); + if (elem1->base() == elem2->base()) { + if (elem1->base() == Type::Int) { + // boolean[], byte[], short[], char[], int[] all use some kinds of TypeInt as their element + // types, klass is used to distinguish between them. As a result, different kinds of array + // should result in bot[]. + CIKlassType klass1 = t1->klass(); + CIKlassType klass2 = t2->klass(); + assert(klass1 != nullptr && klass2 != nullptr, "ambiguous array"); + if (klass1 == klass2) { + elem = elem1->meet_speculative(elem2); + klass = klass1; + } else { + elem = ElemType::BOTTOM; + } + } else { + elem = elem1->meet_speculative(elem2); + } + } else { + if (elem1->make_ptr() != nullptr && elem2->make_ptr() != nullptr) { + elem = elem1->meet_speculative(elem2); + } else { + elem = ElemType::BOTTOM; + } + } + } + + // TypeAryPtr is tricky, an exact Number[][] and an exact Integer[][] should be disjoint. + // However, their elements are non-exact Number[] and exact Integer[], respectively, which are + // not disjoint. This function specifically handle those cases. + template + static bool aryptr_klass_disjoint(const AryOopType* t1, const AryOopType* t2) { + if (!t1->klass_is_exact() && !t2->klass_is_exact()) { + // If t1 and t2 are both non-exact and disjoint, their elems should be disjoint, too. As a + // result, we do not need to handle that case here. + return false; + } + + decltype(t1->is_oopptr()) exact_type; + decltype(t1->is_oopptr()) other_type; + bool both_are_exact; + if (t1->klass_is_exact()) { + exact_type = t1; + other_type = t2; + both_are_exact = t2->klass_is_exact(); + } else { + exact_type = t2; + other_type = t1; + both_are_exact = t1->klass_is_exact(); + } + + // At each iteration, walk down from the array klasses to their element types. Keep + // both_are_exact because the element type of an exact Number[][] is a non-exact Number[], but + // we need to remember that the original type is an exact array. + while (true) { + if (exact_type->base() == Type::InstPtr) { + if (other_type->base() == Type::AryPtr) { + return true; + } + + auto exact_klass = exact_type->is_instptr()->instance_klass(); + auto other_klass = other_type->is_instptr()->instance_klass(); + if (both_are_exact) { + return exact_klass != other_klass || exact_type->interfaces() != other_type->interfaces(); + } else { + return !exact_klass->is_subtype_of(other_klass) || !exact_type->interfaces()->contains(other_type->interfaces()); + } + } + + if (other_type->base() == Type::InstPtr) { + auto other_inst_type = other_type->is_instptr(); + return both_are_exact || !other_inst_type->instance_klass()->is_java_lang_Object() || + !AryOopType::_array_interfaces->contains(other_inst_type->interfaces()); + } + + auto exact_ary_type = exact_type->is_aryptr(); + auto other_ary_type = other_type->is_aryptr(); + if (both_are_exact) { + if (exact_ary_type->is_flat() != other_ary_type->is_flat() || exact_ary_type->is_not_flat() != other_ary_type->is_not_flat() || + exact_ary_type->is_not_null_free() != other_ary_type->is_not_null_free() || exact_ary_type->is_atomic() != other_ary_type->is_atomic()) { + return true; + } + } else { + if (!exact_ary_type->is_atomic() && other_ary_type->is_atomic()) { + return true; + } + } + + auto exact_elem = exact_ary_type->elem(); + auto other_elem = other_ary_type->elem(); + assert(exact_elem->base() != Type::Bottom, "cannot have an exact bot[]"); + assert(!both_are_exact || other_elem->base() != Type::Bottom, "cannot have an exact bot[]"); + if (other_elem->base() == Type::Bottom) { + return false; + } + + if (exact_elem->make_ptr() != nullptr && other_elem->make_ptr() != nullptr) { + exact_type = exact_elem->make_ptr()->is_oopptr(); + other_type = other_elem->make_ptr()->is_oopptr(); + continue; + } + + if (exact_elem->base() != other_elem->base()) { + return true; + } else if (exact_elem->base() == Type::Int) { + return exact_ary_type->klass() != other_ary_type->klass(); + } else { + return false; + } + } + } +}; + +class TypeJavaPtrJoinHelper { +public: + template + static const typename PtrType::PtrType* javaptr_type_xjoin(const PtrType* t1, const PtrType* t2) { + if (t1 == t2) { + return t1; + } + + TypePtr::PTR ptr1 = t1->ptr(); + TypePtr::PTR ptr2 = t2->ptr(); + assert(ptr1 == TypePtr::Constant || ptr1 == TypePtr::NotNull || ptr1 == TypePtr::BotPTR, "unexpected ptr: %d", int(ptr1)); + assert(ptr2 == TypePtr::Constant || ptr2 == TypePtr::NotNull || ptr2 == TypePtr::BotPTR, "unexpected ptr: %d", int(ptr2)); + + if constexpr (PtrType::is_oopptr_type) { + return oopptr_type_xjoin(t1, t2); + } else { + return klassptr_type_xjoin(t1, t2); + } + } + +private: + template + static const typename OopType::PtrType* oopptr_type_xjoin(const OopType* t1, const OopType* t2) { + assert(t1 != t2, "must have been handled"); + Type::TYPES base1 = t1->base(); + Type::TYPES base2 = t2->base(); + assert(base1 == Type::InstPtr || base1 == Type::AryPtr, "must be an oopptr: %d", int(base1)); + assert(base2 == Type::InstPtr || base2 == Type::AryPtr, "must be an oopptr: %d", int(base2)); + + Type::Offset offset = join_offset(t1, t2); + auto interfaces = join_interfaces(t1, t2); + auto flat_in_array = join_flat_in_array(t1, t2); + int instance_id = join_instance_id(t1, t2); + auto speculative = join_speculative(t1, t2); + int inline_depth = join_inline_depth(t1, t2); + if (offset == Type::Offset::top) { + return OopType::PtrType::make(Type::AnyPtr, TypePtr::TopPTR, offset, speculative, inline_depth); + } else if ((t1->ptr() == TypePtr::Constant || t2->ptr() == TypePtr::Constant) && instance_id != TypeOopPtr::InstanceBot) { + // A constant oop cannot be produced by an allocation in the current compilation + return OopType::PtrType::make(Type::AnyPtr, TypePtr::TopPTR, offset, speculative, inline_depth); + } else if (flat_in_array == TypePtr::TopFlat || instance_id == TypeOopPtr::InstanceTop) { + TypePtr::PTR ptr = join_ptr_with_null(join_ptr(t1, t2)); + return OopType::PtrType::make(Type::AnyPtr, ptr, offset, speculative, inline_depth); + } + + if (base1 != base2) { + const typename OopType::InstType* inst_type; + const typename OopType::AryType* ary_type; + if (base1 == Type::InstPtr) { + inst_type = t1->is_instptr(); + ary_type = t2->is_aryptr(); + } else { + inst_type = t2->is_instptr(); + ary_type = t1->is_aryptr(); + } + + TypePtr::PTR ptr = join_ptr(t1, t2); + bool inst_type_can_contain_arrays = inst_type->instance_klass()->is_java_lang_Object() && !inst_type->klass_is_exact() && + OopType::AryType::_array_interfaces->contains(inst_type->interfaces()); + if (inst_type_can_contain_arrays) { + return OopType::AryType::make(ptr, ary_type->const_oop(), ary_type->ary(), ary_type->klass(), ary_type->klass_is_exact(), offset, ary_type->field_offset(), + instance_id, speculative, inline_depth, ary_type->is_autobox_cache()); + } else { + return OopType::PtrType::make(Type::AnyPtr, join_ptr_with_null(ptr), offset, speculative, inline_depth); + } + } else if (base1 == Type::InstPtr) { + return instptr_type_xjoin(t1->is_instptr(), t2->is_instptr(), offset, interfaces, flat_in_array, instance_id, speculative, inline_depth); + } else { + return aryptr_type_xjoin(t1->is_aryptr(), t2->is_aryptr(), offset, instance_id, speculative, inline_depth); + } + } + + template + static const typename InstOopType::PtrType* instptr_type_xjoin(const InstOopType* t1, const InstOopType* t2, Type::Offset offset, InterfacesType interfaces, + TypePtr::FlatInArray flat_in_array, int instance_id, const PtrType* speculative, int inline_depth) { + // Join 2 constants + if (t1->const_oop() != nullptr && t2->const_oop() != nullptr) { + if (t1->const_oop() != t2->const_oop()) { + return InstOopType::PtrType::make(Type::AnyPtr, TypePtr::TopPTR, offset, speculative, inline_depth); + } else { + return InstOopType::make(TypePtr::Constant, t1->instance_klass(), interfaces, true, t1->const_oop(), offset, flat_in_array, instance_id, speculative, inline_depth); + } + } + + // From here, at least one of the operand is not constant + TypePtr::PTR ptr = join_ptr(t1, t2); + auto const_oop = t1->const_oop() != nullptr ? t1->const_oop() : t2->const_oop(); + auto klass1 = t1->instance_klass(); + auto klass2 = t2->instance_klass(); + bool xk1 = t1->klass_is_exact(); + bool xk2 = t2->klass_is_exact(); + + if (xk1 && xk2) { + if (t1->instance_klass() == t2->instance_klass()) { + return InstOopType::make(ptr, klass1, interfaces, true, const_oop, offset, flat_in_array, instance_id, speculative, inline_depth); + } else { + return InstOopType::PtrType::make(Type::AnyPtr, join_ptr_with_null(ptr), offset, speculative, inline_depth); + } + } else if (xk1) { + assert(klass1->is_loaded(), "pointer to an oop of an exact type must be loaded"); + if (non_exact_type_contains_exact_type(t2, t1, interfaces)) { + return InstOopType::make(ptr, klass1, interfaces, true, const_oop, offset, flat_in_array, instance_id, speculative, inline_depth); + } else { + return InstOopType::PtrType::make(Type::AnyPtr, join_ptr_with_null(ptr), offset, speculative, inline_depth); + } + } else if (xk2) { + assert(klass2->is_loaded(), "pointer to an oop of an exact type must be loaded"); + if (non_exact_type_contains_exact_type(t1, t2, interfaces)) { + return InstOopType::make(ptr, klass2, interfaces, true, const_oop, offset, flat_in_array, instance_id, speculative, inline_depth); + } else { + return InstOopType::PtrType::make(Type::AnyPtr, join_ptr_with_null(ptr), offset, speculative, inline_depth); + } + } + + assert(const_oop == nullptr && ptr != TypePtr::Constant, "const oop should have exact klass"); + if (!klass1->is_loaded() || !klass2->is_loaded()) { + // Consider an unloaded class to be a direct child of j.l.O and not have any subclass + if (klass1->is_java_lang_Object()) { + return InstOopType::make(ptr, klass2, interfaces, false, nullptr, offset, flat_in_array, instance_id, speculative, inline_depth); + } else if (klass2->is_java_lang_Object()) { + return InstOopType::make(ptr, klass1, interfaces, false, nullptr, offset, flat_in_array, instance_id, speculative, inline_depth); + } else if (klass1 == klass2) { + return InstOopType::make(ptr, klass1, interfaces, false, nullptr, offset, flat_in_array, instance_id, speculative, inline_depth); + } else { + return InstOopType::PtrType::make(Type::AnyPtr, join_ptr_with_null(ptr), offset, speculative, inline_depth); + } + } + + // There is no opposite of LCA, there exists a non-null object o subtyping both A and B iff A + // is a subtype of B or B is a subtype of A + if (klass1->is_subtype_of(klass2)) { + return InstOopType::make(ptr, klass1, interfaces, false, nullptr, offset, flat_in_array, instance_id, speculative, inline_depth); + } else if (klass2->is_subtype_of(klass1)) { + return InstOopType::make(ptr, klass2, interfaces, false, nullptr, offset, flat_in_array, instance_id, speculative, inline_depth); + } else { + return InstOopType::PtrType::make(Type::AnyPtr, join_ptr_with_null(ptr), offset, speculative, inline_depth); + } + } + + template + static const typename AryOopType::PtrType* aryptr_type_xjoin(const AryOopType* t1, const AryOopType* t2, Type::Offset offset, int instance_id, + const PtrType* speculative, int inline_depth) { + using AryType = std::remove_pointer_tary())>; + using ElemType = decltype(t1->elem()); + using KlassType = decltype(t1->klass()); + + // Join of 2 different constants + if (t1->const_oop() != nullptr && t2->const_oop() != nullptr && t1->const_oop() != t2->const_oop()) { + return AryOopType::PtrType::make(Type::AnyPtr, TypePtr::TopPTR, offset, speculative, inline_depth); + } + + TypePtr::PTR ptr = join_ptr(t1, t2); + ElemType elem; + KlassType klass = nullptr; + join_ary_elem(elem, klass, t1, t2); + if (elem->empty()) { + return AryOopType::PtrType::make(Type::AnyPtr, join_ptr_with_null(ptr), offset, speculative, inline_depth); + } + + if (TypeJavaPtrMeetHelper::aryptr_klass_disjoint(t1, t2)) { + return AryOopType::PtrType::make(Type::AnyPtr, join_ptr_with_null(ptr), offset, speculative, inline_depth); + } + + auto size = t1->size()->join(t2->size())->isa_int(); + if (size == nullptr) { + return AryOopType::PtrType::make(Type::AnyPtr, join_ptr_with_null(ptr), offset, speculative, inline_depth); + } + + auto const_oop = t1->const_oop() != nullptr ? t1->const_oop() : t2->const_oop(); + bool stable = t1->is_stable() || t2->is_stable(); + bool flat = t1->is_flat() || t2->is_flat(); + bool not_flat = t1->is_not_flat() || t2->is_not_flat(); + bool null_free = t1->is_null_free() || t2->is_null_free(); + bool not_null_free = t1->is_not_null_free() || t2->is_not_null_free(); + bool atomic = t1->is_atomic() || t2->is_atomic(); + auto field_offset = t1->field_offset().join(t2->field_offset()); + if ((flat && not_flat) || (null_free && not_null_free) || field_offset == Type::Offset::top) { + return AryOopType::PtrType::make(Type::AnyPtr, join_ptr_with_null(ptr), offset, speculative, inline_depth); + } + + auto ary = AryType::make(elem, size, stable, flat, not_flat, null_free, not_null_free, atomic); + bool xk = t1->klass_is_exact() || t2->klass_is_exact(); + bool autobox_cache = t1->is_autobox_cache() || t2->is_autobox_cache(); + return AryOopType::make(ptr, const_oop, ary, klass, xk, offset, field_offset, instance_id, speculative, inline_depth, autobox_cache); + } + + template + static const typename KlassType::PtrType* klassptr_type_xjoin(const KlassType* t1, const KlassType* t2) { + assert(t1 != t2, "must have been handled"); + Type::TYPES base1 = t1->base(); + Type::TYPES base2 = t2->base(); + assert(base1 == Type::InstKlassPtr || base1 == Type::AryKlassPtr, "must be a klassptr: %d", int(base1)); + assert(base2 == Type::InstKlassPtr || base2 == Type::AryKlassPtr, "must be a klassptr: %d", int(base2)); + + Type::Offset offset = join_offset(t1, t2); + auto interfaces = join_interfaces(t1, t2); + auto flat_in_array = join_flat_in_array(t1, t2); + if (offset == Type::Offset::top) { + return KlassType::PtrType::make(Type::AnyPtr, TypePtr::TopPTR, offset); + } else if (flat_in_array == TypePtr::TopFlat) { + TypePtr::PTR ptr = join_ptr_with_null(join_ptr(t1, t2)); + return KlassType::PtrType::make(Type::AnyPtr, ptr, offset); + } + + if (base1 != base2) { + const typename KlassType::InstType* inst_type; + const typename KlassType::AryType* ary_type; + if (base1 == Type::InstKlassPtr) { + inst_type = t1->is_instklassptr(); + ary_type = t2->is_aryklassptr(); + } else { + inst_type = t2->is_instklassptr(); + ary_type = t1->is_aryklassptr(); + } + + TypePtr::PTR ptr = join_ptr(t1, t2); + bool inst_type_can_contain_arrays = inst_type->instance_klass()->is_java_lang_Object() && !inst_type->klass_is_exact() && + KlassType::AryType::_array_interfaces->contains(inst_type->interfaces()); + if (inst_type_can_contain_arrays) { + return KlassType::AryType::make(ptr, ary_type->elem(), ary_type->klass(), offset, ary_type->is_not_flat(), ary_type->is_not_null_free(), + ary_type->is_flat(), ary_type->is_null_free(), ary_type->is_atomic(), ary_type->is_refined_type()); + } else { + return KlassType::PtrType::make(Type::AnyPtr, join_ptr_with_null(ptr), offset); + } + } else if (base1 == Type::InstKlassPtr) { + return instklassptr_type_xjoin(t1->is_instklassptr(), t2->is_instklassptr(), offset, interfaces, flat_in_array); + } else { + return aryklassptr_type_xjoin(t1->is_aryklassptr(), t2->is_aryklassptr(), offset); + } + } + + template + static const typename InstKlassType::PtrType* instklassptr_type_xjoin(const InstKlassType* t1, const InstKlassType* t2, Type::Offset offset, + InterfacesType interfaces, TypePtr::FlatInArray flat_in_array) { + auto klass1 = t1->instance_klass(); + auto klass2 = t2->instance_klass(); + // Beware, when the exact klass pointer is an interface, instance_klass() is j.l.O and + // interfaces() is not empty + if (t1->ptr() == TypePtr::Constant && t2->ptr() == TypePtr::Constant) { + if (klass1 == klass2 && t1->interfaces() == t2->interfaces()) { + return InstKlassType::make(TypePtr::Constant, klass1, interfaces, offset, flat_in_array); + } else { + return InstKlassType::PtrType::make(Type::AnyPtr, TypePtr::TopPTR, offset); + } + } else if (t1->ptr() == TypePtr::Constant) { + if (non_exact_type_contains_exact_type(t2, t1, interfaces)) { + return InstKlassType::make(TypePtr::Constant, klass1, interfaces, offset, flat_in_array); + } else { + return InstKlassType::PtrType::make(Type::AnyPtr, TypePtr::TopPTR, offset); + } + } else if (t2->ptr() == TypePtr::Constant) { + if (non_exact_type_contains_exact_type(t1, t2, interfaces)) { + return InstKlassType::make(TypePtr::Constant, klass2, interfaces, offset, flat_in_array); + } else { + return InstKlassType::PtrType::make(Type::AnyPtr, TypePtr::TopPTR, offset); + } + } else { + TypePtr::PTR ptr = join_ptr(t1, t2); + if (klass1->is_subtype_of(klass2)) { + return InstKlassType::make(ptr, klass1, interfaces, offset, flat_in_array); + } else if (klass2->is_subtype_of(klass1)) { + return InstKlassType::make(ptr, klass2, interfaces, offset, flat_in_array); + } else { + return InstKlassType::PtrType::make(Type::AnyPtr, join_ptr_with_null(ptr), offset); + } + } + } + + template + static const typename AryKlassType::PtrType* aryklassptr_type_xjoin(const AryKlassType* t1, const AryKlassType* t2, Type::Offset offset) { + using ElemType = decltype(t1->elem()); + using KlassType = decltype(t1->klass()); + + TypePtr::PTR ptr = join_ptr(t1, t2); + ElemType elem; + KlassType klass = nullptr; + join_ary_elem(elem, klass, t1, t2); + bool not_flat = t1->is_not_flat() || t2->is_not_flat(); + bool not_null_free = t1->is_not_null_free() || t2->is_not_null_free(); + bool flat = t1->is_flat() || t2->is_flat(); + bool null_free = t1->is_null_free() || t2->is_null_free(); + if (elem->empty() || (flat && not_flat) || (null_free && not_null_free)) { + return AryKlassType::PtrType::make(Type::AnyPtr, join_ptr_with_null(ptr), offset); + } + + bool atomic = t1->is_atomic() || t2->is_atomic(); + bool refined = t1->is_refined_type() || t2->is_refined_type(); + if (t1->klass_is_exact() && (not_flat != t1->is_not_flat() || not_null_free != t1->is_not_null_free() || flat != t1->is_flat() || + null_free != t1->is_null_free() || atomic != t1->is_atomic() || refined != t1->is_refined_type())) { + return AryKlassType::PtrType::make(Type::AnyPtr, join_ptr_with_null(ptr), offset); + } + if (t2->klass_is_exact() && (not_flat != t2->is_not_flat() || not_null_free != t2->is_not_null_free() || flat != t2->is_flat() || + null_free != t2->is_null_free() || atomic != t2->is_atomic() || refined != t2->is_refined_type())) { + return AryKlassType::PtrType::make(Type::AnyPtr, join_ptr_with_null(ptr), offset); + } + + return AryKlassType::make(ptr, elem, klass, offset, not_flat, not_null_free, flat, null_free, atomic, refined); + } + + template + static TypePtr::PTR join_ptr(const PtrType* t1, const PtrType* t2) { + TypePtr::PTR ptr1 = t1->ptr(); + TypePtr::PTR ptr2 = t2->ptr(); + if (ptr1 == TypePtr::Constant || ptr2 == TypePtr::Constant) { + return TypePtr::Constant; + } else if (ptr1 == TypePtr::NotNull || ptr2 == TypePtr::NotNull) { + return TypePtr::NotNull; + } else { + return TypePtr::BotPTR; + } + } + + // When 2 TypePtrs seem unrelated, they may still join at null if both of them are + // TypePtr::BotPTR. + static TypePtr::PTR join_ptr_with_null(TypePtr::PTR ptr) { + return ptr == TypePtr::BotPTR ? TypePtr::Null : TypePtr::TopPTR; + } + + template + static Type::Offset join_offset(const PtrType* t1, const PtrType* t2) { + return Type::Offset(t1->offset()).join(Type::Offset(t2->offset())); + } + + template > + static InterfacesType join_interfaces(const PtrType* t1, const PtrType* t2) { + return t1->interfaces()->union_with(t2->interfaces()); + } + + template + static int join_instance_id(const OopType* t1, const OopType* t2) { + int id1 = t1->instance_id(); + int id2 = t2->instance_id(); + assert(id1 != TypeOopPtr::InstanceTop && id2 != TypeOopPtr::InstanceTop, "InstanceTop must be normalized to TypePtr::TopPTR"); + if (id1 == TypeOopPtr::InstanceBot) { + return id2; + } else if (id2 == TypeOopPtr::InstanceBot) { + return id1; + } else if (id1 == id2) { + return id1; + } else { + return TypeOopPtr::InstanceTop; + } + } + + template + static auto join_speculative(const OopType* t1, const OopType* t2) { + auto s1 = t1->speculative(); + auto s2 = t2->speculative(); + if (s1 == nullptr && s2 == nullptr) { + return s1; + } + + if (s1 == nullptr) { + s1 = t1; + } else if (s2 == nullptr) { + s2 = t2; + } + return s1->join(s2)->is_ptr(); + } + + template + static int join_inline_depth(const OopType* t1, const OopType* t2) { + return MIN2(t1->inline_depth(), t2->inline_depth()); + } + + template + static TypePtr::FlatInArray join_flat_in_array(const PtrType* t1, const PtrType* t2) { + auto v1 = t1->flat_in_array(); + auto v2 = t2->flat_in_array(); + assert(v1 != TypePtr::TopFlat && v2 != TypePtr::TopFlat, "TopFlat must be normalized to TopPTR"); + if (v1 == TypePtr::MaybeFlat) { + return v2; + } else if (v2 == TypePtr::MaybeFlat) { + return v1; + } else if (v1 == v2) { + return v1; + } else { + return TypePtr::TopFlat; + } + } + + template + static void join_ary_elem(const ElemType*& elem, CIKlassType& klass, const AryType* t1, const AryType* t2) { + const ElemType* elem1 = t1->elem(); + const ElemType* elem2 = t2->elem(); + if (elem1 == ElemType::BOTTOM) { + elem = elem2; + klass = t2->klass(); + return; + } else if (elem2 == ElemType::BOTTOM) { + elem = elem1; + klass = t1->klass(); + return; + } + + if (elem1->make_ptr() != nullptr && elem2->make_ptr() != nullptr) { + elem = elem1->join_speculative(elem2); + if (!elem->empty() && elem->make_ptr()->ptr() == TypePtr::Null) { + elem = ElemType::TOP; + } + return; + } + + if (elem1->base() != elem2->base()) { + elem = ElemType::TOP; + } else if (elem1->base() == Type::Int) { + // boolean[], byte[], short[], char[], int[] all use some kinds of TypeInt as their element + // type, klass is used to distinguish between them. As a result, different kinds of array + // should result in top. + if (t1->klass() != t2->klass()) { + elem = ElemType::TOP; + } else { + elem = elem1->join(elem2); + klass = t1->klass(); + } + } else { + elem = elem1->join(elem2); + } + } + + template + static bool non_exact_type_contains_exact_type(const InstType* non_exact_type, const InstType* exact_type, InterfacesType interfaces) { + assert(!non_exact_type->klass_is_exact() && exact_type->klass_is_exact(), "invalid arguments"); + auto non_exact_klass = non_exact_type->instance_klass(); + auto exact_klass = exact_type->instance_klass(); + // Supertypes of a loaded class should also be loaded + return (exact_klass->is_java_lang_Object() && non_exact_klass->is_java_lang_Object() && exact_type->interfaces() == interfaces) || + (non_exact_klass->is_loaded() && exact_klass->is_subtype_of(non_exact_klass) && interfaces->eq(exact_klass)); + } +}; + +#endif // SHARE_OPTO_TYPEJAVAPTR_HPP diff --git a/src/hotspot/share/prims/forte.cpp b/src/hotspot/share/prims/forte.cpp index 10e8b45dc75a..0e0f0e8a6e2e 100644 --- a/src/hotspot/share/prims/forte.cpp +++ b/src/hotspot/share/prims/forte.cpp @@ -612,17 +612,13 @@ void AsyncGetCallTrace(ASGCT_CallTrace *trace, jint depth, void* ucontext) { switch (thread->thread_state()) { case _thread_new: case _thread_uninitialized: - case _thread_new_trans: // We found the thread on the threads list above, but it is too // young to be useful so return that there are no Java frames. trace->num_frames = 0; break; case _thread_in_native: - case _thread_in_native_trans: case _thread_blocked: - case _thread_blocked_trans: case _thread_in_vm: - case _thread_in_vm_trans: { frame fr; @@ -648,7 +644,6 @@ void AsyncGetCallTrace(ASGCT_CallTrace *trace, jint depth, void* ucontext) { } break; case _thread_in_Java: - case _thread_in_Java_trans: { frame fr; diff --git a/src/hotspot/share/prims/jni.cpp b/src/hotspot/share/prims/jni.cpp index fa61bd74cb8f..f989c6b25672 100644 --- a/src/hotspot/share/prims/jni.cpp +++ b/src/hotspot/share/prims/jni.cpp @@ -203,14 +203,14 @@ bool jfieldIDWorkaround::is_valid_jfieldID(Klass* k, jfieldID id) { } -intptr_t jfieldIDWorkaround::encode_klass_hash(Klass* k, int offset) { +intptr_t jfieldIDWorkaround::encode_klass_hash(InstanceKlass* k, int offset) { if (offset <= small_offset_mask) { - Klass* field_klass = k; - Klass* super_klass = field_klass->super(); + InstanceKlass* field_klass = k; + InstanceKlass* super_klass = field_klass->super(); // With compressed oops the most super class with nonstatic fields would // be the owner of fields embedded in the header. - while (InstanceKlass::cast(super_klass)->has_nonstatic_fields() && - InstanceKlass::cast(super_klass)->contains_field_offset(offset)) { + while (super_klass->has_nonstatic_fields() && + super_klass->contains_field_offset(offset)) { field_klass = super_klass; // super contains the field also super_klass = field_klass->super(); } @@ -376,12 +376,13 @@ JNI_ENTRY(jmethodID, jni_FromReflectedMethod(JNIEnv *env, jobject method)) mirror = java_lang_reflect_Method::clazz(reflected); slot = java_lang_reflect_Method::slot(reflected); } - Klass* k1 = java_lang_Class::as_Klass(mirror); + // The mirror is always an InstanceKlass. + InstanceKlass* k1 = java_lang_Class::as_InstanceKlass(mirror); // Make sure class is initialized before handing id's out to methods k1->initialize(CHECK_NULL); - Method* m = InstanceKlass::cast(k1)->method_with_idnum(slot); - ret = m==nullptr? nullptr : m->jmethod_id(); // return null if reflected method deleted + Method* m = k1->method_with_idnum(slot); + ret = m == nullptr? nullptr : m->jmethod_id(); // return null if reflected method deleted return ret; JNI_END @@ -397,7 +398,8 @@ JNI_ENTRY(jfieldID, jni_FromReflectedField(JNIEnv *env, jobject field)) // field is a handle to a java.lang.reflect.Field object oop reflected = JNIHandles::resolve_non_null(field); oop mirror = java_lang_reflect_Field::clazz(reflected); - Klass* k1 = java_lang_Class::as_Klass(mirror); + // The klass for the field is initialized as an InstanceKlass. + InstanceKlass* k1 = java_lang_Class::as_InstanceKlass(mirror); int slot = java_lang_reflect_Field::slot(reflected); int modifiers = java_lang_reflect_Field::modifiers(reflected); @@ -406,8 +408,8 @@ JNI_ENTRY(jfieldID, jni_FromReflectedField(JNIEnv *env, jobject field)) // First check if this is a static field if (modifiers & JVM_ACC_STATIC) { - int offset = InstanceKlass::cast(k1)->field_offset( slot ); - JNIid* id = InstanceKlass::cast(k1)->jni_id_for(offset); + int offset = k1->field_offset( slot ); + JNIid* id = k1->jni_id_for(offset); assert(id != nullptr, "corrupt Field object"); DEBUG_ONLY(id->set_is_static_field_id();) // A jfieldID for a static field is a JNIid specifying the field holder and the offset within the Klass* @@ -418,9 +420,9 @@ JNI_ENTRY(jfieldID, jni_FromReflectedField(JNIEnv *env, jobject field)) // The slot is the index of the field description in the field-array // The jfieldID is the offset of the field within the object // It may also have hash bits for k, if VerifyJNIFields is turned on. - int offset = InstanceKlass::cast(k1)->field_offset( slot ); - bool is_flat = InstanceKlass::cast(k1)->field_is_flat(slot); - assert(InstanceKlass::cast(k1)->contains_field_offset(offset), "stay within object"); + int offset = k1->field_offset( slot ); + bool is_flat = k1->field_is_flat(slot); + assert(k1->contains_field_offset(offset), "stay within object"); ret = jfieldIDWorkaround::to_instance_jfieldID(k1, offset, is_flat); return ret; JNI_END @@ -1762,17 +1764,19 @@ JNI_ENTRY(jfieldID, jni_GetFieldID(JNIEnv *env, jclass clazz, // Make sure class is initialized before handing id's out to fields k->initialize(CHECK_NULL); - fieldDescriptor fd; - if (!k->is_instance_klass() || - !InstanceKlass::cast(k)->find_field(fieldname, signame, false, &fd)) { - ResourceMark rm; - THROW_MSG_NULL(vmSymbols::java_lang_NoSuchFieldError(), err_msg("%s.%s %s", k->external_name(), name, sig)); + if (k->is_instance_klass()) { + InstanceKlass* ik = InstanceKlass::cast(k); + fieldDescriptor fd; + if (ik->find_field(fieldname, signame, false, &fd)) { + // A jfieldID for a non-static field is simply the offset of the field within the instanceOop + // It may also have hash bits for k, if VerifyJNIFields is turned on. + return jfieldIDWorkaround::to_instance_jfieldID(ik, fd.offset(), fd.is_flat()); + } } - // A jfieldID for a non-static field is simply the offset of the field within the instanceOop - // It may also have hash bits for k, if VerifyJNIFields is turned on. - ret = jfieldIDWorkaround::to_instance_jfieldID(k, fd.offset(), fd.is_flat()); - return ret; + // Not an InstanceKlass or the field wasn't found. + ResourceMark rm; + THROW_MSG_NULL(vmSymbols::java_lang_NoSuchFieldError(), err_msg("%s.%s %s", k->external_name(), name, sig)); JNI_END @@ -1998,7 +2002,8 @@ JNI_ENTRY(jobject, jni_ToReflectedField(JNIEnv *env, jclass cls, jfieldID fieldI fieldDescriptor fd; bool found = false; - Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls)); + // The klass for the field is initialized as an InstanceKlass. + InstanceKlass* k = java_lang_Class::as_InstanceKlass(JNIHandles::resolve_non_null(cls)); assert(jfieldIDWorkaround::is_static_jfieldID(fieldID) == (isStatic != 0), "invalid fieldID"); @@ -2010,7 +2015,7 @@ JNI_ENTRY(jobject, jni_ToReflectedField(JNIEnv *env, jclass cls, jfieldID fieldI } else { // Non-static field. The fieldID is really the offset of the field within the instanceOop. int offset = jfieldIDWorkaround::from_instance_jfieldID(k, fieldID); - found = InstanceKlass::cast(k)->find_field_from_offset(offset, false, &fd); + found = k->find_field_from_offset(offset, false, &fd); } assert(found, "bad fieldID passed into jni_ToReflectedField"); oop reflected = Reflection::new_field(&fd, CHECK_NULL); diff --git a/src/hotspot/share/prims/jvmtiEnv.cpp b/src/hotspot/share/prims/jvmtiEnv.cpp index 2bb6d994c61a..238d2a7c2c17 100644 --- a/src/hotspot/share/prims/jvmtiEnv.cpp +++ b/src/hotspot/share/prims/jvmtiEnv.cpp @@ -1913,9 +1913,7 @@ JvmtiEnv::FollowReferences(jint heap_filter, jclass klass, jobject initial_objec return JVMTI_ERROR_NONE; } k = java_lang_Class::as_Klass(k_mirror); - if (klass == nullptr) { - return JVMTI_ERROR_INVALID_CLASS; - } + assert(k != nullptr, "k_mirror is not null and not primitive, must have a valid klass"); } if (initial_object != nullptr) { diff --git a/src/hotspot/share/prims/unsafe.cpp b/src/hotspot/share/prims/unsafe.cpp index a98da293c358..521ce96acae0 100644 --- a/src/hotspot/share/prims/unsafe.cpp +++ b/src/hotspot/share/prims/unsafe.cpp @@ -346,7 +346,6 @@ UNSAFE_ENTRY(jint, Unsafe_FieldLayout(JNIEnv *env, jobject unsafe, jobject field UNSAFE_ENTRY(jarray, Unsafe_NewSpecialArray(JNIEnv *env, jobject unsafe, jclass elmClass, jint len, jint layoutKind)) { oop mirror = JNIHandles::resolve_non_null(elmClass); Klass* klass = java_lang_Class::as_Klass(mirror); - klass->initialize(CHECK_NULL); if (len < 0) { THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "Array length is negative"); } diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index bcf86d0f6a37..a0d304fe1584 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -310,8 +310,9 @@ static Klass* get_refined_array_klass(Klass* k, frame* fr, RegisterMap* map, Obj assert(k->is_unrefined_objArray_klass(), "Expected unrefined array klass"); nmethod* nm = fr->cb()->as_nmethod_or_null(); assert(sv->has_properties(), "Property information is missing"); - ArrayProperties props(checked_cast(StackValue::create_stack_value(fr, map, sv->properties())->get_jint())); - k = ObjArrayKlass::cast(k)->klass_with_properties(props, THREAD); + uint32_t value = checked_cast(StackValue::create_stack_value(fr, map, sv->properties())->get_jint()); + ArrayDescription description = ArrayDescription::from_value(value); + k = ObjArrayKlass::cast(k)->klass_from_description(description, THREAD); } return k; } diff --git a/src/hotspot/share/runtime/fieldDescriptor.cpp b/src/hotspot/share/runtime/fieldDescriptor.cpp index 17bf5e74e006..aa3bbb76c7f9 100644 --- a/src/hotspot/share/runtime/fieldDescriptor.cpp +++ b/src/hotspot/share/runtime/fieldDescriptor.cpp @@ -195,7 +195,7 @@ void fieldDescriptor::print_on_for(outputStream* st, oop obj, int indent, int ba bool is_null = false; InlineKlass* vk = InlineKlass::cast(field_holder()->get_inline_type_field_klass(index())); int field_offset = offset() - vk->payload_offset(); - int nm_offset; + int nm_offset = 0; if (!is_null_free_inline_type()) { assert(has_null_marker(), "should have null marker"); @@ -220,6 +220,7 @@ void fieldDescriptor::print_on_for(outputStream* st, oop obj, int indent, int ba if (this->field_flags().has_null_marker()) { for (int i = 0; i < indent + 1; i++) st->print(" "); + assert(nm_offset > 0, "must be"); st->print_cr(" - [null_marker] @%d %s", base_offset + nm_offset, is_null ? "Field marked as null" : "Field marked as non-null"); diff --git a/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp b/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp index d48bc1e93a53..24d55c2d52d9 100644 --- a/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp +++ b/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp @@ -329,7 +329,7 @@ JVMFlag::Error TypeProfileLevelConstraintFunc(uint value, bool verbose) { } JVMFlag::Error VerifyIterativeGVNConstraintFunc(uint value, bool verbose) { - const int max_modes = 7; + const int max_modes = 6; uint original_value = value; for (int i = 0; i < max_modes; i++) { if (value % 10 > 1) { diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp index 8e5fde069c68..fc593b7f4364 100644 --- a/src/hotspot/share/runtime/javaThread.cpp +++ b/src/hotspot/share/runtime/javaThread.cpp @@ -1099,14 +1099,10 @@ void JavaThread::verify_not_published() { // Slow path when the native==>Java barriers detect a safepoint/handshake is // pending, when _suspend_flags is non-zero or when we need to process a stack // watermark. Also check for pending async exceptions (except unsafe access error). -// Note only the native==>Java barriers can call this function when thread state -// is _thread_in_native_trans. void JavaThread::check_special_condition_for_native_trans(JavaThread *thread) { - assert(thread->thread_state() == _thread_in_native_trans, "wrong state"); + assert(thread->thread_state() == _thread_in_vm, "wrong state"); assert(!thread->has_last_Java_frame() || thread->frame_anchor()->walkable(), "Unwalkable stack in native->Java transition"); - thread->set_thread_state(_thread_in_vm); - // Enable WXWrite: called directly from interpreter native wrapper. MACOS_AARCH64_ONLY(ThreadWXEnable wx(WXWrite, thread)); @@ -1333,15 +1329,10 @@ static const char* _get_thread_state_name(JavaThreadState _thread_state) { switch (_thread_state) { case _thread_uninitialized: return "_thread_uninitialized"; case _thread_new: return "_thread_new"; - case _thread_new_trans: return "_thread_new_trans"; case _thread_in_native: return "_thread_in_native"; - case _thread_in_native_trans: return "_thread_in_native_trans"; case _thread_in_vm: return "_thread_in_vm"; - case _thread_in_vm_trans: return "_thread_in_vm_trans"; case _thread_in_Java: return "_thread_in_Java"; - case _thread_in_Java_trans: return "_thread_in_Java_trans"; case _thread_blocked: return "_thread_blocked"; - case _thread_blocked_trans: return "_thread_blocked_trans"; default: return "unknown thread state"; } } diff --git a/src/hotspot/share/runtime/jfieldIDWorkaround.hpp b/src/hotspot/share/runtime/jfieldIDWorkaround.hpp index 44f109b3c35c..9919b85183a8 100644 --- a/src/hotspot/share/runtime/jfieldIDWorkaround.hpp +++ b/src/hotspot/share/runtime/jfieldIDWorkaround.hpp @@ -101,7 +101,7 @@ class jfieldIDWorkaround: AllStatic { // the jfieldID is created with. return checked_cast(result); } - static intptr_t encode_klass_hash(Klass* k, int offset); + static intptr_t encode_klass_hash(InstanceKlass* k, int offset); static bool klass_hash_ok(Klass* k, jfieldID id); static void verify_instance_jfieldID(Klass* k, jfieldID id); @@ -122,7 +122,7 @@ class jfieldIDWorkaround: AllStatic { return ((as_uint & flat_mask_in_place) != 0); } - static jfieldID to_instance_jfieldID(Klass* k, int offset, bool is_flat) { + static jfieldID to_instance_jfieldID(InstanceKlass* k, int offset, bool is_flat) { intptr_t as_uint = ((offset & large_offset_mask) << offset_shift) | instance_mask_in_place; if (is_flat) { diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp index c9fa936f203b..291815647d7c 100644 --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -122,6 +122,7 @@ Mutex* JfrStacktrace_lock = nullptr; Monitor* JfrMsg_lock = nullptr; Mutex* JfrBuffer_lock = nullptr; Mutex* SuspendedThreadTask_lock = nullptr; +Mutex* JfrEpochShift_lock = nullptr; #endif Mutex* CodeHeapStateAnalytics_lock = nullptr; @@ -282,6 +283,7 @@ void mutex_init() { MUTEX_DEFN(JfrMsg_lock , PaddedMonitor, event); MUTEX_DEFN(JfrStacktrace_lock , PaddedMutex , event); MUTEX_DEFN(SuspendedThreadTask_lock , PaddedMutex , nosafepoint); + MUTEX_DEFN(JfrEpochShift_lock , PaddedMutex , service-5); // lets keep this just above event #endif MUTEX_DEFN(ContinuationRelativize_lock , PaddedMonitor, nosafepoint-3); diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp index ae9c5e8a1f1a..16fa8ebd5d03 100644 --- a/src/hotspot/share/runtime/mutexLocker.hpp +++ b/src/hotspot/share/runtime/mutexLocker.hpp @@ -140,6 +140,7 @@ extern Mutex* JfrStacktrace_lock; // used to guard access to the extern Monitor* JfrMsg_lock; // protects JFR messaging extern Mutex* JfrBuffer_lock; // protects JFR buffer operations extern Mutex* SuspendedThreadTask_lock; // used to guard SuspendedThreadTask::run +extern Mutex* JfrEpochShift_lock; // exclude non-java threads from interleaving with the JFR epoch shift #endif extern Mutex* Metaspace_lock; // protects Metaspace virtualspace and chunk expansions diff --git a/src/hotspot/share/runtime/safepoint.cpp b/src/hotspot/share/runtime/safepoint.cpp index 5f01b7ac311b..85ae8b800f5e 100644 --- a/src/hotspot/share/runtime/safepoint.cpp +++ b/src/hotspot/share/runtime/safepoint.cpp @@ -297,9 +297,9 @@ void SafepointSynchronize::arm_safepoint() { // 4. Blocked // A thread which is blocked will not be allowed to return from the // block condition until the safepoint operation is complete. - // 5. In VM or Transitioning between states - // If a Java thread is currently running in the VM or transitioning - // between states, the safepointing code will poll the thread state + // 5. In VM + // If a Java thread is currently running in the VM, + // the safepointing code will poll the thread state // until the thread blocks itself when it attempts transitions to a // new state or locking a safepoint checked monitor. diff --git a/src/hotspot/share/runtime/vframe.inline.hpp b/src/hotspot/share/runtime/vframe.inline.hpp index b3e7b2800c54..58d06771eb5f 100644 --- a/src/hotspot/share/runtime/vframe.inline.hpp +++ b/src/hotspot/share/runtime/vframe.inline.hpp @@ -235,12 +235,6 @@ inline bool vframeStreamCommon::fill_from_frame() { JavaThreadState state = _thread != nullptr ? _thread->thread_state() : _thread_in_Java; - // in_Java should be good enough to test safepoint safety - // if state were say in_Java_trans then we'd expect that - // the pc would have already been slightly adjusted to - // one that would produce a pcDesc since the trans state - // would be one that might in fact anticipate a safepoint - if (state == _thread_in_Java ) { // This will get a method a zero bci and no inlining. // Might be nice to have a unique bci to signify this diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 20245684710d..3196b6f31ef3 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -1372,15 +1372,10 @@ \ declare_constant(_thread_uninitialized) \ declare_constant(_thread_new) \ - declare_constant(_thread_new_trans) \ declare_constant(_thread_in_native) \ - declare_constant(_thread_in_native_trans) \ declare_constant(_thread_in_vm) \ - declare_constant(_thread_in_vm_trans) \ declare_constant(_thread_in_Java) \ - declare_constant(_thread_in_Java_trans) \ declare_constant(_thread_blocked) \ - declare_constant(_thread_blocked_trans) \ declare_constant(JavaThread::_not_terminated) \ declare_constant(JavaThread::_thread_exiting) \ \ diff --git a/src/hotspot/share/services/threadService.cpp b/src/hotspot/share/services/threadService.cpp index 5c9e6ad166a8..6e18732284b3 100644 --- a/src/hotspot/share/services/threadService.cpp +++ b/src/hotspot/share/services/threadService.cpp @@ -1095,7 +1095,7 @@ ThreadsListEnumerator::ThreadsListEnumerator(Thread* cur_thread, for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) { // skips JavaThreads in the process of exiting // and also skips VM internal JavaThreads - // Threads in _thread_new or _thread_new_trans state are included. + // Threads in _thread_new state are included. // i.e. threads have been started but not yet running. if (jt->threadObj() == nullptr || jt->is_exiting() || diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 4de462a12873..6d3421bd2cf8 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -738,7 +738,7 @@ inline bool is_java_type(BasicType t) { return T_BOOLEAN <= t && t <= T_VOID; } -inline bool is_java_primitive(BasicType t) { +constexpr inline bool is_java_primitive(BasicType t) { return T_BOOLEAN <= t && t <= T_LONG; } @@ -1018,25 +1018,14 @@ TosState as_TosState(BasicType type); // _thread_in_vm : Executing in the vm // _thread_in_Java : Executing either interpreted or compiled Java code (or could be in a stub) // -// Each state has an associated xxxx_trans state, which is an intermediate state used when a thread is in -// a transition from one state to another. These extra states makes it possible for the safepoint code to -// handle certain thread_states without having to suspend the thread - making the safepoint code faster. -// -// Given a state, the xxxx_trans state can always be found by adding 1. -// enum JavaThreadState { _thread_uninitialized = 0, // should never happen (missing initialization) - _thread_new = 2, // just starting up, i.e., in process of being initialized - _thread_new_trans = 3, // corresponding transition state (not used, included for completeness) - _thread_in_native = 4, // running in native code - _thread_in_native_trans = 5, // corresponding transition state - _thread_in_vm = 6, // running in VM - _thread_in_vm_trans = 7, // corresponding transition state - _thread_in_Java = 8, // running in Java or in stub code - _thread_in_Java_trans = 9, // corresponding transition state (not used, included for completeness) - _thread_blocked = 10, // blocked in vm - _thread_blocked_trans = 11, // corresponding transition state - _thread_max_state = 12 // maximum thread state+1 - used for statistics allocation + _thread_new , // just starting up, i.e., in process of being initialized + _thread_in_native , // running in native code + _thread_in_vm , // running in VM + _thread_in_Java , // running in Java or in stub code + _thread_blocked , // blocked in vm + _thread_max_state // maximum thread state+1 - used for statistics allocation }; //---------------------------------------------------------------------------------------------------- @@ -1207,7 +1196,7 @@ inline int build_int_from_shorts( u2 low, u2 high ) { } // swap a & b -template static void swap(T& a, T& b) { +template inline void swap(T& a, T& b) { T tmp = a; a = b; b = tmp; diff --git a/src/hotspot/share/utilities/parseInteger.hpp b/src/hotspot/share/utilities/parseInteger.hpp index 3fc2f62a19fd..30e0d45d1cfb 100644 --- a/src/hotspot/share/utilities/parseInteger.hpp +++ b/src/hotspot/share/utilities/parseInteger.hpp @@ -115,7 +115,7 @@ inline bool multiply_by_1k(T& n) { // Example: "1024M:oom" will yield true, result=1G, endptr pointing to ":oom" template -static bool parse_integer(const char *s, char **endptr, T* result) { +inline bool parse_integer(const char *s, char **endptr, T* result) { if (!isdigit(s[0]) && s[0] != '-') { // strtoll/strtoull may allow leading spaces. Forbid it. @@ -163,7 +163,7 @@ static bool parse_integer(const char *s, char **endptr, T* result) { // characters. No remainder are allowed here. // Example: "100m" - okay, "100m:oom" -> not okay template -static bool parse_integer(const char *s, T* result) { +inline bool parse_integer(const char *s, T* result) { char* remainder; bool rc = parse_integer(s, &remainder, result); rc = rc && (*remainder == '\0'); diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java index 3d47e8e323aa..0ee59922b015 100644 --- a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java +++ b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java @@ -449,14 +449,14 @@ static void throwCCE() { public final boolean compareAndSet(T obj, V expect, V update) { accessCheck(obj); valueCheck(update); - return U.compareAndSetReference(obj, offset, expect, update); + return U.compareAndSetReference(obj, offset, vclass, expect, update); } public final boolean weakCompareAndSet(T obj, V expect, V update) { // same implementation as strong form for now accessCheck(obj); valueCheck(update); - return U.compareAndSetReference(obj, offset, expect, update); + return U.compareAndSetReference(obj, offset, vclass, expect, update); } public final void set(T obj, V newValue) { diff --git a/src/java.base/share/classes/jdk/internal/vm/annotation/Contended.java b/src/java.base/share/classes/jdk/internal/vm/annotation/Contended.java index f32a3ddfa683..90b97d2568af 100644 --- a/src/java.base/share/classes/jdk/internal/vm/annotation/Contended.java +++ b/src/java.base/share/classes/jdk/internal/vm/annotation/Contended.java @@ -67,6 +67,12 @@ * groups. Contention group tags are not inherited, and the same tag used * in a superclass and subclass, represent distinct contention groups. * + *
+ *
+ *

This annotation has no effect when used on or inside value classes. + *

+ *
+ * * @since 1.8 */ @Retention(RetentionPolicy.RUNTIME) diff --git a/src/java.base/share/classes/sun/security/provider/ML_DSA.java b/src/java.base/share/classes/sun/security/provider/ML_DSA.java index e1b418174350..61fa63ff54ee 100644 --- a/src/java.base/share/classes/sun/security/provider/ML_DSA.java +++ b/src/java.base/share/classes/sun/security/provider/ML_DSA.java @@ -57,14 +57,15 @@ public class ML_DSA { private final int T0_COEFF_SIZE = 13; private static final int MONT_R_BITS = 32; - private static final long MONT_R = 4294967296L; // 1 << MONT_R_BITS private static final int MONT_Q = 8380417; - private static final int MONT_R_SQUARE_MOD_Q = 2365951; private static final int MONT_Q_INV_MOD_R = 58728449; private static final int MONT_R_MOD_Q = 4193792; // toMont((ML_DSA_N)^-1 (mod ML_DSA_Q)) private static final int MONT_DIM_INVERSE = 16382; + // ceil(2^63 / ML_DSA_Q), used with Math.multiplyHigh for Barrett reduction + private static final long BARRETT_MULTIPLIER = 1100586287873L; + // Zeta values for NTT with montgomery factor precomputed private static final int[] MONT_ZETAS_FOR_NTT = new int[]{ 25847, -2608894, -518909, 237124, -777960, -876248, 466468, 1826347, @@ -743,7 +744,7 @@ public boolean verifyInternal(byte[] pkBytes, byte[] message, byte[] sigBytes) int[][] aHatZ = integerMatrixAlloc(mlDsa_k, ML_DSA_N); matrixVectorPointwiseMultiply(aHatZ, aHat, sig.response()); - int[][] t1Hat = vectorConstMul(1 << ML_DSA_D, pk.t1()); + int[][] t1Hat = vectorConstMul(pk.t1()); mlDsaVectorNtt(t1Hat); int[][] ct1 = integerMatrixAlloc(mlDsa_k, ML_DSA_N); @@ -1439,7 +1440,9 @@ public static void mlDsaNttMultiply(int[] product, int[] coeffs1, int[] coeffs2) implDilithiumNttMult(product, coeffs1, coeffs2); } - + // Computes the pointwise product of two ordinary NTT-domain polynomials. + // The Java fallback uses Barrett reduction. Intrinsic implementations may use + // Montgomery multiplication internally, but produce the same result modulo q. @IntrinsicCandidate static int implDilithiumNttMult(int[] product, int[] coeffs1, int[] coeffs2) { implDilithiumNttMultJava(product, coeffs1, coeffs2); @@ -1448,7 +1451,9 @@ static int implDilithiumNttMult(int[] product, int[] coeffs1, int[] coeffs2) { static void implDilithiumNttMultJava(int[] product, int[] coeffs1, int[] coeffs2) { for (int i = 0; i < ML_DSA_N; i++) { - product[i] = montMul(coeffs1[i], toMont(coeffs2[i])); + // Both input coefficients are in the ordinary NTT domain, therefore + // the product is in the same domain. + product[i] = barrettReduce((long) coeffs1[i] * coeffs2[i]); } } @@ -1533,14 +1538,16 @@ private void nttConstMultiply(int[][] res, int[] a, int[][] b) { } } - private int[][] vectorConstMul(int c, int[][] vec) { + // Multiplies t1 by 2^D. Since t1 < 2^10, the product is at most + // q - 1 and needs no reduction. + private int[][] vectorConstMul(int[][] vec) { int[][] res = integerMatrixAlloc(vec.length, vec[0].length); for (int i = 0; i < vec.length; i++) { for (int j = 0; j < vec[0].length; j++) { - res[i][j] = montMul(c, toMont(vec[i][j])); + res[i][j] = (1 << ML_DSA_D) * vec[i][j]; } } - return res; // -q < res[i][j] < q + return res; // 0 <= res[i][j] < q } // Adds two vectors of polynomials @@ -1617,8 +1624,14 @@ private static int montMul(int b, int c) { return (aHigh - (int) (((long)m * MONT_Q) >> MONT_R_BITS)); } - static int toMont(int a) { - return montMul(a, MONT_R_SQUARE_MOD_Q); + // Reduces a product of two NTT coefficients modulo ML_DSA_Q to its + // canonical representative. The product is bounded by ML_DSA_Q squared. + private static int barrettReduce(long product) { + long quotient = Math.multiplyHigh(product, BARRETT_MULTIPLIER) << 1; + long r = product - quotient * ML_DSA_Q; + r -= ML_DSA_Q & ~((r - ML_DSA_Q) >> 63); + r += (r >> 63) & ML_DSA_Q; + return (int) r; } // For multidimensional array initialization, manually allocating each entry is diff --git a/src/java.base/share/native/libzip/zip_util.c b/src/java.base/share/native/libzip/zip_util.c index 96d3050f5270..0e6e0da91f2f 100644 --- a/src/java.base/share/native/libzip/zip_util.c +++ b/src/java.base/share/native/libzip/zip_util.c @@ -84,10 +84,10 @@ DEF_STATIC_JNI_OnLoad /* * Opens the named file for reading, returning a ZFILE. * - * Compare this with winFileHandleOpen in windows/native/java/io/io_util_md.c. + * Compare this with winFileHandleOpen in windows/native/libjava/io_util_md.c. * This function does not take JNIEnv* and uses CreateFile (instead of - * CreateFileW). The expectation is that this function will be called only - * from ZIP_Open_Generic, which in turn is used by the JVM, where we do not + * CreateFileW). The expectation is that this function will be called only + * from ZIP_Open, which in turn is used by the JVM, where we do not * need to concern ourselves with wide chars. */ static ZFILE @@ -766,32 +766,6 @@ readCEN(jzfile *zip, jint knownTotal) return cenpos; } -/* - * Opens a zip file with the specified mode. Returns the jzfile object - * or NULL if an error occurred. If a zip error occurred then *pmsg will - * be set to the error message text if pmsg != 0. Otherwise, *pmsg will be - * set to NULL. Caller doesn't need to free the error message. - * The error message, if set, points to a static thread-safe buffer. - */ -jzfile * -ZIP_Open_Generic(const char *name, char **pmsg, int mode, jlong lastModified) -{ - jzfile *zip = NULL; - - /* Clear zip error message */ - if (pmsg != NULL) { - *pmsg = NULL; - } - - zip = ZIP_Get_From_Cache(name, pmsg, lastModified); - - if (zip == NULL && pmsg != NULL && *pmsg == NULL) { - ZFILE zfd = ZFILE_Open(name, mode); - zip = ZIP_Put_In_Cache(name, zfd, pmsg, lastModified); - } - return zip; -} - /* * Returns the jzfile corresponding to the given file name from the cache of * zip files, or NULL if the file is not in the cache. If the name is longer @@ -799,7 +773,7 @@ ZIP_Open_Generic(const char *name, char **pmsg, int mode, jlong lastModified) * message text if pmsg != 0. Otherwise, *pmsg will be set to NULL. Caller * doesn't need to free the error message. */ -jzfile * +static jzfile * ZIP_Get_From_Cache(const char *name, char **pmsg, jlong lastModified) { char buf[PATH_MAX]; @@ -827,8 +801,9 @@ ZIP_Get_From_Cache(const char *name, char **pmsg, jlong lastModified) MLOCK(zfiles_lock); for (zip = zfiles; zip != NULL; zip = zip->next) { if (strcmp(name, zip->name) == 0 - && (zip->lastModified == lastModified || zip->lastModified == 0) - && zip->refs < MAXREFS) { + && (zip->lastModified == lastModified || zip->lastModified == 0) + && zip->refs < MAXREFS) { + zip->refs++; break; } @@ -844,16 +819,8 @@ ZIP_Get_From_Cache(const char *name, char **pmsg, jlong lastModified) * pmsg != 0. Otherwise, *pmsg will be set to NULL. Caller doesn't need to * free the error message. */ - -jzfile * +static jzfile * ZIP_Put_In_Cache(const char *name, ZFILE zfd, char **pmsg, jlong lastModified) -{ - return ZIP_Put_In_Cache0(name, zfd, pmsg, lastModified, JNI_TRUE); -} - -jzfile * -ZIP_Put_In_Cache0(const char *name, ZFILE zfd, char **pmsg, jlong lastModified, - jboolean usemmap) { char errbuf[256]; jlong len; @@ -864,7 +831,7 @@ ZIP_Put_In_Cache0(const char *name, ZFILE zfd, char **pmsg, jlong lastModified, } #ifdef USE_MMAP - zip->usemmap = usemmap; + zip->usemmap = JNI_TRUE; #endif zip->refs = 1; zip->lastModified = lastModified; @@ -916,15 +883,28 @@ ZIP_Put_In_Cache0(const char *name, ZFILE zfd, char **pmsg, jlong lastModified, /* * Opens a zip file for reading. Returns the jzfile object or NULL - * if an error occurred. If a zip error occurred then *msg will be - * set to the error message text if msg != 0. Otherwise, *msg will be + * if an error occurred. If a zip error occurred then *pmsg will be + * set to the error message text if pmsg != NULL. Otherwise, *pmsg will be * set to NULL. Caller doesn't need to free the error message. + * The error message, if set, points to a static thread-safe buffer. */ JNIEXPORT jzfile * ZIP_Open(const char *name, char **pmsg) { - jzfile *file = ZIP_Open_Generic(name, pmsg, O_RDONLY, 0); - return file; + jzfile *zip = NULL; + + /* Clear zip error message */ + if (pmsg != NULL) { + *pmsg = NULL; + } + const jlong lastModified = 0; + zip = ZIP_Get_From_Cache(name, pmsg, lastModified); + + if (zip == NULL && pmsg != NULL && *pmsg == NULL) { + ZFILE zfd = ZFILE_Open(name, O_RDONLY); + zip = ZIP_Put_In_Cache(name, zfd, pmsg, lastModified); + } + return zip; } /* @@ -1123,6 +1103,24 @@ newEntry(jzfile *zip, jzcell *zc, AccessHint accessHint) return ze; } +/* + * Locks the specified zip file for reading. + */ +static void +ZIP_Lock(jzfile *zip) +{ + MLOCK(zip->lock); +} + +/* + * Unlocks the specified zip file. + */ +static void +ZIP_Unlock(jzfile *zip) +{ + MUNLOCK(zip->lock); +} + /* * Free the given jzentry. * In fact we maintain a one-entry cache of the most recently used @@ -1238,30 +1236,12 @@ ZIP_GetNextEntry(jzfile *zip, jint n) return result; } -/* - * Locks the specified zip file for reading. - */ -void -ZIP_Lock(jzfile *zip) -{ - MLOCK(zip->lock); -} - -/* - * Unlocks the specified zip file. - */ -void -ZIP_Unlock(jzfile *zip) -{ - MUNLOCK(zip->lock); -} - /* * Returns the offset of the entry data within the zip file. * Returns -1 if an error occurred, in which case zip->msg will * contain the error text. */ -jlong +static jlong ZIP_GetEntryDataOffset(jzfile *zip, jzentry *entry) { /* The Zip file spec explicitly allows the LOC extra data size to @@ -1297,7 +1277,7 @@ ZIP_GetEntryDataOffset(jzfile *zip, jzentry *entry) * The current implementation does not support reading an entry that * has the size bigger than 2**32 bytes in ONE invocation. */ -jint +static jint ZIP_Read(jzfile *zip, jzentry *entry, jlong pos, void *buf, jint len) { jlong entry_size; diff --git a/src/java.base/share/native/libzip/zip_util.h b/src/java.base/share/native/libzip/zip_util.h index 8cfe0b261f5c..d34012f88397 100644 --- a/src/java.base/share/native/libzip/zip_util.h +++ b/src/java.base/share/native/libzip/zip_util.h @@ -145,6 +145,11 @@ #define STORED 0 #define DEFLATED 8 +/* + * Index representing end of hash chain + */ +#define ZIP_ENDCHAIN ((jint) -1) + /* * Support for reading ZIP/JAR files. Some things worth noting: * @@ -236,11 +241,6 @@ typedef struct jzfile { /* Zip file */ jlong locpos; /* position of first LOC header (usually 0) */ } jzfile; -/* - * Index representing end of hash chain - */ -#define ZIP_ENDCHAIN ((jint)-1) - /* * Returns the ZIP entry corresponding to the given (NULL terminated) * entry name. Returns NULL if no entry is found by that name. @@ -260,18 +260,6 @@ ZIP_GetNextEntry(jzfile *zip, jint n); JNIEXPORT jzfile * ZIP_Open(const char *name, char **pmsg); -jzfile * -ZIP_Open_Generic(const char *name, char **pmsg, int mode, jlong lastModified); - -jzfile * -ZIP_Get_From_Cache(const char *name, char **pmsg, jlong lastModified); - -jzfile * -ZIP_Put_In_Cache(const char *name, ZFILE zfd, char **pmsg, jlong lastModified); - -jzfile * -ZIP_Put_In_Cache0(const char *name, ZFILE zfd, char **pmsg, jlong lastModified, jboolean usemmap); - JNIEXPORT void ZIP_Close(jzfile *zip); @@ -281,15 +269,8 @@ ZIP_Close(jzfile *zip); */ jzentry * ZIP_GetEntry(jzfile *zip, const char *name); -void -ZIP_Lock(jzfile *zip); -void -ZIP_Unlock(jzfile *zip); -jint -ZIP_Read(jzfile *zip, jzentry *entry, jlong pos, void *buf, jint len); JNIEXPORT void ZIP_FreeEntry(jzfile *zip, jzentry *ze); -jlong ZIP_GetEntryDataOffset(jzfile *zip, jzentry *entry); JNIEXPORT jboolean ZIP_InflateFully(void *inBuf, jlong inLen, void *outBuf, jlong outLen, char **pmsg); diff --git a/src/java.base/unix/native/libnio/ch/Net.c b/src/java.base/unix/native/libnio/ch/Net.c index 2b281f9a2048..0779294cdae3 100644 --- a/src/java.base/unix/native/libnio/ch/Net.c +++ b/src/java.base/unix/native/libnio/ch/Net.c @@ -96,6 +96,11 @@ static void initGroupSourceReq(JNIEnv* env, jbyteArray group, jint index, #ifdef _AIX +static jboolean isIPv4MappedGroup(struct group_source_req *req) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&req->gsr_group; + return IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) ? JNI_TRUE : JNI_FALSE; +} + /* * Checks whether or not "socket extensions for multicast source filters" is supported. * Returns JNI_TRUE if it is supported, JNI_FALSE otherwise @@ -225,7 +230,7 @@ Java_sun_nio_ch_Net_shouldSetBothIPv4AndIPv6Options0(JNIEnv* env, jclass cl) JNIEXPORT jboolean JNICALL Java_sun_nio_ch_Net_canIPv6SocketJoinIPv4Group0(JNIEnv* env, jclass cl) { -#if defined(__linux__) || defined(__APPLE__) +#if defined(__linux__) || defined(__APPLE__) || defined(_AIX) /* IPv6 sockets can join IPv4 multicast groups */ return JNI_TRUE; #else @@ -237,7 +242,7 @@ Java_sun_nio_ch_Net_canIPv6SocketJoinIPv4Group0(JNIEnv* env, jclass cl) JNIEXPORT jboolean JNICALL Java_sun_nio_ch_Net_canJoin6WithIPv4Group0(JNIEnv* env, jclass cl) { -#if defined(__APPLE__) +#if defined(__APPLE__) || defined(_AIX) /* IPV6_ADD_MEMBERSHIP can be used to join IPv4 multicast groups */ return JNI_TRUE; #else @@ -294,11 +299,10 @@ Java_sun_nio_ch_Net_socket0(JNIEnv *env, jclass cl, jboolean preferIPv6, } #if defined(__linux__) - if (type == SOCK_DGRAM) { + /* IPv4 or IPv6 datagram socket: disable IP_MULTICAST_ALL (Linux 2.6.31) */ + if (type == SOCK_DGRAM && ipv4_available()) { int arg = 0; - int level = (domain == AF_INET6) ? IPPROTO_IPV6 : IPPROTO_IP; - if ((setsockopt(fd, level, IP_MULTICAST_ALL, (char*)&arg, sizeof(arg)) < 0) && - (errno != ENOPROTOOPT)) { + if ((setsockopt(fd, IPPROTO_IP, IP_MULTICAST_ALL, (char*)&arg, sizeof(arg)) < 0)) { JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Unable to set IP_MULTICAST_ALL"); @@ -307,25 +311,14 @@ Java_sun_nio_ch_Net_socket0(JNIEnv *env, jclass cl, jboolean preferIPv6, } } - if (domain == AF_INET6 && type == SOCK_DGRAM) { - /* By default, Linux uses the route default */ - int arg = 1; - if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &arg, - sizeof(arg)) < 0) { - JNU_ThrowByNameWithLastError(env, - JNU_JAVANETPKG "SocketException", - "Unable to set IPV6_MULTICAST_HOPS"); - close(fd); - return -1; - } - - /* Disable IPV6_MULTICAST_ALL if option supported */ - arg = 0; + /* IPv6 datagram socket: disable IPV6_MULTICAST_ALL (Linux 4.20) if supported */ + if (type == SOCK_DGRAM && domain == AF_INET6) { + int arg = 0; if ((setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_ALL, (char*)&arg, sizeof(arg)) < 0) && (errno != ENOPROTOOPT)) { JNU_ThrowByNameWithLastError(env, - JNU_JAVANETPKG "SocketException", - "Unable to set IPV6_MULTICAST_ALL"); + JNU_JAVANETPKG "SocketException", + "Unable to set IPV6_MULTICAST_ALL"); close(fd); return -1; } @@ -767,6 +760,11 @@ Java_sun_nio_ch_Net_joinOrDrop6(JNIEnv *env, jobject this, jboolean join, jobjec if (n < 0) { if (join && (errno == ENOPROTOOPT || errno == EOPNOTSUPP)) return IOS_UNAVAILABLE; +#ifdef _AIX + // AIX rejects MCAST_*_SOURCE_GROUP for IPv4-mapped groups with EINVAL + if (source != NULL && errno == EINVAL && isIPv4MappedGroup(&req)) + return IOS_UNAVAILABLE; +#endif handleSocketErrorWithMessage(env, errno, "setsockopt failed"); } return 0; @@ -791,6 +789,11 @@ Java_sun_nio_ch_Net_blockOrUnblock6(JNIEnv *env, jobject this, jboolean block, j if (n < 0) { if (block && (errno == ENOPROTOOPT || errno == EOPNOTSUPP)) return IOS_UNAVAILABLE; +#ifdef _AIX + // AIX rejects MCAST_BLOCK/UNBLOCK_SOURCE for IPv4-mapped groups with EINVAL + if (errno == EINVAL && isIPv4MappedGroup(&req)) + return IOS_UNAVAILABLE; +#endif handleSocketError(env, errno); } return 0; @@ -990,4 +993,3 @@ Java_sun_nio_ch_Net_sendOOB(JNIEnv* env, jclass this, jobject fdo, jbyte b) int n = send(fdval(env, fdo), (const void*)&b, 1, MSG_OOB); return convertReturnVal(env, n, JNI_FALSE); } - diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java index c18bcf8cd377..acff2c2643eb 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java @@ -58,15 +58,10 @@ public class JavaThread extends Thread { // JavaThreadStates read from underlying process private static int UNINITIALIZED; private static int NEW; - private static int NEW_TRANS; private static int IN_NATIVE; - private static int IN_NATIVE_TRANS; private static int IN_VM; - private static int IN_VM_TRANS; private static int IN_JAVA; - private static int IN_JAVA_TRANS; private static int BLOCKED; - private static int BLOCKED_TRANS; private static int NOT_TERMINATED; private static int EXITING; @@ -106,15 +101,10 @@ private static synchronized void initialize(TypeDataBase db) { UNINITIALIZED = db.lookupIntConstant("_thread_uninitialized").intValue(); NEW = db.lookupIntConstant("_thread_new").intValue(); - NEW_TRANS = db.lookupIntConstant("_thread_new_trans").intValue(); IN_NATIVE = db.lookupIntConstant("_thread_in_native").intValue(); - IN_NATIVE_TRANS = db.lookupIntConstant("_thread_in_native_trans").intValue(); IN_VM = db.lookupIntConstant("_thread_in_vm").intValue(); - IN_VM_TRANS = db.lookupIntConstant("_thread_in_vm_trans").intValue(); IN_JAVA = db.lookupIntConstant("_thread_in_Java").intValue(); - IN_JAVA_TRANS = db.lookupIntConstant("_thread_in_Java_trans").intValue(); BLOCKED = db.lookupIntConstant("_thread_blocked").intValue(); - BLOCKED_TRANS = db.lookupIntConstant("_thread_blocked_trans").intValue(); NOT_TERMINATED = db.lookupIntConstant("JavaThread::_not_terminated").intValue(); EXITING = db.lookupIntConstant("JavaThread::_thread_exiting").intValue(); @@ -293,24 +283,14 @@ public JavaThreadState getThreadState() { return JavaThreadState.UNINITIALIZED; } else if (val == NEW) { return JavaThreadState.NEW; - } else if (val == NEW_TRANS) { - return JavaThreadState.NEW_TRANS; } else if (val == IN_NATIVE) { return JavaThreadState.IN_NATIVE; - } else if (val == IN_NATIVE_TRANS) { - return JavaThreadState.IN_NATIVE_TRANS; } else if (val == IN_VM) { return JavaThreadState.IN_VM; - } else if (val == IN_VM_TRANS) { - return JavaThreadState.IN_VM_TRANS; } else if (val == IN_JAVA) { return JavaThreadState.IN_JAVA; - } else if (val == IN_JAVA_TRANS) { - return JavaThreadState.IN_JAVA_TRANS; } else if (val == BLOCKED) { return JavaThreadState.BLOCKED; - } else if (val == BLOCKED_TRANS) { - return JavaThreadState.BLOCKED_TRANS; } else { throw new RuntimeException("Illegal thread state " + val); } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThreadState.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThreadState.java index a89f61369c95..52b1e1932187 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThreadState.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThreadState.java @@ -35,25 +35,14 @@ public class JavaThreadState { public static final JavaThreadState UNINITIALIZED = new JavaThreadState("UNINITIALIZED"); /** Just starting up, i.e., in process of being initialized */ public static final JavaThreadState NEW = new JavaThreadState("NEW"); - /** Corresponding transition state (not used, included for completeness) */ - public static final JavaThreadState NEW_TRANS = new JavaThreadState("NEW_TRANS"); /** Running in native code */ public static final JavaThreadState IN_NATIVE = new JavaThreadState("IN_NATIVE"); - /** Corresponding transition state */ - public static final JavaThreadState IN_NATIVE_TRANS = new JavaThreadState("IN_NATIVE_TRANS"); /** Running in VM */ public static final JavaThreadState IN_VM = new JavaThreadState("IN_VM"); - /** Corresponding transition state */ - public static final JavaThreadState IN_VM_TRANS = new JavaThreadState("IN_VM_TRANS"); /** Running in Java or in stub code */ public static final JavaThreadState IN_JAVA = new JavaThreadState("IN_JAVA"); - /** Corresponding transition state (not used, included for completeness) */ - public static final JavaThreadState IN_JAVA_TRANS = new JavaThreadState("IN_JAVA_TRANS"); /** Blocked in vm */ public static final JavaThreadState BLOCKED = new JavaThreadState("BLOCKED"); - /** Corresponding transition state */ - public static final JavaThreadState BLOCKED_TRANS = new JavaThreadState("BLOCKED_TRANS"); - /** Special state needed, since we cannot suspend a thread when it is in native_trans */ private JavaThreadState(String stringVal) { this.stringVal = stringVal; diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java index 45800be52725..356ba3de6234 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java @@ -89,7 +89,7 @@ public class JlinkTask { // jlink API ignores by default. Remove when signing is implemented. static final boolean IGNORE_SIGNING_DEFAULT = true; - private static final TaskHelper taskHelper + private final TaskHelper taskHelper = new TaskHelper(JLINK_BUNDLE); private static final Option[] recognizedOptions = { new Option(false, (task, opt, arg) -> { @@ -110,7 +110,7 @@ public class JlinkTask { task.options.limitMods.clear(); for (String mn : arg.split(",")) { if (mn.isEmpty()) { - throw taskHelper.newBadArgs("err.mods.must.be.specified", + throw task.taskHelper.newBadArgs("err.mods.must.be.specified", "--limit-modules"); } task.options.limitMods.add(mn); @@ -119,7 +119,7 @@ public class JlinkTask { new Option(true, (task, opt, arg) -> { for (String mn : arg.split(",")) { if (mn.isEmpty()) { - throw taskHelper.newBadArgs("err.mods.must.be.specified", + throw task.taskHelper.newBadArgs("err.mods.must.be.specified", "--add-modules"); } task.options.addMods.add(mn); @@ -139,18 +139,18 @@ public class JlinkTask { String[] values = arg.split("="); // check values if (values.length != 2 || values[0].isEmpty() || values[1].isEmpty()) { - throw taskHelper.newBadArgs("err.launcher.value.format", arg); + throw task.taskHelper.newBadArgs("err.launcher.value.format", arg); } else { String commandName = values[0]; String moduleAndMain = values[1]; int idx = moduleAndMain.indexOf("/"); if (idx != -1) { if (moduleAndMain.substring(0, idx).isEmpty()) { - throw taskHelper.newBadArgs("err.launcher.module.name.empty", arg); + throw task.taskHelper.newBadArgs("err.launcher.module.name.empty", arg); } if (moduleAndMain.substring(idx + 1).isEmpty()) { - throw taskHelper.newBadArgs("err.launcher.main.class.empty", arg); + throw task.taskHelper.newBadArgs("err.launcher.main.class.empty", arg); } } task.options.launchers.put(commandName, moduleAndMain); @@ -162,7 +162,7 @@ public class JlinkTask { } else if ("big".equals(arg)) { task.options.endian = ByteOrder.BIG_ENDIAN; } else { - throw taskHelper.newBadArgs("err.unknown.byte.order", arg); + throw task.taskHelper.newBadArgs("err.unknown.byte.order", arg); } }, "--endian"), new Option(false, (task, opt, arg) -> { @@ -174,7 +174,7 @@ public class JlinkTask { new Option(true, (task, opt, arg) -> { Path path = Paths.get(arg); if (Files.exists(path)) { - throw taskHelper.newBadArgs("err.dir.exists", path); + throw task.taskHelper.newBadArgs("err.dir.exists", path); } task.options.packagedModulesPath = path; }, true, "--keep-packaged-modules"), @@ -201,7 +201,7 @@ public class JlinkTask { private static final String PROGNAME = "jlink"; private final OptionsValues options = new OptionsValues(); - private static final OptionsHelper optionsHelper + private final OptionsHelper optionsHelper = taskHelper.newOptionsHelper(JlinkTask.class, recognizedOptions); private PrintWriter log; @@ -375,6 +375,7 @@ public static void createImage(JlinkConfiguration config, // First create the image provider try (ImageHelper imageProvider = createImageProvider(config, + new TaskHelper(JLINK_BUNDLE), null, IGNORE_SIGNING_DEFAULT, false, @@ -513,6 +514,7 @@ private void createImage(JlinkConfiguration config) throws Exception { // First create the image provider try (ImageHelper imageProvider = createImageProvider(config, + taskHelper, options.packagedModulesPath, options.ignoreSigning, options.bindServices, @@ -606,7 +608,7 @@ private static String getCurrentRuntimeVersion() { * @throws IllegalArgumentException If the `java.base` module reference `target` * is not compatible with this jlink. */ - private static void checkJavaBaseVersion(ModuleReference target) { + private void checkJavaBaseVersion(ModuleReference target) { String currentRelease = getCurrentRuntimeVersion(); String targetRelease = getReleaseInfo(target).orElseThrow(() -> new IllegalArgumentException( @@ -653,6 +655,7 @@ private static Path toPathLocation(ResolvedModule m) { private static ImageHelper createImageProvider(JlinkConfiguration config, + TaskHelper taskHelper, Path retainModulesPath, boolean ignoreSigning, boolean bindService, @@ -733,7 +736,7 @@ private static ImageHelper createImageProvider(JlinkConfiguration config, Map mods = cf.modules().stream() .collect(Collectors.toMap(ResolvedModule::name, JlinkTask::toPathLocation)); // determine the target platform of the image being created - Platform targetPlatform = targetPlatform(cf, mods, config.linkFromRuntimeImage()); + Platform targetPlatform = targetPlatform(cf, taskHelper, mods, config.linkFromRuntimeImage()); // if the user specified any --endian, then it must match the target platform's native // endianness if (endian != null && endian != targetPlatform.arch().byteOrder()) { @@ -764,6 +767,7 @@ private static ImageHelper createImageProvider(JlinkConfiguration config, version, ignoreSigning, config, + taskHelper, log)) .collect(Collectors.toSet()); @@ -778,6 +782,7 @@ private static Archive newArchive(String module, Runtime.Version version, boolean ignoreSigning, JlinkConfiguration config, + TaskHelper taskHelper, PrintWriter log) { if (path.toString().endsWith(".jmod")) { return new JmodArchive(module, path); @@ -812,7 +817,7 @@ private static Archive newArchive(String module, // directory. I.e. Files.isDirectory() would be true. Path modInfoPath = path.resolve("module-info.class"); if (Files.isRegularFile(modInfoPath)) { - return new DirArchive(path, findModuleName(modInfoPath)); + return new DirArchive(path, findModuleName(taskHelper, modInfoPath)); } else { throw new IllegalArgumentException( taskHelper.getMessage("err.not.a.module.directory", path)); @@ -825,7 +830,7 @@ private static Archive newArchive(String module, } } - private static String findModuleName(Path modInfoPath) { + private static String findModuleName(TaskHelper taskHelper, Path modInfoPath) { try (BufferedInputStream bis = new BufferedInputStream( Files.newInputStream(modInfoPath))) { return ModuleDescriptor.read(bis).name(); @@ -836,6 +841,7 @@ private static String findModuleName(Path modInfoPath) { } private static Platform targetPlatform(Configuration cf, + TaskHelper taskHelper, Map modsPaths, boolean runtimeImageLink) throws IOException { Path javaBasePath = modsPaths.get("java.base"); @@ -850,7 +856,7 @@ private static Platform targetPlatform(Configuration cf, // this is an attempt to build a cross-platform image. We now attempt to // find the target platform's arch and thus its endianness from the java.base // module's ModuleTarget attribute - String targetPlatformVal = readJavaBaseTargetPlatform(cf); + String targetPlatformVal = readJavaBaseTargetPlatform(cf, taskHelper); try { return Platform.parsePlatform(targetPlatformVal); } catch (IllegalArgumentException iae) { @@ -879,7 +885,7 @@ private static boolean isJavaBaseFromDefaultModulePath(Path javaBasePath) throws // returns the targetPlatform value from the ModuleTarget attribute of the java.base module. // throws IOException if the targetPlatform cannot be determined. - private static String readJavaBaseTargetPlatform(Configuration cf) throws IOException { + private static String readJavaBaseTargetPlatform(Configuration cf, TaskHelper taskHelper) throws IOException { Optional javaBase = cf.findModule("java.base"); assert javaBase.isPresent() : "java.base module is missing"; ModuleReference ref = javaBase.get().reference(); diff --git a/test/hotspot/gtest/gc/shenandoah/test_shenandoahUtils.cpp b/test/hotspot/gtest/gc/shenandoah/test_shenandoahUtils.cpp new file mode 100644 index 000000000000..22524f68d269 --- /dev/null +++ b/test/hotspot/gtest/gc/shenandoah/test_shenandoahUtils.cpp @@ -0,0 +1,104 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "gc/shenandoah/shenandoahUtils.hpp" +#include "utilities/globalDefinitions.hpp" + +#include +#include + +#include "unittest.hpp" + +static void test_units(double value, double expected_value, const char* expected_unit) { + ShenandoahSignedSize s = ShenandoahSignedSize::get(value); + EXPECT_DOUBLE_EQ(expected_value, s.value); + EXPECT_STREQ(expected_unit, s.unit); +} + +TEST(ShenandoahUtilsTest, format_byte_double) { + test_units(1024.0, 1024.0, "B"); + test_units(-1024.0, -1024.0, "B"); + test_units(1024.5, 1024.5, "B"); + test_units(-1024.5, -1024.5, "B"); +} + +TEST(ShenandoahUtilsTest, format_kilo_double) { + test_units(900.0 * K, 900.0, "K"); + test_units(-900.0 * K, -900.0, "K"); + test_units(900.1 * K, 900.1, "K"); + test_units(-900.1 * K, -900.1, "K"); +} + +TEST(ShenandoahUtilsTest, format_mega_double) { + test_units(900.0 * M, 900.0, "M"); + test_units(-900.0 * M, -900.0, "M"); + test_units(900.1 * M, 900.1, "M"); + test_units(-900.1 * M, -900.1, "M"); +} + +TEST(ShenandoahUtilsTest, format_giga_double) { + test_units(900.0 * G, 900.0, "G"); + test_units(-900.0 * G, -900.0, "G"); + test_units(900.1 * G, 900.1, "G"); + test_units(-900.1 * G, -900.1, "G"); +} + +TEST(ShenandoahUtilsTest, format_negative_zero_double) { + ShenandoahSignedSize s = ShenandoahSignedSize::get(-0.0); + EXPECT_DOUBLE_EQ(0.0, s.value); + EXPECT_STREQ("B", s.unit); + EXPECT_TRUE(std::signbit(s.value)); +} + +TEST(ShenandoahUtilsTest, format_infinite_double) { + const double inf = std::numeric_limits::infinity(); + ShenandoahSignedSize s = ShenandoahSignedSize::get(inf); + EXPECT_EQ(s.value, inf); + EXPECT_TRUE(std::isinf(s.value)); + EXPECT_STREQ("B", s.unit); +} + +TEST(ShenandoahUtilsTest, format_negative_infinite_double) { + const double ninf = -std::numeric_limits::infinity(); + ShenandoahSignedSize s = ShenandoahSignedSize::get(ninf); + EXPECT_EQ(s.value, ninf); + EXPECT_TRUE(std::isinf(s.value)); + EXPECT_STREQ("B", s.unit); +} + +TEST(ShenandoahUtilsTest, format_nan_double) { + ShenandoahSignedSize s = ShenandoahSignedSize::get(std::numeric_limits::quiet_NaN()); + EXPECT_TRUE(std::isnan(s.value)); + EXPECT_STREQ("B", s.unit); +} + +TEST(ShenandoahUtilsTest, format_unit_boundaries) { + // nextafter returns the next representable value of first argument in the direction of the second + EXPECT_STREQ("B", ShenandoahSignedSize::get(std::nextafter(100.0 * K, 0.0)).unit); + EXPECT_STREQ("K", ShenandoahSignedSize::get(std::nextafter(100.0 * M, 0.0)).unit); + EXPECT_STREQ("M", ShenandoahSignedSize::get(std::nextafter(100.0 * G, 0.0)).unit); + EXPECT_STREQ("B", ShenandoahSignedSize::get(std::nextafter(-100.0 * K, 0.0)).unit); + EXPECT_STREQ("K", ShenandoahSignedSize::get(std::nextafter(-100.0 * M, 0.0)).unit); + EXPECT_STREQ("M", ShenandoahSignedSize::get(std::nextafter(-100.0 * G, 0.0)).unit); +} diff --git a/test/hotspot/gtest/opto/test_typejavaptr.cpp b/test/hotspot/gtest/opto/test_typejavaptr.cpp new file mode 100644 index 000000000000..89fbebcf1e41 --- /dev/null +++ b/test/hotspot/gtest/opto/test_typejavaptr.cpp @@ -0,0 +1,2003 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "nmt/memTag.hpp" +#include "opto/type.hpp" +#include "opto/typejavaptr.hpp" +#include "unittest.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/growableArray.hpp" +#include "utilities/ostream.hpp" +#include +#include + +class ciInstanceKlassMirror; +class ciArrayKlassMirror; +class TypeOopPtrMirror; +class TypeInstPtrMirror; +class TypeAryPtrMirror; +class TypeKlassPtrMirror; +class TypeInstKlassPtrMirror; +class TypeAryKlassPtrMirror; + +// This file contains unit tests for the implementation of TypeJavaPtrMeetHelper and +// TypeJavaPtrJoinHelper. We use mirror instances that mimic the behaviour of the real objects. +// There are several advantages in using them: +// - It is really hard to create a Type instance, while we can create mirror instances at will. +// - Type::make can be used to create an arbitrary instance, while we can limit the corresponding +// factories to only return some expected instances. This greatly increases the rigor of the +// tests. +// - Mirror instances are created at compile time, ensuring the absence of unexpected behaviors. + +class InterfaceSet { +public: + bool _i0; + bool _i1; + + constexpr InterfaceSet(bool i0, bool i1) : _i0(i0), _i1(i1) {} + + InterfaceSet intersection_with(InterfaceSet other) const { + return InterfaceSet(_i0 && other._i0, _i1 && other._i1); + } + + InterfaceSet union_with(InterfaceSet other) const { + return InterfaceSet(_i0 || other._i0, _i1 || other._i1); + } + + const InterfaceSet* operator->() const { + return this; + } + + constexpr bool operator==(InterfaceSet o) const { + return _i0 == o._i0 && _i1 == o._i1; + } + + constexpr bool operator!=(InterfaceSet o) const { + return !(*this == o); + } + + bool contains(InterfaceSet sub) const { + return (_i0 || !sub._i0) && (_i1 || !sub._i1); + } + + bool eq(const ciInstanceKlassMirror* ci_klass) const; + + void dump_on(outputStream& st) const { + if (_i0) { + st.print("x"); + } else { + st.print("_"); + } + if (_i1) { + st.print("x"); + } else { + st.print("_"); + } + } +}; + +class ciKlassMirror { +private: + // The instance_id of the allocation corresponding to this type, each type has 3 dedicated id, + // this value is the start of the 3 + int _instance_id; + +protected: + constexpr ciKlassMirror(int instance_id) : _instance_id(instance_id) {} + +public: + static constexpr bool verify_instance_id(); + + constexpr int instance_id() const { return _instance_id; } + + virtual bool is_inst() const = 0; + virtual bool is_ary() const = 0; + const ciInstanceKlassMirror* as_inst() const; + const ciArrayKlassMirror* as_ary() const; + + virtual void dump_on(outputStream& st) const = 0; +}; + +class ciInstanceKlassMirror : public ciKlassMirror { +private: + friend class ciEnvMirror; + + static constexpr size_t _samples_size = 8; + int _parent_idx; + InterfaceSet _interfaces; + bool _is_loaded; + bool _is_interface; + const char* _name; + + constexpr ciInstanceKlassMirror(int parent_idx, InterfaceSet interfaces, bool is_loaded, bool is_interface, const char* name, int instance_id) + : ciKlassMirror(instance_id), _parent_idx(parent_idx), _interfaces(interfaces), _is_loaded(is_loaded), _is_interface(is_interface), _name(name) {} + +public: + static const std::array _samples; + + constexpr InterfaceSet interfaces() const { return _interfaces; } + constexpr const ciInstanceKlassMirror* as_instance_klass() const { return this; } + constexpr bool is_loaded() const { return _is_loaded; } + constexpr bool is_interface() const { return _is_interface; } + constexpr bool is_java_lang_Object() const { return _parent_idx == -1; } + + constexpr const ciInstanceKlassMirror* parent() const { + if (is_java_lang_Object()) { + return nullptr; + } else { + return &_samples[_parent_idx]; + } + } + + constexpr bool is_subtype_of(const ciInstanceKlassMirror* other) const { + for (auto t = this; t != nullptr; t = t->parent()) { + if (t == other) { + return true; + } + } + return false; + } + + constexpr const ciInstanceKlassMirror* least_common_ancestor(const ciInstanceKlassMirror* other) const { + for (auto t = this; t != nullptr; t = t->parent()) { + if (other->is_subtype_of(t)) { + return t; + } + } + ShouldNotReachHere(); + } + + virtual bool is_inst() const override { return true; } + virtual bool is_ary() const override { return false; } + + virtual void dump_on(outputStream& st) const override { + st.print("%s", _name); + } +}; + +constexpr std::array ciInstanceKlassMirror::_samples{ + ciInstanceKlassMirror(-1, InterfaceSet(false, false), true, false, "Object", 1), // j.l.Object + ciInstanceKlassMirror(0, InterfaceSet(false, false), false, false, "unloaded", 4), // unloaded + ciInstanceKlassMirror(0, InterfaceSet(true, false), true, false, "A", 7), // A extends Object implements I0 + ciInstanceKlassMirror(2, InterfaceSet(true, true), true, false, "B", 10), // B extends A implements I1 + ciInstanceKlassMirror(2, InterfaceSet(true, false), true, false, "C", 13), // C extends A + ciInstanceKlassMirror(0, InterfaceSet(false, true), true, false, "D", 16), // D extends Object implements I1 + ciInstanceKlassMirror(0, InterfaceSet(true, false), true, true, "I0", 19), // I0 + ciInstanceKlassMirror(0, InterfaceSet(false, true), true, true, "I1", 22), // I1 +}; + +bool InterfaceSet::eq(const ciInstanceKlassMirror* ci_klass) const { + return ci_klass->interfaces() == *this; +} + +class ciArrayKlassMirror : public ciKlassMirror { +private: + static constexpr size_t _samples_size = (ciInstanceKlassMirror::_samples.size() + 3) * 2; + BasicType _bt; + bool _elem_is_array; + int _elem_idx; + + constexpr ciArrayKlassMirror(BasicType bt, bool elem_is_array, int elem_idx, int instance_id) + : ciKlassMirror(instance_id), _bt(bt), _elem_is_array(elem_is_array), _elem_idx(elem_idx) {} + +public: + static const std::array _samples; + + static constexpr const ciArrayKlassMirror& find(BasicType bt, const ciKlassMirror* elem) { + for (auto& sample : _samples) { + if (sample._bt == bt && sample.elem() == elem) { + return sample; + } + } + + ShouldNotReachHere(); + } + + constexpr BasicType elem_basic_type() const { return _bt; } + + constexpr const ciKlassMirror* elem() const { + if (_elem_idx == -1) { + return nullptr; + } else if (_elem_is_array) { + return &_samples[_elem_idx]; + } else { + return &ciInstanceKlassMirror::_samples[_elem_idx]; + } + } + + virtual bool is_inst() const override { return false; } + virtual bool is_ary() const override { return true; } + + virtual void dump_on(outputStream& st) const override { + if (_bt != T_OBJECT) { + st.print("%s[]", type2name(_bt)); + } else { + elem()->dump_on(st); + st.print("[]"); + } + } +}; + +// byte[] and int[] shares the same element base when they are expressed as a TypeAryPtr, while +// they have a different base from float[], so these 3 are used as representatives of primitive +// arrays +constexpr std::array ciArrayKlassMirror::_samples = { + ciArrayKlassMirror(T_BYTE, false, -1, 25), + ciArrayKlassMirror(T_INT, false, -1, 28), + ciArrayKlassMirror(T_FLOAT, false, -1, 31), + ciArrayKlassMirror(T_OBJECT, false, 0, 34), + ciArrayKlassMirror(T_OBJECT, false, 1, 37), + ciArrayKlassMirror(T_OBJECT, false, 2, 40), + ciArrayKlassMirror(T_OBJECT, false, 3, 43), + ciArrayKlassMirror(T_OBJECT, false, 4, 46), + ciArrayKlassMirror(T_OBJECT, false, 5, 49), + ciArrayKlassMirror(T_OBJECT, false, 6, 52), + ciArrayKlassMirror(T_OBJECT, false, 7, 55), + ciArrayKlassMirror(T_OBJECT, true, 0, 58), + ciArrayKlassMirror(T_OBJECT, true, 1, 61), + ciArrayKlassMirror(T_OBJECT, true, 2, 64), + ciArrayKlassMirror(T_OBJECT, true, 3, 67), + ciArrayKlassMirror(T_OBJECT, true, 4, 70), + ciArrayKlassMirror(T_OBJECT, true, 5, 73), + ciArrayKlassMirror(T_OBJECT, true, 6, 76), + ciArrayKlassMirror(T_OBJECT, true, 7, 79), + ciArrayKlassMirror(T_OBJECT, true, 8, 82), + ciArrayKlassMirror(T_OBJECT, true, 9, 85), + ciArrayKlassMirror(T_OBJECT, true, 10, 88), +}; + +const ciInstanceKlassMirror* ciKlassMirror::as_inst() const { + assert(is_inst(), "not an instance"); + return static_cast(this); +} + +const ciArrayKlassMirror* ciKlassMirror::as_ary() const { + assert(is_ary(), "not an array"); + return static_cast(this); +} + +constexpr bool ciKlassMirror::verify_instance_id() { + int expected = 1; + for (auto& ci_klass : ciInstanceKlassMirror::_samples) { + if (ci_klass.instance_id() != expected) { + return false; + } + expected += 3; + } + for (auto& ci_klass : ciArrayKlassMirror::_samples) { + if (ci_klass.instance_id() != expected) { + return false; + } + expected += 3; + } + + return true; +} + +static_assert(ciKlassMirror::verify_instance_id()); + +class ciEnvMirror { +private: + static const ciEnvMirror _instance; + +public: + static constexpr const ciEnvMirror* current() { return &_instance; } + constexpr const ciInstanceKlassMirror* Object_klass() const { return &ciInstanceKlassMirror::_samples[0]; } +}; +constexpr ciEnvMirror ciEnvMirror::_instance; + +class InstanceMirror { +private: + const ciKlassMirror* _klass; + int _idx; + +public: + constexpr InstanceMirror(const ciKlassMirror& klass, int idx) : _klass(&klass), _idx(idx) {} + constexpr InstanceMirror(std::nullptr_t) : _klass(nullptr), _idx(0) {} + + constexpr int idx() const { return _idx; } + constexpr const ciKlassMirror* klass() const { return _klass; } + + constexpr int array_length() const { + // A little sneaky, static_cast to the incorrect type will be rejected in a constexpr + // evaluation. May change to _klass->is_ary() when C++20 is available. + assert(_klass != nullptr && static_cast(_klass) != nullptr, "must be an array"); + switch (_idx) { + case 0: + case 1: + return 1; + case 2: + return 2; + case 3: + return 0; + case 4: + case 5: + return 3; + default: + ShouldNotReachHere(); + } + } + + constexpr bool operator==(const InstanceMirror& o) const { + return _klass == o._klass && _idx == o._idx; + } + + constexpr bool operator!=(const InstanceMirror& o) const { + return !(*this == o); + } + + constexpr bool operator!=(std::nullptr_t) const { + return _klass != nullptr; + } +}; + +template +class AryElemType { +private: + static constexpr size_t _1d_samples_size = ciInstanceKlassMirror::_samples.size() + 5; + + BasicType _bt; + const PtrType* _ptr_type; + +public: + static const AryElemType* const TOP; + static const AryElemType* const BOTTOM; + + constexpr AryElemType(BasicType bt, const PtrType* ptr_type) : _bt(bt), _ptr_type(ptr_type) { + assert((bt == T_OBJECT) == (ptr_type != nullptr), ""); + } + + constexpr AryElemType() : AryElemType(T_ILLEGAL, nullptr) {} + + static constexpr const AryElemType* find(BasicType bt, const PtrType* ptr_type) { + auto res = find_1d(bt, ptr_type); + if (res != nullptr) { + return res; + } + + res = find_2d(bt, ptr_type); + assert(res != nullptr, "must find an instance"); + return res; + } + + static constexpr const AryElemType* find_1d(BasicType bt, const PtrType* ptr_type); + static constexpr const AryElemType* find_2d(BasicType bt, const PtrType* ptr_type); + + constexpr BasicType basic_type() const { return _bt; } + constexpr bool empty() const { return _bt == T_ILLEGAL; } + constexpr const PtrType* make_ptr() const { return _ptr_type; } + + constexpr Type::TYPES base() const { + if (_bt == T_VOID) { + return Type::Bottom; + } else if (_bt == T_ILLEGAL) { + ShouldNotReachHere(); + } else if (_bt == T_BYTE || _bt == T_INT) { + return Type::Int; + } else if (_bt == T_FLOAT) { + return Type::FloatBot; + } else { + assert(_bt == T_OBJECT, "unexpected BasicType %s", type2name(_bt)); + assert(_ptr_type != nullptr, "must have element type info"); + return _ptr_type->base(); + } + } + + const AryElemType* meet(const AryElemType* other) const { return meet_speculative(other); } + const AryElemType* join(const AryElemType* other) const { return join_speculative(other); } + + const AryElemType* meet_speculative(const AryElemType* other) const { + if (_bt != other->_bt) { + return BOTTOM; + } else if (_bt != T_OBJECT) { + return this; + } + + return find(T_OBJECT, TypeJavaPtrMeetHelper::javaptr_type_xmeet(_ptr_type, other->_ptr_type)); + } + + const AryElemType* join_speculative(const AryElemType* other) const { + if (_bt != other->_bt) { + return TOP; + } else if (_bt != T_OBJECT) { + return this; + } + + auto ptr_type = TypeJavaPtrJoinHelper::javaptr_type_xjoin(_ptr_type, other->_ptr_type); + if (TypePtr::PTR ptr_ptr = ptr_type->ptr(); ptr_ptr == TypePtr::TopPTR || ptr_ptr == TypePtr::Null) { + return TOP; + } + + return find(T_OBJECT, static_cast(ptr_type)); + } +}; + +class TypePtrMirror { +private: + static constexpr size_t _samples_size = 8; + Type::TYPES _base; + TypePtr::PTR _ptr; + Type::Offset _offset; + + static const std::array _samples; + +protected: + constexpr TypePtrMirror(Type::TYPES base, TypePtr::PTR ptr, Type::Offset offset) : _base(base), _ptr(ptr), _offset(offset) {} + + void dump_ptr(outputStream& st) const { + if (ptr() == TypePtr::BotPTR) { + st.print("BotPTR"); + } else if (ptr() == TypePtr::NotNull) { + st.print("NotNull"); + } else if (ptr() == TypePtr::Constant) { + st.print("Constant"); + } else if (ptr() == TypePtr::Null) { + st.print("Null"); + } else { + assert(ptr() == TypePtr::TopPTR, "unexpected ptr %d", int(ptr())); + st.print("TopPTR"); + } + } + + void dump_offset(outputStream& st) const { + if (offset() == Type::OffsetBot) { + st.print("offset=bot"); + } else if (offset() == Type::OffsetTop) { + st.print("offset=top"); + } else { + st.print("offset=%d", offset()); + } + } + +public: + using ciEnv = ciEnvMirror; + using PtrType = TypePtrMirror; + + static const TypePtrMirror* make(Type::TYPES base, TypePtr::PTR ptr, Type::Offset offset, const TypePtrMirror* speculative = nullptr, int inline_depth = 0) { + assert(base == Type::AnyPtr, "unexpected base %d", int(base)); + assert(speculative == nullptr && inline_depth == 0, "unsupported"); + for (auto& sample : _samples) { + if (sample.ptr() == ptr && sample.offset() == offset.get()) { + return &sample; + } + } + + ShouldNotReachHere(); + } + + constexpr Type::TYPES base() const { return _base; } + constexpr TypePtr::PTR ptr() const { return _ptr; } + constexpr int offset() const { return _offset.get(); } + constexpr TypePtr::FlatInArray flat_in_array() const { return TypePtr::NotFlat; } + constexpr bool empty() const { return ptr() == TypePtr::TopPTR; } + + const TypePtrMirror* meet(const TypePtrMirror* o) const { ShouldNotReachHere(); } + const TypePtrMirror* join(const TypePtrMirror* o) const { ShouldNotReachHere(); } + const TypePtrMirror* is_ptr() const { ShouldNotReachHere(); } + + virtual const TypeOopPtrMirror* is_oopptr() const { ShouldNotReachHere(); } + virtual const TypeKlassPtrMirror* is_klassptr() const { ShouldNotReachHere(); } + + virtual void dump_on(outputStream& st) const { + st.print("AnyPtr:"); + dump_ptr(st); + st.print(" - "); + dump_offset(st); + } +}; + +constexpr std::array TypePtrMirror::_samples = { + TypePtrMirror(Type::AnyPtr, TypePtr::TopPTR, Type::Offset::bottom), + TypePtrMirror(Type::AnyPtr, TypePtr::TopPTR, Type::Offset(0)), + TypePtrMirror(Type::AnyPtr, TypePtr::TopPTR, Type::Offset(1)), + TypePtrMirror(Type::AnyPtr, TypePtr::TopPTR, Type::Offset::top), + TypePtrMirror(Type::AnyPtr, TypePtr::Null, Type::Offset::bottom), + TypePtrMirror(Type::AnyPtr, TypePtr::Null, Type::Offset(0)), + TypePtrMirror(Type::AnyPtr, TypePtr::Null, Type::Offset(1)), + TypePtrMirror(Type::AnyPtr, TypePtr::Null, Type::Offset::top), +}; + +class TypeOopPtrMirror : public TypePtrMirror { +private: + InterfaceSet _interfaces; + int _instance_id; + InstanceMirror _const_oop; + bool _klass_is_exact; + +protected: + constexpr TypeOopPtrMirror(Type::TYPES base, TypePtr::PTR ptr, InterfaceSet interfaces, InstanceMirror const_oop, bool klass_is_exact, Type::Offset offset, int instance_id) + : TypePtrMirror(base, ptr, offset), _interfaces(interfaces), _instance_id(instance_id), _const_oop(const_oop), _klass_is_exact(klass_is_exact) {} + + void dump_instance(outputStream& st) const { + st.print("const_oop="); + if (const_oop() == nullptr) { + st.print("null"); + } else { + st.print("%d", const_oop().idx()); + } + st.print(" - instance_id="); + if (instance_id() == TypeOopPtr::InstanceBot) { + st.print("bot"); + } else { + st.print("%d", instance_id()); + } + } + +public: + static constexpr bool is_oopptr_type = true; + using InstType = TypeInstPtrMirror; + using AryType = TypeAryPtrMirror; + + constexpr InterfaceSet interfaces() const { return _interfaces; } + constexpr InstanceMirror const_oop() const { return _const_oop; } + constexpr bool klass_is_exact() const { return _klass_is_exact; } + constexpr int instance_id() const { return _instance_id; } + constexpr const TypePtrMirror* speculative() const { return nullptr; } + constexpr int inline_depth() const { return 0; } + + virtual const TypeOopPtrMirror* is_oopptr() const override { return this; } + virtual const TypeInstPtrMirror* is_instptr() const = 0; + virtual const TypeAryPtrMirror* is_aryptr() const = 0; + + virtual void dump_on(outputStream& st) const override = 0; +}; + +static constexpr size_t TypeInstPtr_samples_size() { + size_t res = 0; + for (auto& ci_klass : ciInstanceKlassMirror::_samples) { + if (ci_klass.is_interface()) { + continue; + } + + // The constant instance + res += ci_klass.is_loaded() ? 2 : 0; + + // The exact-klass instances, with different instance_id values + res += ci_klass.is_loaded() ? 4 : 0; + + // The non-exact-klass instances + if (ci_klass.interfaces()._i0 && ci_klass.interfaces()._i1) { + res += 2; + } else if (ci_klass.interfaces()._i0 || ci_klass.interfaces()._i1) { + res += 4; + } else { + res += 8; + } + } + + // Different offset values + return res * 3; +} + +class TypeInstPtrMirror : public TypeOopPtrMirror { +private: + const ciInstanceKlassMirror* _klass; + + constexpr TypeInstPtrMirror(TypePtr::PTR ptr, const ciInstanceKlassMirror& klass, InterfaceSet interfaces, bool klass_is_exact, + InstanceMirror const_oop, Type::Offset offset, int instance_id) + : TypeOopPtrMirror(Type::InstPtr, ptr, interfaces, const_oop, klass_is_exact, offset, instance_id), + _klass(&klass) { + assert(!klass.is_interface(), ""); + } + + static constexpr auto generate_samples(); + +public: + static const std::array _samples; + + constexpr TypeInstPtrMirror() + : TypeOopPtrMirror(Type::Bad, TypePtr::TopPTR, InterfaceSet(false, false), nullptr, false, Type::Offset(0), 0), + _klass(nullptr) {} + + static constexpr const TypeInstPtrMirror* make(TypePtr::PTR ptr, const ciInstanceKlassMirror* klass, InterfaceSet interfaces, bool klass_is_exact, + InstanceMirror const_oop = nullptr, Type::Offset offset = Type::Offset(0), TypePtr::FlatInArray flat_in_array = TypePtr::NotFlat, + int instance_id = TypeOopPtr::InstanceBot, const TypePtrMirror* speculative = nullptr, int inline_depth = 0) { + assert(flat_in_array == TypePtr::NotFlat && speculative == nullptr && inline_depth == 0, "unsupported"); + for (auto& sample : _samples) { + if (sample.ptr() == ptr && sample.instance_klass() == klass && sample.interfaces() == interfaces && sample.klass_is_exact() == klass_is_exact && + sample.const_oop() == const_oop && sample.offset() == offset.get() && sample.instance_id() == instance_id) { + return &sample; + } + } + + ShouldNotReachHere(); + } + + constexpr const ciInstanceKlassMirror* instance_klass() const { return _klass; } + + virtual const TypeInstPtrMirror* is_instptr() const override { return this; } + virtual const TypeAryPtrMirror* is_aryptr() const override { ShouldNotReachHere(); } + + virtual void dump_on(outputStream& st) const override { + st.print("InstPtr:"); + dump_ptr(st); + st.print(" - "); + instance_klass()->dump_on(st); + st.print("("); + interfaces().dump_on(st); + st.print(") - klass_is_exact=%d - ", klass_is_exact()); + dump_instance(st); + st.print(" - "); + dump_offset(st); + } +}; + +constexpr auto TypeInstPtrMirror::generate_samples() { + std::array res; + size_t sample_idx = 0; + auto fill_result = [&](TypePtr::PTR ptr, const ciInstanceKlassMirror& ci_klass, InterfaceSet interfaces, bool klass_is_exact, InstanceMirror const_oop, int instance_id) { + res[sample_idx] = TypeInstPtrMirror(ptr, ci_klass, interfaces, klass_is_exact, const_oop, Type::Offset::bottom, instance_id); + sample_idx++; + res[sample_idx] = TypeInstPtrMirror(ptr, ci_klass, interfaces, klass_is_exact, const_oop, Type::Offset(0), instance_id); + sample_idx++; + res[sample_idx] = TypeInstPtrMirror(ptr, ci_klass, interfaces, klass_is_exact, const_oop, Type::Offset(1), instance_id); + sample_idx++; + }; + + for (auto& ci_klass : ciInstanceKlassMirror::_samples) { + if (ci_klass.is_interface()) { + continue; + } + + InterfaceSet interfaces = ci_klass.interfaces(); + if (ci_klass.is_loaded()) { + fill_result(TypePtr::Constant, ci_klass, interfaces, true, InstanceMirror(ci_klass, 0), TypeOopPtr::InstanceBot); + fill_result(TypePtr::Constant, ci_klass, interfaces, true, InstanceMirror(ci_klass, 1), TypeOopPtr::InstanceBot); + fill_result(TypePtr::BotPTR, ci_klass, interfaces, true, nullptr, TypeOopPtr::InstanceBot); + fill_result(TypePtr::NotNull, ci_klass, interfaces, true, nullptr, TypeOopPtr::InstanceBot); + fill_result(TypePtr::NotNull, ci_klass, interfaces, true, nullptr, ci_klass.instance_id()); + fill_result(TypePtr::NotNull, ci_klass, interfaces, true, nullptr, ci_klass.instance_id() + 1); + } + + fill_result(TypePtr::BotPTR, ci_klass, interfaces, false, nullptr, TypeOopPtr::InstanceBot); + fill_result(TypePtr::NotNull, ci_klass, interfaces, false, nullptr, TypeOopPtr::InstanceBot); + if (!interfaces._i0) { + fill_result(TypePtr::BotPTR, ci_klass, InterfaceSet(true, interfaces._i1), false, nullptr, TypeOopPtr::InstanceBot); + fill_result(TypePtr::NotNull, ci_klass, InterfaceSet(true, interfaces._i1), false, nullptr, TypeOopPtr::InstanceBot); + } + if (!interfaces._i1) { + fill_result(TypePtr::BotPTR, ci_klass, InterfaceSet(interfaces._i0, true), false, nullptr, TypeOopPtr::InstanceBot); + fill_result(TypePtr::NotNull, ci_klass, InterfaceSet(interfaces._i0, true), false, nullptr, TypeOopPtr::InstanceBot); + } + if (!interfaces._i0 && !interfaces._i1) { + fill_result(TypePtr::BotPTR, ci_klass, InterfaceSet(true, true), false, nullptr, TypeOopPtr::InstanceBot); + fill_result(TypePtr::NotNull, ci_klass, InterfaceSet(true, true), false, nullptr, TypeOopPtr::InstanceBot); + } + } + + assert(sample_idx == res.size(), ""); + return res; +} + +constexpr std::array TypeInstPtrMirror::_samples = generate_samples(); + +class ArySizeType { +private: + friend class TypeAryPtrMirror; + + bool _empty; + int _lo; + int _hi; + + constexpr ArySizeType(bool empty, int lo, int hi) : _empty(empty), _lo(lo), _hi(hi) { + assert((!empty && lo <= hi) || (empty && lo == 0 && hi == 0), "invariant"); + assert(lo >= 0 && hi <= 3, "constraint"); + } + +public: + static const ArySizeType BOTTOM; + + ArySizeType meet(ArySizeType other) const { + assert(!_empty && !other._empty, "must not have empty type here"); + return ArySizeType(false, MIN2(_lo, other._lo), MAX2(_hi, other._hi)); + } + + ArySizeType join(ArySizeType other) const { + assert(!_empty && !other._empty, "must not have empty type here"); + int lo = MAX2(_lo, other._lo); + int hi = MIN2(_hi, other._hi); + if (lo > hi) { + return ArySizeType(true, 0, 0); + } else { + return ArySizeType(false, lo, hi); + } + } + + const ArySizeType* operator->() const { return this; } + const ArySizeType& is_int() const { assert(!_empty, "must not be empty"); return *this; } + const ArySizeType& isa_int() const { return *this; } + + constexpr bool contains(int len) const { + assert(0 <= len && len <= 3, "unsupported"); + return _lo <= len && len <= _hi; + } + + constexpr bool operator==(const ArySizeType& other) const { + return _empty == other._empty && _lo == other._lo && _hi == other._hi; + } + + constexpr bool operator!=(const ArySizeType& other) const { + return !(*this == other); + } + + constexpr bool operator==(std::nullptr_t) const { + return _empty; + } + + constexpr bool operator!=(std::nullptr_t) const { + return !_empty; + } +}; + +constexpr ArySizeType ArySizeType::BOTTOM(false, 0, 3); + +class TypeAryMirror { +private: + friend class TypeAryPtrMirror; + + const AryElemType* _elem; + ArySizeType _size; + +public: + constexpr TypeAryMirror(const AryElemType* elem, ArySizeType size) : _elem(elem), _size(size) { + assert(size != nullptr, ""); + } + + static TypeAryMirror make(const AryElemType* elem, ArySizeType size, bool is_stable, bool flat, bool not_flat, bool null_free, bool not_null_free, bool atomic) { + assert(!is_stable && !flat && not_flat && !null_free && not_null_free && atomic, "unsupported"); + return TypeAryMirror(elem, size); + } +}; + +static constexpr size_t TypeAryPtr_1d_elem_samples_size() { + size_t res = 0; + // top, bot, byte, int, float + res += 5; + for (auto& ci_klass : ciInstanceKlassMirror::_samples) { + if (ci_klass.is_interface()) { + continue; + } + + if (ci_klass.interfaces()._i0 && ci_klass.interfaces()._i1) { + res += 1; + } else if (ci_klass.interfaces()._i0 || ci_klass.interfaces()._i1) { + res += 2; + } else { + res += 4; + } + } + + return res; +} + +static constexpr size_t TypeAryPtr_1d_exact_samples_size() { + size_t res = 0; + // byte[], int[], float[] + res += 3; + for (auto& ci_klass : ciInstanceKlassMirror::_samples) { + if (ci_klass.is_loaded()) { + res += 1; + } + } + + return res; +} + +static constexpr size_t TypeAryPtr_1d_nonexact_samples_size() { + size_t res = 0; + // bot[] + res += 1; + for (auto& ci_klass : ciInstanceKlassMirror::_samples) { + if (ci_klass.is_interface()) { + continue; + } + + if (ci_klass.interfaces()._i0 && ci_klass.interfaces()._i1) { + res += 1; + } else if (ci_klass.interfaces()._i0 || ci_klass.interfaces()._i1) { + res += 2; + } else { + res += 4; + } + } + + return res; +} + +// All test instances have is_stable() == false, is_auto_box_cache() == false, is_flat() == false, +// is_not_flat() == true, is_null_free() == false, is_not_null_free() == true, is_atomic() == true, +// field_offset() == Offset::bottom. All other parameters are included exhaustively. +class TypeAryPtrMirror : public TypeOopPtrMirror { +private: + static constexpr size_t _1d_elem_samples_size = TypeAryPtr_1d_elem_samples_size(); + static constexpr size_t _1d_samples_size = (TypeAryPtr_1d_exact_samples_size() * 15 + TypeAryPtr_1d_nonexact_samples_size() * 10) * 3; + static constexpr size_t _2d_elem_samples_size = TypeAryPtr_1d_nonexact_samples_size() + 3; + static constexpr size_t _2d_samples_size = _1d_samples_size; + + TypeAryMirror _ary; + const ciArrayKlassMirror* _klass; + + constexpr TypeAryPtrMirror(TypePtr::PTR ptr, InstanceMirror const_oop, const TypeAryMirror& ary, + const ciArrayKlassMirror* klass, bool klass_is_exact, Type::Offset offset, int instance_id) + : TypeOopPtrMirror(TypePtr::AryPtr, ptr, _array_interfaces, const_oop, klass_is_exact, offset, instance_id), + _ary(ary), _klass(klass) { + assert(ary._elem != nullptr, ""); + } + + template + static constexpr void fill_samples_helper(R& res, size_t& sample_idx, TypePtr::PTR ptr, InstanceMirror const_oop, const AryElemType* elem, + const ciArrayKlassMirror* klass, bool klass_is_exact, int instance_id, Type::Offset offset); + + static constexpr auto generate_1d_elem_samples(); + static constexpr auto generate_1d_samples(); + static constexpr auto generate_2d_elem_samples(); + static constexpr auto generate_2d_samples(); + +public: + static constexpr InterfaceSet _array_interfaces = InterfaceSet(false, true); + static const std::array, _1d_elem_samples_size> _1d_elem_samples; + static const std::array _1d_samples; + static const std::array, _2d_elem_samples_size> _2d_elem_samples; + static const std::array _2d_samples; + + constexpr TypeAryPtrMirror() + : TypeOopPtrMirror(Type::Bad, TypePtr::TopPTR, _array_interfaces, nullptr, false, Type::Offset(0), 0), + _ary(nullptr, ArySizeType(false, 0, 0)), _klass(nullptr) {} + + static constexpr const TypeAryPtrMirror* make(TypePtr::PTR ptr, InstanceMirror const_oop, const TypeAryMirror& ary, const ciArrayKlassMirror* klass, bool klass_is_exact, + Type::Offset offset, Type::Offset field_offset, int instance_id, const TypePtrMirror* speculative = nullptr, int inline_depth = 0, bool is_autobox_cache = false) { + assert(field_offset == Type::Offset::bottom && speculative == nullptr && inline_depth == 0 && !is_autobox_cache, "unsupported"); + auto match = [&](const TypeAryPtrMirror& sample) { + return sample.ptr() == ptr && sample.const_oop() == const_oop && sample.elem() == ary._elem && sample.size() == ary._size && + sample.klass() == klass && sample.klass_is_exact() == klass_is_exact && sample.offset() == offset.get() && sample.instance_id() == instance_id; + }; + + for (auto& sample : _1d_samples) { + if (match(sample)) { + return &sample; + } + } + + for (auto& sample : _2d_samples) { + if (match(sample)) { + return &sample; + } + } + + ShouldNotReachHere(); + } + + constexpr const TypeAryMirror ary() const { return _ary; } + constexpr const AryElemType* elem() const { return _ary._elem; } + constexpr ArySizeType size() const { return _ary._size; } + constexpr bool is_stable() const { return false; } + constexpr const ciArrayKlassMirror* klass() const { return _klass; } + constexpr bool is_autobox_cache() const { return false; } + + bool is_flat() const { return false; } + bool is_not_flat() const { return true; } + bool is_null_free() const { return false; } + bool is_not_null_free() const { return true; } + bool is_atomic() const { return true; } + Type::Offset field_offset() const { return Type::Offset::bottom; } + + virtual const TypeInstPtrMirror* is_instptr() const override { ShouldNotReachHere(); } + virtual const TypeAryPtrMirror* is_aryptr() const override { return this; } + + virtual void dump_on(outputStream& st) const override { + st.print("AryPtr:"); + dump_ptr(st); + st.print(" - "); + if (elem()->base() == Type::Bottom) { + st.print("bot"); + } else if (elem()->basic_type() != T_OBJECT) { + st.print("%s", type2name(elem()->basic_type())); + } else { + st.print("("); + elem()->make_ptr()->dump_on(st); + st.print(")"); + } + st.print("[%d - %d] - klass_is_exact=%d - ", size()._lo, size()._hi, klass_is_exact()); + dump_instance(st); + st.print(" - "); + dump_offset(st); + } +}; + +template <> +constexpr const AryElemType* AryElemType::TOP = &TypeAryPtrMirror::_1d_elem_samples[0]; + +template <> +constexpr const AryElemType* AryElemType::BOTTOM = &TypeAryPtrMirror::_1d_elem_samples[1]; + +template <> +constexpr const AryElemType* AryElemType::find_1d(BasicType bt, const TypeOopPtrMirror* ptr_type) { + for (auto& sample : TypeAryPtrMirror::_1d_elem_samples) { + if (sample._bt == bt && sample._ptr_type == ptr_type) { + return &sample; + } + } + return nullptr; +} + +template <> +constexpr const AryElemType* AryElemType::find_2d(BasicType bt, const TypeOopPtrMirror* ptr_type) { + for (auto& sample : TypeAryPtrMirror::_2d_elem_samples) { + if (sample._bt == bt && sample._ptr_type == ptr_type) { + return &sample; + } + } + return nullptr; +} + +constexpr auto TypeAryPtrMirror::generate_1d_elem_samples() { + std::array, _1d_elem_samples_size> res; + res[0] = AryElemType(T_ILLEGAL, nullptr); + res[1] = AryElemType(T_VOID, nullptr); + res[2] = AryElemType(T_BYTE, nullptr); + res[3] = AryElemType(T_INT, nullptr); + res[4] = AryElemType(T_FLOAT, nullptr); + + size_t sample_idx = 5; + for (auto& ci_klass : ciInstanceKlassMirror::_samples) { + if (ci_klass.is_interface()) { + continue; + } + + InterfaceSet interfaces = ci_klass.interfaces(); + res[sample_idx] = AryElemType(T_OBJECT, TypeInstPtrMirror::make(TypePtr::BotPTR, &ci_klass, interfaces, false)); + sample_idx++; + if (!interfaces._i0) { + res[sample_idx] = AryElemType(T_OBJECT, TypeInstPtrMirror::make(TypePtr::BotPTR, &ci_klass, InterfaceSet(true, interfaces._i1), false)); + sample_idx++; + } + if (!interfaces._i1) { + res[sample_idx] = AryElemType(T_OBJECT, TypeInstPtrMirror::make(TypePtr::BotPTR, &ci_klass, InterfaceSet(interfaces._i0, true), false)); + sample_idx++; + } + if (!interfaces._i0 && !interfaces._i1) { + res[sample_idx] = AryElemType(T_OBJECT, TypeInstPtrMirror::make(TypePtr::BotPTR, &ci_klass, InterfaceSet(true, true), false)); + sample_idx++; + } + } + + assert(sample_idx == res.size(), ""); + return res; +} + +constexpr std::array, TypeAryPtrMirror::_1d_elem_samples_size> TypeAryPtrMirror::_1d_elem_samples = generate_1d_elem_samples(); + +template +constexpr void TypeAryPtrMirror::fill_samples_helper(R& res, size_t& sample_idx, TypePtr::PTR ptr, InstanceMirror const_oop, const AryElemType* elem, + const ciArrayKlassMirror* klass, bool klass_is_exact, int instance_id, Type::Offset offset) { + if (const_oop != nullptr) { + TypeAryMirror ary(elem, ArySizeType(false, const_oop.array_length(), const_oop.array_length())); + res[sample_idx] = TypeAryPtrMirror(ptr, const_oop, ary, klass, klass_is_exact, offset, instance_id); + sample_idx++; + } else if (instance_id != TypeOopPtr::InstanceBot) { + // Each klass is reserved 3 distinct instance_id, so we try to infer whether which instance this + // is by finding its remainder modulo 3 + if (instance_id % 3 == 1) { + TypeAryMirror ary(elem, ArySizeType(false, 0, 1)); + res[sample_idx] = TypeAryPtrMirror(ptr, const_oop, ary, klass, klass_is_exact, offset, instance_id); + sample_idx++; + ary = TypeAryMirror(elem, ArySizeType(false, 1, 1)); + res[sample_idx] = TypeAryPtrMirror(ptr, const_oop, ary, klass, klass_is_exact, offset, instance_id); + sample_idx++; + } else { + TypeAryMirror ary(elem, ArySizeType(false, 2, 3)); + res[sample_idx] = TypeAryPtrMirror(ptr, const_oop, ary, klass, klass_is_exact, offset, instance_id); + sample_idx++; + } + } else { + TypeAryMirror ary(elem, ArySizeType(false, 0, 1)); + res[sample_idx] = TypeAryPtrMirror(ptr, const_oop, ary, klass, klass_is_exact, offset, instance_id); + sample_idx++; + ary = TypeAryMirror(elem, ArySizeType(false, 1, 1)); + res[sample_idx] = TypeAryPtrMirror(ptr, const_oop, ary, klass, klass_is_exact, offset, instance_id); + sample_idx++; + + ary = TypeAryMirror(elem, ArySizeType(false, 0, 3)); + res[sample_idx] = TypeAryPtrMirror(ptr, const_oop, ary, klass, klass_is_exact, offset, instance_id); + sample_idx++; + ary = TypeAryMirror(elem, ArySizeType(false, 1, 3)); + res[sample_idx] = TypeAryPtrMirror(ptr, const_oop, ary, klass, klass_is_exact, offset, instance_id); + sample_idx++; + ary = TypeAryMirror(elem, ArySizeType(false, 2, 3)); + res[sample_idx] = TypeAryPtrMirror(ptr, const_oop, ary, klass, klass_is_exact, offset, instance_id); + sample_idx++; + } +} + +constexpr auto TypeAryPtrMirror::generate_1d_samples() { + std::array res; + size_t sample_idx = 0; + auto fill_result = [&](TypePtr::PTR ptr, InstanceMirror const_oop, const AryElemType* elem, + const ciArrayKlassMirror* klass, bool klass_is_exact, int instance_id) { + fill_samples_helper(res, sample_idx, ptr, const_oop, elem, klass, klass_is_exact, instance_id, Type::Offset::bottom); + fill_samples_helper(res, sample_idx, ptr, const_oop, elem, klass, klass_is_exact, instance_id, Type::Offset(0)); + fill_samples_helper(res, sample_idx, ptr, const_oop, elem, klass, klass_is_exact, instance_id, Type::Offset(1)); + }; + + auto bot_ary_elem = AryElemType::find_1d(T_VOID, nullptr); + fill_result(TypePtr::BotPTR, nullptr, bot_ary_elem, nullptr, false, TypeOopPtr::InstanceBot); + fill_result(TypePtr::NotNull, nullptr, bot_ary_elem, nullptr, false, TypeOopPtr::InstanceBot); + + auto byte_ary_elem = AryElemType::find_1d(T_BYTE, nullptr); + auto& byte_ary_klass = ciArrayKlassMirror::find(T_BYTE, nullptr); + fill_result(TypePtr::BotPTR, nullptr, byte_ary_elem, &byte_ary_klass, true, TypeOopPtr::InstanceBot); + fill_result(TypePtr::NotNull, nullptr, byte_ary_elem, &byte_ary_klass, true, TypeOopPtr::InstanceBot); + fill_result(TypePtr::NotNull, nullptr, byte_ary_elem, &byte_ary_klass, true, byte_ary_klass.instance_id()); + fill_result(TypePtr::NotNull, nullptr, byte_ary_elem, &byte_ary_klass, true, byte_ary_klass.instance_id() + 1); + fill_result(TypePtr::Constant, InstanceMirror(byte_ary_klass, 0), byte_ary_elem, &byte_ary_klass, true, TypeOopPtr::InstanceBot); + fill_result(TypePtr::Constant, InstanceMirror(byte_ary_klass, 1), byte_ary_elem, &byte_ary_klass, true, TypeOopPtr::InstanceBot); + + auto int_ary_elem = AryElemType::find_1d(T_INT, nullptr); + auto& int_ary_klass = ciArrayKlassMirror::find(T_INT, nullptr); + fill_result(TypePtr::BotPTR, nullptr, int_ary_elem, &int_ary_klass, true, TypeOopPtr::InstanceBot); + fill_result(TypePtr::NotNull, nullptr, int_ary_elem, &int_ary_klass, true, TypeOopPtr::InstanceBot); + fill_result(TypePtr::NotNull, nullptr, int_ary_elem, &int_ary_klass, true, int_ary_klass.instance_id()); + fill_result(TypePtr::NotNull, nullptr, int_ary_elem, &int_ary_klass, true, int_ary_klass.instance_id() + 1); + fill_result(TypePtr::Constant, InstanceMirror(int_ary_klass, 0), int_ary_elem, &int_ary_klass, true, TypeOopPtr::InstanceBot); + fill_result(TypePtr::Constant, InstanceMirror(int_ary_klass, 1), int_ary_elem, &int_ary_klass, true, TypeOopPtr::InstanceBot); + + auto float_ary_elem = AryElemType::find_1d(T_FLOAT, nullptr); + auto& float_ary_klass = ciArrayKlassMirror::find(T_FLOAT, nullptr); + fill_result(TypePtr::BotPTR, nullptr, float_ary_elem, nullptr, true, TypeOopPtr::InstanceBot); + fill_result(TypePtr::NotNull, nullptr, float_ary_elem, nullptr, true, TypeOopPtr::InstanceBot); + fill_result(TypePtr::NotNull, nullptr, float_ary_elem, nullptr, true, float_ary_klass.instance_id()); + fill_result(TypePtr::NotNull, nullptr, float_ary_elem, nullptr, true, float_ary_klass.instance_id() + 1); + fill_result(TypePtr::Constant, InstanceMirror(float_ary_klass, 0), float_ary_elem, nullptr, true, TypeOopPtr::InstanceBot); + fill_result(TypePtr::Constant, InstanceMirror(float_ary_klass, 1), float_ary_elem, nullptr, true, TypeOopPtr::InstanceBot); + + for (auto& ci_klass : ciInstanceKlassMirror::_samples) { + if (ci_klass.is_loaded()) { + auto& ci_instance_klass = ci_klass.is_interface() ? *ciEnvMirror::current()->Object_klass() : ci_klass; + auto elem_ptr_type = TypeInstPtrMirror::make(TypePtr::BotPTR, &ci_instance_klass, ci_klass.interfaces(), false); + auto elem = AryElemType::find_1d(T_OBJECT, elem_ptr_type); + fill_result(TypePtr::BotPTR, nullptr, elem, nullptr, true, TypeOopPtr::InstanceBot); + fill_result(TypePtr::NotNull, nullptr, elem, nullptr, true, TypeOopPtr::InstanceBot); + + auto& ci_ary_klass = ciArrayKlassMirror::find(T_OBJECT, &ci_klass); + fill_result(TypePtr::NotNull, nullptr, elem, nullptr, true, ci_ary_klass.instance_id()); + fill_result(TypePtr::NotNull, nullptr, elem, nullptr, true, ci_ary_klass.instance_id() + 1); + + InstanceMirror const_oop_0(ci_ary_klass, 0); + fill_result(TypePtr::Constant, const_oop_0, elem, nullptr, true, TypeOopPtr::InstanceBot); + InstanceMirror const_oop_1(ci_ary_klass, 1); + fill_result(TypePtr::Constant, const_oop_1, elem, nullptr, true, TypeOopPtr::InstanceBot); + } + + if (ci_klass.is_interface()) { + continue; + } + + InterfaceSet interfaces = ci_klass.interfaces(); + { + auto elem_ptr_type = TypeInstPtrMirror::make(TypePtr::BotPTR, &ci_klass, interfaces, false); + auto elem = AryElemType::find_1d(T_OBJECT, elem_ptr_type); + fill_result(TypePtr::BotPTR, nullptr, elem, nullptr, false, TypeOopPtr::InstanceBot); + fill_result(TypePtr::NotNull, nullptr, elem, nullptr, false, TypeOopPtr::InstanceBot); + } + + if (!interfaces._i0) { + auto elem_ptr_type = TypeInstPtrMirror::make(TypePtr::BotPTR, &ci_klass, InterfaceSet(true, interfaces._i1), false); + auto elem = AryElemType::find_1d(T_OBJECT, elem_ptr_type); + fill_result(TypePtr::BotPTR, nullptr, elem, nullptr, false, TypeOopPtr::InstanceBot); + fill_result(TypePtr::NotNull, nullptr, elem, nullptr, false, TypeOopPtr::InstanceBot); + } + + if (!interfaces._i1) { + auto elem_ptr_type = TypeInstPtrMirror::make(TypePtr::BotPTR, &ci_klass, InterfaceSet(interfaces._i0, true), false); + auto elem = AryElemType::find_1d(T_OBJECT, elem_ptr_type); + fill_result(TypePtr::BotPTR, nullptr, elem, nullptr, false, TypeOopPtr::InstanceBot); + fill_result(TypePtr::NotNull, nullptr, elem, nullptr, false, TypeOopPtr::InstanceBot); + } + + if (!interfaces._i0 && !interfaces._i1) { + auto elem_ptr_type = TypeInstPtrMirror::make(TypePtr::BotPTR, &ci_klass, InterfaceSet(true, true), false); + auto elem = AryElemType::find_1d(T_OBJECT, elem_ptr_type); + fill_result(TypePtr::BotPTR, nullptr, elem, nullptr, false, TypeOopPtr::InstanceBot); + fill_result(TypePtr::NotNull, nullptr, elem, nullptr, false, TypeOopPtr::InstanceBot); + } + } + + assert(sample_idx == res.size(), ""); + return res; +} + +constexpr std::array TypeAryPtrMirror::_1d_samples = generate_1d_samples(); + +constexpr auto TypeAryPtrMirror::generate_2d_elem_samples() { + std::array, _2d_elem_samples_size> res; + size_t sample_idx = 0; + + for (auto& elem_ptr_type : _1d_samples) { + if (elem_ptr_type.ptr() != TypePtr::BotPTR || elem_ptr_type.size() != ArySizeType::BOTTOM || elem_ptr_type.offset() != 0) { + continue; + } + + bool klass_is_exact = is_java_primitive(elem_ptr_type.elem()->basic_type()); + if (elem_ptr_type.klass_is_exact() != klass_is_exact) { + continue; + } + + res[sample_idx] = AryElemType(T_OBJECT, &elem_ptr_type); + sample_idx++; + } + + assert(sample_idx == res.size(), ""); + return res; +} + +constexpr std::array, TypeAryPtrMirror::_2d_elem_samples_size> TypeAryPtrMirror::_2d_elem_samples = generate_2d_elem_samples(); + +constexpr auto TypeAryPtrMirror::generate_2d_samples() { + std::array res; + size_t sample_idx = 0; + auto fill_result = [&](TypePtr::PTR ptr, InstanceMirror const_oop, const AryElemType* elem, + const ciArrayKlassMirror* klass, bool klass_is_exact, int instance_id) { + fill_samples_helper(res, sample_idx, ptr, const_oop, elem, klass, klass_is_exact, instance_id, Type::Offset::bottom); + fill_samples_helper(res, sample_idx, ptr, const_oop, elem, klass, klass_is_exact, instance_id, Type::Offset(0)); + fill_samples_helper(res, sample_idx, ptr, const_oop, elem, klass, klass_is_exact, instance_id, Type::Offset(1)); + }; + + for (auto& sample : _1d_samples) { + if (sample.ptr() == TypePtr::Constant && sample.const_oop().idx() == 0 && sample.offset() == 0) { + bool klass_is_exact = is_java_primitive(sample.elem()->basic_type()); + TypeAryMirror ary(sample.elem(), ArySizeType::BOTTOM); + auto elem_ptr_type = TypeAryPtrMirror::make(TypePtr::BotPTR, nullptr, ary, sample.klass(), klass_is_exact, Type::Offset(0), Type::Offset::bottom, TypeOopPtr::InstanceBot); + auto elem = AryElemType::find_2d(T_OBJECT, elem_ptr_type); + fill_result(TypePtr::BotPTR, nullptr, elem, nullptr, true, TypeOopPtr::InstanceBot); + fill_result(TypePtr::NotNull, nullptr, elem, nullptr, true, TypeOopPtr::InstanceBot); + + auto& ci_ary_klass = ciArrayKlassMirror::find(T_OBJECT, sample.const_oop().klass()); + fill_result(TypePtr::NotNull, nullptr, elem, nullptr, true, ci_ary_klass.instance_id()); + fill_result(TypePtr::NotNull, nullptr, elem, nullptr, true, ci_ary_klass.instance_id() + 1); + + InstanceMirror const_oop_0(ci_ary_klass, 0); + fill_result(TypePtr::Constant, const_oop_0, elem, nullptr, true, TypeOopPtr::InstanceBot); + InstanceMirror const_oop_1(ci_ary_klass, 1); + fill_result(TypePtr::Constant, const_oop_1, elem, nullptr, true, TypeOopPtr::InstanceBot); + } + + if (sample.ptr() == TypePtr::BotPTR && sample.size() == ArySizeType::BOTTOM && !sample.klass_is_exact() && sample.offset() == 0) { + auto elem = AryElemType::find_2d(T_OBJECT, &sample); + fill_result(TypePtr::BotPTR, nullptr, elem, nullptr, false, TypeOopPtr::InstanceBot); + fill_result(TypePtr::NotNull, nullptr, elem, nullptr, false, TypeOopPtr::InstanceBot); + } + } + + assert(sample_idx == res.size(), ""); + return res; +} + +constexpr std::array TypeAryPtrMirror::_2d_samples = generate_2d_samples(); + +class TypeKlassPtrMirror : public TypePtrMirror { +private: + InterfaceSet _interfaces; + +protected: + constexpr TypeKlassPtrMirror(Type::TYPES base, TypePtr::PTR ptr, InterfaceSet interfaces, Type::Offset offset) + : TypePtrMirror(base, ptr, offset), _interfaces(interfaces) {} + +public: + static constexpr bool is_oopptr_type = false; + using InstType = TypeInstKlassPtrMirror; + using AryType = TypeAryKlassPtrMirror; + + constexpr InterfaceSet interfaces() const { return _interfaces; } + constexpr bool klass_is_exact() const { return ptr() == TypePtr::Constant; } + + virtual const TypeKlassPtrMirror* is_klassptr() const { return this; } + virtual const TypeInstKlassPtrMirror* is_instklassptr() const = 0; + virtual const TypeAryKlassPtrMirror* is_aryklassptr() const = 0; +}; + +static constexpr size_t TypeInstKlassPtr_samples_size() { + size_t res = 0; + for (auto& ci_klass : ciInstanceKlassMirror::_samples) { + if (!ci_klass.is_loaded() || ci_klass.is_interface()) { + continue; + } + + if (ci_klass.is_java_lang_Object()) { + // j.l.O has 2 additional constant klass pointers corresponding to the interfaces I0 and I1 + res += 11; + continue; + } + + // The constant instance + res += 1; + + // The non-exact-klass instances + if (ci_klass.interfaces()._i0 && ci_klass.interfaces()._i1) { + res += 2; + } else if (ci_klass.interfaces()._i0 || ci_klass.interfaces()._i1) { + res += 4; + } else { + res += 8; + } + } + + // Different offset values + return res * 3; +} + +class TypeInstKlassPtrMirror : public TypeKlassPtrMirror { +private: + const ciInstanceKlassMirror* _klass; + + static constexpr auto generate_samples(); + + constexpr TypeInstKlassPtrMirror(TypePtr::PTR ptr, const ciInstanceKlassMirror& klass, InterfaceSet interfaces, Type::Offset offset) + : TypeKlassPtrMirror(Type::InstKlassPtr, ptr, interfaces, offset), _klass(&klass) { + assert(klass.is_loaded() && !klass.is_interface(), ""); + } + +public: + static const std::array _samples; + + constexpr TypeInstKlassPtrMirror() : TypeKlassPtrMirror(Type::Bad, TypePtr::TopPTR, InterfaceSet(false, false), Type::Offset(0)), _klass(nullptr) {} + + static constexpr const TypeInstKlassPtrMirror* make(TypePtr::PTR ptr, const ciInstanceKlassMirror* klass, InterfaceSet interfaces, Type::Offset offset = Type::Offset(0), + TypePtr::FlatInArray flat_in_array = TypePtr::NotFlat) { + assert(flat_in_array == TypePtr::NotFlat, "unsupported"); + for (auto& sample : _samples) { + if (sample.ptr() == ptr && sample.instance_klass() == klass && sample.interfaces() == interfaces && sample.offset() == offset.get()) { + return &sample; + } + } + + ShouldNotReachHere(); + } + + constexpr const ciInstanceKlassMirror* instance_klass() const { return _klass; } + + virtual const TypeInstKlassPtrMirror* is_instklassptr() const override { return this; } + virtual const TypeAryKlassPtrMirror* is_aryklassptr() const override { ShouldNotReachHere(); } + + virtual void dump_on(outputStream& st) const override { + st.print("InstKlassPtr:"); + dump_ptr(st); + st.print(" - "); + instance_klass()->dump_on(st); + st.print("("); + interfaces().dump_on(st); + st.print(")"); + dump_offset(st); + } +}; + +constexpr auto TypeInstKlassPtrMirror::generate_samples() { + std::array res; + size_t sample_idx = 0; + auto fill_result = [&](TypePtr::PTR ptr, const ciInstanceKlassMirror& ci_klass, InterfaceSet interfaces) { + res[sample_idx] = TypeInstKlassPtrMirror(ptr, ci_klass, interfaces, Type::Offset::bottom); + sample_idx++; + res[sample_idx] = TypeInstKlassPtrMirror(ptr, ci_klass, interfaces, Type::Offset(0)); + sample_idx++; + res[sample_idx] = TypeInstKlassPtrMirror(ptr, ci_klass, interfaces, Type::Offset(1)); + sample_idx++; + }; + + for (auto& ci_klass : ciInstanceKlassMirror::_samples) { + if (!ci_klass.is_loaded() || ci_klass.is_interface()) { + continue; + } + + if (ci_klass.is_java_lang_Object()) { + fill_result(TypePtr::Constant, ci_klass, InterfaceSet(false, false)); + fill_result(TypePtr::BotPTR, ci_klass, InterfaceSet(false, false)); + fill_result(TypePtr::NotNull, ci_klass, InterfaceSet(false, false)); + fill_result(TypePtr::Constant, ci_klass, InterfaceSet(false, true)); + fill_result(TypePtr::BotPTR, ci_klass, InterfaceSet(false, true)); + fill_result(TypePtr::NotNull, ci_klass, InterfaceSet(false, true)); + fill_result(TypePtr::Constant, ci_klass, InterfaceSet(true, false)); + fill_result(TypePtr::BotPTR, ci_klass, InterfaceSet(true, false)); + fill_result(TypePtr::NotNull, ci_klass, InterfaceSet(true, false)); + fill_result(TypePtr::BotPTR, ci_klass, InterfaceSet(true, true)); + fill_result(TypePtr::NotNull, ci_klass, InterfaceSet(true, true)); + continue; + } + + InterfaceSet interfaces = ci_klass.interfaces(); + fill_result(TypePtr::Constant, ci_klass, interfaces); + fill_result(TypePtr::BotPTR, ci_klass, interfaces); + fill_result(TypePtr::NotNull, ci_klass, interfaces); + if (!interfaces._i0) { + fill_result(TypePtr::BotPTR, ci_klass, InterfaceSet(true, interfaces._i1)); + fill_result(TypePtr::NotNull, ci_klass, InterfaceSet(true, interfaces._i1)); + } + if (!interfaces._i1) { + fill_result(TypePtr::BotPTR, ci_klass, InterfaceSet(interfaces._i0, true)); + fill_result(TypePtr::NotNull, ci_klass, InterfaceSet(interfaces._i0, true)); + } + if (!interfaces._i0 && !interfaces._i1) { + fill_result(TypePtr::BotPTR, ci_klass, InterfaceSet(true, true)); + fill_result(TypePtr::NotNull, ci_klass, InterfaceSet(true, true)); + } + } + + assert(sample_idx == res.size(), ""); + return res; +} + +constexpr std::array TypeInstKlassPtrMirror::_samples = generate_samples(); + +static constexpr size_t TypeAryKlassPtr_1d_elem_samples_size() { + size_t res = 0; + // Top, Bot, byte, int, float + res += 5; + for (auto& ci_klass : ciInstanceKlassMirror::_samples) { + if (!ci_klass.is_loaded()) { + continue; + } + + // The constant instance + res += 1; + if (ci_klass.is_interface()) { + continue; + } + + InterfaceSet interfaces = ci_klass.interfaces(); + if (interfaces._i0 && interfaces._i1) { + res += 1; + } else if (interfaces._i0 || interfaces._i1) { + res += 2; + } else { + res += 4; + } + } + + return res; +} + +static constexpr size_t TypeAryKlassPtr_1d_samples_constant_size() { + size_t res = 0; + // byte[], int[], float[] + res += 3; + + for (auto& ci_klass : ciInstanceKlassMirror::_samples) { + if (!ci_klass.is_loaded()) { + continue; + } + + res += 1; + } + + return res; +} + +static constexpr size_t TypeAryKlassPtr_1d_samples_notnull_size() { + size_t res = 0; + // bot[], byte[], int[], float[] + res += 4; + + for (auto& ci_klass : ciInstanceKlassMirror::_samples) { + if (!ci_klass.is_loaded()) { + continue; + } + + // The notnull instance with constant elem + res += 1; + + if (ci_klass.is_interface()) { + continue; + } + + InterfaceSet interfaces = ci_klass.interfaces(); + if (interfaces._i0 && interfaces._i1) { + res += 1; + } else if (interfaces._i0 || interfaces._i1) { + res += 2; + } else { + res += 4; + } + } + + return res; +} + +// All test instances have is_flat() == false, is_not_flat() == true, is_null_free() == false, +// is_not_null_free() == true, is_atomic() == true, is_refined_type() == false. All other +// parameters are included exhaustively. +class TypeAryKlassPtrMirror : public TypeKlassPtrMirror { +private: + static constexpr size_t _1d_elem_samples_size = TypeAryKlassPtr_1d_elem_samples_size(); + static constexpr size_t _1d_samples_size = (TypeAryKlassPtr_1d_samples_constant_size() + TypeAryKlassPtr_1d_samples_notnull_size() * 2) * 3; + static constexpr size_t _2d_elem_samples_size = TypeAryKlassPtr_1d_samples_constant_size() + TypeAryKlassPtr_1d_samples_notnull_size(); + static constexpr size_t _2d_samples_size = (TypeAryKlassPtr_1d_samples_constant_size() * 3 + TypeAryKlassPtr_1d_samples_notnull_size() * 2) * 3; + + const AryElemType* _elem; + const ciArrayKlassMirror* _klass; + + constexpr TypeAryKlassPtrMirror(TypePtr::PTR ptr, const AryElemType* elem, const ciArrayKlassMirror* klass, Type::Offset offset) + : TypeKlassPtrMirror(TypePtr::AryKlassPtr, ptr, _array_interfaces, offset), _elem(elem), _klass(klass) { + assert(elem != nullptr, ""); + assert((elem->base() == Type::Int) == (klass != nullptr), "only have klass for int/char/byte/short arrays"); + assert(klass == nullptr || klass->elem_basic_type() == elem->basic_type(), "mismatched klass"); + } + + static constexpr auto generate_1d_elem_samples(); + static constexpr auto generate_1d_samples(); + static constexpr auto generate_2d_elem_samples(); + static constexpr auto generate_2d_samples(); + +public: + static constexpr InterfaceSet _array_interfaces = TypeAryPtrMirror::_array_interfaces; + static const std::array, _1d_elem_samples_size> _1d_elem_samples; + static const std::array _1d_samples; + + static const std::array, _2d_elem_samples_size> _2d_elem_samples; + static const std::array _2d_samples; + + constexpr TypeAryKlassPtrMirror() : TypeKlassPtrMirror(Type::Bad, TypePtr::TopPTR, _array_interfaces, Type::Offset(0)), _elem(nullptr), _klass(nullptr) {} + + static const TypeAryKlassPtrMirror* make(TypePtr::PTR ptr, const AryElemType* elem, const ciArrayKlassMirror* klass, Type::Offset offset, + bool not_flat, bool not_null_free, bool flat, bool null_free, bool atomic, bool refined) { + assert(!flat && not_flat && !null_free && not_null_free && atomic && !refined, "unsupported"); + for (auto& sample : _1d_samples) { + if (sample.ptr() == ptr && sample.elem() == elem && sample.klass() == klass && sample.offset() == offset.get()) { + return &sample; + } + } + + for (auto& sample : _2d_samples) { + if (sample.ptr() == ptr && sample.elem() == elem && sample.klass() == klass && sample.offset() == offset.get()) { + return &sample; + } + } + + ShouldNotReachHere(); + } + + const AryElemType* elem() const { return _elem; } + const ciArrayKlassMirror* klass() const { return _klass; } + + bool is_flat() const { return false; } + bool is_not_flat() const { return true; } + bool is_null_free() const { return false; } + bool is_not_null_free() const { return true; } + bool is_atomic() const { return true; } + bool is_refined_type() const { return false; } + + virtual const TypeInstKlassPtrMirror* is_instklassptr() const override { ShouldNotReachHere(); } + virtual const TypeAryKlassPtrMirror* is_aryklassptr() const override { return this; } + + virtual void dump_on(outputStream& st) const override { + st.print("AryKlassPtr:"); + dump_ptr(st); + st.print(" - "); + if (elem()->base() == Type::Bottom) { + st.print("bot[]"); + } else if (BasicType elem_bt = elem()->basic_type(); is_java_primitive(elem_bt)) { + st.print("%s[]", type2name(elem_bt)); + } else { + st.print("("); + elem()->make_ptr()->dump_on(st); + st.print(")[]"); + } + dump_offset(st); + } +}; + +template <> +constexpr const AryElemType* AryElemType::TOP = &TypeAryKlassPtrMirror::_1d_elem_samples[0]; + +template <> +constexpr const AryElemType* AryElemType::BOTTOM = &TypeAryKlassPtrMirror::_1d_elem_samples[1]; + +template <> +constexpr const AryElemType* AryElemType::find_1d(BasicType bt, const TypeKlassPtrMirror* ptr_type) { + for (auto& sample : TypeAryKlassPtrMirror::_1d_elem_samples) { + if (sample._bt == bt && sample._ptr_type == ptr_type) { + return &sample; + } + } + return nullptr; +} + +template <> +constexpr const AryElemType* AryElemType::find_2d(BasicType bt, const TypeKlassPtrMirror* ptr_type) { + for (auto& sample : TypeAryKlassPtrMirror::_2d_elem_samples) { + if (sample._bt == bt && sample._ptr_type == ptr_type) { + return &sample; + } + } + return nullptr; +} + +constexpr auto TypeAryKlassPtrMirror::generate_1d_elem_samples() { + std::array, _1d_elem_samples_size> res; + res[0] = AryElemType(T_ILLEGAL, nullptr); + res[1] = AryElemType(T_VOID, nullptr); + res[2] = AryElemType(T_BYTE, nullptr); + res[3] = AryElemType(T_INT, nullptr); + res[4] = AryElemType(T_FLOAT, nullptr); + + size_t sample_idx = 5; + for (auto& ci_klass : ciInstanceKlassMirror::_samples) { + if (!ci_klass.is_loaded()) { + continue; + } + + if (ci_klass.is_interface()) { + res[sample_idx] = AryElemType(T_OBJECT, TypeInstKlassPtrMirror::make(TypePtr::Constant, ciEnvMirror::current()->Object_klass(), ci_klass.interfaces())); + sample_idx++; + } else { + res[sample_idx] = AryElemType(T_OBJECT, TypeInstKlassPtrMirror::make(TypePtr::Constant, &ci_klass, ci_klass.interfaces())); + sample_idx++; + } + } + + for (auto& ci_klass : ciInstanceKlassMirror::_samples) { + if (!ci_klass.is_loaded() || ci_klass.is_interface()) { + continue; + } + + InterfaceSet interfaces = ci_klass.interfaces(); + res[sample_idx] = AryElemType(T_OBJECT, TypeInstKlassPtrMirror::make(TypePtr::NotNull, &ci_klass, interfaces)); + sample_idx++; + if (!interfaces._i0) { + res[sample_idx] = AryElemType(T_OBJECT, TypeInstKlassPtrMirror::make(TypePtr::NotNull, &ci_klass, InterfaceSet(true, interfaces._i1))); + sample_idx++; + } + if (!interfaces._i1) { + res[sample_idx] = AryElemType(T_OBJECT, TypeInstKlassPtrMirror::make(TypePtr::NotNull, &ci_klass, InterfaceSet(interfaces._i0, true))); + sample_idx++; + } + if (!interfaces._i0 && !interfaces._i1) { + res[sample_idx] = AryElemType(T_OBJECT, TypeInstKlassPtrMirror::make(TypePtr::NotNull, &ci_klass, InterfaceSet(true, true))); + sample_idx++; + } + } + + assert(sample_idx == res.size(), ""); + return res; +} + +constexpr std::array, TypeAryKlassPtrMirror::_1d_elem_samples_size> TypeAryKlassPtrMirror::_1d_elem_samples = generate_1d_elem_samples(); + +constexpr auto TypeAryKlassPtrMirror::generate_1d_samples() { + std::array res; + size_t sample_idx = 0; + auto fill_result = [&](TypePtr::PTR ptr, const AryElemType* elem, const ciArrayKlassMirror* klass) { + res[sample_idx] = TypeAryKlassPtrMirror(ptr, elem, klass, Type::Offset::bottom); + sample_idx++; + res[sample_idx] = TypeAryKlassPtrMirror(ptr, elem, klass, Type::Offset(0)); + sample_idx++; + res[sample_idx] = TypeAryKlassPtrMirror(ptr, elem, klass, Type::Offset(1)); + sample_idx++; + }; + + fill_result(TypePtr::BotPTR, AryElemType::find_1d(T_VOID, nullptr), nullptr); + fill_result(TypePtr::NotNull, AryElemType::find_1d(T_VOID, nullptr), nullptr); + + auto& byte_ary_klass = ciArrayKlassMirror::find(T_BYTE, nullptr); + fill_result(TypePtr::BotPTR, AryElemType::find_1d(T_BYTE, nullptr), &byte_ary_klass); + fill_result(TypePtr::NotNull, AryElemType::find_1d(T_BYTE, nullptr), &byte_ary_klass); + fill_result(TypePtr::Constant, AryElemType::find_1d(T_BYTE, nullptr), &byte_ary_klass); + + auto& int_ary_klass = ciArrayKlassMirror::find(T_INT, nullptr); + fill_result(TypePtr::BotPTR, AryElemType::find_1d(T_INT, nullptr), &int_ary_klass); + fill_result(TypePtr::NotNull, AryElemType::find_1d(T_INT, nullptr), &int_ary_klass); + fill_result(TypePtr::Constant, AryElemType::find_1d(T_INT, nullptr), &int_ary_klass); + + fill_result(TypePtr::BotPTR, AryElemType::find_1d(T_FLOAT, nullptr), nullptr); + fill_result(TypePtr::NotNull, AryElemType::find_1d(T_FLOAT, nullptr), nullptr); + fill_result(TypePtr::Constant, AryElemType::find_1d(T_FLOAT, nullptr), nullptr); + + for (auto& elem : _1d_elem_samples) { + if (elem.make_ptr() == nullptr) { + continue; + } + + fill_result(TypePtr::BotPTR, &elem, nullptr); + fill_result(TypePtr::NotNull, &elem, nullptr); + + if (elem.make_ptr()->ptr() == TypePtr::Constant) { + fill_result(TypePtr::Constant, &elem, nullptr); + } + } + + assert(sample_idx == res.size(), ""); + return res; +} + +constexpr std::array TypeAryKlassPtrMirror::_1d_samples = generate_1d_samples(); + +constexpr auto TypeAryKlassPtrMirror::generate_2d_elem_samples() { + std::array, _2d_elem_samples_size> res; + size_t sample_idx = 0; + for (auto& ptr_type : _1d_samples) { + if (ptr_type.ptr() == TypePtr::BotPTR || ptr_type.offset() != 0) { + continue; + } + + res[sample_idx] = AryElemType(T_OBJECT, &ptr_type); + sample_idx++; + } + + assert(sample_idx == res.size(), ""); + return res; +} + +constexpr std::array, TypeAryKlassPtrMirror::_2d_elem_samples_size> TypeAryKlassPtrMirror::_2d_elem_samples = generate_2d_elem_samples(); + +constexpr auto TypeAryKlassPtrMirror::generate_2d_samples() { + std::array res; + size_t sample_idx = 0; + for (auto& elem : _2d_elem_samples) { + res[sample_idx] = TypeAryKlassPtrMirror(TypePtr::BotPTR, &elem, nullptr, Type::Offset::bottom); + sample_idx++; + res[sample_idx] = TypeAryKlassPtrMirror(TypePtr::BotPTR, &elem, nullptr, Type::Offset(0)); + sample_idx++; + res[sample_idx] = TypeAryKlassPtrMirror(TypePtr::BotPTR, &elem, nullptr, Type::Offset(1)); + sample_idx++; + res[sample_idx] = TypeAryKlassPtrMirror(TypePtr::NotNull, &elem, nullptr, Type::Offset::bottom); + sample_idx++; + res[sample_idx] = TypeAryKlassPtrMirror(TypePtr::NotNull, &elem, nullptr, Type::Offset(0)); + sample_idx++; + res[sample_idx] = TypeAryKlassPtrMirror(TypePtr::NotNull, &elem, nullptr, Type::Offset(1)); + sample_idx++; + + if (elem.make_ptr()->ptr() == TypePtr::Constant) { + res[sample_idx] = TypeAryKlassPtrMirror(TypePtr::Constant, &elem, nullptr, Type::Offset::bottom); + sample_idx++; + res[sample_idx] = TypeAryKlassPtrMirror(TypePtr::Constant, &elem, nullptr, Type::Offset(0)); + sample_idx++; + res[sample_idx] = TypeAryKlassPtrMirror(TypePtr::Constant, &elem, nullptr, Type::Offset(1)); + sample_idx++; + } + } + + assert(sample_idx == res.size(), ""); + return res; +} + +constexpr std::array TypeAryKlassPtrMirror::_2d_samples = generate_2d_samples(); + +// OopPtrMirror is the mirror of oop +class OopPtrMirror { +private: + static constexpr size_t _samples_size = (ciInstanceKlassMirror::_samples.size() + ciArrayKlassMirror::_samples.size()) * 18 + 3; + InstanceMirror _pointee; + int _instance_id; + int _offset; + + constexpr OopPtrMirror(InstanceMirror pointee, int instance_id, int offset) : _pointee(pointee), _instance_id(instance_id), _offset(offset) {} + + static constexpr auto generate_samples(); + +public: + static const std::array _samples; + + constexpr OopPtrMirror() : _pointee(nullptr), _instance_id(0), _offset(0) {} + + template + static bool klass_satisfies(const ciKlassMirror* klass, const PtrType* type, bool type_is_exact) { + type_is_exact |= type->klass_is_exact(); + + if (klass->is_inst()) { + if (type->base() == Type::AryPtr || type->base() == Type::AryKlassPtr) { + return false; + } + + auto inst_klass = klass->as_inst(); + auto inst_interfaces = inst_klass->interfaces(); + if (inst_klass->is_interface()) { + inst_klass = ciEnvMirror::current()->Object_klass(); + } + + const typename PtrType::InstType* type_inst; + if constexpr (PtrType::is_oopptr_type) { + type_inst = type->is_oopptr()->is_instptr(); + } else { + type_inst = type->is_klassptr()->is_instklassptr(); + } + + if (type_is_exact) { + return inst_klass == type_inst->instance_klass() && + inst_interfaces == type_inst->interfaces(); + } else { + return inst_klass->is_subtype_of(type_inst->instance_klass()) && + inst_interfaces->contains(type_inst->interfaces()); + } + } + + if (type->base() == Type::InstPtr || type->base() == Type::InstKlassPtr) { + const typename PtrType::InstType* type_inst; + if constexpr (PtrType::is_oopptr_type) { + type_inst = type->is_oopptr()->is_instptr(); + } else { + type_inst = type->is_klassptr()->is_instklassptr(); + } + + return !type_is_exact && type_inst->instance_klass()->is_java_lang_Object() && + TypeAryKlassPtrMirror::_array_interfaces.contains(type_inst->interfaces()); + } + + const typename PtrType::AryType* type_ary; + if constexpr (PtrType::is_oopptr_type) { + type_ary = type->is_oopptr()->is_aryptr(); + } else { + type_ary = type->is_klassptr()->is_aryklassptr(); + } + auto type_elem = type_ary->elem(); + auto ary_klass = klass->as_ary(); + if (type_elem->base() == Type::Bottom) { + assert(type->ptr() != TypePtr::Constant, "cannot have an exact bot[]"); + return true; + } + + if (type_elem->basic_type() != ary_klass->elem_basic_type()) { + return false; + } + + if (type_elem->basic_type() != T_OBJECT) { + return true; + } else { + return klass_satisfies(ary_klass->elem(), type_elem->make_ptr(), type_is_exact); + } + } + + bool satisfies(const TypePtrMirror* type) const { + if (type->offset() != Type::OffsetBot && type->offset() != _offset) { + return false; + } + + if (_pointee.klass() == nullptr) { + return type->ptr() == TypePtr::BotPTR || type->ptr() == TypePtr::Null; + } + + if (type->base() == Type::AnyPtr) { + assert(type->ptr() == TypePtr::TopPTR || type->ptr() == TypePtr::Null, "must be top or null"); + return false; + } + assert(type->ptr() == TypePtr::BotPTR || type->ptr() == TypePtr::NotNull || type->ptr() == TypePtr::Constant, "only AnyPtr can be top or null"); + + auto type_oop = type->is_oopptr(); + if (_pointee.klass()->is_ary() && type->base() == Type::AryPtr && + !type_oop->is_aryptr()->size()->contains(_pointee.array_length())) { + return false; + } + + if (type->ptr() == TypePtr::Constant) { + auto const_oop = type_oop->const_oop(); + return const_oop == _pointee; + } + + if (type_oop->instance_id() != TypeOopPtr::InstanceBot && type_oop->instance_id() != _instance_id) { + return false; + } + + return klass_satisfies(_pointee.klass(), type_oop, false); + } + + void dump_on(outputStream& st) const { + st.print("klassptr_instance:"); + if (_pointee.klass() == nullptr) { + st.print("null"); + } else { + _pointee.klass()->dump_on(st); + st.print(" - idx=%d - instance_id=%d", _pointee.idx(), _instance_id); + } + st.print(" - offset=%d", _offset); + } +}; + +constexpr auto OopPtrMirror::generate_samples() { + std::array res; + res[0] = OopPtrMirror(InstanceMirror(nullptr), 0, 0); + res[1] = OopPtrMirror(InstanceMirror(nullptr), 0, 1); + res[2] = OopPtrMirror(InstanceMirror(nullptr), 0, 2); + + size_t sample_idx = 3; + auto fill_result_helper = [&](const ciKlassMirror& ci_klass, int idx, int instance_id) { + res[sample_idx] = OopPtrMirror(InstanceMirror(ci_klass, idx), instance_id, 0); + sample_idx++; + res[sample_idx] = OopPtrMirror(InstanceMirror(ci_klass, idx), instance_id, 1); + sample_idx++; + res[sample_idx] = OopPtrMirror(InstanceMirror(ci_klass, idx), instance_id, 2); + sample_idx++; + }; + + auto fill_result = [&](const ciKlassMirror& ci_klass) { + fill_result_helper(ci_klass, 0, 0); + fill_result_helper(ci_klass, 1, 0); + fill_result_helper(ci_klass, 2, 0); + fill_result_helper(ci_klass, 3, ci_klass.instance_id()); + fill_result_helper(ci_klass, 3, ci_klass.instance_id() + 1); + fill_result_helper(ci_klass, 3, ci_klass.instance_id() + 2); + }; + + for (auto& ci_klass : ciInstanceKlassMirror::_samples) { + fill_result(ci_klass); + } + for (auto& ci_klass : ciArrayKlassMirror::_samples) { + fill_result(ci_klass); + } + + assert(sample_idx == res.size(), ""); + return res; +} + +constexpr std::array OopPtrMirror::_samples = generate_samples(); + +// KlassPtrMirror is the mirror of Klass* +class KlassPtrMirror { +private: + static constexpr size_t _samples_size = (ciInstanceKlassMirror::_samples.size() + ciArrayKlassMirror::_samples.size() + 1) * 3; + const ciKlassMirror* _klass; + int _offset; + + constexpr KlassPtrMirror(const ciKlassMirror* klass, int offset) : _klass(klass), _offset(offset) {} + + static constexpr auto generate_samples(); + +public: + static const std::array _samples; + + constexpr KlassPtrMirror() : _klass(nullptr), _offset(0) {} + + bool satisfies(const TypePtrMirror* type) const { + if (type->offset() != _offset && type->offset() != Type::OffsetBot) { + return false; + } + + if (_klass == nullptr) { + return type->ptr() == TypePtr::BotPTR || type->ptr() == TypePtr::Null; + } + + if (type->base() == Type::AnyPtr) { + assert(type->ptr() == TypePtr::TopPTR || type->ptr() == TypePtr::Null, "must be top or null"); + return false; + } + + assert(type->ptr() == TypePtr::BotPTR || type->ptr() == TypePtr::NotNull || type->ptr() == TypePtr::Constant, "only AnyPtr can be top or null"); + return OopPtrMirror::klass_satisfies(_klass, type->is_klassptr(), false); + } + + void dump_on(outputStream& st) const { + st.print("klassptr_instance:"); + if (_klass == nullptr) { + st.print("null"); + } else { + _klass->dump_on(st); + } + st.print(" - offset=%d", _offset); + } +}; + +constexpr auto KlassPtrMirror::generate_samples() { + std::array res; + res[0] = KlassPtrMirror(nullptr, 0); + res[1] = KlassPtrMirror(nullptr, 1); + res[2] = KlassPtrMirror(nullptr, 2); + + size_t sample_idx = 3; + for (auto& ci_klass : ciInstanceKlassMirror::_samples) { + res[sample_idx] = KlassPtrMirror(&ci_klass, 0); + res[sample_idx + 1] = KlassPtrMirror(&ci_klass, 1); + res[sample_idx + 2] = KlassPtrMirror(&ci_klass, 2); + sample_idx += 3; + } + for (auto& ci_klass : ciArrayKlassMirror::_samples) { + res[sample_idx] = KlassPtrMirror(&ci_klass, 0); + res[sample_idx + 1] = KlassPtrMirror(&ci_klass, 1); + res[sample_idx + 2] = KlassPtrMirror(&ci_klass, 2); + sample_idx += 3; + } + + assert(sample_idx == res.size(), ""); + return res; +} + +constexpr std::array KlassPtrMirror::_samples = generate_samples(); + +template +static void test_meet_join() { + constexpr size_t samples_size = PtrType::InstType::_samples.size() + PtrType::AryType::_1d_samples.size() + PtrType::AryType::_2d_samples.size(); + if constexpr (PtrType::is_oopptr_type) { + static_assert(samples_size == 2040); + } else { + static_assert(samples_size == 471); + } + + // Running all instances takes a lot of time, so we only run a fraction of them regularly + auto sample_hit = [] { + constexpr double sampling_prob = 0.02; + return uint(os::random()) < max_juint * sampling_prob; + }; + + GrowableArray type_samples(samples_size, MemTag::mtOther); + for (auto& sample : PtrType::InstType::_samples) { + if (sample_hit()) { + type_samples.append(&sample); + } + } + for (auto& sample : PtrType::AryType::_1d_samples) { + if (sample_hit()) { + type_samples.append(&sample); + } + } + for (auto& sample : PtrType::AryType::_2d_samples) { + if (sample_hit()) { + type_samples.append(&sample); + } + } + + for (int i = 0; i < type_samples.length(); i++) { + const PtrType* t1 = type_samples.at(i); + for (int j = i; j < type_samples.length(); j++) { + const PtrType* t2 = type_samples.at(j); + auto meet = TypeJavaPtrMeetHelper::javaptr_type_xmeet(t1, t2); + auto join = TypeJavaPtrJoinHelper::javaptr_type_xjoin(t1, t2); + + // Commutativity + ASSERT_EQ(TypeJavaPtrMeetHelper::javaptr_type_xmeet(t2, t1), meet); + ASSERT_EQ(TypeJavaPtrJoinHelper::javaptr_type_xjoin(t2, t1), join); + + // Verify that t1 and t2 must be subsets of meet + ASSERT_EQ(TypeJavaPtrMeetHelper::javaptr_type_xmeet(t1, meet), meet); + ASSERT_EQ(TypeJavaPtrMeetHelper::javaptr_type_xmeet(t2, meet), meet); + ASSERT_EQ(TypeJavaPtrJoinHelper::javaptr_type_xjoin(t1, meet), t1); + ASSERT_EQ(TypeJavaPtrJoinHelper::javaptr_type_xjoin(t2, meet), t2); + + if (join->base() != Type::AnyPtr) { + const PtrType* typed_join; + if constexpr (PtrType::is_oopptr_type) { + typed_join = join->is_oopptr(); + } else { + typed_join = join->is_klassptr(); + } + + // Verify that join must be a subset of both t1 and t2 + ASSERT_EQ(TypeJavaPtrMeetHelper::javaptr_type_xmeet(t1, typed_join), t1); + ASSERT_EQ(TypeJavaPtrMeetHelper::javaptr_type_xmeet(t2, typed_join), t2); + ASSERT_EQ(TypeJavaPtrJoinHelper::javaptr_type_xjoin(t1, typed_join), typed_join); + ASSERT_EQ(TypeJavaPtrJoinHelper::javaptr_type_xjoin(t2, typed_join), typed_join); + } + + // Element-wise verification + for (auto& elem : Ptr::_samples) { + bool in_t1 = elem.satisfies(t1); + bool in_t2 = elem.satisfies(t2); + bool in_meet = elem.satisfies(meet); + bool in_join = elem.satisfies(join); + + ASSERT_TRUE((!in_t1 && !in_t2) || in_meet); + ASSERT_EQ(in_t1 && in_t2, in_join); + } + } + } +} + +TEST(opto, typejavaptr) { + test_meet_join(); + test_meet_join(); +} diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 2cf40e4f57a7..f7e80982e785 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -97,12 +97,6 @@ gc/TestGCALotAtAllSafepoints.java#Shenandoah 8390661 generic-all # :hotspot_runtime -runtime/cds/appcds/aotCache/aotClassLinking/AOTClassLinkingVMOptions.java 8390485 generic-all -runtime/cds/appcds/aotCache/aotClassLinking/AddExports.java 8390485 generic-all -runtime/cds/appcds/aotCache/aotClassLinking/AddOpens.java 8390485 generic-all -runtime/cds/appcds/aotCache/aotClassLinking/AddReads.java 8390485 generic-all -runtime/cds/appcds/resolvedConstants/AOTLinkedLambdas.java 8390485 generic-all -runtime/cds/appcds/resolvedConstants/AOTLinkedVarHandles.java 8390485 generic-all # SapMachine 2022-12-16 Exclude a failing test runtime/jni/daemonDestroy/TestDaemonDestroy.java windows-all runtime/jni/terminatedThread/TestTerminatedThread.java 8317789 aix-ppc64 @@ -116,11 +110,12 @@ runtime/os/TestTracePageSizes.java#Serial 8267460 linux-aarch64 runtime/ErrorHandling/MachCodeFramesInErrorFile.java 8313315 linux-ppc64le runtime/NMT/VirtualAllocCommitMerge.java 8309698 linux-s390x runtime/Thread/TestAlwaysPreTouchStacks.java 8383372 macosx-aarch64 -runtime/valhalla/inlinetypes/NPEInPreviewTest.java 8390257 generic-all applications/jcstress/copy.java 8229852 linux-all containers/docker/TestJFREvents.java 8327723 linux-x64 +containers/docker/TestLimitsUpdating.java 8390823 linux-all +containers/docker/ShareTmpDir.java 8390823 linux-all ############################################################################# diff --git a/test/hotspot/jtreg/compiler/c2/TestDUPinchPointReuse.java b/test/hotspot/jtreg/compiler/c2/TestDUPinchPointReuse.java new file mode 100644 index 000000000000..51445fa32d64 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestDUPinchPointReuse.java @@ -0,0 +1,61 @@ +/* + * 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 8328078 + * @summary Test that block scheduling reuses detached D-U pinch-point nodes + * @requires vm.compiler2.enabled + * @library /test/lib + * @run main ${test.main.class} + * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:+UnlockDiagnosticVMOptions + * -XX:+AbortVMOnCompilationFailure -XX:+OptoScheduling + * -XX:CompileCommand=compileonly,${test.main.class}::test + * ${test.main.class} + */ + +package compiler.c2; + +import jdk.test.lib.Asserts; + +public class TestDUPinchPointReuse { + private static String append(String s) { + return new StringBuilder().append(s).append(s).append(s).append(s).append(s).toString(); + } + + private static String test() { + String s = "x"; + s = append(s); + s = append(s); + s = append(s); + s = append(s); + s = append(s); + s = append(s); + return s; + } + + public static void main(String[] args) { + new StringBuilder(); + Asserts.assertEQ(test().length(), 15625); + } +} diff --git a/test/hotspot/jtreg/compiler/c2/TestVerifyIterativeGVN.java b/test/hotspot/jtreg/compiler/c2/TestVerifyIterativeGVN.java index be8fbc79d0bc..d9340beaaf47 100644 --- a/test/hotspot/jtreg/compiler/c2/TestVerifyIterativeGVN.java +++ b/test/hotspot/jtreg/compiler/c2/TestVerifyIterativeGVN.java @@ -25,9 +25,9 @@ * @test * @bug 8238756 8351889 * @requires vm.debug == true & vm.flavor == "server" - * @summary Run with -Xcomp to test -XX:VerifyIterativeGVN=1111111 in debug builds. + * @summary Run with -Xcomp to test -XX:VerifyIterativeGVN=111111 in debug builds. * - * @run main/othervm/timeout=300 -Xcomp -XX:VerifyIterativeGVN=1111111 compiler.c2.TestVerifyIterativeGVN + * @run main/othervm/timeout=300 -Xcomp -XX:VerifyIterativeGVN=111111 compiler.c2.TestVerifyIterativeGVN */ package compiler.c2; diff --git a/test/hotspot/jtreg/compiler/c2/gvn/TestBitCompressValueTransform.java b/test/hotspot/jtreg/compiler/c2/gvn/TestBitCompressValueTransform.java index 01fe54218fe7..fd2b1cdd6af4 100644 --- a/test/hotspot/jtreg/compiler/c2/gvn/TestBitCompressValueTransform.java +++ b/test/hotspot/jtreg/compiler/c2/gvn/TestBitCompressValueTransform.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -73,7 +73,7 @@ public class TestBitCompressValueTransform { public final long BOUND2_HI_L = GEN_L.next(); @Test - @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "bmi2", "true" }) + @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "fast_bmi2", "true" }) public long test1(long value) { return Long.compress(0x8000_0000_0000_0000L, value); } @@ -86,7 +86,7 @@ public void run1() { @Test - @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "bmi2", "true" }) + @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "fast_bmi2", "true" }) public int test2(int value) { return Integer.compress(0x8000_0000, value); } @@ -98,7 +98,7 @@ public void run2() { } @Test - @IR (counts = { IRNode.COMPRESS_BITS, " 0 "} , failOn = { IRNode.UNSTABLE_IF_TRAP }, applyIfCPUFeature = { "bmi2", "true" }) + @IR (counts = { IRNode.COMPRESS_BITS, " 0 "} , failOn = { IRNode.UNSTABLE_IF_TRAP }, applyIfCPUFeature = { "fast_bmi2", "true" }) public int test3(int value) { int filter_bits = value & 0xF; int compress_bits = Integer.compress(15, filter_bits); @@ -118,7 +118,7 @@ public void run3() { } @Test - @IR (counts = { IRNode.COMPRESS_BITS, " 0 "} , failOn = { IRNode.UNSTABLE_IF_TRAP }, applyIfCPUFeature = { "bmi2", "true" }) + @IR (counts = { IRNode.COMPRESS_BITS, " 0 "} , failOn = { IRNode.UNSTABLE_IF_TRAP }, applyIfCPUFeature = { "fast_bmi2", "true" }) public long test4(long value) { long filter_bits = value & 0xFL; long compress_bits = Long.compress(15L, filter_bits); @@ -138,7 +138,7 @@ public void run4() { } @Test - @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "bmi2", "true" }) + @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "fast_bmi2", "true" }) public long test5(long value) { // Since value range includes -1 hence with mask // and value as -1 all the result bits will be set. @@ -156,7 +156,7 @@ public void run5() { } @Test - @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "bmi2", "true" }) + @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "fast_bmi2", "true" }) public long test6(long value) { // For mask within a strictly -ve value range less than -1, // result of compression will always be a +ve value. @@ -174,7 +174,7 @@ public void run6() { } @Test - @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "bmi2", "true" }) + @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "fast_bmi2", "true" }) public long test7(long value) { // For mask within a strictly +ve value range, // result of compression will always be a +ve value with @@ -193,7 +193,7 @@ public void run7() { } @Test - @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "bmi2", "true" }) + @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "fast_bmi2", "true" }) public int test8(int value) { // Since value range includes -1 hence with mask // and value as -1 all the result bits will be set. @@ -211,7 +211,7 @@ public void run8() { } @Test - @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "bmi2", "true" }) + @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "fast_bmi2", "true" }) public int test9(int value) { // For mask within a strictly -ve value range less than -1, // result of compression will always be a +ve value. @@ -229,7 +229,7 @@ public void run9() { } @Test - @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "bmi2", "true" }) + @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "fast_bmi2", "true" }) public int test10(int value) { // For mask within a strictly +ve value range, // result of compression will always be a +ve value with @@ -316,7 +316,7 @@ public void run14() { } @Test - @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = {"bmi2" , "true"}) + @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = {"fast_bmi2" , "true"}) public int test15(int src, int mask) { // src_type = [min_int + 1, -1] src = Math.max(Integer.MIN_VALUE + 1, Math.min(src, -1)); @@ -363,7 +363,7 @@ public int test16_interpreted(int src, int mask) { } @Test - @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = {"bmi2" , "true"}) + @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = {"fast_bmi2" , "true"}) public int test16(int src, int mask) { src = Math.max(BOUND1_LO_I, Math.min(src, BOUND1_HI_I)); mask = Math.max(BOUND2_LO_I, Math.min(mask, BOUND2_HI_I)); @@ -449,7 +449,7 @@ public int test17_interpreted(int src, int mask) { } @Test - @IR (counts = { IRNode.EXPAND_BITS, " >0 " }, applyIfCPUFeature = {"bmi2" , "true"}) + @IR (counts = { IRNode.EXPAND_BITS, " >0 " }, applyIfCPUFeature = {"fast_bmi2" , "true"}) public int test17(int src, int mask) { src = Math.max(BOUND1_LO_I, Math.min(src, BOUND1_HI_I)); mask = Math.max(BOUND2_LO_I, Math.min(mask, BOUND2_HI_I)); @@ -535,7 +535,7 @@ public long test18_interpreted(long src, long mask) { } @Test - @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = {"bmi2" , "true"}) + @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = {"fast_bmi2" , "true"}) public long test18(long src, long mask) { src = Math.max(BOUND1_LO_L, Math.min(src, BOUND1_HI_L)); mask = Math.max(BOUND2_LO_L, Math.min(mask, BOUND2_HI_L)); @@ -621,7 +621,7 @@ public long test19_interpreted(long src, long mask) { } @Test - @IR (counts = { IRNode.EXPAND_BITS, " >0 " }, applyIfCPUFeature = {"bmi2" , "true"}) + @IR (counts = { IRNode.EXPAND_BITS, " >0 " }, applyIfCPUFeature = {"fast_bmi2" , "true"}) public long test19(long src, long mask) { src = Math.max(BOUND1_LO_L, Math.min(src, BOUND1_HI_L)); mask = Math.max(BOUND2_LO_L, Math.min(mask, BOUND2_HI_L)); @@ -674,7 +674,7 @@ public void run19() { } @Test - @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "bmi2", "true" }) + @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "fast_bmi2", "true" }) public static long test20(int x) { // Analysis of when this is used to produce wrong results on Windows: // @@ -723,7 +723,7 @@ public void run20() { } @Test - @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "bmi2", "true" }) + @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "fast_bmi2", "true" }) public static long test21(long x) { // Analysis of when this is used to produce wrong results on Windows: // diff --git a/test/hotspot/jtreg/compiler/c2/irTests/AddHFNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/AddHFNodeIdealizationTests.java new file mode 100644 index 000000000000..f4e71cdc7fe4 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/irTests/AddHFNodeIdealizationTests.java @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package compiler.c2.irTests; + +import compiler.lib.ir_framework.*; +import compiler.lib.verify.Verify; +import jdk.incubator.vector.Float16; +import static jdk.incubator.vector.Float16.valueOf; + +/* + * @test + * @bug 8388873 + * @summary Test that AddHFNode is not reassociated. + * @modules jdk.incubator.vector + * @library /test/lib / + * @run driver ${test.main.class} + */ +public class AddHFNodeIdealizationTests { + + private static final Float16 SMALL = valueOf(3.0e-4f); + private static final Float16 HALF = valueOf(0.5f); + private static final Float16 ONE = valueOf(1.0f); + + private Float16 a1 = valueOf(1.0f); + private Float16 a1024 = valueOf(1024.0f); + private Float16 a2048 = valueOf(2048.0f); + private Float16 b = valueOf(1.0f); + private Float16 bHalf = valueOf(0.5f); + + private Float16 dst1; + private Float16 dst2; + private Float16 dst3; + private Float16 dst4; + private Float16 dst5; + + public static void main(String[] args) { + TestFramework.runWithFlags("--add-modules=jdk.incubator.vector"); + } + + // Same semantics as Float16.add, but force-inlined so the ConvF2HF idealization + // can pattern-match ConvF2HF(AddF(ConvHF2F(x), ConvHF2F(y))) into AddHF. + @ForceInline + private static Float16 add(Float16 x, Float16 y) { + return valueOf(x.floatValue() + y.floatValue()); + } + + @DontCompile + private static Float16 addHF(Float16 x, Float16 y) { + // Single, rounded Float16 addition: (x + y). + return valueOf(x.floatValue() + y.floatValue()); + } + + @DontCompile + private static Float16 goldLeft(Float16 x, Float16 y, Float16 z) { + // ((x + y) + z) + return addHF(addHF(x, y), z); + } + + @DontCompile + private static Float16 goldRight(Float16 x, Float16 y, Float16 z) { + // (x + (y + z)) + return addHF(x, addHF(y, z)); + } + + @DontCompile + private static Float16 goldChain(Float16 x, Float16 c1, Float16 c2, Float16 c3) { + // (((x + c1) + c2) + c3) + return addHF(addHF(addHF(x, c1), c2), c3); + } + + // Pattern 1: "(x + c1) + c2" -> "x + (c1 + c2)" (combine constants). + + @Test + @IR(counts = {IRNode.ADD_HF, "2"}, + applyIfCPUFeatureOr = {"avx512_fp16", "true", "zfh", "true"}) + @IR(counts = {IRNode.ADD_HF, "2"}, + applyIfCPUFeatureAnd = {"fphp", "true", "asimdhp", "true"}) + public void test1() { + dst1 = add(add(a1, SMALL), SMALL); + } + + @Check(test = "test1") + public void check1() { + Verify.checkEQ(dst1, goldLeft(a1, SMALL, SMALL)); + } + + // Pattern 1 with a large operand, where the rounding difference is + // large and easy to observe: (1024 + 0.5) + 0.5. + + @Test + @IR(counts = {IRNode.ADD_HF, "2"}, + applyIfCPUFeatureOr = {"avx512_fp16", "true", "zfh", "true"}) + @IR(counts = {IRNode.ADD_HF, "2"}, + applyIfCPUFeatureAnd = {"fphp", "true", "asimdhp", "true"}) + public void test2() { + dst2 = add(add(a1024, HALF), HALF); + } + + @Check(test = "test2") + public void check2() { + Verify.checkEQ(dst2, goldLeft(a1024, HALF, HALF)); + } + + // Pattern 1, longer chain: "((x + c1) + c2) + c3". Without the fix the + // three constants collapse into one, leaving a single AddHF; with the + // fix all three additions survive. + + @Test + @IR(counts = {IRNode.ADD_HF, "3"}, + applyIfCPUFeatureOr = {"avx512_fp16", "true", "zfh", "true"}) + @IR(counts = {IRNode.ADD_HF, "3"}, + applyIfCPUFeatureAnd = {"fphp", "true", "asimdhp", "true"}) + public void test3() { + dst3 = add(add(add(a2048, ONE), ONE), ONE); + } + + @Check(test = "test3") + public void check3() { + Verify.checkEQ(dst3, goldChain(a2048, ONE, ONE, ONE)); + } + + // Pattern 2: "(x + c) + y" -> "(x + y) + c" (push constant down). + // The node count stays at two, so only the numeric result exposes the bug. + + @Test + public void test4() { + dst4 = add(add(a1024, HALF), b); + } + + @Check(test = "test4") + public void check4() { + Verify.checkEQ(dst4, goldLeft(a1024, HALF, b)); + } + + // Pattern 3: "x + (y + c)" -> "(x + y) + c" (push constant down). + + @Test + public void test5() { + dst5 = add(a1024, add(bHalf, HALF)); + } + + @Check(test = "test5") + public void check5() { + Verify.checkEQ(dst5, goldRight(a1024, bHalf, HALF)); + } +} diff --git a/test/hotspot/jtreg/compiler/cha/packagePrivate/NonOverridenParent.java b/test/hotspot/jtreg/compiler/cha/packagePrivate/NonOverridenParent.java new file mode 100644 index 000000000000..560fff00b6d7 --- /dev/null +++ b/test/hotspot/jtreg/compiler/cha/packagePrivate/NonOverridenParent.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package compiler.cha.packagePrivate; + +/** + * An abstract class such that all of its concrete subclasses have a single implementation of + * {@link #call}. The subclasses lie in a different package so they cannot override {@link #call}. + */ +public abstract class NonOverridenParent { + int call() { + return 0; + } +} diff --git a/test/hotspot/jtreg/runtime/signal/TestSigstop.java b/test/hotspot/jtreg/compiler/cha/packagePrivate/OverridenParent.java similarity index 73% rename from test/hotspot/jtreg/runtime/signal/TestSigstop.java rename to test/hotspot/jtreg/compiler/cha/packagePrivate/OverridenParent.java index 24acfd2c8dab..0c156a485ccc 100644 --- a/test/hotspot/jtreg/runtime/signal/TestSigstop.java +++ b/test/hotspot/jtreg/compiler/cha/packagePrivate/OverridenParent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -20,16 +20,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ +package compiler.cha.packagePrivate; - -/* - * @test - * @requires os.family != "windows" & os.family != "aix" - * - * @summary converted from VM testbase runtime/signal/sigstop01. - * VM testbase keywords: [signal, runtime, linux, macosx] - * - * @library /test/lib - * @run main/native SigTestDriver SIGSTOP +/** + * An abstract class such that all of its concrete subclasses have a single implementation of + * {@link #call} that is defined in a subclass of this class. */ - +public abstract class OverridenParent { + int call() { + return 0; + } +} diff --git a/test/hotspot/jtreg/compiler/cha/packagePrivate/OverridingChild.java b/test/hotspot/jtreg/compiler/cha/packagePrivate/OverridingChild.java new file mode 100644 index 000000000000..9877286662f5 --- /dev/null +++ b/test/hotspot/jtreg/compiler/cha/packagePrivate/OverridingChild.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package compiler.cha.packagePrivate; + +/** + * This class provides the unique implementation for {@link OverridenParent#call}, it is abstract + * so that {@code ciInstanceKlass::unique_concrete_subklass} does not find it. + */ +public abstract class OverridingChild extends OverridenParent { + // 3 concrete implementations to defeat the bimorphic inlining heuristic + public static class GrandChild1 extends OverridingChild {} + public static class GrandChild2 extends OverridingChild {} + public static class GrandChild3 extends OverridingChild {} + + @Override + int call() { + return 1; + } +} diff --git a/test/hotspot/jtreg/compiler/cha/packagePrivate/TestDevirtualizePackageMethod.java b/test/hotspot/jtreg/compiler/cha/packagePrivate/TestDevirtualizePackageMethod.java new file mode 100644 index 000000000000..22f987baaa3e --- /dev/null +++ b/test/hotspot/jtreg/compiler/cha/packagePrivate/TestDevirtualizePackageMethod.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package compiler.cha.packagePrivate; + +import compiler.cha.packagePrivate.differentPackage.NonOverridingChild; +import compiler.lib.ir_framework.*; +import jdk.test.lib.Asserts; + +/* + * @test + * @bug 8390499 + * @summary Verify that C2 correctly devirtualizes package-private methods. + * @library /test/lib / + * @run driver ${test.main.class} + */ +public class TestDevirtualizePackageMethod { + public static void main(String[] args) { + var framework = new TestFramework(); + framework.setDefaultWarmup(1); + framework.addFlags("-XX:CompileCommand=dontinline,*::call"); + framework.start(); + } + + @Run(test = {"testOverriding1", "testOverriding2"}) + public void runOverriding() { + var v1 = new OverridingChild.GrandChild1(); + var v2 = new OverridingChild.GrandChild2(); + var v3 = new OverridingChild.GrandChild3(); + Asserts.assertEQ(1, testOverriding1(v1)); + Asserts.assertEQ(1, testOverriding1(v2)); + Asserts.assertEQ(1, testOverriding1(v3)); + Asserts.assertEQ(1, testOverriding2(v1)); + Asserts.assertEQ(1, testOverriding2(v2)); + Asserts.assertEQ(1, testOverriding2(v3)); + } + + @Test + @IR(failOn = {IRNode.DYNAMIC_CALL_OF_METHOD, "OverridenParent::call", IRNode.DYNAMIC_CALL_OF_METHOD, "OverridingChild::call"}) + @IR(counts = {IRNode.STATIC_CALL_OF_METHOD, "OverridingChild::call", "1"}) + private static int testOverriding1(OverridenParent v) { + return v.call(); + } + + @Test + @IR(failOn = {IRNode.DYNAMIC_CALL_OF_METHOD, "OverridenParent::call", IRNode.DYNAMIC_CALL_OF_METHOD, "OverridingChild::call"}) + @IR(counts = {IRNode.STATIC_CALL_OF_METHOD, "OverridingChild::call", "1"}) + private static int testOverriding2(OverridingChild v) { + return v.call(); + } + + @Run(test = {"testNonOverriding1", "testNonOverriding2"}) + public void runNonOverriding() { + var v1 = new NonOverridingChild.GrandChild1(); + var v2 = new NonOverridingChild.GrandChild2(); + var v3 = new NonOverridingChild.GrandChild3(); + Asserts.assertEQ(0, testNonOverriding1(v1)); + Asserts.assertEQ(0, testNonOverriding1(v2)); + Asserts.assertEQ(0, testNonOverriding1(v3)); + Asserts.assertEQ(1, testNonOverriding2(v1)); + Asserts.assertEQ(1, testNonOverriding2(v2)); + Asserts.assertEQ(1, testNonOverriding2(v3)); + } + + @Test + @IR(failOn = {IRNode.DYNAMIC_CALL_OF_METHOD, "NonOverridenParent::call", IRNode.DYNAMIC_CALL_OF_METHOD, "NonOverridingChild::call"}) + @IR(counts = {IRNode.STATIC_CALL_OF_METHOD, "NonOverridenParent::call", "1"}) + private static int testNonOverriding1(NonOverridenParent v) { + return v.call(); + } + + @Test + @IR(failOn = {IRNode.DYNAMIC_CALL_OF_METHOD, "NonOverridenParent::call", IRNode.DYNAMIC_CALL_OF_METHOD, "NonOverridingChild::call"}) + @IR(counts = {IRNode.STATIC_CALL_OF_METHOD, "NonOverridingChild::call", "1"}) + private static int testNonOverriding2(NonOverridingChild v) { + return v.call(); + } +} diff --git a/test/hotspot/jtreg/compiler/cha/packagePrivate/differentPackage/NonOverridingChild.java b/test/hotspot/jtreg/compiler/cha/packagePrivate/differentPackage/NonOverridingChild.java new file mode 100644 index 000000000000..337b308f873b --- /dev/null +++ b/test/hotspot/jtreg/compiler/cha/packagePrivate/differentPackage/NonOverridingChild.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package compiler.cha.packagePrivate.differentPackage; + +import compiler.cha.packagePrivate.NonOverridenParent; + +/** + * Although this class also has {@link #call}, it does not override + * {@link NonOverridenParent#call}. + */ +public abstract class NonOverridingChild extends NonOverridenParent { + // 3 subclasses to defeat the bimorphic inlining heuristic + public static class GrandChild1 extends NonOverridingChild {} + public static class GrandChild2 extends NonOverridingChild {} + public static class GrandChild3 extends NonOverridingChild {} + + public int call() { + return 1; + } +} diff --git a/test/hotspot/jtreg/compiler/escapeAnalysis/TestReadOnlyStringIntrinsicDuringEA.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReadOnlyStringIntrinsicDuringEA.java new file mode 100644 index 000000000000..d50a112cea7f --- /dev/null +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReadOnlyStringIntrinsicDuringEA.java @@ -0,0 +1,71 @@ +/* + * 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 8357381 + * @summary C2 compilation fails with C2: assert(false) failed: should not be here + * @requires vm.compiler2.enabled + * @run main ${test.main.class} + * @run main/othervm -XX:-TieredCompilation -Xbatch + * -XX:CompileCommand=compileonly,${test.main.class}::test* + * ${test.main.class} + */ + +package compiler.escapeAnalysis; + +public class TestReadOnlyStringIntrinsicDuringEA { + static int test1(String id, String nameKey) { + try { + java.io.ByteArrayOutputStream stream = new java.io.ByteArrayOutputStream(); + java.math.BigInteger num = java.math.BigInteger.valueOf(123); + int length = num.toByteArray().length; + stream.write(num.toByteArray()); + } catch (Exception e) { + throw new RuntimeException(e); + } + if ("UTC".equals(id) && id.equals(nameKey)) { + } + return 0; + } + + static boolean test2(String b) { + String t1 = ""; + var s1 = new StringBuffer(); + var s2 = s1.append(String.valueOf(t1)); + var s3 = s2.append(7); + var s4 = String.valueOf("AB"); + var s5 = s4.equals(String.valueOf(b)); + return s5; + } + + public static void main(String[] strArr) { + for (int i = 0; i < 10_000; i++) { + new StringBuffer().append(i); + } + for (int t = 0; t < 50_000; t++) { + test1("123456abc", "123456abc"); + test2("X"); + } + } +} diff --git a/test/hotspot/jtreg/compiler/intrinsics/TestBitShuffleOpers.java b/test/hotspot/jtreg/compiler/intrinsics/TestBitShuffleOpers.java index d2d707d6efbc..4c70014dba33 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/TestBitShuffleOpers.java +++ b/test/hotspot/jtreg/compiler/intrinsics/TestBitShuffleOpers.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,7 @@ * @summary To test various transforms added for bit COMPRESS_BITS and EXPAND_BITS operations * @requires vm.compiler2.enabled * @requires (((os.arch=="x86" | os.arch=="amd64" | os.arch=="x86_64") & - * (vm.cpu.features ~= ".*bmi2.*" & vm.cpu.features ~= ".*bmi1.*" & + * (vm.cpu.features ~= ".*fast_bmi2.*" & vm.cpu.features ~= ".*bmi1.*" & * vm.cpu.features ~= ".*sse2.*")) | * (os.arch=="aarch64" & vm.cpu.features ~= ".*svebitperm.*")) * @library /test/lib / diff --git a/test/hotspot/jtreg/compiler/intrinsics/TestUnsafeAccessWithZeroOffset.java b/test/hotspot/jtreg/compiler/intrinsics/TestUnsafeAccessWithZeroOffset.java new file mode 100644 index 000000000000..627b465cee7d --- /dev/null +++ b/test/hotspot/jtreg/compiler/intrinsics/TestUnsafeAccessWithZeroOffset.java @@ -0,0 +1,95 @@ +/* + * 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 8390650 + * @summary Test unreachable unsafe accesses at offset zero. + * @requires vm.compiler2.enabled + * @modules java.base/jdk.internal.misc + * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:CompileCommand=delayinline,${test.main.class}::getOffset + * -XX:CompileCommand=compileonly,${test.main.class}::test* + * ${test.main.class} + */ + +import jdk.internal.misc.Unsafe; + +public class TestUnsafeAccessWithZeroOffset { + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + + static Object testPlain(Object obj, Object x, boolean b) { + Object unused = new Object(); // Trigger EA + long offset = getOffset(); + if (b) { + // Never reached + Object result = UNSAFE.getReference(obj, 0L); + UNSAFE.putReference(obj, offset, x); + return result; + } + return null; + } + + static Object testLoadStore(Object obj, Object expected, Object x, boolean b) { + Object unused = new Object(); // Trigger EA + if (b) { + // Never reached + return UNSAFE.compareAndExchangeReference(obj, 0L, expected, x); + } + return null; + } + + static long getOffset() { + return 0L; + } + + static Object testPlainDelayed(Object obj, Object x, boolean b) { + Object unused = new Object(); // Trigger EA + long offset = getOffset(); + if (b) { + // Never reached + Object result = UNSAFE.getReference(obj, offset); + UNSAFE.putReference(obj, offset, x); + return result; + } + return null; + } + + static Object testLoadStoreDelayed(Object obj, Object expected, Object x, boolean b) { + Object unused = new Object(); // Trigger EA + long offset = getOffset(); + if (b) { + // Never reached + return UNSAFE.compareAndExchangeReference(obj, offset, expected, x); + } + return null; + } + + public static void main(String[] args) { + Object value = new Object(); + testPlain(value, value, false); + testLoadStore(value, value, value, false); + testPlainDelayed(value, value, false); + testLoadStoreDelayed(value, value, value, false); + } +} + diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/Scenario.java b/test/hotspot/jtreg/compiler/lib/ir_framework/Scenario.java index 4971b87a236d..014a65efc793 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/Scenario.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/Scenario.java @@ -104,6 +104,17 @@ public void addFlags(String... flags) { } } + /** + * Prepend additional VM flags to this scenario. + * + * @param flags the additional scenario VM flags. + */ + public void prependFlags(String... flags) { + if (flags != null) { + this.flags.addAll(0, Arrays.asList(flags)); + } + } + /** * Get all scenario specific VM flags as defined in {@link #Scenario(int, String...)}. * diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java index 9f1687e8cfdf..59115c891157 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java @@ -110,6 +110,7 @@ public class ApplicableIRRulesPrinter { "avx512_vbmi2", "avx10_2", "bmi2", + "fast_bmi2", // Intel APX "apx_f", // AArch64 @@ -441,6 +442,7 @@ private boolean hasNoRequiredFlags(String[] orRules, String ruleType) { return returnValue; } + @SuppressWarnings("preview") private boolean check(String flag, String value) { if (flag.isEmpty()) { TestFormat.failNoThrow("Provided empty flag" + failAt()); diff --git a/test/hotspot/jtreg/compiler/macronodes/TestMacroExpansion.java b/test/hotspot/jtreg/compiler/macronodes/TestMacroExpansion.java new file mode 100644 index 000000000000..d47cd787ebb1 --- /dev/null +++ b/test/hotspot/jtreg/compiler/macronodes/TestMacroExpansion.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8390550 + * @key stress + * @requires vm.compiler2.enabled + * @summary Test scalarized calls and entry points around calling convention limits. + * @library /test/lib / + * @run driver ${test.main.class} + */ + +package compiler.macronodes; + +import compiler.lib.compile_framework.CompileFramework; +import compiler.lib.template_framework.Template; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +import static compiler.lib.template_framework.Template.*; + +public class TestMacroExpansion { + private static final String GENERATED_CLASS_NAME = "GeneratedTest"; + private static final String PACKAGE_NAME = "compiler.macronodes"; + private static final String QUALIFIED_NAME = PACKAGE_NAME + "." + GENERATED_CLASS_NAME; + + public static void main(String[] args) throws Exception { + CompileFramework compileFramework = new CompileFramework(); + compileFramework.addJavaSourceCode(GENERATED_CLASS_NAME, generate()); + compileFramework.compile(); + String[] command = { + "-classpath", + compileFramework.getEscapedClassPathOfCompiledClasses(), + "-Xcomp", + "-XX:-TieredCompilation", + "-XX:CompileCommand=compileonly," + QUALIFIED_NAME + "::test", + "-XX:+IgnoreUnrecognizedVMOptions", + "-XX:VerifyIterativeGVN=1110", + QUALIFIED_NAME + }; + + OutputAnalyzer analyzer = ProcessTools.executeTestJava(command); + analyzer.shouldHaveExitValue(0); + } + + static String generate() { + final int rows = 140; + final int args = 9; + return Template.make(() -> scope( + let("className", GENERATED_CLASS_NAME), + let("packageName", PACKAGE_NAME), + """ + package #packageName; + + public class #className { + interface I { + } + + public static void main(String[] args) { + test(); + } + + static class A implements I { + } + + static A a = new A(); + + static Object[] arr; + + """, + " static void init(", repeatAndJoin(args, ", ", i -> scope("I i" + i)), ") {\n", + " arr = new Object[] {", repeatAndJoin(args, ", ", i -> scope("i" + i)), "};\n", + """ + } + + """, + repeatAndJoin(rows, "\n", rowIndex -> scope( + " static Object ", repeatAndJoin(args, ", ", index -> scope( "o" + (index + rowIndex * args))), ";" + )), "\n", + """ + + static void test() { + """, + repeatAndJoin(rows, "\n", rowIndex -> scope( + " init(", repeatAndJoin(args, ", ", index -> scope( "(I)o" + (index + rowIndex * args))), ");" + )), "\n", + """ + } + } + """ + )).render(); + } +} diff --git a/test/hotspot/jtreg/compiler/macronodes/TestMacroExpansionCleanupCount.java b/test/hotspot/jtreg/compiler/macronodes/TestMacroExpansionCleanupCount.java new file mode 100644 index 000000000000..29febbdfa312 --- /dev/null +++ b/test/hotspot/jtreg/compiler/macronodes/TestMacroExpansionCleanupCount.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test id=min + * @bug 8390550 + * @requires vm.compiler2.enabled + * @summary Test MacroExpansionCleanupCount flag minimum value + * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:MacroExpansionCleanupCount=1 ${test.main.class} + */ + +/* + * @test id=max + * @bug 8390550 + * @requires vm.compiler2.enabled + * @summary Test MacroExpansionCleanupCount flag maximum value + * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:MacroExpansionCleanupCount=100 ${test.main.class} + */ + +package compiler.macronodes; + +public class TestMacroExpansionCleanupCount { + + public static void main(String[] args) throws Exception { + System.out.println("Test passed"); + } +} diff --git a/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsMany.java b/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsMany.java index dbc6e4955942..42a759d9f881 100644 --- a/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsMany.java +++ b/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsMany.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,19 +23,17 @@ /* * @test - * @bug 8362394 + * @bug 8328078 8362394 * @summary Test that repeated stacked string concatenations do not * consume too many compilation resources. * @requires vm.compiler2.enabled * @library /test/lib / - * @run main/othervm -XX:-OptoScheduling compiler.stringopts.TestStackedConcatsMany - * @run main/othervm -XX:-TieredCompilation -Xcomp -XX:-OptoScheduling - * -XX:CompileOnly=compiler.stringopts.TestStackedConcatsMany::f - * compiler.stringopts.TestStackedConcatsMany + * @run main ${test.main.class} + * @run main/othervm -XX:-TieredCompilation -Xcomp + * -XX:CompileOnly=${test.main.class}::f + * ${test.main.class} */ -// The test uses -XX:-OptoScheduling to avoid the assert "too many D-U pinch points" on aarch64 (JDK-8328078). - package compiler.stringopts; import jdk.test.lib.Asserts; diff --git a/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsManyUTF16Overflow.java b/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsManyUTF16Overflow.java index 1c9bdf153e8f..fc19276beb4d 100644 --- a/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsManyUTF16Overflow.java +++ b/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsManyUTF16Overflow.java @@ -27,15 +27,13 @@ * @summary Test that UTF-16 string concat overflow does not produce a negative size backing array * @requires vm.compiler2.enabled & os.maxMemory > 4G * @library /test/lib / - * @run main/othervm -Xmx4g -XX:-OptoScheduling ${test.main.class} + * @run main/othervm -Xmx4g ${test.main.class} * @run main/othervm -Xmx4g -Xint ${test.main.class} - * @run main/othervm -Xmx4g -XX:-TieredCompilation -Xcomp -XX:-OptoScheduling + * @run main/othervm -Xmx4g -XX:-TieredCompilation -Xcomp * -XX:CompileOnly=${test.main.class}::f * ${test.main.class} */ -// The test uses -XX:-OptoScheduling to avoid the assert "too many D-U pinch points" on aarch64 (JDK-8328078). - package compiler.stringopts; import jdk.test.lib.Asserts; diff --git a/test/hotspot/jtreg/compiler/types/TestMeetInstanceId.java b/test/hotspot/jtreg/compiler/types/TestMeetInstanceId.java new file mode 100644 index 000000000000..9c6fcd01e07c --- /dev/null +++ b/test/hotspot/jtreg/compiler/types/TestMeetInstanceId.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package compiler.types; + +/* + * @test + * @bug 8370914 + * @summary C2 incorrectly computes the instance id of a meet, leading to symmetry assert. + * @run main ${test.main.class} + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:CompileThreshold=1 -XX:+UnlockDiagnosticVMOptions + * -XX:+StressIncrementalInlining -XX:TypeProfileLevel=200 + * -XX:CompileOnly=${test.main.class}::test ${test.main.class} + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:CompileThreshold=1 -XX:+UnlockDiagnosticVMOptions + * -XX:+StressIncrementalInlining -XX:TypeProfileLevel=200 -XX:StressSeed=823469094 + * -XX:CompileOnly=${test.main.class}::test ${test.main.class} + */ +public class TestMeetInstanceId { + static Object getString() { + return "42"; + } + + static Object profileObject(Object obj) { + return obj; + } + + static Object test(boolean b) { + if (b) { + return null; + } + return profileObject(getString()); + } + + public static void main(String[] args) { + Object[] array = new Object[0]; + for (int i = 0; i < 20_000; i++) { + profileObject(array); + } + + test(true); + test(true); + } +} diff --git a/test/hotspot/jtreg/compiler/types/TestSameArrayDifferentViews.java b/test/hotspot/jtreg/compiler/types/TestSameArrayDifferentViews.java new file mode 100644 index 000000000000..5de22def6877 --- /dev/null +++ b/test/hotspot/jtreg/compiler/types/TestSameArrayDifferentViews.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package compiler.types; + +import jdk.internal.vm.annotation.Stable; + +/* + * @test + * @bug 8370914 + * @summary The same constant array can be assigned to different variables with different Stable + * presence. + * @modules java.base/jdk.internal.vm.annotation + * @run main/bootclasspath/othervm -XX:-TieredCompilation -Xcomp + * -XX:CompileOnly=${test.main.class}::test ${test.main.class} + */ +public class TestSameArrayDifferentViews { + + @Stable + private static final int[] STABLE_VIEW = new int[3]; + + private static final int[] NON_STABLE_VIEW = STABLE_VIEW; + + public static void main(String[] args) { + for (int i = 0; i < 10; i++) { + test(true); + test(false); + } + } + + private static int[] test(boolean b) { + return b ? STABLE_VIEW : NON_STABLE_VIEW; + } +} diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/CompareAndSetFlatArrayField.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/CompareAndSetFlatArrayField.java new file mode 100644 index 000000000000..48ffe1626386 --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/CompareAndSetFlatArrayField.java @@ -0,0 +1,169 @@ +/* + * 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 8389390 + * @summary [Valhalla] Compile::adjust_flat_array_access_aliases asserts due SCMemProj + * @enablePreview + * @modules java.base/jdk.internal.misc + * java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main/othervm -XX:-BackgroundCompilation ${test.main.class} + */ + +package compiler.valhalla.inlinetypes; + +import java.lang.reflect.Field; +import jdk.internal.misc.Unsafe; +import jdk.internal.value.ValueClass; + +public class CompareAndSetFlatArrayField { + static public value class MyValue1 { + Object field; + MyValue1(Object v) { + field = v; + } + } + + static public value class MyValue2 { + int field; + MyValue2(int v) { + field = v; + } + } + + private static final Unsafe U = Unsafe.getUnsafe(); + + private static final MyValue1[] array1; + private static final long ARRAY1_BASE_OFFSET; + private static final int ARRAY1_INDEX_SCALE; + private static final boolean ARRAY1_FLATTENED; + private static final int ARRAY_LAYOUT1; + private static final long VALUE1_HEADER_SIZE; + private static final long VALUE1_FIELD_OFFSET; + + private static final MyValue2[] array2; + private static final long ARRAY2_BASE_OFFSET; + private static final int ARRAY2_INDEX_SCALE; + private static final boolean ARRAY2_FLATTENED; + private static final int ARRAY2_LAYOUT; + private static final long VALUE2_HEADER_SIZE; + private static final long FIELD_OFFSET2; + + private MyValue1 field1; + private static final boolean FLAT_FIELD1; + private static final long FIELD1_OFFSET; + private static final int FIELD1_LAYOUT; + + private MyValue2 field2; + private static final boolean FLAT_FIELD2; + private static final long FIELD2_OFFSET; + private static final int FIELD2_LAYOUT; + + private static Object o1 = new Object(); + private static Object o2 = new Object(); + + private static CompareAndSetFlatArrayField testObject = new CompareAndSetFlatArrayField(); + + static { + try { + array1 = (MyValue1[])ValueClass.newNullRestrictedNonAtomicArray(MyValue1.class, 1, new MyValue1(o1)); + ARRAY1_BASE_OFFSET = U.arrayInstanceBaseOffset(array1); + ARRAY1_INDEX_SCALE = U.arrayInstanceIndexScale(array1); + ARRAY1_FLATTENED = ValueClass.isFlatArray(array1); + ARRAY_LAYOUT1 = U.arrayLayout(array1); + VALUE1_HEADER_SIZE = U.valueHeaderSize(MyValue1.class); + Field f = MyValue1.class.getDeclaredField("field"); + VALUE1_FIELD_OFFSET = U.objectFieldOffset(f); + + array2 = (MyValue2[])ValueClass.newNullRestrictedNonAtomicArray(MyValue2.class, 1, new MyValue2(42)); + ARRAY2_BASE_OFFSET = U.arrayInstanceBaseOffset(array2); + ARRAY2_INDEX_SCALE = U.arrayInstanceIndexScale(array2); + ARRAY2_FLATTENED = ValueClass.isFlatArray(array2); + ARRAY2_LAYOUT = U.arrayLayout(array2); + VALUE2_HEADER_SIZE = U.valueHeaderSize(MyValue2.class); + f = MyValue2.class.getDeclaredField("field"); + FIELD_OFFSET2 = U.objectFieldOffset(f); + + f = CompareAndSetFlatArrayField.class.getDeclaredField("field1"); + FIELD1_OFFSET = U.objectFieldOffset(f); + FLAT_FIELD1 = U.isFlatField(f); + FIELD1_LAYOUT = U.fieldLayout(f); + + f = CompareAndSetFlatArrayField.class.getDeclaredField("field2"); + FIELD2_OFFSET = U.objectFieldOffset(f); + FLAT_FIELD2 = U.isFlatField(f); + FIELD2_LAYOUT = U.fieldLayout(f); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + static public boolean test1(Object oldVal, Object newVal) { + array1[0] = new MyValue1(oldVal); + return U.compareAndSetReference(array1, ARRAY1_BASE_OFFSET, oldVal, newVal); + } + + static public boolean test2(int oldVal, int newVal) { + array2[0] = new MyValue2(oldVal); + return U.compareAndSetFlatValue(array2, ARRAY2_BASE_OFFSET, ARRAY2_LAYOUT, MyValue2.class, new MyValue2(oldVal), new MyValue2(newVal)); + } + + static public boolean test3(Object oldVal, Object newVal) { + testObject.field1 = new MyValue1(oldVal); + return U.compareAndSetReference(testObject, FIELD1_OFFSET, oldVal, newVal); + } + + static public boolean test4(int oldVal, int newVal) { + testObject.field2 = new MyValue2(oldVal); + return U.compareAndSetFlatValue(testObject, FIELD2_OFFSET, FIELD2_LAYOUT, MyValue2.class, new MyValue2(oldVal), new MyValue2(newVal)); + } + + static public void main(String args[]) { + if (!ARRAY1_FLATTENED || !ARRAY2_FLATTENED || !FLAT_FIELD1 || !FLAT_FIELD2) { + return; + } + if (VALUE1_FIELD_OFFSET != VALUE1_HEADER_SIZE) { + throw new RuntimeException("fix test: test assumes MyValue1[0].f is at offset 0 in MyValue1[0]"); + } + for (int i = 0; i < 20_000; i++) { + boolean res = test1(o1, o2); + if (!res) { + throw new RuntimeException("CAS failed"); + } + res = test2(42, 0x42); + if (!res) { + throw new RuntimeException("CAS failed"); + } + res = test3(o1, o2); + if (!res) { + throw new RuntimeException("CAS failed"); + } + res = test4(42, 0x42); + if (!res) { + throw new RuntimeException("CAS failed"); + } + } + } +} diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypes.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypes.java index 36575da28715..7a13e56d3bf8 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypes.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypes.java @@ -34,30 +34,17 @@ public class InlineTypes { public static final Scenario[] DEFAULT_SCENARIOS = { new Scenario(0, - "--enable-preview", - "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED", - "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED", - "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-UseACmpProfile", - "-XX:+AlwaysIncrementalInline", "-XX:FlatArrayElementMaxOops=5", "-XX:+UseArrayFlattening", "-XX:-UseArrayLoadStoreProfile", "-XX:+UseFieldFlattening", "-XX:+InlineTypePassFieldsAsArgs", - "-XX:+InlineTypeReturnedAsFields" + "-XX:+InlineTypeReturnedAsFields", + "-XX:+IgnoreUnrecognizedVMOptions", + "-XX:+AlwaysIncrementalInline" ), new Scenario(1, - "--enable-preview", - "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED", - "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED", - "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-UseACmpProfile", "-XX:-UseCompressedOops", "-XX:FlatArrayElementMaxOops=5", @@ -68,13 +55,6 @@ public class InlineTypes { "-XX:-InlineTypeReturnedAsFields" ), new Scenario(2, - "--enable-preview", - "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED", - "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED", - "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-UseACmpProfile", "-XX:-UseCompressedOops", "-XX:FlatArrayElementMaxOops=0", @@ -85,29 +65,16 @@ public class InlineTypes { "-XX:+InlineTypeReturnedAsFields" ), new Scenario(3, - "--enable-preview", - "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED", - "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED", - "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+IgnoreUnrecognizedVMOptions", "-DVerifyIR=false", - "-XX:+AlwaysIncrementalInline", "-XX:FlatArrayElementMaxOops=0", "-XX:-UseArrayFlattening", "-XX:-UseFieldFlattening", "-XX:+InlineTypePassFieldsAsArgs", - "-XX:+InlineTypeReturnedAsFields" + "-XX:+InlineTypeReturnedAsFields", + "-XX:+IgnoreUnrecognizedVMOptions", + "-XX:+AlwaysIncrementalInline" ), new Scenario(4, - "--enable-preview", - "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED", - "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED", - "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+IgnoreUnrecognizedVMOptions", "-DVerifyIR=false", "-XX:FlatArrayElementMaxOops=-1", "-XX:+UseArrayFlattening", @@ -117,32 +84,18 @@ public class InlineTypes { "-XX:-ReduceInitialCardMarks" ), new Scenario(5, - "--enable-preview", - "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED", - "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED", - "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-UseACmpProfile", - "-XX:+AlwaysIncrementalInline", "-XX:FlatArrayElementMaxOops=5", "-XX:+UseArrayFlattening", "-XX:-UseArrayLoadStoreProfile", "-XX:+UseFieldFlattening", "-XX:-InlineTypePassFieldsAsArgs", - "-XX:-InlineTypeReturnedAsFields" + "-XX:-InlineTypeReturnedAsFields", + "-XX:+IgnoreUnrecognizedVMOptions", + "-XX:+AlwaysIncrementalInline" ), new Scenario(6, - "--enable-preview", - "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED", - "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED", - "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-UseACmpProfile", - "-XX:+AlwaysIncrementalInline", "-XX:FlatArrayElementMaxOops=5", "-XX:+UseArrayFlattening", "-XX:-UseArrayLoadStoreProfile", @@ -151,10 +104,28 @@ public class InlineTypes { "-XX:+UseNullFreeAtomicValueFlattening", "-XX:+UseNullFreeNonAtomicValueFlattening", "-XX:+InlineTypePassFieldsAsArgs", - "-XX:+InlineTypeReturnedAsFields" + "-XX:+InlineTypeReturnedAsFields", + "-XX:+IgnoreUnrecognizedVMOptions", + "-XX:+AlwaysIncrementalInline" ), }; + static { + // Add common flags + for (Scenario scenario : DEFAULT_SCENARIOS) { + scenario.prependFlags("--enable-preview", + "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED", + "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED", + "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+UnlockExperimentalVMOptions", + // Force inline the methods called by ValueClass::validateArrayArguments used by the array factories + "-XX:CompileCommand=inline,jdk.internal.value.ValueClass::isConcreteValueClass", + "-XX:CompileCommand=inline,java.lang.Class::isValue", + "-XX:CompileCommand=inline,java.lang.reflect.Modifier::isAbstract"); + } + } + public static TestFramework getFramework() { StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); return new TestFramework(walker.getCallerClass()).setDefaultWarmup(251); diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java index 4966465665c4..9d3a610e3a32 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java @@ -145,10 +145,10 @@ public class TestArrays { public static void main(String[] args) { Scenario[] scenarios = InlineTypes.DEFAULT_SCENARIOS; - scenarios[2].addFlags("--enable-preview", "-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast", "-XX:+StressArrayCopyMacroNode"); - scenarios[3].addFlags("--enable-preview", "-XX:-MonomorphicArrayCheck", "-XX:+UnlockDiagnosticVMOptions", "-XX:+UseArrayFlattening", "-XX:-UncommonNullCast"); - scenarios[4].addFlags("--enable-preview", "-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast"); - scenarios[5].addFlags("--enable-preview", "-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast", "-XX:+StressArrayCopyMacroNode"); + scenarios[2].addFlags("--enable-preview", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast", "-XX:+StressArrayCopyMacroNode"); + scenarios[3].addFlags("--enable-preview", "-XX:+UnlockDiagnosticVMOptions", "-XX:+UseArrayFlattening", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast"); + scenarios[4].addFlags("--enable-preview", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast"); + scenarios[5].addFlags("--enable-preview", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast", "-XX:+StressArrayCopyMacroNode"); InlineTypes.getFramework() .addScenarios(scenarios[Integer.parseInt(args[0])]) diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArraysCopyOf.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArraysCopyOf.java index 7e0c3ad14937..134aba00aeff 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArraysCopyOf.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArraysCopyOf.java @@ -120,10 +120,10 @@ public class TestArraysCopyOf { public static void main(String[] args) { Scenario[] scenarios = InlineTypes.DEFAULT_SCENARIOS; - scenarios[2].addFlags("--enable-preview", "-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast", "-XX:+StressArrayCopyMacroNode"); - scenarios[3].addFlags("--enable-preview", "-XX:-MonomorphicArrayCheck", "-XX:+UnlockDiagnosticVMOptions", "-XX:+UseArrayFlattening", "-XX:-UncommonNullCast"); - scenarios[4].addFlags("--enable-preview", "-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast"); - scenarios[5].addFlags("--enable-preview", "-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast", "-XX:+StressArrayCopyMacroNode"); + scenarios[2].addFlags("--enable-preview", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast", "-XX:+StressArrayCopyMacroNode"); + scenarios[3].addFlags("--enable-preview", "-XX:+UnlockDiagnosticVMOptions", "-XX:+UseArrayFlattening", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast"); + scenarios[4].addFlags("--enable-preview", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast"); + scenarios[5].addFlags("--enable-preview", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast", "-XX:+StressArrayCopyMacroNode"); String[] flagsForScenario = scenarios[Integer.parseInt(args[0])].getFlags().toArray(new String[0]); CompileFramework comp = new CompileFramework(); diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestFlatArrayCASAssert.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestFlatArrayCASAssert.java new file mode 100644 index 000000000000..20e4a6facb66 --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestFlatArrayCASAssert.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; + +/* + * @test + * @bug 8389390 + * @summary [Valhalla] Compile::adjust_flat_array_access_aliases asserts due SCMemProj + * @enablePreview + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:+AlwaysIncrementalInline ${test.main.class} + */ + +public class TestFlatArrayCASAssert { + static value class V { } + + static final VarHandle VH = MethodHandles.arrayElementVarHandle(V[].class); + + static void test() { + VH.getAndSet(new V[1], 0, null); + } + + public static void main(String[] args) { + for (int i = 0; i < 30_000; i++) { + if ((i & 255) == 0) { + test(); + } + } + } +} diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIncorrectlyEliminatedStoreToFlatArray.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIncorrectlyEliminatedStoreToFlatArray.java new file mode 100644 index 000000000000..8cb26b598a6b --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIncorrectlyEliminatedStoreToFlatArray.java @@ -0,0 +1,71 @@ +/* + * 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 8388476 + * @summary [Valhalla] C2 incorrectly eliminates store to flat array + * + * @enablePreview + * @modules java.base/jdk.internal.value + * @run main/othervm -XX:-TieredCompilation -Xbatch -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:-UseArrayLoadStoreProfile + * -XX:CompileCommand=compileonly,${test.main.class}::test + * -XX:+AlwaysIncrementalInline ${test.main.class} + * @run main ${test.main.class} + */ + + +package compiler.valhalla.inlinetypes; +import jdk.internal.value.ValueClass; + +public class TestIncorrectlyEliminatedStoreToFlatArray { + static value class IntegerBox { + int value; + IntegerBox(int value) { this.value = value; } + public String toString() { return "value: " + value; } + } + + static Object test(Object obj, IntegerBox box) { + Object[] array = (Object[])obj; + try { + array[0] = null; + throw new NullPointerException("No NPE thrown"); + } catch (NullPointerException expected) { + array[0] = box; + } + return array[0]; + } + + public static void main(String[] args) { + IntegerBox[] array = (IntegerBox[])ValueClass.newNullRestrictedAtomicArray(IntegerBox.class, 1, new IntegerBox(1)); + + for (int i = 0; i < 50_000; i++) { + IntegerBox box = new IntegerBox(i); + Object res = test(array, box); + if (res != box) { + throw new AssertionError(res + " vs. " + box); + } + } + } +} diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIntrinsics.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIntrinsics.java index 23b42bad191b..e455d7ee68c8 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIntrinsics.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIntrinsics.java @@ -155,8 +155,8 @@ public TestIntrinsics() { public static void main(String[] args) { Scenario[] scenarios = InlineTypes.DEFAULT_SCENARIOS; - scenarios[3].addFlags("-XX:-MonomorphicArrayCheck", "-XX:+UseArrayFlattening"); - scenarios[4].addFlags("-XX:-MonomorphicArrayCheck", "-XX:+UnlockExperimentalVMOptions", "-XX:PerMethodSpecTrapLimit=0", "-XX:PerMethodTrapLimit=0"); + scenarios[3].addFlags("-XX:+UseArrayFlattening", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-MonomorphicArrayCheck"); + scenarios[4].addFlags("-XX:+UnlockExperimentalVMOptions", "-XX:PerMethodSpecTrapLimit=0", "-XX:PerMethodTrapLimit=0", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-MonomorphicArrayCheck"); InlineTypes.getFramework() .addScenarios(scenarios[Integer.parseInt(args[0])]) diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorld.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorld.java index 513ee0c20644..f4187db48981 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorld.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorld.java @@ -178,8 +178,8 @@ public static void main(String[] args) { class2.getDeclaredFields(); Scenario[] scenarios = InlineTypes.DEFAULT_SCENARIOS; - scenarios[3].addFlags("-XX:-MonomorphicArrayCheck", "-XX:+UseArrayFlattening"); - scenarios[4].addFlags("-XX:-MonomorphicArrayCheck"); + scenarios[3].addFlags("-XX:+UseArrayFlattening", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-MonomorphicArrayCheck"); + scenarios[4].addFlags("-XX:+IgnoreUnrecognizedVMOptions", "-XX:-MonomorphicArrayCheck"); InlineTypes.getFramework() // TODO 8337821: Temporarily increased MemLimit - remove again with JDK-8378328 once fixed. diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableArrays.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableArrays.java index 9f8242cdd9e9..db0eb31fe491 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableArrays.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableArrays.java @@ -136,10 +136,10 @@ public class TestNullableArrays { public static void main(String[] args) { Scenario[] scenarios = InlineTypes.DEFAULT_SCENARIOS; - scenarios[2].addFlags("-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast", "-XX:+StressArrayCopyMacroNode"); - scenarios[3].addFlags("-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast"); - scenarios[4].addFlags("-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast"); - scenarios[5].addFlags("-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast", "-XX:+StressArrayCopyMacroNode"); + scenarios[2].addFlags("-XX:+IgnoreUnrecognizedVMOptions", "-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast", "-XX:+StressArrayCopyMacroNode"); + scenarios[3].addFlags("-XX:+IgnoreUnrecognizedVMOptions", "-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast"); + scenarios[4].addFlags("-XX:+IgnoreUnrecognizedVMOptions", "-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast"); + scenarios[5].addFlags("-XX:+IgnoreUnrecognizedVMOptions", "-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast", "-XX:+StressArrayCopyMacroNode"); InlineTypes.getFramework() .addScenarios(scenarios[Integer.parseInt(args[0])]) @@ -979,7 +979,7 @@ public void test28_verifier() { } @Test - @IR(failOn = {ALLOC_ARRAY_OF_MYVALUE_KLASS, LOOP, UNSTABLE_IF_TRAP, PREDICATE_TRAP}) + @IR(failOn = {LOOP, UNSTABLE_IF_TRAP, PREDICATE_TRAP}) @IR(failOn = LOAD_OF_ANY_KLASS, applyIf = {"InlineTypeReturnedAsFields", "false"}) @IR(counts = {LOAD_OF_ANY_KLASS, "4"}, applyIf = {"InlineTypeReturnedAsFields", "true"}) public MyValue2 test29(MyValue2[] src) { diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableInlineTypes.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableInlineTypes.java index 43446ec688d2..0a641ba2961a 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableInlineTypes.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableInlineTypes.java @@ -164,8 +164,8 @@ public TestNullableInlineTypes() { public static void main(String[] args) { Scenario[] scenarios = InlineTypes.DEFAULT_SCENARIOS; - scenarios[3].addFlags("-XX:-MonomorphicArrayCheck", "-XX:+UnlockDiagnosticVMOptions", "-XX:+UseArrayFlattening"); - scenarios[4].addFlags("-XX:-MonomorphicArrayCheck"); + scenarios[3].addFlags("-XX:+UnlockDiagnosticVMOptions", "-XX:+UseArrayFlattening", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-MonomorphicArrayCheck"); + scenarios[4].addFlags("-XX:+IgnoreUnrecognizedVMOptions", "-XX:-MonomorphicArrayCheck"); InlineTypes.getFramework() .addScenarios(scenarios[Integer.parseInt(args[0])]) diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestReferenceArrayClone.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestReferenceArrayClone.java index 681eb5687b0f..df5f4878090d 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestReferenceArrayClone.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestReferenceArrayClone.java @@ -23,15 +23,18 @@ /* * @test - * @summary Verify that clone preserves the layout of reference arrays. - * @bug 8388256 + * @summary Verify that C2 preserves the layout of reference array clones. + * @bug 8388256 8390451 * @requires vm.compiler2.enabled * @library /test/lib / * @enablePreview * @modules java.base/jdk.internal.value - * @run main ${test.main.class} + * @run main/othervm -Xbatch ${test.main.class} * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:-UseTLAB - * -XX:CompileCommand=compileonly,${test.main.class}::testClone + * -XX:CompileCommand=compileonly,${test.main.class}::test* + * ${test.main.class} + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:-UseTLAB -XX:-DoEscapeAnalysis + * -XX:CompileCommand=compileonly,${test.main.class}::test* * ${test.main.class} */ @@ -41,17 +44,43 @@ import jdk.test.lib.Asserts; public class TestReferenceArrayClone { + static final Integer[] ARRAY = (Integer[])ValueClass.newReferenceArray(Integer.class, 1); + static Integer[] testClone(Integer[] a) { return a.clone(); } - public static void main(String[] args) { - Integer[] array = (Integer[])ValueClass.newReferenceArray(Integer.class, 1); + static void testDeopt(boolean deopt) { + Integer[] array = (Integer[])ValueClass.newReferenceArray(Integer.class, 1).clone(); array[0] = 42; - array = testClone(array); - Asserts.assertEQ(array[0], 42, "unexpected element"); + if (deopt) { + verify(array); + } + } + + static void testDeoptClone(boolean deopt) { + Integer[] clone = ARRAY.clone(); + if (deopt) { + verify(clone); + } + } + + static void verify(Integer[] array) { Asserts.assertFalse(ValueClass.isFlatArray(array), "should not be flat"); Asserts.assertFalse(ValueClass.isNullRestrictedArray(array), "should not be null-restricted"); Asserts.assertTrue(ValueClass.isAtomicArray(array), "should be atomic"); + Asserts.assertEQ(array[0], 42, "unexpected element"); + } + + public static void main(String[] args) { + ARRAY[0] = 42; + verify(testClone(ARRAY)); + + for (int i = 0; i < 20_000; i++) { + testDeopt(false); + testDeoptClone(false); + } + testDeopt(true); + testDeoptClone(true); } } diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueClasses.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueClasses.java index ebf246cd7d60..947082760288 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueClasses.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueClasses.java @@ -129,8 +129,8 @@ public static void main(String[] args) { // Don't generate bytecodes but call through runtime for reflective calls scenarios[0].addFlags("-Dsun.reflect.inflationThreshold=10000"); scenarios[1].addFlags("-Dsun.reflect.inflationThreshold=10000"); - scenarios[3].addFlags("-XX:-MonomorphicArrayCheck", "-XX:+UnlockDiagnosticVMOptions", "-XX:+UseArrayFlattening"); - scenarios[4].addFlags("-XX:-UseTLAB", "-XX:-MonomorphicArrayCheck"); + scenarios[3].addFlags("-XX:+UnlockDiagnosticVMOptions", "-XX:+UseArrayFlattening", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-MonomorphicArrayCheck"); + scenarios[4].addFlags("-XX:-UseTLAB", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-MonomorphicArrayCheck"); InlineTypes.getFramework() .addScenarios(scenarios[Integer.parseInt(args[0])]) diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestMaskedMacroLogicVector.java b/test/hotspot/jtreg/compiler/vectorapi/TestMaskedMacroLogicVector.java index 22c35d80c70c..74d650d07a14 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/TestMaskedMacroLogicVector.java +++ b/test/hotspot/jtreg/compiler/vectorapi/TestMaskedMacroLogicVector.java @@ -23,7 +23,7 @@ /** * @test - * @bug 8273322 8387145 + * @bug 8273322 8387145 8387204 * @key randomness * @summary Enhance macro logic optimization for masked logic operations. * @modules jdk.incubator.vector @@ -646,6 +646,137 @@ public void verifyInt11(int[] r, int[] a, int[] b, int[] c, boolean [] mask, boo } } + static int intFunc12(int a, int b, boolean mask) { + int left = mask ? (-1 ^ a) : -1; + return mask ? (left | b) : left; + } + + @ForceInline + public void testInt12Kernel(VectorSpecies SPECIES, int[] r, int[] a, int[] b, boolean [] mask) { + for (int i = 0; i < SPECIES.loopBound(r.length); i += SPECIES.length()) { + VectorMask vmask = VectorMask.fromArray(SPECIES, mask , i); + IntVector vall = IntVector.broadcast(SPECIES, -1); + IntVector va = IntVector.fromArray(SPECIES, a, i); + IntVector vb = IntVector.fromArray(SPECIES, b, i); + vall.lanewise(VectorOperators.XOR, va, vmask) + .lanewise(VectorOperators.OR, vb, vmask) + .intoArray(r, i); + } + } + + @Test + @IR(applyIf = {"UseAVX", "3"}, counts = {IRNode.MACRO_LOGIC_V, " > 0 "}) + public void testInt12_Int128(int[] r, int[] a, int[] b, boolean [] mask) { + testInt12Kernel(IntVector.SPECIES_128, r, a, b, mask); + } + @Test + @IR(applyIf = {"UseAVX", "3"}, counts = {IRNode.MACRO_LOGIC_V, " > 0 "}) + public void testInt12_Int256(int[] r, int[] a, int[] b, boolean [] mask) { + testInt12Kernel(IntVector.SPECIES_256, r, a, b, mask); + } + @Test + @IR(applyIf = {"UseAVX", "3"}, counts = {IRNode.MACRO_LOGIC_V, " > 0 "}) + public void testInt12_Int512(int[] r, int[] a, int[] b, boolean [] mask) { + testInt12Kernel(IntVector.SPECIES_512, r, a, b, mask); + } + + public void verifyInt12(int[] r, int[] a, int[] b, boolean [] mask) { + for (int i = 0; i < r.length; i++) { + int expected = intFunc12(a[i], b[i], mask[i]); + if (r[i] != expected) { + throw new AssertionError(String.format("testInt12: at #%d: r=%d, expected = %d = intFunc12(%d,%d,%b)", + i, r[i], expected, a[i], b[i], mask[i])); + } + } + } + + static int intFunc13(int a, int b, boolean mask) { + int left = mask ? (-1 ^ a) : -1; + return mask ? (left & b) : left; + } + + @ForceInline + public void testInt13Kernel(VectorSpecies SPECIES, int[] r, int[] a, int[] b, boolean [] mask) { + for (int i = 0; i < SPECIES.loopBound(r.length); i += SPECIES.length()) { + VectorMask vmask = VectorMask.fromArray(SPECIES, mask , i); + IntVector vall = IntVector.broadcast(SPECIES, -1); + IntVector va = IntVector.fromArray(SPECIES, a, i); + IntVector vb = IntVector.fromArray(SPECIES, b, i); + vall.lanewise(VectorOperators.XOR, va, vmask) + .lanewise(VectorOperators.AND, vb, vmask) + .intoArray(r, i); + } + } + + @Test + @IR(applyIf = {"UseAVX", "3"}, counts = {IRNode.MACRO_LOGIC_V, " > 0 "}) + public void testInt13_Int128(int[] r, int[] a, int[] b, boolean [] mask) { + testInt13Kernel(IntVector.SPECIES_128, r, a, b, mask); + } + @Test + @IR(applyIf = {"UseAVX", "3"}, counts = {IRNode.MACRO_LOGIC_V, " > 0 "}) + public void testInt13_Int256(int[] r, int[] a, int[] b, boolean [] mask) { + testInt13Kernel(IntVector.SPECIES_256, r, a, b, mask); + } + @Test + @IR(applyIf = {"UseAVX", "3"}, counts = {IRNode.MACRO_LOGIC_V, " > 0 "}) + public void testInt13_Int512(int[] r, int[] a, int[] b, boolean [] mask) { + testInt13Kernel(IntVector.SPECIES_512, r, a, b, mask); + } + + public void verifyInt13(int[] r, int[] a, int[] b, boolean [] mask) { + for (int i = 0; i < r.length; i++) { + int expected = intFunc13(a[i], b[i], mask[i]); + if (r[i] != expected) { + throw new AssertionError(String.format("testInt13: at #%d: r=%d, expected = %d = intFunc13(%d,%d,%b)", + i, r[i], expected, a[i], b[i], mask[i])); + } + } + } + + static int intFunc14(int a, int b, boolean mask) { + int left = mask ? (-1 ^ a) : -1; + return mask ? (left ^ b) : left; + } + + @ForceInline + public void testInt14Kernel(VectorSpecies SPECIES, int[] r, int[] a, int[] b, boolean [] mask) { + for (int i = 0; i < SPECIES.loopBound(r.length); i += SPECIES.length()) { + VectorMask vmask = VectorMask.fromArray(SPECIES, mask , i); + IntVector vall = IntVector.broadcast(SPECIES, -1); + IntVector va = IntVector.fromArray(SPECIES, a, i); + IntVector vb = IntVector.fromArray(SPECIES, b, i); + vall.lanewise(VectorOperators.XOR, va, vmask) + .lanewise(VectorOperators.XOR, vb, vmask) + .intoArray(r, i); + } + } + + @Test + @IR(applyIf = {"UseAVX", "3"}, counts = {IRNode.MACRO_LOGIC_V, " > 0 "}) + public void testInt14_Int128(int[] r, int[] a, int[] b, boolean [] mask) { + testInt14Kernel(IntVector.SPECIES_128, r, a, b, mask); + } + @Test + @IR(applyIf = {"UseAVX", "3"}, counts = {IRNode.MACRO_LOGIC_V, " > 0 "}) + public void testInt14_Int256(int[] r, int[] a, int[] b, boolean [] mask) { + testInt14Kernel(IntVector.SPECIES_256, r, a, b, mask); + } + @Test + @IR(applyIf = {"UseAVX", "3"}, counts = {IRNode.MACRO_LOGIC_V, " > 0 "}) + public void testInt14_Int512(int[] r, int[] a, int[] b, boolean [] mask) { + testInt14Kernel(IntVector.SPECIES_512, r, a, b, mask); + } + + public void verifyInt14(int[] r, int[] a, int[] b, boolean [] mask) { + for (int i = 0; i < r.length; i++) { + int expected = intFunc14(a[i], b[i], mask[i]); + if (r[i] != expected) { + throw new AssertionError(String.format("testInt14: at #%d: r=%d, expected = %d = intFunc14(%d,%d,%b)", + i, r[i], expected, a[i], b[i], mask[i])); + } + } + } // ===================================================== // @@ -695,6 +826,123 @@ public void verifyLong(long[] r, long[] a, long[] b, long[] c) { } } + static long longFunc15(long a, long b, boolean mask) { + long left = mask ? (-1L ^ a) : -1L; + return mask ? (left | b) : left; + } + + @ForceInline + public void testLong15Kernel(VectorSpecies SPECIES, long[] r, long[] a, long[] b, boolean [] mask) { + for (int i = 0; i < SPECIES.loopBound(r.length); i += SPECIES.length()) { + VectorMask vmask = VectorMask.fromArray(SPECIES, mask , i); + LongVector vall = LongVector.broadcast(SPECIES, -1L); + LongVector va = LongVector.fromArray(SPECIES, a, i); + LongVector vb = LongVector.fromArray(SPECIES, b, i); + vall.lanewise(VectorOperators.XOR, va, vmask) + .lanewise(VectorOperators.OR, vb, vmask) + .intoArray(r, i); + } + } + + @Test + @IR(applyIf = {"UseAVX", "3"}, counts = {IRNode.MACRO_LOGIC_V, " > 0 "}) + public void testLong15_Long256(long[] r, long[] a, long[] b, boolean [] mask) { + testLong15Kernel(LongVector.SPECIES_256, r, a, b, mask); + } + @Test + @IR(applyIf = {"UseAVX", "3"}, counts = {IRNode.MACRO_LOGIC_V, " > 0 "}) + public void testLong15_Long512(long[] r, long[] a, long[] b, boolean [] mask) { + testLong15Kernel(LongVector.SPECIES_512, r, a, b, mask); + } + + public void verifyLong15(long[] r, long[] a, long[] b, boolean [] mask) { + for (int i = 0; i < r.length; i++) { + long expected = longFunc15(a[i], b[i], mask[i]); + if (r[i] != expected) { + throw new AssertionError(String.format("testLong15: at #%d: r=%d, expected = %d = longFunc15(%d,%d,%b)", + i, r[i], expected, a[i], b[i], mask[i])); + } + } + } + + static long longFunc16(long a, long b, boolean mask) { + long left = mask ? (-1L ^ a) : -1L; + return mask ? (left & b) : left; + } + + @ForceInline + public void testLong16Kernel(VectorSpecies SPECIES, long[] r, long[] a, long[] b, boolean [] mask) { + for (int i = 0; i < SPECIES.loopBound(r.length); i += SPECIES.length()) { + VectorMask vmask = VectorMask.fromArray(SPECIES, mask , i); + LongVector vall = LongVector.broadcast(SPECIES, -1L); + LongVector va = LongVector.fromArray(SPECIES, a, i); + LongVector vb = LongVector.fromArray(SPECIES, b, i); + vall.lanewise(VectorOperators.XOR, va, vmask) + .lanewise(VectorOperators.AND, vb, vmask) + .intoArray(r, i); + } + } + + @Test + @IR(applyIf = {"UseAVX", "3"}, counts = {IRNode.MACRO_LOGIC_V, " > 0 "}) + public void testLong16_Long256(long[] r, long[] a, long[] b, boolean [] mask) { + testLong16Kernel(LongVector.SPECIES_256, r, a, b, mask); + } + @Test + @IR(applyIf = {"UseAVX", "3"}, counts = {IRNode.MACRO_LOGIC_V, " > 0 "}) + public void testLong16_Long512(long[] r, long[] a, long[] b, boolean [] mask) { + testLong16Kernel(LongVector.SPECIES_512, r, a, b, mask); + } + + public void verifyLong16(long[] r, long[] a, long[] b, boolean [] mask) { + for (int i = 0; i < r.length; i++) { + long expected = longFunc16(a[i], b[i], mask[i]); + if (r[i] != expected) { + throw new AssertionError(String.format("testLong16: at #%d: r=%d, expected = %d = longFunc16(%d,%d,%b)", + i, r[i], expected, a[i], b[i], mask[i])); + } + } + } + + static long longFunc17(long a, long b, boolean mask) { + long left = mask ? (-1L ^ a) : -1L; + return mask ? (left ^ b) : left; + } + + @ForceInline + public void testLong17Kernel(VectorSpecies SPECIES, long[] r, long[] a, long[] b, boolean [] mask) { + for (int i = 0; i < SPECIES.loopBound(r.length); i += SPECIES.length()) { + VectorMask vmask = VectorMask.fromArray(SPECIES, mask , i); + LongVector vall = LongVector.broadcast(SPECIES, -1L); + LongVector va = LongVector.fromArray(SPECIES, a, i); + LongVector vb = LongVector.fromArray(SPECIES, b, i); + vall.lanewise(VectorOperators.XOR, va, vmask) + .lanewise(VectorOperators.XOR, vb, vmask) + .intoArray(r, i); + } + } + + @Test + @IR(applyIf = {"UseAVX", "3"}, counts = {IRNode.MACRO_LOGIC_V, " > 0 "}) + public void testLong17_Long256(long[] r, long[] a, long[] b, boolean [] mask) { + testLong17Kernel(LongVector.SPECIES_256, r, a, b, mask); + } + @Test + @IR(applyIf = {"UseAVX", "3"}, counts = {IRNode.MACRO_LOGIC_V, " > 0 "}) + public void testLong17_Long512(long[] r, long[] a, long[] b, boolean [] mask) { + testLong17Kernel(LongVector.SPECIES_512, r, a, b, mask); + } + + public void verifyLong17(long[] r, long[] a, long[] b, boolean [] mask) { + for (int i = 0; i < r.length; i++) { + long expected = longFunc17(a[i], b[i], mask[i]); + if (r[i] != expected) { + throw new AssertionError(String.format("testLong17: at #%d: r=%d, expected = %d = longFunc17(%d,%d,%b)", + i, r[i], expected, a[i], b[i], mask[i])); + } + } + } + // ===================================================== // private static final Random R = Utils.getRandomInstance(); @@ -1006,6 +1254,72 @@ public void kernel_testInt11_Int512() { } } + @Run(test = {"testInt12_Int128"}, mode = RunMode.STANDALONE) + public void kernel_testInt12_Int128() { + for (int i = 0; i < 10000; i++) { + testInt12_Int128(r, a, b, mask); + verifyInt12(r, a, b, mask); + } + } + @Run(test = {"testInt12_Int256"}, mode = RunMode.STANDALONE) + public void kernel_testInt12_Int256() { + for (int i = 0; i < 10000; i++) { + testInt12_Int256(r, a, b, mask); + verifyInt12(r, a, b, mask); + } + } + @Run(test = {"testInt12_Int512"}, mode = RunMode.STANDALONE) + public void kernel_testInt12_Int512() { + for (int i = 0; i < 10000; i++) { + testInt12_Int512(r, a, b, mask); + verifyInt12(r, a, b, mask); + } + } + + @Run(test = {"testInt13_Int128"}, mode = RunMode.STANDALONE) + public void kernel_testInt13_Int128() { + for (int i = 0; i < 10000; i++) { + testInt13_Int128(r, a, b, mask); + verifyInt13(r, a, b, mask); + } + } + @Run(test = {"testInt13_Int256"}, mode = RunMode.STANDALONE) + public void kernel_testInt13_Int256() { + for (int i = 0; i < 10000; i++) { + testInt13_Int256(r, a, b, mask); + verifyInt13(r, a, b, mask); + } + } + @Run(test = {"testInt13_Int512"}, mode = RunMode.STANDALONE) + public void kernel_testInt13_Int512() { + for (int i = 0; i < 10000; i++) { + testInt13_Int512(r, a, b, mask); + verifyInt13(r, a, b, mask); + } + } + + @Run(test = {"testInt14_Int128"}, mode = RunMode.STANDALONE) + public void kernel_testInt14_Int128() { + for (int i = 0; i < 10000; i++) { + testInt14_Int128(r, a, b, mask); + verifyInt14(r, a, b, mask); + } + } + @Run(test = {"testInt14_Int256"}, mode = RunMode.STANDALONE) + public void kernel_testInt14_Int256() { + for (int i = 0; i < 10000; i++) { + testInt14_Int256(r, a, b, mask); + verifyInt14(r, a, b, mask); + } + } + @Run(test = {"testInt14_Int512"}, mode = RunMode.STANDALONE) + public void kernel_testInt14_Int512() { + for (int i = 0; i < 10000; i++) { + testInt14_Int512(r, a, b, mask); + verifyInt14(r, a, b, mask); + } + } + @Run(test = {"testLong_Long256"}, mode = RunMode.STANDALONE) public void kernel_testLong_Long256() { for (int i = 0; i < 10000; i++) { @@ -1021,6 +1335,51 @@ public void kernel_testLong_Long512() { } } + @Run(test = {"testLong15_Long256"}, mode = RunMode.STANDALONE) + public void kernel_testLong15_Long256() { + for (int i = 0; i < 10000; i++) { + testLong15_Long256(rl, al, bl, mask); + verifyLong15(rl, al, bl, mask); + } + } + @Run(test = {"testLong15_Long512"}, mode = RunMode.STANDALONE) + public void kernel_testLong15_Long512() { + for (int i = 0; i < 10000; i++) { + testLong15_Long512(rl, al, bl, mask); + verifyLong15(rl, al, bl, mask); + } + } + + @Run(test = {"testLong16_Long256"}, mode = RunMode.STANDALONE) + public void kernel_testLong16_Long256() { + for (int i = 0; i < 10000; i++) { + testLong16_Long256(rl, al, bl, mask); + verifyLong16(rl, al, bl, mask); + } + } + @Run(test = {"testLong16_Long512"}, mode = RunMode.STANDALONE) + public void kernel_testLong16_Long512() { + for (int i = 0; i < 10000; i++) { + testLong16_Long512(rl, al, bl, mask); + verifyLong16(rl, al, bl, mask); + } + } + + @Run(test = {"testLong17_Long256"}, mode = RunMode.STANDALONE) + public void kernel_testLong17_Long256() { + for (int i = 0; i < 10000; i++) { + testLong17_Long256(rl, al, bl, mask); + verifyLong17(rl, al, bl, mask); + } + } + @Run(test = {"testLong17_Long512"}, mode = RunMode.STANDALONE) + public void kernel_testLong17_Long512() { + for (int i = 0; i < 10000; i++) { + testLong17_Long512(rl, al, bl, mask); + verifyLong17(rl, al, bl, mask); + } + } + public TestMaskedMacroLogicVector() { br = new boolean[SIZE]; ba = fillBooleanRandom((()-> new boolean[SIZE])); diff --git a/test/hotspot/jtreg/containers/systemd/SystemdMemoryExceedHostMemTest.java b/test/hotspot/jtreg/containers/systemd/SystemdMemoryExceedHostMemTest.java new file mode 100644 index 000000000000..358c52eb117f --- /dev/null +++ b/test/hotspot/jtreg/containers/systemd/SystemdMemoryExceedHostMemTest.java @@ -0,0 +1,83 @@ +/* + * 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. + */ + +import jdk.test.lib.containers.systemd.SystemdRunOptions; +import jdk.test.lib.containers.systemd.SystemdTestUtils; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.whitebox.WhiteBox; +import jtreg.SkippedException; + +/* + * @test + * @bug 8390314 + * @summary Verify no asserts are triggered in cgroup adjusting code + * when memory limit exceeds physical host memory. + * @requires systemd.support + * @library /test/lib + * @modules java.base/jdk.internal.platform + * @build HelloSystemd jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar whitebox.jar jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:whitebox.jar -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI SystemdMemoryExceedHostMemTest + */ +public class SystemdMemoryExceedHostMemTest { + + private static final int MB = 1024 * 1024; + private static final WhiteBox wb = WhiteBox.getWhiteBox(); + private static final String TEST_SLICE_NAME = SystemdMemoryExceedHostMemTest.class.getSimpleName() + "HS"; + + public static void main(String[] args) throws Exception { + testMemExceedsPhysical(); + } + + private static void testMemExceedsPhysical() throws Exception { + SystemdRunOptions opts = SystemdTestUtils.newOpts("HelloSystemd"); + int expectedMemLimit = 1024; + // 1 GB memory, the lower hierarchy has a value exceeding physical memory + opts.memoryLimit(String.format("%dM", expectedMemLimit)); + // Set the memory limit of a slice stricly larger than the host + // max memory + String exceedingHostMem = getHostMaxMemory() + "0"; // add a zero + opts.sliceDMemoryLimit(exceedingHostMem); + opts.cpuLimit("100%"); // 1 core + opts.sliceName(TEST_SLICE_NAME); + + OutputAnalyzer out = SystemdTestUtils.buildAndRunSystemdJava(opts); + // On affected systems this asserts in fastdebug + out.shouldHaveExitValue(0) + .shouldContain("Hello Systemd"); + try { + out.shouldContain(String.format("Memory Limit is: %d", (expectedMemLimit * MB))); + } catch (RuntimeException e) { + // memory delegation needs to be enabled when run as user on cg v2 + if (SystemdTestUtils.RUN_AS_USER) { + String hint = "When run as user on cg v2 memory delegation needs to be configured!"; + throw new SkippedException(hint); + } + throw e; + } + } + + private static String getHostMaxMemory() { + return Long.valueOf(wb.hostPhysicalMemory()).toString(); + } +} diff --git a/test/hotspot/jtreg/gc/TestUseGCOverheadLimit.java b/test/hotspot/jtreg/gc/TestUseGCOverheadLimit.java index dcc501c7710a..ba253f3e8fc7 100644 --- a/test/hotspot/jtreg/gc/TestUseGCOverheadLimit.java +++ b/test/hotspot/jtreg/gc/TestUseGCOverheadLimit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,6 +44,7 @@ import java.util.Arrays; import java.util.stream.Stream; +import jdk.test.lib.Platform; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; @@ -62,8 +63,11 @@ public static void main(String args[]) throws Exception { String[] selectedArgs = args[0].equals("G1") ? g1Args : parallelArgs; + final String[] vm64bitArgs = { + "-XX:-UseCompactObjectHeaders" // Object sizes are calculated such that the heap is tight. + }; + final String[] commonArgs = { - "-XX:-UseCompactObjectHeaders", // Object sizes are calculated such that the heap is tight. "-XX:ParallelGCThreads=1", // Make GCs take longer. "-XX:+UseGCOverheadLimit", "-Xlog:gc=debug", @@ -72,7 +76,9 @@ public static void main(String args[]) throws Exception { Allocating.class.getName() }; - String[] vmArgs = Stream.concat(Arrays.stream(selectedArgs), Arrays.stream(commonArgs)).toArray(String[]::new); + String[] vmArgs = Stream.of(selectedArgs, Platform.is64bit() ? vm64bitArgs : new String[0], commonArgs) + .flatMap(Arrays::stream) + .toArray(String[]::new); OutputAnalyzer output = ProcessTools.executeLimitedTestJava(vmArgs); output.shouldNotHaveExitValue(0); diff --git a/test/hotspot/jtreg/gc/shenandoah/generational/TestPromoteInPlaceDuringAbbreviatedCycle.java b/test/hotspot/jtreg/gc/shenandoah/generational/TestPromoteInPlaceDuringAbbreviatedCycle.java new file mode 100644 index 000000000000..e9be9f39fbd1 --- /dev/null +++ b/test/hotspot/jtreg/gc/shenandoah/generational/TestPromoteInPlaceDuringAbbreviatedCycle.java @@ -0,0 +1,225 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package gc.shenandoah.generational; + +import com.sun.management.GarbageCollectionNotificationInfo; + +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.TimeUnit; + +import javax.management.Notification; +import javax.management.NotificationEmitter; +import javax.management.NotificationListener; +import javax.management.openmbean.CompositeData; + +import jdk.test.whitebox.WhiteBox; + + +/* + * @test id=generational + * @bug 8390310 + * @requires vm.gc.Shenandoah + * @summary Aged regions must be promoted in place during an abbreviated cycle + * (one that skips the evacuation and update-refs phases). + * @library /testlibrary /test/lib / + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. + * -Xms512m -Xmx512m + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:ShenandoahGenerationalMinPIPUsage=1 + * -XX:ShenandoahOldGarbageThreshold=100 + * -XX:ShenandoahRegionSize=1m + * -XX:ShenandoahImmediateThreshold=0 + * -XX:ShenandoahGenerationalMinTenuringAge=1 + * -XX:ShenandoahGenerationalMaxTenuringAge=1 + * -XX:-DisableExplicitGC -XX:+ExplicitGCInvokesConcurrent + * gc.shenandoah.generational.TestPromoteInPlaceDuringAbbreviatedCycle + */ + + /* + * @test id=generational-always-tenure + * @bug 8390310 + * @requires vm.gc.Shenandoah + * @summary Tests that promotion potential is correct when the always tenure + * override is in effect (set by WB.fullGC()) + * @library /testlibrary /test/lib / + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. + * -Xms512m -Xmx512m + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:ShenandoahGenerationalMinPIPUsage=1 + * -XX:ShenandoahOldGarbageThreshold=100 + * -XX:ShenandoahRegionSize=1m + * -XX:ShenandoahImmediateThreshold=0 + * -XX:ShenandoahGenerationalMinTenuringAge=2 + * -XX:ShenandoahGenerationalMaxTenuringAge=2 + * -XX:-DisableExplicitGC -XX:+ExplicitGCInvokesConcurrent + * gc.shenandoah.generational.TestPromoteInPlaceDuringAbbreviatedCycle always-tenure + */ +public class TestPromoteInPlaceDuringAbbreviatedCycle { + + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + // Make a humongous array (with 1MB regions, this will be humongous with + // and without compressed oops). + private static final int HUMONGOUS_REFS = 512 * 1024; + + // Also make a not humongous array to test regular region promotion path + private static final int REGULAR_REFS = 256; + + // Used to create pure garbage regions to satisfy immediate garbage + // threshold + private static final int GARBAGE_BYTES = 2 * 1024 * 1024; + + // Test will fail if our humongous object isn't promoted in this many cycles + private static final int MAX_CYCLES = 5; + + // Keep references so the arrays under test stay live and age in young. + private static Object[] humongous; + private static Object[] regular; + + // Reference used to publish, then drop, the per-cycle garbage (to keep + // local var from being eliminated) + private static Object garbage; + + private static boolean isCollectorNotification(Notification n) { + return n.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION); + } + + private static boolean isIllegalPause(GarbageCollectionNotificationInfo info) { + return info.getGcName().equals("Shenandoah Pauses") + && (info.getGcAction().contains("Update Refs") + || info.getGcAction().contains("Degen") + || info.getGcAction().contains("Full")); + } + + private static void subscribeToCollectorNotifications(NotificationListener listener) { + for (GarbageCollectorMXBean b : ManagementFactory.getGarbageCollectorMXBeans()) { + ((NotificationEmitter) b).addNotificationListener(listener, null, null); + } + } + + private static void unsubscribeToCollectorNotifications(NotificationListener listener) throws Exception { + for (GarbageCollectorMXBean b : ManagementFactory.getGarbageCollectorMXBeans()) { + ((NotificationEmitter) b).removeNotificationListener(listener, null, null); + } + } + + private static GarbageCollectorMXBean cycleBean() { + for (GarbageCollectorMXBean b : ManagementFactory.getGarbageCollectorMXBeans()) { + if (b.getName().equals("Shenandoah Cycles")) { + return b; + } + } + throw new IllegalStateException("No \"Shenandoah Cycles\" bean found"); + } + + public static void main(String[] args) throws Exception { + boolean alwaysTenure = args.length > 0 && "always-tenure".equals(args[0]); + humongous = new Object[HUMONGOUS_REFS]; + regular = new Object[REGULAR_REFS]; + + // Listen for events to detect if a non-abbreviated cycle runs + final GarbageCollectorMXBean cycles = cycleBean(); + final AtomicLong illegalPauses = new AtomicLong(); + final AtomicLong maxCycleId = new AtomicLong(); + NotificationListener listener = (Notification n, Object o) -> { + if (isCollectorNotification(n)) { + GarbageCollectionNotificationInfo info = GarbageCollectionNotificationInfo.from((CompositeData) n.getUserData()); + if (isIllegalPause(info)) { + illegalPauses.incrementAndGet(); + } else if (info.getGcName().equals("Shenandoah Cycles")) { + long gcId = info.getGcInfo().getId(); + if (gcId > maxCycleId.get()) { + maxCycleId.set(gcId); + } + } + } + }; + + subscribeToCollectorNotifications(listener); + + if (WB.isObjectInOldGen(humongous)) { + throw new IllegalStateException("Expected young humongous array"); + } + + if (WB.isObjectInOldGen(regular)) { + throw new IllegalStateException("Expected young regular array"); + } + + for (int cycle = 1; cycle <= MAX_CYCLES; cycle++) { + // Produce one whole dead region so the upcoming cycle is abbreviated. + garbage = new byte[GARBAGE_BYTES]; + garbage = null; + + if (alwaysTenure) { + // This also runs a global cycle, but with an effective tenuring threshold of zero + WB.fullGC(); + } else { + // Runs a concurrent global cycle and blocks until it completes. + System.gc(); + } + + // Both objects are in old, exit the test loop + if (WB.isObjectInOldGen(humongous) && WB.isObjectInOldGen(regular)) { + break; + } + } + + // Flush gc notification events + long targetCycles = cycles.getCollectionCount(); + long deadlineNanos = System.nanoTime() + TimeUnit.SECONDS.toNanos(30); + while (maxCycleId.get() < targetCycles) { + if (System.nanoTime() > deadlineNanos) { + throw new RuntimeException("Timed out flushing GC notifications: delivered " + + maxCycleId.get() + " of " + + targetCycles + " cycle notifications"); + } + Thread.sleep(10); + } + + unsubscribeToCollectorNotifications(listener); + + if (illegalPauses.get() != 0) { + throw new RuntimeException(illegalPauses.get() + " non-abbreviated cycles happened"); + } + + if (!WB.isObjectInOldGen(humongous)) { + throw new RuntimeException("Humongous region was not promoted in place."); + } + + if (!WB.isObjectInOldGen(regular)) { + throw new RuntimeException("Regular region was not promoted in place"); + } + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/FlatArrayTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/FlatArrayTest.java index 94f2234f2a40..452ed24f25df 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/FlatArrayTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/FlatArrayTest.java @@ -27,22 +27,43 @@ * @requires vm.cds.supports.aot.class.linking * @requires vm.debug * @enablePreview - * @library /test/jdk/lib/testlibrary /test/lib + * @library /test/jdk/lib/testlibrary /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes/ * @modules java.base/jdk.internal.value + * @modules java.base/jdk.internal.vm.annotation * @build FlatArrayTest * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar - * FlatArrayTestApp MyAOTInitedClass CharPair Wrapper + * FlatArrayTestApp + * MyAOTInitedClass + * valueclasses.BytePair + * valueclasses.BytePairWrapper + * valueclasses.BytePairWrapperWrapper + * valueclasses.CharPair + * valueclasses.IntegerWrapper + * valueclasses.ShortPair + * valueclasses.ShortPairWrapper + * valueclasses.ValueClassHelper * @run driver FlatArrayTest AOT --two-step-training */ import java.util.Arrays; import jdk.internal.value.ValueClass; +import jdk.internal.vm.annotation.NullRestricted; import jdk.test.lib.cds.CDSAppTester; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.helpers.ClassFileInstaller; import jdk.test.lib.StringArrayUtils; +// From ../test-classes/ +import valueclasses.BytePair; +import valueclasses.BytePairWrapper; +import valueclasses.BytePairWrapperWrapper; +import valueclasses.CharPair; +import valueclasses.ShortPair; +import valueclasses.ShortPairWrapper; +import valueclasses.IntegerWrapper; +import valueclasses.ValueClassHelper; + public class FlatArrayTest { static final String appJar = ClassFileInstaller.getJarPath("app.jar"); static final String mainClass = FlatArrayTestApp.class.getName(); @@ -89,8 +110,13 @@ public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception out.shouldContain("Y = 123"); } else if (runMode == RunMode.ASSEMBLY) { out.shouldMatch("klasses.* app .*MyAOTInitedClass .* inited"); + out.shouldMatch("klasses.* app .*BytePair .* inited"); + out.shouldMatch("klasses.* app .*BytePairWrapper .* inited"); + out.shouldMatch("klasses.* app .*BytePairWrapperWrapper .* inited"); out.shouldMatch("klasses.* app .*CharPair .* inited"); - out.shouldMatch("klasses.* app .*Wrapper .* inited"); + out.shouldMatch("klasses.* app .*IntegerWrapper .* inited"); + out.shouldMatch("klasses.* app .*ShortPair .* inited"); + out.shouldMatch("klasses.* app .*ShortPairWrapper .* inited"); } else if (runMode == RunMode.PRODUCTION) { out.shouldContain("Y = 45"); } @@ -98,47 +124,16 @@ public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception } } +// NOTE: this class is NOT aot-initialized. class FlatArrayTestApp { static int X = 45; + public static void main(String[] args) { X = 123; MyAOTInitedClass.test(args[0]); } } -value class CharPair implements Comparable { - char c0, c1; - - public String toString() { - return "(" + c0 + ", " + c1 + ")"; - } - - public int compareTo(CharPair o) { - return (c0 - o.c0) - (c1 - o.c1); - } - - public CharPair(char c0, char c1) { - this.c0 = c0; - this.c1 = c1; - } -} - -value class Wrapper implements Comparable { - Integer i; - - public String toString() { - return i.toString(); - } - - public int compareTo(Wrapper o) { - return i - o.i; - } - - Wrapper(int i) { - this.i = new Integer(i); - } -} - // This class is stored in the AOT cache in the initialized state. class MyAOTInitedClass { // Note that when MyAOTInitedClass is initialized in the assembly run, FlatArrayTestApp.main() @@ -147,15 +142,23 @@ class MyAOTInitedClass { static Integer[] intArray; static CharPair[] charPairArray; - static Wrapper[] wrapperArray; + static IntegerWrapper[] integerWrapperArray; + static ShortPairWrapper[] spwArray; + static BytePairWrapperWrapper[] bpwwArray; + // A non-flattened instance of CharPair. static CharPair charPair; - static Wrapper wrapper; + + // We don't have non-flattened instances of IntegerWrapper, but + // the IntegerWrapper class should still be AOT-initialized, as + // we can read a reference object of type IntegerWrapper from + // integerWrapperArray[0] + // + // The same is also true for BytePair, BytePairWrapper, BytePairWrapperWrapper, + // ShortPair, and ShortPairWrapper, for similar reasons. static { intArray = new Integer[3]; - intArray[0] = null; - System.out.println("TEST: " + (intArray[0] == null)); intArray[0] = new Integer(0); intArray[1] = new Integer(1); intArray[2] = new Integer(2); @@ -165,13 +168,22 @@ class MyAOTInitedClass { charPairArray[1] = new CharPair('c', 'd'); charPairArray[2] = new CharPair('e', 'f'); - wrapperArray = new Wrapper[3]; - wrapperArray[0] = new Wrapper(0); - wrapperArray[1] = new Wrapper(1); - wrapperArray[2] = new Wrapper(2); + integerWrapperArray = new IntegerWrapper[3]; + integerWrapperArray[0] = new IntegerWrapper(0); + integerWrapperArray[1] = new IntegerWrapper(1); + integerWrapperArray[2] = new IntegerWrapper(2); + + spwArray = new ShortPairWrapper[3]; + spwArray[0] = new ShortPairWrapper(0, 1); + spwArray[1] = new ShortPairWrapper(2, 3); + spwArray[2] = new ShortPairWrapper(4, 5); + + bpwwArray = new BytePairWrapperWrapper[3]; + bpwwArray[0] = new BytePairWrapperWrapper(0, 1); + bpwwArray[1] = new BytePairWrapperWrapper(2, 3); + bpwwArray[2] = new BytePairWrapperWrapper(4, 5); charPair = new CharPair('x', 'y'); - wrapper = new Wrapper(5); } static void test(String runMode) { @@ -188,36 +200,72 @@ static void test(String runMode) { throw new RuntimeException("CharPair array should be flat"); } - if (!ValueClass.isFlatArray(wrapperArray)) { - throw new RuntimeException("Wrapper array should be flat"); + if (!ValueClass.isFlatArray(integerWrapperArray)) { + throw new RuntimeException("IntegerWrapper array should be flat"); + } + + if (!ValueClass.isFlatArray(spwArray)) { + throw new RuntimeException("ShortPairWrapper array should be flat"); + } + + if (!ValueClass.isFlatArray(bpwwArray)) { + throw new RuntimeException("BytePairWrapperWrapper array should be flat"); } // Ensure archived arrays are restored properly - Integer[] runtimeIntArray = new Integer[3]; - runtimeIntArray[0] = new Integer(0); - runtimeIntArray[1] = new Integer(1); - runtimeIntArray[2] = new Integer(2); - - CharPair[] runtimeCharPairArray = new CharPair[3]; - runtimeCharPairArray[0] = new CharPair('a', 'b'); - runtimeCharPairArray[1] = new CharPair('c', 'd'); - runtimeCharPairArray[2] = new CharPair('e', 'f'); - - Wrapper[] runtimeWrapperArray = new Wrapper[3]; - runtimeWrapperArray[0] = new Wrapper(0); - runtimeWrapperArray[1] = new Wrapper(1); - runtimeWrapperArray[2] = new Wrapper(2); - - if (Arrays.compare(intArray, runtimeIntArray) != 0) { + Integer[] runtime_intArray = new Integer[3]; + runtime_intArray[0] = new Integer(0); + runtime_intArray[1] = new Integer(1); + runtime_intArray[2] = new Integer(2); + + CharPair[] runtime_charPairArray = new CharPair[3]; + runtime_charPairArray[0] = new CharPair('a', 'b'); + runtime_charPairArray[1] = new CharPair('c', 'd'); + runtime_charPairArray[2] = new CharPair('e', 'f'); + + IntegerWrapper[] runtime_integerWrapperArray = new IntegerWrapper[3]; + runtime_integerWrapperArray[0] = new IntegerWrapper(0); + runtime_integerWrapperArray[1] = new IntegerWrapper(1); + runtime_integerWrapperArray[2] = new IntegerWrapper(2); + + ShortPairWrapper[] runtime_spwArray = new ShortPairWrapper[3]; + runtime_spwArray[0] = new ShortPairWrapper(0, 1); + runtime_spwArray[1] = new ShortPairWrapper(2, 3); + runtime_spwArray[2] = new ShortPairWrapper(4, 5); + + BytePairWrapperWrapper[] runtime_bpwwArray = new BytePairWrapperWrapper[3]; + runtime_bpwwArray[0] = new BytePairWrapperWrapper(0, 1); + runtime_bpwwArray[1] = new BytePairWrapperWrapper(2, 3); + runtime_bpwwArray[2] = new BytePairWrapperWrapper(4, 5); + + if (Arrays.compare(intArray, runtime_intArray) != 0) { throw new RuntimeException("Integer array not restored correctly"); } - if (Arrays.compare(charPairArray, runtimeCharPairArray) != 0) { + if (Arrays.compare(charPairArray, runtime_charPairArray) != 0) { throw new RuntimeException("CharPair array not restored correctly"); } - if (Arrays.compare(wrapperArray, runtimeWrapperArray) != 0) { - throw new RuntimeException("Wrapper array not restored correctly"); + if (Arrays.compare(integerWrapperArray, runtime_integerWrapperArray) != 0) { + throw new RuntimeException("IntegerWrapper array not restored correctly"); + } + + if (Arrays.compare(spwArray, runtime_spwArray) != 0) { + throw new RuntimeException("ShortPairWrapper array not restored correctly"); + } + + if (Arrays.compare(bpwwArray, runtime_bpwwArray) != 0) { + throw new RuntimeException("BytePairWrapperWrapper array not restored correctly"); + } + + if (runMode.equals("PRODUCTION")) { + ValueClassHelper.assertAOTInited_BytePair(); + ValueClassHelper.assertAOTInited_BytePairWrapper(); + ValueClassHelper.assertAOTInited_BytePairWrapperWrapper(); + ValueClassHelper.assertAOTInited_CharPair(); + ValueClassHelper.assertAOTInited_IntegerWrapper(); + ValueClassHelper.assertAOTInited_ShortPair(); + ValueClassHelper.assertAOTInited_ShortPairWrapper(); } } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/FlattenedFieldTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/FlattenedFieldTest.java new file mode 100644 index 000000000000..471598c27ffd --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/FlattenedFieldTest.java @@ -0,0 +1,174 @@ +/* + * 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 Test AOT-cached flattened fields + * @requires vm.cds.supports.aot.class.linking + * @requires vm.debug + * @enablePreview + * @library /test/jdk/lib/testlibrary /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes/ + * @modules java.base/jdk.internal.value + * @modules java.base/jdk.internal.vm.annotation + * @build FlattenedFieldTest + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar + * FlattenedFieldTestApp + * MyAOTInitedClass + * valueclasses.BytePair + * valueclasses.BytePairWrapper + * valueclasses.BytePairWrapperWrapper + * valueclasses.CharPair + * valueclasses.ShortPair + * valueclasses.ShortPairWrapper + * valueclasses.ValueClassHelper + * @run driver FlattenedFieldTest AOT --two-step-training + */ + +import java.util.Arrays; +import jdk.internal.value.ValueClass; +import jdk.internal.vm.annotation.NullRestricted; + +import jdk.test.lib.cds.CDSAppTester; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.helpers.ClassFileInstaller; +import jdk.test.lib.StringArrayUtils; + +// From ../test-classes/ +import valueclasses.BytePair; +import valueclasses.BytePairWrapper; +import valueclasses.BytePairWrapperWrapper; +import valueclasses.CharPair; +import valueclasses.ShortPair; +import valueclasses.ShortPairWrapper; +import valueclasses.ValueClassHelper; + +public class FlattenedFieldTest { + static final String appJar = ClassFileInstaller.getJarPath("app.jar"); + static final String mainClass = FlattenedFieldTestApp.class.getName(); + + public static void main(String[] args) throws Exception { + new Tester().run(args); + } + + static class Tester extends CDSAppTester { + public Tester() { + super(mainClass); + } + + @Override + public String classpath(RunMode runMode) { + return appJar; + } + + @Override + public String[] vmArgs(RunMode runMode) { + String args[] = StringArrayUtils.concat("--enable-preview", + "--add-exports", + "java.base/jdk.internal.value=ALL-UNNAMED"); + if (runMode == RunMode.ASSEMBLY) { + args = StringArrayUtils.concat(args, + "-Xlog:aot+class=debug", + "-XX:AOTInitTestClass=MyAOTInitedClass"); + } + + return args; + } + + @Override + public String[] appCommandLine(RunMode runMode) { + return new String[] { + mainClass, + runMode.toString(), + }; + } + + @Override + public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception { + if (runMode == RunMode.TRAINING) { + out.shouldContain("Y = 123"); + } else if (runMode == RunMode.ASSEMBLY) { + out.shouldMatch("klasses.* app .*MyAOTInitedClass .* inited"); + out.shouldMatch("klasses.* app .*BytePair .* inited"); + out.shouldMatch("klasses.* app .*BytePairWrapper .* inited"); + out.shouldMatch("klasses.* app .*BytePairWrapperWrapper .* inited"); + out.shouldMatch("klasses.* app .*CharPair .* inited"); + out.shouldMatch("klasses.* app .*ShortPair .* inited"); + out.shouldMatch("klasses.* app .*ShortPairWrapper .* inited"); + } else if (runMode == RunMode.PRODUCTION) { + out.shouldContain("Y = 45"); + } + } + } +} + +// NOTE: this class is NOT aot-initialized. +class FlattenedFieldTestApp { + static int X = 45; + + public static void main(String[] args) { + X = 123; + MyAOTInitedClass.test(args[0]); + } +} + +// This class is stored in the AOT cache in the initialized state. +class MyAOTInitedClass { + // Note that when MyAOTInitedClass is initialized in the assembly run, FlattenedFieldTestApp.main() + // is not executed, so the cached value of MyAOTInitedClass.Y will be 45; + static int Y = FlattenedFieldTestApp.X; + + static CharPair cp = new CharPair('a', 'b'); + static ShortPairWrapper spw = new ShortPairWrapper(2, 3); + static BytePairWrapperWrapper bpww = new BytePairWrapperWrapper(4, 5); + + static void test(String runMode) { + System.out.println("Y = " + Y); + if (runMode.equals("PRODUCTION") && Y != 45) { + throw new RuntimeException("MyAOTInitedClass must be AOT-inited"); + } + + CharPair runtime_cp = new CharPair('a', 'b'); + ShortPairWrapper runtime_spw = new ShortPairWrapper(2, 3); + BytePairWrapperWrapper runtime_bpww = new BytePairWrapperWrapper(4, 5); + + if (runtime_cp.compareTo(cp) != 0) { + throw new RuntimeException("CharPair not restored correctly"); + } + + if (runtime_spw.compareTo(spw) != 0) { + throw new RuntimeException("ShortPairWrapper not restored correctly"); + } + + if (runtime_bpww.compareTo(bpww) != 0) { + throw new RuntimeException("BytePairWrapperWrapper not not restored correctly"); + } + + if (runMode.equals("PRODUCTION")) { + ValueClassHelper.assertAOTInited_BytePair(); + ValueClassHelper.assertAOTInited_BytePairWrapper(); + ValueClassHelper.assertAOTInited_BytePairWrapperWrapper(); + ValueClassHelper.assertAOTInited_CharPair(); + ValueClassHelper.assertAOTInited_ShortPairWrapper(); + } + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/LoadableDescriptorTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/LoadableDescriptorTest.java new file mode 100644 index 000000000000..da85f306bd4e --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/LoadableDescriptorTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +/* + * @test + * @summary Handling of missing classes referred to by loadable descriptors. + * @bug 8390613 + * @requires vm.cds.supports.aot.class.linking + * @library /test/lib + * @enablePreview + * @build LoadableDescriptorTest + * @comment Omit the Line class when creating app.jar + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar LoadableDescriptorApp Point + * @run driver LoadableDescriptorTest -XX:+AOTClassLinking + * @run driver LoadableDescriptorTest -XX:-AOTClassLinking + */ + +import jdk.test.lib.cds.SimpleCDSAppTester; +import jdk.test.lib.helpers.ClassFileInstaller; +import jdk.test.lib.process.OutputAnalyzer; + +public class LoadableDescriptorTest { + public static void main(String[] args) throws Exception { + final String mainClass = LoadableDescriptorApp.class.getName(); + final String appJar = ClassFileInstaller.getJarPath("app.jar"); + + SimpleCDSAppTester tester = SimpleCDSAppTester.of("LoadableDescriptorTest"); + tester.addVmArgs("-Xlog:aot+class=debug,class+preload", "--enable-preview", args[0]) + .appCommandLine(mainClass) + .classpath(appJar) + .setProductionChecker((OutputAnalyzer out) -> { + out.shouldContain("LoadableDescriptorApp: success") + .shouldContain("Preloading of class Point during linking of class LoadableDescriptorApp (cause: LoadableDescriptors attribute) succeeded") + .shouldContain("Preloading of class Line during linking of class LoadableDescriptorApp (cause: LoadableDescriptors attribute) failed"); + }); + tester.runAOTWorkflow(); + } +} + +class LoadableDescriptorApp { + public static void main(String[] args) { + LoadableDescriptorApp app = new LoadableDescriptorApp(); + app.foo(null, null); + app.bar(null, null); + System.out.println("LoadableDescriptorApp: success"); + } + + // "Point" and "Line" are (symbolically) declared in the loadable descriptors of the + // LoadableDescriptorApp class. However, Line is not in app.jar so it cannot be resolved + // when LoadableDescriptorApp is linked. + + void foo(Point p1, Point p2) {} + void bar(Line a, Line b) {} +} + +value record Point(byte x, byte y) { } +value record Line(Point a, Point b) { } + diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/SharedSecretsTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/SharedSecretsTest.java index 5d6e909b7515..91077591793d 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/SharedSecretsTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/SharedSecretsTest.java @@ -46,7 +46,7 @@ public class SharedSecretsTest { public static void main(String[] args) throws Exception { Tester t = new Tester(mainClass); t.setCheckExitValue(false); - t.runAOTAssemblyWorkflow(); + t.runAOTTrainingAndAssemblyWorkflow(); } static class Tester extends CDSAppTester { diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AOTClassLinkingVMOptions.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AOTClassLinkingVMOptions.java index bee0fa60f49e..2214d5e4e5e4 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AOTClassLinkingVMOptions.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AOTClassLinkingVMOptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,14 +40,15 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import jdk.test.lib.cds.SimpleCDSAppTester; import jdk.test.lib.cds.CDSModulePackager; import jdk.test.lib.cds.CDSTestUtils; import jdk.test.lib.helpers.ClassFileInstaller; import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.process.ProcessTools; public class AOTClassLinkingVMOptions { static final String appJar = ClassFileInstaller.getJarPath("app.jar"); + static final String mainClass = "Hello"; static int testCaseNum = 0; static void testCase(String s) { @@ -56,53 +57,95 @@ static void testCase(String s) { } public static void main(String[] args) throws Exception { - TestCommon.testDump(appJar, TestCommon.list("Hello"), - "-XX:+AOTClassLinking"); + SimpleCDSAppTester t = SimpleCDSAppTester.of("AOTClassLinking") + .classpath(appJar) + .addVmArgs("-XX:+AOTClassLinking") + .appCommandLine(mainClass) + .runAOTTrainingAndAssemblyWorkflow(); testCase("Archived full module graph must be enabled at runtime"); - TestCommon.run("-cp", appJar, "-Djdk.module.validation=1", "Hello") - .assertAbnormalExit("shared archive file has aot-linked classes." + + t.setVmArgs("-Djdk.module.validation=1") + .setCheckExitValue(false) + .setProductionChecker((OutputAnalyzer out) -> { + out.shouldHaveExitValue(1); + out.shouldMatch("AOT cache has aot-linked classes." + " It cannot be used when archived full module graph is not used"); + }) + .productionRun(); testCase("Cannot use -Djava.system.class.loader"); - TestCommon.run("-cp", appJar, "-Djava.system.class.loader=dummy", "Hello") - .assertAbnormalExit("shared archive file has aot-linked classes." + - " It cannot be used when the java.system.class.loader property is specified."); + t.setVmArgs("-Djava.system.class.loader=dummy") + .setCheckExitValue(false) + .setProductionChecker((OutputAnalyzer out) -> { + out.shouldHaveExitValue(1); + out.shouldMatch("AOT cache has aot-linked classes." + + " It cannot be used when the java.system.class.loader property is specified."); + }) + .productionRun(); testCase("Cannot use a different main module"); - TestCommon.run("-cp", appJar, "-Xlog:cds", "-m", "jdk.compiler/com.sun.tools.javac.Main") - .assertAbnormalExit("shared archive file has aot-linked classes." + + t.setVmArgs() + .appCommandLine("-m", "jdk.compiler/com.sun.tools.javac.Main") + .setCheckExitValue(false) + .setProductionChecker((OutputAnalyzer out) -> { + out.shouldHaveExitValue(1); + out.shouldMatch("AOT cache has aot-linked classes." + " It cannot be used when archived full module graph is not used."); + }) + .productionRun(); + testCase("Cannot use security manager"); - TestCommon.run("-cp", appJar, "-Xlog:cds", "-Djava.security.manager=allow") - .assertAbnormalExit("shared archive file has aot-linked classes." + + t.setVmArgs("-Djava.security.manager=allow") + .appCommandLine(mainClass) + .setCheckExitValue(false) + .setProductionChecker((OutputAnalyzer out) -> { + out.shouldHaveExitValue(1); + out.shouldMatch("AOT cache has aot-linked classes." + " It cannot be used with -Djava.security.manager=allow."); - TestCommon.run("-cp", appJar, "-Xlog:cds", "-Djava.security.manager=default") - .assertAbnormalExit("shared archive file has aot-linked classes." + + }) + .productionRun(); + + t.setVmArgs("-Djava.security.manager=default") + .appCommandLine(mainClass) + .setCheckExitValue(false) + .setProductionChecker((OutputAnalyzer out) -> { + out.shouldHaveExitValue(1); + out.shouldMatch("AOT cache has aot-linked classes." + " It cannot be used with -Djava.security.manager=default."); + }) + .productionRun(); // Dumping with AOTInvokeDynamicLinking disabled - TestCommon.testDump(appJar, TestCommon.list("Hello"), - "-XX:+UnlockDiagnosticVMOptions", "-XX:+AOTClassLinking", "-XX:-AOTInvokeDynamicLinking"); + t.setVmArgs("-XX:+UnlockDiagnosticVMOptions", "-XX:+AOTClassLinking", "-XX:-AOTInvokeDynamicLinking") + .appCommandLine(mainClass) + .setCheckExitValue(true) + .runAOTTrainingAndAssemblyWorkflow(); testCase("Use the archive that was created with -XX:-AOTInvokeDynamicLinking."); - TestCommon.run("-cp", appJar, "Hello") - .assertNormalExit("Hello"); + t.setVmArgs() + .appCommandLine(mainClass) + .setCheckExitValue(true) + .setProductionChecker((OutputAnalyzer out) -> { + out.shouldHaveExitValue(0); + out.shouldMatch("Hello"); + }) + .productionRun(); testCase("Archived full module graph must be enabled at runtime (with -XX:-AOTInvokeDynamicLinking)"); - TestCommon.run("-cp", appJar, "-Djdk.module.validation=1", "Hello") - .assertAbnormalExit("shared archive file has aot-linked classes." + + t.setVmArgs("-Djdk.module.validation=1") + .appCommandLine(mainClass) + .setCheckExitValue(false) + .setProductionChecker((OutputAnalyzer out) -> { + out.shouldHaveExitValue(1); + out.shouldMatch("AOT cache has aot-linked classes." + " It cannot be used when archived full module graph is not used"); + }) + .productionRun(); // NOTE: tests for ClassFileLoadHook + AOTClassLinking is in // ../jvmti/ClassFileLoadHookTest.java - boolean dynamicMode = Boolean.getBoolean("test.dynamic.cds.archive"); - if (!dynamicMode) { - // These tests need to dump the full module graph, which is not possible with - // dynamic dump. - modulePathTests(); - } + modulePathTests(); } static void modulePathTests() throws Exception { @@ -112,47 +155,61 @@ static void modulePathTests() throws Exception { String MAIN_MODULE = "com.foos"; String MAIN_CLASS = "com.foos.Test"; - String[] appClasses = {MAIN_CLASS}; - CDSModulePackager modulePackager = new CDSModulePackager(SRC_DIR); modulePackager.createModularJarWithMainClass(MAIN_MODULE, MAIN_CLASS); String modulePath = modulePackager.getOutputDir().toString(); testCase("Cannot use mis-matched module path"); - TestCommon.testDump(null, appClasses, - "--module-path", modulePath, - "-XX:+AOTClassLinking", - "-m", MAIN_MODULE); - TestCommon.run("-Xlog:aot", - "-Xlog:cds", - "--module-path", modulePath, - "-m", MAIN_MODULE) - .assertNormalExit("Using AOT-linked classes: true"); - - TestCommon.run("-Xlog:aot", - "-Xlog:cds", - "--module-path", modulePath + "/bad", - "-m", MAIN_MODULE) - .assertAbnormalExit("shared archive file has aot-linked classes. It cannot be used when archived full module graph is not used."); + SimpleCDSAppTester t = SimpleCDSAppTester.of("ModuleTests") + .modulepath(modulePath) + .addVmArgs("-XX:+AOTClassLinking") + .appCommandLine("-m", MAIN_MODULE) + .runAOTTrainingAndAssemblyWorkflow(); + + t.setVmArgs("-Xlog:aot", "-Xlog:cds") + .modulepath(modulePath) + .appCommandLine("-m", MAIN_MODULE) + .setProductionChecker((OutputAnalyzer out) -> { + out.shouldContain("Using AOT-linked classes: true"); + }) + .productionRun(); + + t.setVmArgs("-Xlog:aot=debug", "-Xlog:cds") + .modulepath(modulePath + "/bad") + .appCommandLine( "-m", MAIN_MODULE) + .setCheckExitValue(false) + .setProductionChecker((OutputAnalyzer out) -> { + out.shouldHaveExitValue(1); + out.shouldContain("shared class paths mismatch (hint: enable -Xlog:class+path=info to diagnose the failure)"); + }) + .productionRun(); testCase("Cannot use mis-matched --add-modules"); - TestCommon.testDump(null, appClasses, - "--module-path", modulePath, - "-XX:+AOTClassLinking", - "--add-modules", MAIN_MODULE); - TestCommon.run("-Xlog:aot", - "-Xlog:cds", - "--module-path", modulePath, - "--add-modules", MAIN_MODULE, - MAIN_CLASS) - .assertNormalExit("Using AOT-linked classes: true"); - - TestCommon.run("-Xlog:cds", - "--module-path", modulePath, - "--add-modules", "java.base", - MAIN_CLASS) - .assertAbnormalExit("Mismatched values for property jdk.module.addmods", - "shared archive file has aot-linked classes. It cannot be used when archived full module graph is not used."); + t.setVmArgs("-XX:+AOTClassLinking", "--add-modules", MAIN_MODULE) + .modulepath(modulePath) + .appCommandLine(MAIN_CLASS) + .setCheckExitValue(true) + .runAOTTrainingAndAssemblyWorkflow(); + + t.setVmArgs("-Xlog:aot", "-Xlog:cds", "--add-modules", MAIN_MODULE) + .modulepath(modulePath) + .appCommandLine(MAIN_CLASS) + .setCheckExitValue(true) + .setProductionChecker((OutputAnalyzer out) -> { + out.shouldContain("Using AOT-linked classes: true"); + }) + .productionRun(); + + t.setVmArgs("-Xlog:cds", "--add-modules", "java.base") + .modulepath(modulePath) + .appCommandLine(MAIN_CLASS) + .setCheckExitValue(false) + .setProductionChecker((OutputAnalyzer out) -> { + out.shouldHaveExitValue(1); + out.shouldContain("Mismatched values for property jdk.module.addmods"); + out.shouldContain("AOT cache has aot-linked classes. It cannot be used when archived full module graph is not used."); + }) + .productionRun(); } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddExports.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddExports.java index fc3e945cf9d9..fc9766685f27 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddExports.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddExports.java @@ -44,8 +44,9 @@ public class AddExports { static final String SEP = File.separator; + // runtime/cds/appcds/jigsaw/modulepath/src static final Path SRC = Paths.get(System.getProperty("test.src")). - resolve( ".." + SEP + "jigsaw" + SEP + "modulepath" + SEP + "src"); + resolve( ".." + SEP + ".." + SEP + "jigsaw" + SEP + "modulepath" + SEP + "src"); static final Path nonModuleNeedsJdkAddExportDir = SRC.resolve("com.nomodule.needsjdkaddexport"); static final String nonModuleNeedsJdkAddExportJar = "nonModuleNeedsJdkAddExport.jar"; diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddOpens.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddOpens.java index 694e653676e4..6f209370710c 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddOpens.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddOpens.java @@ -46,8 +46,9 @@ public class AddOpens { private static final Path USER_DIR = Paths.get(CDSTestUtils.getOutputDir()); + // runtime/cds/appcds/jigsaw/modulepath/src private static final Path SRC_DIR = Paths.get(System.getProperty("test.src")). - resolve( ".." + SEP + "jigsaw" + SEP + "modulepath" + SEP + "src"); + resolve( ".." + SEP + ".." + SEP + "jigsaw" + SEP + "modulepath" + SEP + "src"); private static final Path MODS_DIR = Paths.get("mods"); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddReads.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddReads.java index 347e61fed4ca..558f0149fe47 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddReads.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddReads.java @@ -48,8 +48,9 @@ public class AddReads { private static final Path USER_DIR = Paths.get(CDSTestUtils.getOutputDir()); + // runtime/cds/appcds/jigsaw/modulepath/src private static final Path SRC_DIR = Paths.get(System.getProperty("test.src")). - resolve( ".." + SEP + "jigsaw" + SEP + "modulepath" + SEP + "src"); + resolve( ".." + SEP + ".." + SEP + "jigsaw" + SEP + "modulepath" + SEP + "src"); private static final Path MODS_DIR = Paths.get("mods"); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCompressedOopsTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCompressedOopsTest.java index 29b0f59b2380..b0d65fe48be1 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCompressedOopsTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCompressedOopsTest.java @@ -51,7 +51,7 @@ public static void main(String... args) throws Exception { { Tester t = new Tester(); t.setHeapConfig(Tester.RunMode.ASSEMBLY, true, true); - t.runAOTAssemblyWorkflow(); + t.runAOTTrainingAndAssemblyWorkflow(); t.setHeapConfig(Tester.RunMode.PRODUCTION, true, true); t.productionRun(); t.setHeapConfig(Tester.RunMode.PRODUCTION, true, false); @@ -62,7 +62,7 @@ public static void main(String... args) throws Exception { { Tester t = new Tester(); t.setHeapConfig(Tester.RunMode.ASSEMBLY, true, false); - t.runAOTAssemblyWorkflow(); + t.runAOTTrainingAndAssemblyWorkflow(); t.setHeapConfig(Tester.RunMode.PRODUCTION, true, true); t.productionRun(); t.setHeapConfig(Tester.RunMode.PRODUCTION, true, false); @@ -73,7 +73,7 @@ public static void main(String... args) throws Exception { { Tester t = new Tester(); t.setHeapConfig(Tester.RunMode.ASSEMBLY, false, false); - t.runAOTAssemblyWorkflow(); + t.runAOTTrainingAndAssemblyWorkflow(); t.setHeapConfig(Tester.RunMode.PRODUCTION, true, true); t.productionRun(); t.setHeapConfig(Tester.RunMode.PRODUCTION, true, false); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeFlags.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeFlags.java index 966bacc3b143..d7ff823734ca 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeFlags.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeFlags.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,12 +31,9 @@ * @comment Both C1 and C2 JIT compilers are required because the test verifies * compiler's runtime blobs generation. * @library /test/lib /test/setup_aot - * @build AOTCodeFlags JavacBenchApp + * @build AOTCodeFlags * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar - * JavacBenchApp - * JavacBenchApp$ClassFile - * JavacBenchApp$FileManager - * JavacBenchApp$SourceFile + * AOTCodeSimpleTestApp * @run driver/timeout=1500 AOTCodeFlags */ /** @@ -48,12 +45,9 @@ * @comment Both C1 and C2 JIT compilers are required because the test verifies * compiler's runtime blobs generation. * @library /test/lib /test/setup_aot - * @build AOTCodeFlags JavacBenchApp + * @build AOTCodeFlags * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar - * JavacBenchApp - * JavacBenchApp$ClassFile - * JavacBenchApp$FileManager - * JavacBenchApp$SourceFile + * AOTCodeSimpleTestApp * @run driver/timeout=1500 AOTCodeFlags Z */ /** @@ -65,12 +59,9 @@ * @comment Both C1 and C2 JIT compilers are required because the test verifies * compiler's runtime blobs generation. * @library /test/lib /test/setup_aot - * @build AOTCodeFlags JavacBenchApp + * @build AOTCodeFlags * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar - * JavacBenchApp - * JavacBenchApp$ClassFile - * JavacBenchApp$FileManager - * JavacBenchApp$SourceFile + * AOTCodeSimpleTestApp * @run driver/timeout=1500 AOTCodeFlags Shenandoah */ /** @@ -82,12 +73,9 @@ * @comment Both C1 and C2 JIT compilers are required because the test verifies * compiler's runtime blobs generation. * @library /test/lib /test/setup_aot - * @build AOTCodeFlags JavacBenchApp + * @build AOTCodeFlags * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar - * JavacBenchApp - * JavacBenchApp$ClassFile - * JavacBenchApp$FileManager - * JavacBenchApp$SourceFile + * AOTCodeSimpleTestApp * @run driver/timeout=1500 AOTCodeFlags Parallel */ @@ -98,6 +86,7 @@ import jdk.test.lib.process.OutputAnalyzer; public class AOTCodeFlags { + private static String appName = AOTCodeSimpleTestApp.class.getName(); private static String gcName = null; public static void main(String... args) throws Exception { Tester t = new Tester(args.length == 0 ? null : args[0]); @@ -171,6 +160,10 @@ public String[] vmArgs(RunMode runMode) { args.addAll(List.of("-Xlog:aot+codecache+init=debug", "-Xlog:aot+codecache+exit=debug", "-Xlog:aot+codecache+stubs=debug")); + + // Ensure compilations are finished before the JVM exits. + args.add("-Xbatch"); + switch (runMode) { case RunMode.ASSEMBLY: args.addAll(getVMArgsForTestMode(aMode)); @@ -186,7 +179,7 @@ public String[] vmArgs(RunMode runMode) { @Override public String[] appCommandLine(RunMode runMode) { - return new String[] { "JavacBenchApp", "10" }; + return new String[] { appName }; } @Override diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeSimpleTestApp.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeSimpleTestApp.java new file mode 100644 index 000000000000..5debc6ab481c --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeSimpleTestApp.java @@ -0,0 +1,46 @@ +/* + * 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. + * + */ + +// Run long enough and with enough iterations to ensure C1 and C2 compilations. +// Note: run with -Xbatch to ensure compilations are finished before the JVM exits. +public class AOTCodeSimpleTestApp { + public static volatile int counter; + + public static void main(String args[]) { + long started = System.currentTimeMillis(); + + while (System.currentTimeMillis() - started < 150) { + outer(); + } + } + static void outer() { + for (int i = 0; i < 50 * 1000; i++) { + inner(); + } + } + + static void inner() { + counter++; + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/AOTLinkedLambdas.java b/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/AOTLinkedLambdas.java index ecfaa2659234..08c9191a33c8 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/AOTLinkedLambdas.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/AOTLinkedLambdas.java @@ -43,48 +43,43 @@ import static java.util.stream.Collectors.*; import jdk.test.lib.cds.CDSOptions; import jdk.test.lib.cds.CDSTestUtils; +import jdk.test.lib.cds.SimpleCDSAppTester; import jdk.test.lib.helpers.ClassFileInstaller; import jdk.test.lib.process.OutputAnalyzer; public class AOTLinkedLambdas { - static final String classList = "AOTLinkedLambdas.classlist"; static final String appJar = ClassFileInstaller.getJarPath("app.jar"); static final String mainClass = AOTLinkedLambdasApp.class.getName(); public static void main(String[] args) throws Exception { - CDSTestUtils.dumpClassList(classList, "-cp", appJar, mainClass) - .assertNormalExit(output -> { - output.shouldContain("Hello AOTLinkedLambdasApp"); - }); - - CDSOptions opts = (new CDSOptions()) - .addPrefix("-XX:ExtraSharedClassListFile=" + classList, + SimpleCDSAppTester t = SimpleCDSAppTester.of("AOTLinkedLambdas") + .classpath(appJar) + .addVmArgs("-esa", // see JDK-8340836 "-XX:+AOTClassLinking", "-Xlog:aot+resolve=trace", "-Xlog:aot+class=debug", - "-Xlog:cds+class=debug", - "-cp", appJar); - - OutputAnalyzer dumpOut = CDSTestUtils.createArchiveAndCheck(opts); - dumpOut.shouldContain("Can aot-resolve Lambda proxy of interface type IA"); - dumpOut.shouldContain("Can aot-resolve Lambda proxy of interface type IB"); - dumpOut.shouldContain("Cannot aot-resolve Lambda proxy of interface type IC"); - dumpOut.shouldContain("Can aot-resolve Lambda proxy of interface type ID2"); - dumpOut.shouldContain("Cannot aot-resolve Lambda proxy of interface type IE2"); // unsupported = IE1 - dumpOut.shouldContain("Cannot aot-resolve Lambda proxy of interface type IF2"); - dumpOut.shouldContain("Cannot aot-resolve Lambda proxy of interface type IG2"); - dumpOut.shouldContain("Cannot aot-resolve Lambda proxy of interface type IH3"); // unsupported = IH1 - - CDSOptions runOpts = (new CDSOptions()) - .setUseVersion(false) - .addPrefix("-Xlog:cds", - "-esa", // see JDK-8340836 - "-cp", appJar) - .addSuffix(mainClass); - - CDSTestUtils.run(runOpts) - .assertNormalExit("Hello AOTLinkedLambdasApp", - "hello, world"); + "-Xlog:cds+class=debug") + .appCommandLine(mainClass) + .setTrainingChecker((OutputAnalyzer output) -> { + output.shouldContain("Hello AOTLinkedLambdasApp"); + }) + .setAssemblyChecker((OutputAnalyzer dumpOut) -> { + dumpOut.shouldContain("Can aot-resolve Lambda proxy of interface type IA"); + dumpOut.shouldContain("Can aot-resolve Lambda proxy of interface type IB"); + dumpOut.shouldContain("Cannot aot-resolve Lambda proxy of interface type IC"); + dumpOut.shouldContain("Can aot-resolve Lambda proxy of interface type ID2"); + dumpOut.shouldContain("Cannot aot-resolve Lambda proxy of interface type IE2"); // unsupported = IE1 + dumpOut.shouldContain("Cannot aot-resolve Lambda proxy of interface type IF2"); + dumpOut.shouldContain("Cannot aot-resolve Lambda proxy of interface type IG2"); + dumpOut.shouldContain("Cannot aot-resolve Lambda proxy of interface type IH3"); // unsupported = IH1 + }) + .runAOTTrainingAndAssemblyWorkflow(); + + t.setProductionChecker((OutputAnalyzer out) -> { + out.shouldContain("Hello AOTLinkedLambdasApp"); + out.shouldContain("hello, world"); + }) + .productionRun(); } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/AOTLinkedVarHandles.java b/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/AOTLinkedVarHandles.java index 1089e849a905..78e881a99753 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/AOTLinkedVarHandles.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/AOTLinkedVarHandles.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,42 +39,38 @@ import java.lang.invoke.VarHandle; import jdk.test.lib.cds.CDSOptions; import jdk.test.lib.cds.CDSTestUtils; +import jdk.test.lib.cds.SimpleCDSAppTester; import jdk.test.lib.helpers.ClassFileInstaller; import jdk.test.lib.process.OutputAnalyzer; public class AOTLinkedVarHandles { - static final String classList = "AOTLinkedVarHandles.classlist"; static final String appJar = ClassFileInstaller.getJarPath("app.jar"); static final String mainClass = AOTLinkedVarHandlesApp.class.getName(); public static void main(String[] args) throws Exception { - CDSTestUtils.dumpClassList(classList, "-cp", appJar, mainClass) - .assertNormalExit(output -> { - output.shouldContain("Hello AOTLinkedVarHandlesApp"); - }); - - CDSOptions opts = (new CDSOptions()) - .addPrefix("-XX:ExtraSharedClassListFile=" + classList, + SimpleCDSAppTester t = SimpleCDSAppTester.of("AOTLinkedVarHandles") + .classpath(appJar) + .appCommandLine(mainClass) + .addVmArgs("-esa", "-XX:+AOTClassLinking", "-Xlog:aot+resolve=trace", - "-Xlog:cds+class=debug", - "-cp", appJar); - - String s = "archived method CP entry.* AOTLinkedVarHandlesApp "; - OutputAnalyzer dumpOut = CDSTestUtils.createArchiveAndCheck(opts); - dumpOut.shouldMatch(s + "java/lang/invoke/VarHandle.compareAndExchangeAcquire:\\(\\[DIDI\\)D =>"); - dumpOut.shouldMatch(s + "java/lang/invoke/VarHandle.get:\\(\\[DI\\)D => "); - dumpOut.shouldNotContain("rejected .* CP entry.*"); - - CDSOptions runOpts = (new CDSOptions()) - .setUseVersion(false) - .addPrefix("-Xlog:cds", - "-esa", - "-cp", appJar) - .addSuffix(mainClass); + "-Xlog:cds+class=debug") + .setTrainingChecker((OutputAnalyzer output) -> { + output.shouldContain("Hello AOTLinkedVarHandlesApp"); + }) + .setAssemblyChecker((OutputAnalyzer output) -> { + String s = "archived method CP entry.* AOTLinkedVarHandlesApp "; + output.shouldMatch(s + "java/lang/invoke/VarHandle.compareAndExchangeAcquire:\\(\\[DIDI\\)D =>"); + output.shouldMatch(s + "java/lang/invoke/VarHandle.get:\\(\\[DI\\)D => "); + output.shouldNotContain("rejected .* CP entry.*"); + }) + .runAOTTrainingAndAssemblyWorkflow(); - CDSTestUtils.run(runOpts) - .assertNormalExit("Hello AOTLinkedVarHandlesApp"); + t.setVmArgs("-Xlog:cds,aot", "-esa") + .setProductionChecker((OutputAnalyzer output) -> { + output.shouldContain("Hello AOTLinkedVarHandlesApp"); + }) + .productionRun(); } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/test-classes/valueclasses/BytePair.java b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/valueclasses/BytePair.java new file mode 100644 index 000000000000..b0ecef96851c --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/valueclasses/BytePair.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package valueclasses; + +public value class BytePair implements Comparable { + static { + ValueClassHelper.clinit_called_for_BytePair = true; + } + byte b0, b1; + + public String toString() { + return "(" + b0 + ", " + b1 + ")"; + } + + public int compareTo(BytePair o) { + int n = b0 - o.b0; + if (n != n) { + return n; + } else { + return (b1 - o.b1); + } + } + + public BytePair(int b0, int b1) { + this.b0 = (byte)b0; + this.b1 = (byte)b1; + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/test-classes/valueclasses/BytePairWrapper.java b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/valueclasses/BytePairWrapper.java new file mode 100644 index 000000000000..fdd342a56ba2 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/valueclasses/BytePairWrapper.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package valueclasses; + +import jdk.internal.vm.annotation.NullRestricted; + +public value class BytePairWrapper implements Comparable { + static { + ValueClassHelper.clinit_called_for_BytePairWrapper = true; + } + @NullRestricted + BytePair bp; + + public String toString() { + return "Wrapping {" + bp.toString() + "}"; + } + + public int compareTo(BytePairWrapper other) { + return bp.compareTo(other.bp); + } + + public BytePairWrapper(int b0, int b1) { + bp = new BytePair(b0, b1); + super(); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/test-classes/valueclasses/BytePairWrapperWrapper.java b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/valueclasses/BytePairWrapperWrapper.java new file mode 100644 index 000000000000..9769d7a72fcd --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/valueclasses/BytePairWrapperWrapper.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package valueclasses; + +import jdk.internal.vm.annotation.NullRestricted; + +public value class BytePairWrapperWrapper implements Comparable { + static { + ValueClassHelper.clinit_called_for_BytePairWrapperWrapper = true; + } + @NullRestricted + BytePairWrapper bpw; + + public String toString() { + return "Wrapping {" + bpw.toString() + "}"; + } + + public int compareTo(BytePairWrapperWrapper other) { + return bpw.compareTo(other.bpw); + } + + public BytePairWrapperWrapper(int b0, int b1) { + bpw = new BytePairWrapper(b0, b1); + super(); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/test-classes/valueclasses/CharPair.java b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/valueclasses/CharPair.java new file mode 100644 index 000000000000..b87df18d4e04 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/valueclasses/CharPair.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package valueclasses; + +public value class CharPair implements Comparable { + static { + ValueClassHelper.clinit_called_for_CharPair = true; + } + char c0, c1; + + public String toString() { + return "(" + c0 + ", " + c1 + ")"; + } + + public int compareTo(CharPair o) { + int n = c0 - o.c0; + if (n != n) { + return n; + } else { + return (c1 - o.c1); + } + } + + public CharPair(char c0, char c1) { + this.c0 = c0; + this.c1 = c1; + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/test-classes/valueclasses/IntegerWrapper.java b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/valueclasses/IntegerWrapper.java new file mode 100644 index 000000000000..942a821fa335 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/valueclasses/IntegerWrapper.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package valueclasses; + +public value class IntegerWrapper implements Comparable { + static { + ValueClassHelper.clinit_called_for_IntegerWrapper = true; + } + Integer i; + + public String toString() { + return i.toString(); + } + + public int compareTo(IntegerWrapper o) { + return i - o.i; + } + + public IntegerWrapper(int i) { + this.i = new Integer(i); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/test-classes/valueclasses/ShortPair.java b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/valueclasses/ShortPair.java new file mode 100644 index 000000000000..42c010d4fc39 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/valueclasses/ShortPair.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package valueclasses; + +public value class ShortPair implements Comparable { + static { + ValueClassHelper.clinit_called_for_ShortPair = true; + } + short s0, s1; + + public String toString() { + return "(" + s0 + ", " + s1 + ")"; + } + + public int compareTo(ShortPair o) { + int n = s0 - o.s0; + if (n != n) { + return n; + } else { + return (s1 - o.s1); + } + } + + public ShortPair(int s0, int s1) { + this.s0 = (short)s0; + this.s1 = (short)s1; + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/test-classes/valueclasses/ShortPairWrapper.java b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/valueclasses/ShortPairWrapper.java new file mode 100644 index 000000000000..2652c0c6ecd3 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/valueclasses/ShortPairWrapper.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package valueclasses; + +import jdk.internal.vm.annotation.NullRestricted; + +public value class ShortPairWrapper implements Comparable { + static { + ValueClassHelper.clinit_called_for_ShortPairWrapper = true; + } + @NullRestricted + ShortPair sp; + + public String toString() { + return "ShortPair: " + sp.toString(); + } + + public int compareTo(ShortPairWrapper other) { + return sp.compareTo(other.sp); + } + + public ShortPairWrapper(int s0, int s1) { + sp = new ShortPair((short)s0, (short)s1); + super(); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/test-classes/valueclasses/ValueClassHelper.java b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/valueclasses/ValueClassHelper.java new file mode 100644 index 000000000000..c442caefba06 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/valueclasses/ValueClassHelper.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package valueclasses; + +import jdk.internal.value.ValueClass; +import jdk.internal.vm.annotation.NullRestricted; + +public class ValueClassHelper { + static boolean clinit_called_for_BytePair; + static boolean clinit_called_for_BytePairWrapper; + static boolean clinit_called_for_BytePairWrapperWrapper; + static boolean clinit_called_for_CharPair; + static boolean clinit_called_for_IntegerWrapper; + static boolean clinit_called_for_ShortPair; + static boolean clinit_called_for_ShortPairWrapper; + + public static void assertAOTInited_BytePair() { + new BytePair(1, 2); + if (clinit_called_for_BytePair == true) { + throw new RuntimeException("BytePair. must not execute, as this clas should be AOT-initialized"); + } + } + + public static void assertAOTInited_BytePairWrapper() { + new BytePairWrapper(1, 2); + if (clinit_called_for_BytePairWrapper == true) { + throw new RuntimeException("BytePairWrapper. must not execute, as this clas should be AOT-initialized"); + } + } + + public static void assertAOTInited_BytePairWrapperWrapper() { + new BytePairWrapperWrapper(1, 2); + if (clinit_called_for_BytePairWrapperWrapper == true) { + throw new RuntimeException("BytePairWrapperWrapper. must not execute, as this clas should be AOT-initialized"); + } + } + + public static void assertAOTInited_CharPair() { + new CharPair('a', 'b'); + if (clinit_called_for_CharPair == true) { + throw new RuntimeException("CharPair. must not execute, as this clas should be AOT-initialized"); + } + } + + public static void assertAOTInited_ShortPair() { + new ShortPair((short)0, (short)1); + if (clinit_called_for_ShortPair == true) { + throw new RuntimeException("ShortPair. must not execute, as this clas should be AOT-initialized"); + } + } + + public static void assertAOTInited_IntegerWrapper() { + new IntegerWrapper(0); + if (clinit_called_for_IntegerWrapper == true) { + throw new RuntimeException("IntegerWrapper. must not execute, as this clas should be AOT-initialized"); + } + } + + public static void assertAOTInited_ShortPairWrapper() { + new ShortPairWrapper(0, 1); + if (clinit_called_for_ShortPairWrapper == true) { + throw new RuntimeException("ShortPairWrapper. must not execute, as this clas should be AOT-initialized"); + } + } + +} diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/NullPointerException/NullPointerExceptionTest.java b/test/hotspot/jtreg/runtime/exceptionMsgs/NullPointerException/NullPointerExceptionTest.java index 399cda958db0..01b74a1365dd 100644 --- a/test/hotspot/jtreg/runtime/exceptionMsgs/NullPointerException/NullPointerExceptionTest.java +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/NullPointerException/NullPointerExceptionTest.java @@ -111,8 +111,15 @@ public static void checkMessage(Throwable t, String expression, } if (obtainedMsg != expectedMsg && // E.g. both are null. !obtainedMsg.equals(expectedMsg)) { - System.out.println("expected msg: " + expectedMsg); - Asserts.assertEquals(expectedMsg, obtainedMsg); + try { + System.out.println("expected msg: " + expectedMsg); + Asserts.assertEquals(expectedMsg, obtainedMsg); + } catch (RuntimeException rte) { + // Due to lack of information about null restricted fields in Xcomp, we may also guess + // that that was the reason for the NPE. + Asserts.assertTrue(obtainedMsg.contains(expectedMsg) && + obtainedMsg.contains("is a null restricted field and there's an attempt to store null in it")); + } } System.out.println("\n----"); } diff --git a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/FlatArrayDoesNotInitialize.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/FlatArrayDoesNotInitialize.java new file mode 100644 index 000000000000..50138c433b54 --- /dev/null +++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/FlatArrayDoesNotInitialize.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * @test + * @summary Test that creating an array of T does not initialize class T + * @enablePreview + * @compile FlatArrayDoesNotInitialize.java + * @run main/othervm runtime.valhalla.inlinetypes.FlatArrayDoesNotInitialize + */ +package runtime.valhalla.inlinetypes; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; + + +public class FlatArrayDoesNotInitialize { + static boolean initialized; + + static value class MyValue { + static { + initialized = true; + } + } + + public static void main(String[] args) { + MyValue[] array = new MyValue[1]; + VarHandle handle = MethodHandles.arrayElementVarHandle(MyValue[].class); + if (initialized) { + throw new AssertionError("Should not be initialized"); + } + if (!(boolean) handle.compareAndSet(array, 0, null, null)) { + throw new AssertionError("CAS failed"); + } + if (initialized) { + throw new AssertionError("Should not be initialized"); + } + } +} + diff --git a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/NPEInPreviewTest.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/NPEInPreviewTest.java index e1f7bfe9796e..0488efed4a9c 100644 --- a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/NPEInPreviewTest.java +++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/NPEInPreviewTest.java @@ -29,6 +29,7 @@ * java.base/jdk.internal.vm.annotation * java.base/jdk.internal.misc * @library /test/lib + * @requires vm.flagless * @enablePreview * @compile -g NPEInPreviewTest.java * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+ShowCodeDetailsInExceptionMessages NPEInPreviewTest interpreter @@ -100,6 +101,9 @@ static void testNullRestrictedFieldStoredInNullError() { static void testActualNullFieldError() { String expectedMessage = "Cannot assign field \"nullVal\" because \"self\" is null"; + // In C1 Xcomp mode, the field or klass isn't resolved, so no idea which this is. + String c1ExpectedMessage = "Cannot assign field \"nullVal\" because \"self\" is null or \"nullVal\" is a null " + + "restricted field and there's an attempt to store null in it"; try { NPEInPreviewTest self = null; @@ -107,7 +111,11 @@ static void testActualNullFieldError() { } catch (NullPointerException npe) { String message = npe.getMessage(); System.out.println("*** " + message); - Asserts.assertEquals(expectedMessage, message); + if (c1Mode) { + Asserts.assertEquals(c1ExpectedMessage, message); + } else { + Asserts.assertEquals(expectedMessage, message); + } } } diff --git a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/ContendedValueClassInheritanceTest.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/ContendedValueClassInheritanceTest.java new file mode 100644 index 000000000000..4f56b9c75131 --- /dev/null +++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/ContendedValueClassInheritanceTest.java @@ -0,0 +1,167 @@ +/* + * 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 8389452 + * @summary Test that contended annotations are ignored on value class layouts + * @library /test/lib + * @requires vm.flagless + * @modules java.base/jdk.internal.vm.annotation + * @enablePreview + * @compile FieldLayoutAnalyzer.java ContendedValueClassInheritanceTest.java + * @run main runtime.valhalla.inlinetypes.field_layout.ContendedValueClassInheritanceTest + */ + +package runtime.valhalla.inlinetypes.field_layout; + +import jdk.internal.vm.annotation.Contended; +import jdk.test.lib.Asserts; +import jdk.test.lib.process.ProcessTools; + +public class ContendedValueClassInheritanceTest { + private static final int CONTENDED_PADDING_WIDTH = 128; + + static abstract value class Base { + long base = 0; + } + + @Contended + static abstract value class ContendedBase { + long base = 0; + } + + static value class Value extends ContendedBase { + int value = 0; + } + + @Contended + static value class ContendedValue extends Base { + int value = 0; + } + + static class Identity extends ContendedBase { + int value; + } + + @Contended + static class ContendedIdentity extends Base { + int value; + } + + static class TestRunner { + public static void main(String[] args) { + new Value(); + new ContendedValue(); + new Identity(); + new ContendedIdentity(); + } + } + + private static FieldLayoutAnalyzer.ClassLayout getLayout(FieldLayoutAnalyzer fla, Class type) { + String className = type.getName().replace('.', '/'); + FieldLayoutAnalyzer.ClassLayout layout = fla.getClassLayoutFromName(className); + Asserts.assertNotNull(layout, "Missing layout for " + type.getName()); + return layout; + } + + private static int contendedPaddingBlocks(FieldLayoutAnalyzer.ClassLayout layout) { + int count = 0; + for (FieldLayoutAnalyzer.FieldBlock block : layout.nonStaticFields) { + if (block.type() == FieldLayoutAnalyzer.BlockType.PADDING && block.size() == CONTENDED_PADDING_WIDTH) { + count++; + } + } + return count; + } + + private static void assertSameFieldLayout(FieldLayoutAnalyzer.ClassLayout first, + FieldLayoutAnalyzer.ClassLayout second, + String fieldName) { + FieldLayoutAnalyzer.FieldBlock firstField = first.getFieldFromName(fieldName, false); + FieldLayoutAnalyzer.FieldBlock secondField = second.getFieldFromName(fieldName, false); + Asserts.assertEquals(firstField.offset(), secondField.offset(), fieldName + " offset"); + Asserts.assertEquals(firstField.size(), secondField.size(), fieldName + " size"); + Asserts.assertEquals(firstField.alignment(), secondField.alignment(), fieldName + " alignment"); + } + + private static void checkLayouts(FieldLayoutAnalyzer fla) { + // Contended annotations should not affect value base classes. + var base = getLayout(fla, Base.class); + var contendedBase = getLayout(fla, ContendedBase.class); + + Asserts.assertEquals(base.instanceSize, contendedBase.instanceSize); + assertSameFieldLayout(base, contendedBase, "base"); + Asserts.assertEquals(contendedPaddingBlocks(contendedBase), 0); + + // Neither inherited nor declared contended annotations should affect value subclasses. + var value = getLayout(fla, Value.class); + var contendedValue = getLayout(fla, ContendedValue.class); + + Asserts.assertEquals(value.instanceSize, contendedValue.instanceSize); + Asserts.assertEquals(value.payloadSize, contendedValue.payloadSize); + + assertSameFieldLayout(value, contendedValue, "base"); + assertSameFieldLayout(value, contendedValue, "value"); + + Asserts.assertEquals(contendedPaddingBlocks(value), 0); + Asserts.assertEquals(contendedPaddingBlocks(contendedValue), 0); + + // Only the contended identity subclass gets leading and trailing padding. + var identity = getLayout(fla, Identity.class); + var contendedIdentity = getLayout(fla, ContendedIdentity.class); + var identityField = identity.getFieldFromName("value", false); + var contendedIdentityField = contendedIdentity.getFieldFromName("value", false); + + assertSameFieldLayout(identity, contendedIdentity, "base"); + + Asserts.assertEquals(contendedIdentityField.offset(), identityField.offset() + CONTENDED_PADDING_WIDTH); + Asserts.assertEquals(contendedIdentity.instanceSize, identity.instanceSize + 2 * CONTENDED_PADDING_WIDTH); + + Asserts.assertEquals(contendedPaddingBlocks(identity), 0); + Asserts.assertEquals(contendedPaddingBlocks(contendedIdentity), 2); + } + + public static void main(String[] args) throws Exception { + var out = ProcessTools.executeTestJava( + "--enable-preview", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+PrintFieldLayout", + "-XX:-RestrictContended", + "-XX:ContendedPaddingWidth=" + CONTENDED_PADDING_WIDTH, + "-Xshare:off", + "-cp", System.getProperty("java.class.path"), + TestRunner.class.getName()); + out.shouldHaveExitValue(0); + + FieldLayoutAnalyzer.LogOutput log = new FieldLayoutAnalyzer.LogOutput(out.asLines()); + FieldLayoutAnalyzer fla = FieldLayoutAnalyzer.createFieldLayoutAnalyzer(log); + try { + checkLayouts(fla); + fla.check(); + } catch (Throwable t) { + System.out.print(out.getOutput()); + throw t; + } + } +} diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdb/hidden_class/hc001/hc001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdb/hidden_class/hc001/hc001.java index a7c74e28aaec..9a818d01832e 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdb/hidden_class/hc001/hc001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdb/hidden_class/hc001/hc001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -88,33 +88,33 @@ static void throwFailure(String msg) throws Failure { private String runPrologue() { String[] reply = null; - log.println("\n### Debugger: runPrologue"); + log.display("\n### Debugger: runPrologue"); // uncomment this line to enable verbose output from jdb // log.enableVerbose(true); // run jdb command "stop in" jdb.setBreakpointInMethod(EMPTY_METHOD_NAME); - log.println("\nDebugger: breakpoint is set at:\n\t" + EMPTY_METHOD_NAME); + log.display("\nDebugger: breakpoint is set at:\n\t" + EMPTY_METHOD_NAME); // run jdb command "cont" reply = jdb.receiveReplyFor(JdbCommand.cont); if (!jdb.isAtBreakpoint(reply, EMPTY_METHOD_NAME)) { throwFailure("Debugger: Missed breakpoint at:\n\t" + EMPTY_METHOD_NAME); } - log.println("\nDebugger: breakpoint is hit at:\n\t" + EMPTY_METHOD_NAME); + log.display("\nDebugger: breakpoint is hit at:\n\t" + EMPTY_METHOD_NAME); // run jdb command "eval" for hidden class field HC_NAME_FIELD reply = jdb.receiveReplyFor(JdbCommand.eval + HC_NAME_FIELD); int beg = reply[0].indexOf('"') + 1; int end = reply[0].lastIndexOf('"'); if (end == -1 || beg > end) { - log.println("\nDebugger: the jdb command:\n\t" + JdbCommand.eval + HC_NAME_FIELD); - log.println("\treturned bad reply:\n\t" + reply[0]); + log.display("\nDebugger: the jdb command:\n\t" + JdbCommand.eval + HC_NAME_FIELD); + log.display("\treturned bad reply:\n\t" + reply[0]); throwFailure("Debugger: failed to evaluate debuggee field:\n\t" + HC_NAME_FIELD); } String hiddenClassName = reply[0].substring(beg, end); // we know the hidden class name now - log.println("\nDebugger: jdb command eval returned hidden class name:\n\t" + hiddenClassName); + log.display("\nDebugger: jdb command eval returned hidden class name:\n\t" + hiddenClassName); return hiddenClassName; } @@ -123,21 +123,21 @@ private String runPrologue() { private void testClassCommands(String hcName) { String[] reply = null; - log.println("\n### Debugger: testClassCommands"); + log.display("\n### Debugger: testClassCommands"); // run jdb command "classes" reply = jdb.receiveReplyFor(JdbCommand.classes); if (!checkPattern(reply, hcName)) { throwFailure("Debugger: expected jdb command classes to list hidden class:\n\t" + hcName); } - log.println("\nDebugger: found matched class in jdb command classes reply:\n\t" + hcName); + log.display("\nDebugger: found matched class in jdb command classes reply:\n\t" + hcName); // run jdb command "class" for hidden class reply = jdb.receiveReplyFor(JdbCommand._class + hcName); if (!checkPattern(reply, hcName)) { throwFailure("Debugger: expected hiddenclass name in jdb command class reply: " + hcName); } - log.println("\nDebugger: found matched class in jdb command class reply:\n\t" + hcName); + log.display("\nDebugger: found matched class in jdb command class reply:\n\t" + hcName); } /* Transition the debuggee's execution to the hidden class method start. */ @@ -145,22 +145,22 @@ private void stopInHiddenClassMethod(String hcName) { String hcMethodName = hcName + "." + HC_METHOD_NAME; String[] reply = null; - log.println("\n### Debugger: stopInHiddenClassMethod"); + log.display("\n### Debugger: stopInHiddenClassMethod"); // set a breakpoint in hidden class method hcMethodName() jdb.setBreakpointInMethod(hcMethodName); - log.println("\nDebugger: breakpoint is set at:\n\t" + hcMethodName); + log.display("\nDebugger: breakpoint is set at:\n\t" + hcMethodName); // run jdb command "clear": should list breakpoint in hcMethodName reply = jdb.receiveReplyFor(JdbCommand.clear); if (!checkPattern(reply, hcMethodName)) { throwFailure("Debugger: expected jdb clear command to list breakpoint: " + hcMethodName); } - log.println("\nDebugger: jdb command clear lists breakpoint at:\n\t" + hcMethodName); + log.display("\nDebugger: jdb command clear lists breakpoint at:\n\t" + hcMethodName); // run jdb command "cont" jdb.receiveReplyFor(JdbCommand.cont); - log.println("\nDebugger: executed jdb command cont"); + log.display("\nDebugger: executed jdb command cont"); } /* Test the jdb commands "up" and "where" for hidden class. */ @@ -168,25 +168,25 @@ private void testUpWhereCommands(String hcName) { String hcMethodName = hcName + "." + HC_METHOD_NAME; String[] reply = null; - log.println("\n### Debugger: testUpWhereCommands"); + log.display("\n### Debugger: testUpWhereCommands"); // run jdb command "where": should list hcMethodName frame reply = jdb.receiveReplyFor(JdbCommand.where); if (!checkPattern(reply, hcMethodName)) { throwFailure("Debugger: jdb command where does not show expected frame: " + hcMethodName); } - log.println("\nDebugger: jdb command where showed expected frame:\n\t" + hcMethodName); + log.display("\nDebugger: jdb command where showed expected frame:\n\t" + hcMethodName); // run jdb command "up" jdb.receiveReplyFor(JdbCommand.up); - log.println("\nDebugger: executed jdb command up"); + log.display("\nDebugger: executed jdb command up"); // run jdb command "where": should not list hcMethodName frame reply = jdb.receiveReplyFor(JdbCommand.where); if (checkPattern(reply, hcMethodName)) { throwFailure("Debugger: jdb command where showed unexpected frame: " + hcMethodName); } - log.println("\nDebugger: jdb command where does not show unexpected frame:\n\t" + hcMethodName); + log.display("\nDebugger: jdb command where does not show unexpected frame:\n\t" + hcMethodName); } /* Test the jdb commands "down" and "where" for hidden class. */ @@ -194,39 +194,39 @@ private void testDownWhereCommands(String hcName) { String hcMethodName = hcName + "." + HC_METHOD_NAME; String[] reply = null; - log.println("\n### Debugger: testDownWhereCommands"); + log.display("\n### Debugger: testDownWhereCommands"); // run jdb command "down" jdb.receiveReplyFor(JdbCommand.down); - log.println("\nDebugger: executed jdb command down"); + log.display("\nDebugger: executed jdb command down"); // run jdb command "where": should list hcMethodName frame again reply = jdb.receiveReplyFor(JdbCommand.where); if (!checkPattern(reply, hcMethodName)) { throwFailure("Debugger: jdb command where does not show expected frame: " + hcMethodName); } - log.println("\nDebugger: jdb command where showed expected frame:\n\t" + hcMethodName); + log.display("\nDebugger: jdb command where showed expected frame:\n\t" + hcMethodName); } /* Test the jdb commands "fields" and "methods" for hidden class. */ private void testFieldsMethods(String hcName) { String[] reply = null; - log.println("\n### Debugger: testFieldsMethods"); + log.display("\n### Debugger: testFieldsMethods"); // run jdb command "methods" for hidden class reply = jdb.receiveReplyFor(JdbCommand.methods + hcName); if (!checkPattern(reply, hcName)) { throwFailure("Debugger: no expected hidden class name in its methods: " + hcName); } - log.println("\nDebugger: jdb command \"methods\" showed expected method:\n\t" + HC_METHOD_NAME); + log.display("\nDebugger: jdb command \"methods\" showed expected method:\n\t" + HC_METHOD_NAME); // run jdb command "fields" for hidden class reply = jdb.receiveReplyFor(JdbCommand.fields + hcName); if (!checkPattern(reply, HC_FIELD_NAME)) { throwFailure("Debugger: no expected hidden class field in its fields: " + HC_FIELD_NAME); } - log.println("\nDebugger: jdb command \"fields\" showed expected field:\n\t" + HC_FIELD_NAME); + log.display("\nDebugger: jdb command \"fields\" showed expected field:\n\t" + HC_FIELD_NAME); } /* Test the jdb commands "watch" and "unwatch" for hidden class. */ @@ -234,14 +234,14 @@ private void testWatchCommands(String hcName) { String hcFieldName = hcName + "." + HC_FIELD_NAME; String[] reply = null; - log.println("\n### Debugger: testWatchCommands"); + log.display("\n### Debugger: testWatchCommands"); // run jdb command "watch" for hidden class field HC_FIELD_NAME reply = jdb.receiveReplyFor(JdbCommand.watch + hcFieldName); if (!checkPattern(reply, HC_FIELD_NAME)) { throwFailure("Debugger: was not able to set watch point: " + hcFieldName); } - log.println("\nDebugger: jdb command \"watch\" added expected field to watch:\n\t" + hcFieldName); + log.display("\nDebugger: jdb command \"watch\" added expected field to watch:\n\t" + hcFieldName); // run jdb command "cont" jdb.receiveReplyFor(JdbCommand.cont); @@ -252,7 +252,7 @@ private void testWatchCommands(String hcName) { if (!checkPattern(reply, HC_FIELD_NAME)) { throwFailure("Debugger: expect field name in unwatch reply: " + hcFieldName); } - log.println("\nDebugger: jdb command \"unwatch\" removed expected field from watch:\n\t" + hcFieldName); + log.display("\nDebugger: jdb command \"unwatch\" removed expected field from watch:\n\t" + hcFieldName); } /* Test the jdb commands "eval", "print" and "dump" for hidden class. */ @@ -260,28 +260,28 @@ private void testEvalCommands(String hcName) { String hcFieldName = hcName + "." + HC_FIELD_NAME; String[] reply = null; - log.println("\n### Debugger: testEvalCommands"); + log.display("\n### Debugger: testEvalCommands"); // run jdb command "eval" for hidden class field HC_FIELD_NAME reply = jdb.receiveReplyFor(JdbCommand.eval + hcFieldName); if (!checkPattern(reply, hcFieldName)) { throwFailure("Debugger: expected field name in jdb command eval field reply: " + hcFieldName); } - log.println("\nDebugger: jdb command \"eval\" showed expected hidden class field name:\n\t" + hcFieldName); + log.display("\nDebugger: jdb command \"eval\" showed expected hidden class field name:\n\t" + hcFieldName); // run jdb command "print" for hidden class field HC_FIELD_NAME reply = jdb.receiveReplyFor(JdbCommand.print + hcFieldName); if (!checkPattern(reply, hcFieldName)) { throwFailure("Debugger: expected field name in jdb command print field reply: " + hcFieldName); } - log.println("\nDebugger: jdb command \"print\" showed expected hidden class field name:\n\t" + hcFieldName); + log.display("\nDebugger: jdb command \"print\" showed expected hidden class field name:\n\t" + hcFieldName); // execute jdb command "dump" for hidden class field HC_FIELD_NAME reply = jdb.receiveReplyFor(JdbCommand.dump + hcFieldName); if (!checkPattern(reply, hcFieldName)) { throwFailure("Debugger: expected field name in jdb command dump field reply: " + hcFieldName); } - log.println("\nDebugger: jdb command \"dump\" showed expected hidden class field name:\n\t" + hcFieldName); + log.display("\nDebugger: jdb command \"dump\" showed expected hidden class field name:\n\t" + hcFieldName); } /* Test the jdb command "watch" with an invalid class name. */ @@ -295,7 +295,7 @@ private void testInvWatchCommand(String hcName) { if (checkPattern(reply, "Deferring watch modification")) { throwFailure(MsgBase + " must not set deferred watch point"); } - log.println(MsgBase + " did not set deferred watch point"); + log.display(MsgBase + " did not set deferred watch point"); } /* Test the jdb command "eval" with an invalid class name. */ @@ -309,7 +309,7 @@ private void testInvEvalCommand(String hcName) { if (!checkPattern(reply, "ParseException")) { throwFailure(MsgBase + " must be rejected with ParseException"); } - log.println(MsgBase + " was rejected with ParseException"); + log.display(MsgBase + " was rejected with ParseException"); } /* Test the jdb commands "watch" and "eval" with various invalid class names. */ @@ -321,7 +321,7 @@ private void testInvalidCommands() { "xx.yyy.zzz/" }; - log.println("\n### Debugger: testInvalidCommands"); + log.display("\n### Debugger: testInvalidCommands"); // run jdb commands "watch" and "eval" with invalid class names for (int idx = 0; idx < invClassNames.length; idx++) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdb/hidden_class/hc001/hc001a.java b/test/hotspot/jtreg/vmTestbase/nsk/jdb/hidden_class/hc001/hc001a.java index 7946b5e65379..79a9599ac164 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdb/hidden_class/hc001/hc001a.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdb/hidden_class/hc001/hc001a.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 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 diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdb/options/connect/connect001/connect001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdb/options/connect/connect001/connect001.java index 85cdbb279974..a23c1dde0197 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdb/options/connect/connect001/connect001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdb/options/connect/connect001/connect001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -86,7 +86,7 @@ public static void main (String argv[]) { protected boolean shouldPass() { String feature = "com.sun.jdi.CommandLineLaunch"; if (argumentHandler.shouldPass(feature)) { - log.println("Test passes because of not implemented feature: " + feature); + log.display("Test passes because of not implemented feature: " + feature); return true; } return super.shouldPass(); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdb/options/connect/connect002/connect002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdb/options/connect/connect002/connect002.java index a3219de0cc4e..8c592f366864 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdb/options/connect/connect002/connect002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdb/options/connect/connect002/connect002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -86,7 +86,7 @@ public static void main (String argv[]) { protected boolean shouldPass() { String feature = "com.sun.jdi.SocketAttach"; if (argumentHandler.shouldPass(feature)) { - log.println("Test passes because of not implemented feature: " + feature); + log.display("Test passes because of not implemented feature: " + feature); return true; } return super.shouldPass(); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdb/options/connect/connect003/connect003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdb/options/connect/connect003/connect003.java index 465e3c9deb24..8a65ea934a24 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdb/options/connect/connect003/connect003.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdb/options/connect/connect003/connect003.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -86,7 +86,7 @@ public static void main (String argv[]) { protected boolean shouldPass() { String feature = "com.sun.jdi.SharedMemoryAttach"; if (argumentHandler.shouldPass(feature)) { - log.println("Test passes because of not implemented feature: " + feature); + log.display("Test passes because of not implemented feature: " + feature); return true; } return super.shouldPass(); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdb/options/connect/connect004/connect004.java b/test/hotspot/jtreg/vmTestbase/nsk/jdb/options/connect/connect004/connect004.java index 075e9c30268e..5396c7ff5fd9 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdb/options/connect/connect004/connect004.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdb/options/connect/connect004/connect004.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -86,7 +86,7 @@ public static void main (String argv[]) { protected boolean shouldPass() { String feature = "com.sun.jdi.SocketListen"; if (argumentHandler.shouldPass(feature)) { - log.println("Test passes because of not implemented feature: " + feature); + log.display("Test passes because of not implemented feature: " + feature); return true; } return super.shouldPass(); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdb/options/connect/connect005/connect005.java b/test/hotspot/jtreg/vmTestbase/nsk/jdb/options/connect/connect005/connect005.java index 7bd44a5927cc..f3d0e65f139f 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdb/options/connect/connect005/connect005.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdb/options/connect/connect005/connect005.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -85,7 +85,7 @@ public class connect005 extends JdbTest { protected boolean shouldPass() { String feature = "com.sun.jdi.SharedMemoryListen"; if (argumentHandler.shouldPass(feature)) { - log.println("Test passes because of not implemented feature: " + feature); + log.display("Test passes because of not implemented feature: " + feature); return true; } return super.shouldPass(); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/AttachConnector/plugAttachConnect001/plugAttachConnect001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/AttachConnector/plugAttachConnect001/plugAttachConnect001.java index b1badcc533f0..218edd2c7d55 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/AttachConnector/plugAttachConnect001/plugAttachConnect001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/AttachConnector/plugAttachConnect001/plugAttachConnect001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -122,7 +122,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } public static void main (String argv[]) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/AttachConnector/plugAttachConnect002/plugAttachConnect002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/AttachConnector/plugAttachConnect002/plugAttachConnect002.java index 7c0f8fc83108..5695200a5d6c 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/AttachConnector/plugAttachConnect002/plugAttachConnect002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/AttachConnector/plugAttachConnect002/plugAttachConnect002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -140,7 +140,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } public static void main (String argv[]) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/AttachConnector/plugAttachConnect003/plugAttachConnect003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/AttachConnector/plugAttachConnect003/plugAttachConnect003.java index 0bc4527ecf9f..6f54533b7a4e 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/AttachConnector/plugAttachConnect003/plugAttachConnect003.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/AttachConnector/plugAttachConnect003/plugAttachConnect003.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -108,7 +108,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } public static void main (String argv[]) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/LaunchConnector/plugLaunchConnect001/plugLaunchConnect001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/LaunchConnector/plugLaunchConnect001/plugLaunchConnect001.java index 096e6b970af0..7a6a396e3cb7 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/LaunchConnector/plugLaunchConnect001/plugLaunchConnect001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/LaunchConnector/plugLaunchConnect001/plugLaunchConnect001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -124,7 +124,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } public static void main (String argv[]) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/LaunchConnector/plugLaunchConnect002/plugLaunchConnect002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/LaunchConnector/plugLaunchConnect002/plugLaunchConnect002.java index 71c0cc2987e3..bfa446ffa3b3 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/LaunchConnector/plugLaunchConnect002/plugLaunchConnect002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/LaunchConnector/plugLaunchConnect002/plugLaunchConnect002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -140,7 +140,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } public static void main (String argv[]) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/LaunchConnector/plugLaunchConnect003/plugLaunchConnect003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/LaunchConnector/plugLaunchConnect003/plugLaunchConnect003.java index 2f5898dfd868..e4ea8fb27107 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/LaunchConnector/plugLaunchConnect003/plugLaunchConnect003.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/LaunchConnector/plugLaunchConnect003/plugLaunchConnect003.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -108,7 +108,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } public static void main (String argv[]) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/ListenConnector/plugListenConnect001/plugListenConnect001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/ListenConnector/plugListenConnect001/plugListenConnect001.java index bab07c442dd3..50a7dadb137b 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/ListenConnector/plugListenConnect001/plugListenConnect001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/ListenConnector/plugListenConnect001/plugListenConnect001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -124,7 +124,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } public static void main (String argv[]) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/ListenConnector/plugListenConnect002/plugListenConnect002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/ListenConnector/plugListenConnect002/plugListenConnect002.java index 2861c75491fc..09e441e008b4 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/ListenConnector/plugListenConnect002/plugListenConnect002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/ListenConnector/plugListenConnect002/plugListenConnect002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -140,7 +140,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } public static void main (String argv[]) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/ListenConnector/plugListenConnect003/plugListenConnect003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/ListenConnector/plugListenConnect003/plugListenConnect003.java index 300af2f68516..37e869485929 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/ListenConnector/plugListenConnect003/plugListenConnect003.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/ListenConnector/plugListenConnect003/plugListenConnect003.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -108,7 +108,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } public static void main (String argv[]) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/MultiConnectors/plugMultiConnect001/plugMultiConnect001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/MultiConnectors/plugMultiConnect001/plugMultiConnect001.java index 250b30a9472c..d89a534a3998 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/MultiConnectors/plugMultiConnect001/plugMultiConnect001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/MultiConnectors/plugMultiConnect001/plugMultiConnect001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -151,7 +151,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } public static void main (String argv[]) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/MultiConnectors/plugMultiConnect002/plugMultiConnect002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/MultiConnectors/plugMultiConnect002/plugMultiConnect002.java index b5f63b7e2ef1..80e79f774588 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/MultiConnectors/plugMultiConnect002/plugMultiConnect002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/MultiConnectors/plugMultiConnect002/plugMultiConnect002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -176,7 +176,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } public static void main (String argv[]) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/MultiConnectors/plugMultiConnect003/plugMultiConnect003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/MultiConnectors/plugMultiConnect003/plugMultiConnect003.java index 6e883c779acb..5a5c55cc7d88 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/MultiConnectors/plugMultiConnect003/plugMultiConnect003.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/MultiConnectors/plugMultiConnect003/plugMultiConnect003.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -189,7 +189,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } public static void main (String argv[]) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/MultiConnectors/plugMultiConnect004/plugMultiConnect004.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/MultiConnectors/plugMultiConnect004/plugMultiConnect004.java index 00b57cb67316..b1b93bfaa5a0 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/MultiConnectors/plugMultiConnect004/plugMultiConnect004.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/MultiConnectors/plugMultiConnect004/plugMultiConnect004.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -208,7 +208,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } public static void main (String argv[]) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/MultiConnectors/plugMultiConnect005/plugMultiConnect005.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/MultiConnectors/plugMultiConnect005/plugMultiConnect005.java index 577cdd23f9d1..2adc52d5fc42 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/MultiConnectors/plugMultiConnect005/plugMultiConnect005.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/MultiConnectors/plugMultiConnect005/plugMultiConnect005.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -213,7 +213,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } public static void main (String argv[]) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/MultiConnectors/plugMultiConnect006/plugMultiConnect006.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/MultiConnectors/plugMultiConnect006/plugMultiConnect006.java index e753a3656fe4..e840f7c2fde1 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/MultiConnectors/plugMultiConnect006/plugMultiConnect006.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/MultiConnectors/plugMultiConnect006/plugMultiConnect006.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -238,7 +238,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } public static void main (String argv[]) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/TransportService/transportService001/transportService001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/TransportService/transportService001/transportService001.java index bf090ce7fa7e..d44875f84159 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/TransportService/transportService001/transportService001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/TransportService/transportService001/transportService001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -160,7 +160,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } public static void main (String argv[]) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/TransportService/transportService002/transportService002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/TransportService/transportService002/transportService002.java index 370b5dbd7b31..db7a2567b333 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/TransportService/transportService002/transportService002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/TransportService/transportService002/transportService002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -160,7 +160,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } public static void main (String argv[]) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/TransportService/transportService003/transportService003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/TransportService/transportService003/transportService003.java index 889942922b4d..b19810698457 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/TransportService/transportService003/transportService003.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/PlugConnectors/TransportService/transportService003/transportService003.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -111,7 +111,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } public static void main (String argv[]) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/equals/equals001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/equals/equals001.java index 4d561c87d1b7..bba74edbaa01 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/equals/equals001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/equals/equals001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -108,7 +108,7 @@ private static void print_log_on_verbose(String message) { } private static void print_log_anyway(String message) { - test_log_handler.println(message); + test_log_handler.display(message); } /** diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/equals/equals002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/equals/equals002.java index bcfcac4f8d0d..3448a52b690f 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/equals/equals002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/equals/equals002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,8 +51,6 @@ public class equals002 { /** Debugee's class for check **/ private final static String checked_class = package_prefix + "equals002b"; - - public static void main (String argv[]) { int result = run(argv,System.out); if (result != 0) { @@ -83,12 +81,8 @@ private static void print_log_on_verbose(String message) { test_log_handler.display(message); } - private static void print_log_without_verbose(String message) { - test_log_handler.comment(message); - } - private static void print_log_anyway(String message) { - test_log_handler.println(message); + test_log_handler.display(message); } /** @@ -152,8 +146,6 @@ private int runThis (String argv[], PrintStream out) { ("--> equals002: getting ReferenceType object for loaded checked class..."); ReferenceType refType = debugee.classByName(checked_class); if (refType == null) { - print_log_without_verbose - ("--> equals002: getting ReferenceType object for loaded checked class..."); print_log_anyway("##> equals002: FAILED: Could NOT FIND checked class: " + checked_class); class_not_found_error = true; break; @@ -173,8 +165,6 @@ private int runThis (String argv[], PrintStream out) { if ( debugee_signal.equals("not_unloaded")) { print_log_anyway ("--> equals002: debugee's \"not_unloaded\" signal recieved!"); - print_log_without_verbose - ("--> checked class may be NOT unloaded!"); print_log_anyway ("--> ReferenceType.equals() method can NOT be checked!"); break; @@ -195,8 +185,6 @@ private int runThis (String argv[], PrintStream out) { ("--> equals002: checked class has been unloaded realy: " + checked_class); } else { - print_log_without_verbose - ("--> equals002: check that checked class has been unloaded realy..."); print_log_anyway ("--> equals002: checked class FOUND: " + checked_class + " => it has NOT been unloaded!"); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/failedToInitialize/failedtoinit002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/failedToInitialize/failedtoinit002.java index 515e7f0140ef..4ff051ab3cde 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/failedToInitialize/failedtoinit002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/failedToInitialize/failedtoinit002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,8 +50,6 @@ public class failedtoinit002 { /** Debugee's class for check **/ private final static String checked_class = package_prefix + "failedtoinit002b"; - - public static void main (String argv[]) { int result = run(argv,System.out); if (result != 0) { @@ -82,12 +80,8 @@ private static void print_log_on_verbose(String message) { test_log_handler.display(message); } - private static void print_log_without_verbose(String message) { - test_log_handler.comment(message); - } - private static void print_log_anyway(String message) { - test_log_handler.println(message); + test_log_handler.display(message); } /** @@ -151,8 +145,6 @@ private int runThis (String argv[], PrintStream out) { ("--> failedtoinit002: getting ReferenceType object for loaded checked class..."); ReferenceType refType = debugee.classByName(checked_class); if (refType == null) { - print_log_without_verbose - ("--> failedtoinit002: getting ReferenceType object for loaded checked class..."); print_log_anyway("##> failedtoinit002: FAILED: Could NOT FIND checked class: " + checked_class); class_not_found_error = true; break; @@ -172,8 +164,6 @@ private int runThis (String argv[], PrintStream out) { if ( debugee_signal.equals("not_unloaded")) { print_log_anyway ("--> failedtoinit002: debugee's \"not_unloaded\" signal recieved!"); - print_log_without_verbose - ("--> checked class may be NOT unloaded!"); print_log_anyway ("--> ReferenceType.failedToInitialize() method can NOT be checked!"); break; @@ -194,8 +184,6 @@ private int runThis (String argv[], PrintStream out) { ("--> failedtoinit002: checked class has been unloaded really: " + checked_class); } else { - print_log_without_verbose - ("--> failedtoinit002: check that checked class has been unloaded really..."); print_log_anyway ("--> failedtoinit002: checked class FOUND: " + checked_class + " => it has NOT been unloaded!"); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fields/fields001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fields/fields001.java index 0e6bd85a1486..64cff44c397a 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fields/fields001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fields/fields001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -116,7 +116,7 @@ private static void print_log_on_verbose(String message) { } private static void print_log_anyway(String message) { - test_log_handler.println(message); + test_log_handler.display(message); } /** diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fields/fields003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fields/fields003.java index 03e9399f47e1..42e359e29fe9 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fields/fields003.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fields/fields003.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,8 +50,6 @@ public class fields003 { /** Debugee's class for check **/ private final static String checked_class = package_prefix + "fields003b"; - - public static void main (String argv[]) { int result = run(argv,System.out); if (result != 0) { @@ -82,12 +80,8 @@ private static void print_log_on_verbose(String message) { test_log_handler.display(message); } - private static void print_log_without_verbose(String message) { - test_log_handler.comment(message); - } - private static void print_log_anyway(String message) { - test_log_handler.println(message); + test_log_handler.display(message); } /** @@ -151,8 +145,6 @@ private int runThis (String argv[], PrintStream out) { ("--> fields003: getting ReferenceType object for loaded checked class..."); ReferenceType refType = debugee.classByName(checked_class); if (refType == null) { - print_log_without_verbose - ("--> fields003: getting ReferenceType object for loaded checked class..."); print_log_anyway("##> fields003: FAILED: Could NOT FIND checked class: " + checked_class); class_not_found_error = true; break; @@ -172,8 +164,6 @@ private int runThis (String argv[], PrintStream out) { if ( debugee_signal.equals("not_unloaded")) { print_log_anyway ("--> fields003: debugee's \"not_unloaded\" signal recieved!"); - print_log_without_verbose - ("--> checked class may be NOT unloaded!"); print_log_anyway ("--> ReferenceType.fields() method can NOT be checked!"); break; @@ -194,8 +184,6 @@ private int runThis (String argv[], PrintStream out) { ("--> fields003: checked class has been unloaded realy: " + checked_class); } else { - print_log_without_verbose - ("--> fields003: check that checked class has been unloaded realy..."); print_log_anyway ("--> fields003: checked class FOUND: " + checked_class + " => it has NOT been unloaded!"); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fields/fields004.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fields/fields004.java index bcfa4e323c1e..fcece8cefe23 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fields/fields004.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fields/fields004.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -81,7 +81,7 @@ private static void print_log_on_verbose(String message) { } private static void print_log_anyway(String message) { - test_log_handler.println(message); + test_log_handler.display(message); } /** diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/genericSignature/genericSignature001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/genericSignature/genericSignature001.java index 20b473062b33..2ef55e3063c7 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/genericSignature/genericSignature001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/genericSignature/genericSignature001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -80,7 +80,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/genericSignature/genericSignature001a.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/genericSignature/genericSignature001a.java index 42c738fa86eb..6f8b59167257 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/genericSignature/genericSignature001a.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/genericSignature/genericSignature001a.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -56,7 +56,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } // instantiating of arrays of primitive types for check ReferenceType.genericSignature() method diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/genericSignature/genericSignature002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/genericSignature/genericSignature002.java index 781b0f6f4ba6..1e3852838475 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/genericSignature/genericSignature002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/genericSignature/genericSignature002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -77,7 +77,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/genericSignature/genericSignature002a.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/genericSignature/genericSignature002a.java index 2caa90e0a929..be72c7566db5 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/genericSignature/genericSignature002a.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/genericSignature/genericSignature002a.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -56,7 +56,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } // instantiating of non-generic interface types and arrays of non-generic interface types diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/hashCode/hashcode001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/hashCode/hashcode001.java index 57f08ebe7f04..123d1f12a495 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/hashCode/hashcode001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/hashCode/hashcode001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -108,7 +108,7 @@ private static void print_log_on_verbose(String message) { } private static void print_log_anyway(String message) { - test_log_handler.println(message); + test_log_handler.display(message); } /** diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/hashCode/hashcode002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/hashCode/hashcode002.java index b3df10164766..403400499718 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/hashCode/hashcode002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/hashCode/hashcode002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,8 +51,6 @@ public class hashcode002 { /** Debugee's class for check **/ private final static String checked_class = package_prefix + "hashcode002b"; - - public static void main (String argv[]) { int result = run(argv,System.out); if (result != 0) { @@ -83,12 +81,8 @@ private static void print_log_on_verbose(String message) { test_log_handler.display(message); } - private static void print_log_without_verbose(String message) { - test_log_handler.comment(message); - } - private static void print_log_anyway(String message) { - test_log_handler.println(message); + test_log_handler.display(message); } /** @@ -152,8 +146,6 @@ private int runThis (String argv[], PrintStream out) { ("--> hashcode002: getting ReferenceType object for loaded checked class..."); ReferenceType refType = debugee.classByName(checked_class); if (refType == null) { - print_log_without_verbose - ("--> hashcode002: getting ReferenceType object for loaded checked class..."); print_log_anyway("##> hashcode002: FAILED: Could NOT FIND checked class: " + checked_class); class_not_found_error = true; break; @@ -173,8 +165,6 @@ private int runThis (String argv[], PrintStream out) { if ( debugee_signal.equals("not_unloaded")) { print_log_anyway ("--> hashcode002: debugee's \"not_unloaded\" signal recieved!"); - print_log_without_verbose - ("--> checked class may be NOT unloaded!"); print_log_anyway ("--> ReferenceType.hashCode() method can NOT be checked!"); break; @@ -195,8 +185,6 @@ private int runThis (String argv[], PrintStream out) { ("--> hashcode002: checked class has been unloaded really: " + checked_class); } else { - print_log_without_verbose - ("--> hashcode002: check that checked class has been unloaded really..."); print_log_anyway ("--> hashcode002: checked class FOUND: " + checked_class + " => it has NOT been unloaded!"); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isAbstract/isabstract002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isAbstract/isabstract002.java index 5d504d878e12..7c7f15c03b19 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isAbstract/isabstract002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isAbstract/isabstract002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,8 +50,6 @@ public class isabstract002 { /** Debugee's class for check **/ private final static String checked_class = package_prefix + "isabstract002b"; - - public static void main (String argv[]) { int result = run(argv,System.out); if (result != 0) { @@ -82,12 +80,8 @@ private static void print_log_on_verbose(String message) { test_log_handler.display(message); } - private static void print_log_without_verbose(String message) { - test_log_handler.comment(message); - } - private static void print_log_anyway(String message) { - test_log_handler.println(message); + test_log_handler.display(message); } /** @@ -151,8 +145,6 @@ private int runThis (String argv[], PrintStream out) { ("--> isabstract002: getting ReferenceType object for loaded checked class..."); ReferenceType refType = debugee.classByName(checked_class); if (refType == null) { - print_log_without_verbose - ("--> isabstract002: getting ReferenceType object for loaded checked class..."); print_log_anyway("##> isabstract002: FAILED: Could NOT FIND checked class: " + checked_class); class_not_found_error = true; break; @@ -172,8 +164,6 @@ private int runThis (String argv[], PrintStream out) { if ( debugee_signal.equals("not_unloaded")) { print_log_anyway ("--> isabstract002: debugee's \"not_unloaded\" signal recieved!"); - print_log_without_verbose - ("--> checked class may be NOT unloaded!"); print_log_anyway ("--> ReferenceType.isAbstract() method can NOT be checked!"); break; @@ -194,8 +184,6 @@ private int runThis (String argv[], PrintStream out) { ("--> isabstract002: checked class has been unloaded really: " + checked_class); } else { - print_log_without_verbose - ("--> isabstract002: check that checked class has been unloaded really..."); print_log_anyway ("--> isabstract002: checked class FOUND: " + checked_class + " => it has NOT been unloaded!"); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isInitialized/isinit001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isInitialized/isinit001.java index 5674f53f5212..2046927d21ca 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isInitialized/isinit001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isInitialized/isinit001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -94,7 +94,7 @@ private static void print_log_on_verbose(String message) { } private static void print_log_anyway(String message) { - test_log_handler.println(message); + test_log_handler.display(message); } /** diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isInitialized/isinit002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isInitialized/isinit002.java index 1675eb444c0d..8f29bc0ba2e1 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isInitialized/isinit002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isInitialized/isinit002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,8 +50,6 @@ public class isinit002 { /** Debugee's class for check **/ private final static String checked_class = package_prefix + "isinit002b"; - - public static void main (String argv[]) { int result = run(argv,System.out); if (result != 0) { @@ -82,12 +80,8 @@ private static void print_log_on_verbose(String message) { test_log_handler.display(message); } - private static void print_log_without_verbose(String message) { - test_log_handler.comment(message); - } - private static void print_log_anyway(String message) { - test_log_handler.println(message); + test_log_handler.display(message); } /** @@ -151,8 +145,6 @@ private int runThis (String argv[], PrintStream out) { ("--> isinit002: getting ReferenceType object for loaded checked class..."); ReferenceType refType = debugee.classByName(checked_class); if (refType == null) { - print_log_without_verbose - ("--> isinit002: getting ReferenceType object for loaded checked class..."); print_log_anyway("##> isinit002: FAILED: Could NOT FIND checked class: " + checked_class); class_not_found_error = true; break; @@ -172,8 +164,6 @@ private int runThis (String argv[], PrintStream out) { if ( debugee_signal.equals("not_unloaded")) { print_log_anyway ("--> isinit002: debugee's \"not_unloaded\" signal recieved!"); - print_log_without_verbose - ("--> checked class may be NOT unloaded!"); print_log_anyway ("--> ReferenceType.isInitialized() method can NOT be checked!"); break; @@ -194,8 +184,6 @@ private int runThis (String argv[], PrintStream out) { ("--> isinit002: checked class has been unloaded really: " + checked_class); } else { - print_log_without_verbose - ("--> isinit002: check that checked class has been unloaded really..."); print_log_anyway ("--> isinit002: checked class FOUND: " + checked_class + " => it has NOT been unloaded!"); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isPrepared/isprepared001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isPrepared/isprepared001.java index 2eff33ed6c94..5b98a1ee949d 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isPrepared/isprepared001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isPrepared/isprepared001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -93,7 +93,7 @@ private static void print_log_on_verbose(String message) { } private static void print_log_anyway(String message) { - test_log_handler.println(message); + test_log_handler.display(message); } /** diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isPrepared/isprepared002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isPrepared/isprepared002.java index e3680b64ab91..604fde97babf 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isPrepared/isprepared002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isPrepared/isprepared002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,8 +51,6 @@ public class isprepared002 { /** Debugee's class for check **/ private final static String checked_class = package_prefix + "isprepared002b"; - - public static void main (String argv[]) { int result = run(argv,System.out); if (result != 0) { @@ -83,12 +81,8 @@ private static void print_log_on_verbose(String message) { test_log_handler.display(message); } - private static void print_log_without_verbose(String message) { - test_log_handler.comment(message); - } - private static void print_log_anyway(String message) { - test_log_handler.println(message); + test_log_handler.display(message); } /** @@ -152,8 +146,6 @@ private int runThis (String argv[], PrintStream out) { ("--> isprepared002: getting ReferenceType object for loaded checked class..."); ReferenceType refType = debugee.classByName(checked_class); if (refType == null) { - print_log_without_verbose - ("--> isprepared002: getting ReferenceType object for loaded checked class..."); print_log_anyway("##> isprepared002: FAILED: Could NOT FIND checked class: " + checked_class); class_not_found_error = true; break; @@ -173,8 +165,6 @@ private int runThis (String argv[], PrintStream out) { if ( debugee_signal.equals("not_unloaded")) { print_log_anyway ("--> isprepared002: debugee's \"not_unloaded\" signal recieved!"); - print_log_without_verbose - ("--> checked class may be NOT unloaded!"); print_log_anyway ("--> ReferenceType.isPrepared() method can NOT be checked!"); break; @@ -195,8 +185,6 @@ private int runThis (String argv[], PrintStream out) { ("--> isprepared002: checked class has been unloaded really: " + checked_class); } else { - print_log_without_verbose - ("--> isprepared002: check that checked class has been unloaded really..."); print_log_anyway ("--> isprepared002: checked class FOUND: " + checked_class + " => it has NOT been unloaded!"); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isVerified/isverified002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isVerified/isverified002.java index 237f8e8f780d..2ac8aaa99ea2 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isVerified/isverified002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isVerified/isverified002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -52,8 +52,6 @@ public class isverified002 { /** Debugee's class for check **/ private final static String checked_class = package_prefix + "isverified002b"; - - public static void main (String argv[]) { int result = run(argv,System.out); if (result != 0) { @@ -84,12 +82,8 @@ private static void print_log_on_verbose(String message) { test_log_handler.display(message); } - private static void print_log_without_verbose(String message) { - test_log_handler.comment(message); - } - private static void print_log_anyway(String message) { - test_log_handler.println(message); + test_log_handler.display(message); } /** @@ -153,8 +147,6 @@ private int runThis (String argv[], PrintStream out) { ("--> isverified002: getting ReferenceType object for loaded checked class..."); ReferenceType refType = debugee.classByName(checked_class); if (refType == null) { - print_log_without_verbose - ("--> isverified002: getting ReferenceType object for loaded checked class..."); print_log_anyway("##> isverified002: FAILED: Could NOT FIND checked class: " + checked_class); class_not_found_error = true; break; @@ -174,8 +166,6 @@ private int runThis (String argv[], PrintStream out) { if ( debugee_signal.equals("not_unloaded")) { print_log_anyway ("--> isverified002: debugee's \"not_unloaded\" signal recieved!"); - print_log_without_verbose - ("--> checked class may be NOT unloaded!"); print_log_anyway ("--> ReferenceType.isVerified() method can NOT be checked!"); break; @@ -196,8 +186,6 @@ private int runThis (String argv[], PrintStream out) { ("--> isverified002: checked class has been unloaded really: " + checked_class); } else { - print_log_without_verbose - ("--> isverified002: check that checked class has been unloaded really..."); print_log_anyway ("--> isverified002: checked class FOUND: " + checked_class + " => it has NOT been unloaded!"); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methods/methods001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methods/methods001.java index b004aeb59c95..64de3285c972 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methods/methods001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methods/methods001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -172,7 +172,7 @@ private static void print_log_on_verbose(String message) { } private static void print_log_anyway(String message) { - test_log_handler.println(message); + test_log_handler.display(message); } /** diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methods/methods003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methods/methods003.java index 7b37f8718d9d..739006c98fad 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methods/methods003.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methods/methods003.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,8 +51,6 @@ public class methods003 { /** Debugee's class for check **/ private final static String checked_class = package_prefix + "methods003b"; - - public static void main (String argv[]) { int result = run(argv,System.out); if (result != 0) { @@ -83,12 +81,8 @@ private static void print_log_on_verbose(String message) { test_log_handler.display(message); } - private static void print_log_without_verbose(String message) { - test_log_handler.comment(message); - } - private static void print_log_anyway(String message) { - test_log_handler.println(message); + test_log_handler.display(message); } /** @@ -152,8 +146,6 @@ private int runThis (String argv[], PrintStream out) { ("--> methods003: getting ReferenceType object for loaded checked class..."); ReferenceType refType = debugee.classByName(checked_class); if (refType == null) { - print_log_without_verbose - ("--> methods003: getting ReferenceType object for loaded checked class..."); print_log_anyway("##> methods003: FAILED: Could NOT FIND checked class: " + checked_class); class_not_found_error = true; break; @@ -173,8 +165,6 @@ private int runThis (String argv[], PrintStream out) { if ( debugee_signal.equals("not_unloaded")) { print_log_anyway ("--> methods003: debugee's \"not_unloaded\" signal recieved!"); - print_log_without_verbose - ("--> checked class may be NOT unloaded!"); print_log_anyway ("--> ReferenceType.methods() method can NOT be checked!"); break; @@ -195,8 +185,6 @@ private int runThis (String argv[], PrintStream out) { ("--> methods003: checked class has been unloaded realy: " + checked_class); } else { - print_log_without_verbose - ("--> methods003: check that checked class has been unloaded realy..."); print_log_anyway ("--> methods003: checked class FOUND: " + checked_class + " => it has NOT been unloaded!"); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methods/methods004.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methods/methods004.java index fd86fd11b6c0..0d684445ea61 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methods/methods004.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methods/methods004.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -82,7 +82,7 @@ private static void print_log_on_verbose(String message) { } private static void print_log_anyway(String message) { - test_log_handler.println(message); + test_log_handler.display(message); } /** diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss003.java index 413d410037fa..ab3d21dcda5b 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss003.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss003.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -52,8 +52,6 @@ public class methbyname_ss003 { /** Debugee's class for check **/ private final static String checked_class = package_prefix + "methbyname_ss003b"; - - public static void main (String argv[]) { int result = run(argv,System.out); if (result != 0) { @@ -84,12 +82,8 @@ private static void print_log_on_verbose(String message) { test_log_handler.display(message); } - private static void print_log_without_verbose(String message) { - test_log_handler.comment(message); - } - private static void print_log_anyway(String message) { - test_log_handler.println(message); + test_log_handler.display(message); } /** @@ -153,8 +147,6 @@ private int runThis (String argv[], PrintStream out) { ("--> methbyname_ss003: getting ReferenceType object for loaded checked class..."); ReferenceType refType = debugee.classByName(checked_class); if (refType == null) { - print_log_without_verbose - ("--> methbyname_ss003: getting ReferenceType object for loaded checked class..."); print_log_anyway("##> methbyname_ss003: FAILED: Could NOT FIND checked class: " + checked_class); class_not_found_error = true; break; @@ -174,8 +166,6 @@ private int runThis (String argv[], PrintStream out) { if ( debugee_signal.equals("not_unloaded")) { print_log_anyway ("--> methbyname_ss003: debugee's \"not_unloaded\" signal recieved!"); - print_log_without_verbose - ("--> checked class may be NOT unloaded!"); print_log_anyway ("--> ReferenceType.methodsByName_ss() method can NOT be checked!"); break; @@ -196,8 +186,6 @@ private int runThis (String argv[], PrintStream out) { ("--> methbyname_ss003: checked class has been unloaded realy: " + checked_class); } else { - print_log_without_verbose - ("--> methbyname_ss003: check that checked class has been unloaded realy..."); print_log_anyway ("--> methbyname_ss003: checked class FOUND: " + checked_class + " => it has NOT been unloaded!"); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/name/name001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/name/name001.java index bfd68bcae4f6..0a1600bbca02 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/name/name001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/name/name001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -108,7 +108,7 @@ private static void print_log_on_verbose(String message) { } private static void print_log_anyway(String message) { - test_log_handler.println(message); + test_log_handler.display(message); } /** diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/name/name002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/name/name002.java index d9af04da19b6..1caec8899703 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/name/name002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/name/name002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,8 +51,6 @@ public class name002 { /** Debugee's class for check **/ private final static String checked_class = package_prefix + "name002b"; - - public static void main (String argv[]) { int result = run(argv,System.out); if (result != 0) { @@ -83,12 +81,8 @@ private static void print_log_on_verbose(String message) { test_log_handler.display(message); } - private static void print_log_without_verbose(String message) { - test_log_handler.comment(message); - } - private static void print_log_anyway(String message) { - test_log_handler.println(message); + test_log_handler.display(message); } /** @@ -152,8 +146,6 @@ private int runThis (String argv[], PrintStream out) { ("--> name002: getting ReferenceType object for loaded checked class..."); ReferenceType refType = debugee.classByName(checked_class); if (refType == null) { - print_log_without_verbose - ("--> name002: getting ReferenceType object for loaded checked class..."); print_log_anyway("##> name002: FAILED: Could NOT FIND checked class: " + checked_class); class_not_found_error = true; break; @@ -173,8 +165,6 @@ private int runThis (String argv[], PrintStream out) { if ( debugee_signal.equals("not_unloaded")) { print_log_anyway ("--> name002: debugee's \"not_unloaded\" signal recieved!"); - print_log_without_verbose - ("--> checked class may be NOT unloaded!"); print_log_anyway ("--> ReferenceType.name() method can NOT be checked!"); break; @@ -195,8 +185,6 @@ private int runThis (String argv[], PrintStream out) { ("--> name002: checked class has been unloaded really: " + checked_class); } else { - print_log_without_verbose - ("--> name002: check that checked class has been unloaded really..."); print_log_anyway ("--> name002: checked class FOUND: " + checked_class + " => it has NOT been unloaded!"); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/sourceName/sourcename001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/sourceName/sourcename001.java index 7840670aa438..352ee94815dc 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/sourceName/sourcename001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/sourceName/sourcename001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -95,7 +95,7 @@ private static void print_log_on_verbose(String message) { } private static void print_log_anyway(String message) { - test_log_handler.println(message); + test_log_handler.display(message); } /** diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/sourceName/sourcename002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/sourceName/sourcename002.java index df523a5a5595..d791b075cedb 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/sourceName/sourcename002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/sourceName/sourcename002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,8 +51,6 @@ public class sourcename002 { /** Debugee's class for check **/ private final static String checked_class = package_prefix + "sourcename002b"; - - public static void main (String argv[]) { int result = run(argv,System.out); if (result != 0) { @@ -83,12 +81,8 @@ private static void print_log_on_verbose(String message) { test_log_handler.display(message); } - private static void print_log_without_verbose(String message) { - test_log_handler.comment(message); - } - private static void print_log_anyway(String message) { - test_log_handler.println(message); + test_log_handler.display(message); } /** @@ -152,8 +146,6 @@ private int runThis (String argv[], PrintStream out) { ("--> sourcename002: getting ReferenceType object for loaded checked class..."); ReferenceType refType = debugee.classByName(checked_class); if (refType == null) { - print_log_without_verbose - ("--> sourcename002: getting ReferenceType object for loaded checked class..."); print_log_anyway("##> sourcename002: FAILED: Could NOT FIND checked class: " + checked_class); class_not_found_error = true; break; @@ -173,8 +165,6 @@ private int runThis (String argv[], PrintStream out) { if ( debugee_signal.equals("not_unloaded")) { print_log_anyway ("--> sourcename002: debugee's \"not_unloaded\" signal recieved!"); - print_log_without_verbose - ("--> checked class may be NOT unloaded!"); print_log_anyway ("--> ReferenceType.sourceName() method can NOT be checked!"); break; @@ -195,8 +185,6 @@ private int runThis (String argv[], PrintStream out) { ("--> sourcename002: checked class has been unloaded really: " + checked_class); } else { - print_log_without_verbose - ("--> sourcename002: check that checked class has been unloaded really..."); print_log_anyway ("--> sourcename002: checked class FOUND: " + checked_class + " => it has NOT been unloaded!"); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/sourceName/sourcename003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/sourceName/sourcename003.java index c4163f123b4e..645f6229661e 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/sourceName/sourcename003.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/sourceName/sourcename003.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -52,8 +52,6 @@ public class sourcename003 { /** Debugee's class for check **/ private final static String checked_class = thisClassName + "[]"; - - public static void main (String argv[]) { int result = run(argv,System.out); if (result != 0) { @@ -84,12 +82,8 @@ private static void print_log_on_verbose(String message) { test_log_handler.display(message); } - private static void print_log_without_verbose(String message) { - test_log_handler.comment(message); - } - private static void print_log_anyway(String message) { - test_log_handler.println(message); + test_log_handler.display(message); } /** @@ -136,8 +130,6 @@ private int runThis (String argv[], PrintStream out) { ("--> sourcename003: getting ReferenceType object for loaded checked class..."); ReferenceType refType = debugee.classByName(checked_class); if (refType == null) { - print_log_without_verbose - ("--> sourcename003: getting ReferenceType object for loaded checked class..."); print_log_anyway("##> sourcename003: FAILED: Could NOT FIND checked class: " + checked_class); class_not_found_error = true; break; diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ThreadReference/stop/stop002t.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ThreadReference/stop/stop002t.java index 02d49f76de70..0d87338f6781 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ThreadReference/stop/stop002t.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ThreadReference/stop/stop002t.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -119,8 +119,6 @@ private int runIt(String args[]) { return Consts.TEST_FAILED; } } catch (Throwable t) { - // Call Thread.interrupted(). Workaround for JDK-8306324 - log.display("TEST #3: interrupted = " + Thread.interrupted()); // We don't expect the exception to be thrown when in vthread mode. if (!vthreadMode && t instanceof MyThrowable) { log.display("TEST #3: Caught expected exception while in loop: " + t); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/TypeComponent/genericSignature/genericSignature001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/TypeComponent/genericSignature/genericSignature001.java index d5368568becc..3f9d74852cf0 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/TypeComponent/genericSignature/genericSignature001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/TypeComponent/genericSignature/genericSignature001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -80,7 +80,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/TypeComponent/genericSignature/genericSignature001a.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/TypeComponent/genericSignature/genericSignature001a.java index e479baa584bc..bef870721251 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/TypeComponent/genericSignature/genericSignature001a.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/TypeComponent/genericSignature/genericSignature001a.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -56,7 +56,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } // primitive type fields and arrays of primitive types diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/TypeComponent/genericSignature/genericSignature002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/TypeComponent/genericSignature/genericSignature002.java index 8eabd51f7138..c0fdf0037c91 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/TypeComponent/genericSignature/genericSignature002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/TypeComponent/genericSignature/genericSignature002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -76,7 +76,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/TypeComponent/genericSignature/genericSignature002a.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/TypeComponent/genericSignature/genericSignature002a.java index 476393e39cb5..adca0ea35bc6 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/TypeComponent/genericSignature/genericSignature002a.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/TypeComponent/genericSignature/genericSignature002a.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -56,7 +56,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } // methods without generic signature diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachineManager/createVirtualMachine/createVM001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachineManager/createVirtualMachine/createVM001.java index 26ad37078d80..1c5fa47e0d0e 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachineManager/createVirtualMachine/createVM001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachineManager/createVirtualMachine/createVM001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -67,7 +67,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } public static void main (String argv[]) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachineManager/createVirtualMachine/createVM002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachineManager/createVirtualMachine/createVM002.java index dee135fbfedc..c2543a30350f 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachineManager/createVirtualMachine/createVM002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachineManager/createVirtualMachine/createVM002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -69,7 +69,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } public static void main (String argv[]) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachineManager/createVirtualMachine/createVM003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachineManager/createVirtualMachine/createVM003.java index d1e8f67b2934..be08afe69622 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachineManager/createVirtualMachine/createVM003.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachineManager/createVirtualMachine/createVM003.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -74,7 +74,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } public static void main (String argv[]) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachineManager/createVirtualMachine/createVM004.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachineManager/createVirtualMachine/createVM004.java index 804a92b2b496..a22841750ae9 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachineManager/createVirtualMachine/createVM004.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachineManager/createVirtualMachine/createVM004.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -74,7 +74,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } public static void main (String argv[]) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachineManager/createVirtualMachine/createVM005.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachineManager/createVirtualMachine/createVM005.java index aef6720f9171..2eb1f0e1efd5 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachineManager/createVirtualMachine/createVM005.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachineManager/createVirtualMachine/createVM005.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -73,7 +73,7 @@ private static void logOnError(String message) { } private static void logAlways(String message) { - logHandler.println(message); + logHandler.display(message); } public static void main (String argv[]) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdwp/Event/CLASS_UNLOAD/clsunload001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdwp/Event/CLASS_UNLOAD/clsunload001.java index ccfec8efeb9e..79aff2af8626 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdwp/Event/CLASS_UNLOAD/clsunload001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdwp/Event/CLASS_UNLOAD/clsunload001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -441,7 +441,7 @@ void waitForTestedEvent() { if (eventKind == JDWP.EventKind.VM_DEATH) { log.display("Got VM_DEATH event while waiting for tested event"); dead = true; - log.println("No CLASS_UNLOAD event occured at all so treat test as PASSED"); + log.display("No CLASS_UNLOAD event occured at all so treat test as PASSED"); return; } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ForceGarbageCollection/forcegc001.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ForceGarbageCollection/forcegc001.java index ffb5df089d8e..5f3523aca39c 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ForceGarbageCollection/forcegc001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ForceGarbageCollection/forcegc001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -98,7 +98,7 @@ public int runIt(String argv[], PrintStream out) { log.display("Checking soft references " + kind + ": " + count + " references"); int found1 = checkObjects(count, refs, kind); if (found1 < found) { - log.println("# WARNING: " + found1 + " of " + found + log.display("# WARNING: " + found1 + " of " + found + " softly reachable objects were GCed\n" + "# by System.gc() but not by ForceGarbageCollection()"); } @@ -117,7 +117,7 @@ public int checkObjects(int count, SoftReference refs[], String kind) { } if (found > 0) { - log.println("# WARNING: " + found + " of " + count + log.display("# WARNING: " + found + " of " + count + " softly reachable objects not GCed " + kind); } else { log.display("All " + found + " of " + count diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS104/hs104t002/hs104t002.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS104/hs104t002/hs104t002.java index 6094caab16f9..253e62150f5b 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS104/hs104t002/hs104t002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS104/hs104t002/hs104t002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, 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 @@ -112,9 +112,9 @@ public boolean agentMethod() { pass=false; } if ( pass ) { - log.println(" Testcase hs104t002 :: Passed."); + log.display(" Testcase hs104t002 :: Passed."); } else { - log.println(" Testcase hs104t002 :: Failed."); + log.display(" Testcase hs104t002 :: Failed."); } return pass; } @@ -133,7 +133,7 @@ public boolean startAllThreads() { //notify all the threads to start their jobs. wicket.unlock(); started=true; - log.println(" startAllThreads :: All threads are running."); + log.display(" startAllThreads :: All threads are running."); } catch (IllegalStateException ise) { log.complain(" startAllThreads :: Error occured while" +" waiting for threads."); @@ -176,7 +176,7 @@ private boolean waitForAllThreads() { thread.join(); } allExited= true; - log.println(" All threads terminated without " + log.display(" All threads terminated without " +"java.lang.InterruptedException."); } catch(java.lang.InterruptedException ie ) { log.complain(" waitForAllThreads ::" diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS202/hs202t002/hs202t002.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS202/hs202t002/hs202t002.java index dd2951955439..5b8b2034bd02 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS202/hs202t002/hs202t002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS202/hs202t002/hs202t002.java @@ -92,9 +92,9 @@ public boolean agentMethod() { boolean passed = false; if (successState == state && isRedefined()) { passed = true; - log.println(" ... Passed state (" + state + ")"); + log.display(" ... Passed state (" + state + ")"); } else { - log.println(" ... Failed state (" + state + ")"); + log.display(" ... Failed state (" + state + ")"); } return passed; } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS203/hs203t001/hs203t001.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS203/hs203t001/hs203t001.java index e42bede4e011..f561c26e6e1b 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS203/hs203t001/hs203t001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS203/hs203t001/hs203t001.java @@ -79,7 +79,7 @@ public boolean agentMethod() { popThreadFrame(mt.getThread()); resumeThread(mt.getThread()); mt.join(); - log.println(" ..."+mt.threadState); + log.display(" ..."+mt.threadState); } catch(Exception ie) { ie.printStackTrace(); } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS203/hs203t002/hs203t002.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS203/hs203t002/hs203t002.java index f62bd49f674e..4623237058c6 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS203/hs203t002/hs203t002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS203/hs203t002/hs203t002.java @@ -87,7 +87,7 @@ public boolean agentMethod() { resumeThread(mt.getThread()); MyThread.resume.set(true); mt.join(); - log.println(" ..."+mt.threadState); + log.display(" ..."+mt.threadState); } catch(Exception ie) { ie.printStackTrace(); } @@ -95,7 +95,7 @@ public boolean agentMethod() { if ( (mt.threadState < 1000) && (redefineAttempted() && isRedefined()) ) { passed = true; } else { - log.println(" FAILED ..."); + log.display(" FAILED ..."); } return passed; diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS301/hs301t001/hs301t001.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS301/hs301t001/hs301t001.java index 1422d5802eae..d4e9c30b6e4b 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS301/hs301t001/hs301t001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS301/hs301t001/hs301t001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, 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 @@ -85,11 +85,11 @@ public boolean agentMethod() { } cls.doThis(); if (!pass) { - log.println(" Error occured, error in redefineing (as expected)."); - log.println(" Case passed."); + log.display(" Error occured, error in redefineing (as expected)."); + log.display(" Case passed."); } else { - log.println(" Successfully redefined, (which is not execpeted)."); - log.println(" Case failed."); + log.display(" Successfully redefined, (which is not execpeted)."); + log.display(" Case failed."); } return pass; } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t002/hs302t002.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t002/hs302t002.java index 4a13fbc250c1..e46a572630b7 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t002/hs302t002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t002/hs302t002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, 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 @@ -61,14 +61,14 @@ public boolean agentMethod() { boolean pass=false; MyClass cls = new MyClass(); cls.setName("SOME NAME"); - log.println(" cls.toString() "+cls.toString()); + log.display(" cls.toString() "+cls.toString()); // Redefine should be attempted and failed. if (!cls.toString().equals("Default") && ( redefineAttempted() && !isRedefined()) ) { pass =true; - log.println(" Passed .."); + log.display(" Passed .."); } else { - log.println(" Failed .."); + log.display(" Failed .."); } return pass; } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t003/hs302t003.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t003/hs302t003.java index 6cf324858185..c25885f53a82 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t003/hs302t003.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t003/hs302t003.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, 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 @@ -61,13 +61,13 @@ public boolean agentMethod() { MyClass cls = new MyClass(); try { cls.setName("SOME NAME"); - log.println(" cls.toString() "+cls.toString()); + log.display(" cls.toString() "+cls.toString()); } catch(Exception exp) { if (cls.toString().equals("Default") && isRedefined() ) { pass =true; - log.println(" Passed .."); + log.display(" Passed .."); } else { - log.println(" Failed .."); + log.display(" Failed .."); } return pass; } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t004/hs302t004.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t004/hs302t004.java index ed8ec89ecf2d..36305b2589af 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t004/hs302t004.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t004/hs302t004.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, 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 @@ -65,9 +65,9 @@ public boolean agentMethod(){ Method[] methods = klass.getDeclaredMethods(); for(Method method : methods) { if (method.getName().equals("setName")) { - log.println(" Modified "+method.getModifiers()); + log.display(" Modified "+method.getModifiers()); if ( (Modifier.PRIVATE & method.getModifiers())==Modifier.PRIVATE ) { - log.println("...Private.."); + log.display("...Private.."); pass = true; } } @@ -76,9 +76,9 @@ public boolean agentMethod(){ }catch(Exception exp) { if ( isRedefined() ) { pass =true; - log.println(" Passed .."); + log.display(" Passed .."); } else { - log.println(" Failed .."); + log.display(" Failed .."); } return pass; } @@ -86,7 +86,7 @@ public boolean agentMethod(){ if ( redefineAttempted() && !isRedefined() ) { pass = true; } - log.println(" PASS = "+pass); + log.display(" PASS = "+pass); return pass; } } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t005/hs302t005.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t005/hs302t005.java index e78e87992bdd..ae5c9ccdb56f 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t005/hs302t005.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t005/hs302t005.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, 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 @@ -65,10 +65,10 @@ public boolean agentMethod() { Method[] methods = klass.getDeclaredMethods(); for(Method method : methods) { if (method.getName().equals("setName")) { - log.println(" Modified "+method.getModifiers()); + log.display(" Modified "+method.getModifiers()); // Still its private good. if ( (Modifier.PRIVATE & method.getModifiers())==Modifier.PRIVATE ) { - log.println("...Private.."); + log.display("...Private.."); pass = true; } } @@ -76,14 +76,14 @@ public boolean agentMethod() { } catch(Exception exp) { if ( isRedefined() ) { pass =true; - log.println(" Passed .."); + log.display(" Passed .."); } } // If the execption is failed to throw. if ( redefineAttempted() && !isRedefined() ) { pass = true; } - log.println(" PASS = "+pass); + log.display(" PASS = "+pass); return pass; } } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t006/hs302t006.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t006/hs302t006.java index d4320dc9e8e4..5f9646e6393d 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t006/hs302t006.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t006/hs302t006.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, 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 @@ -76,7 +76,7 @@ public boolean agentMethod() { }catch(Exception exp) { if ( isRedefined() ) { pass =true; - log.println(" Passed .."); + log.display(" Passed .."); } } // If the execption is failed to throw. diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t007/hs302t007.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t007/hs302t007.java index 1c9c2924915e..92507e1cbb13 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t007/hs302t007.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t007/hs302t007.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, 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 @@ -66,9 +66,9 @@ public boolean agentMethod() { Method[] methods = klass.getDeclaredMethods(); for(Method method : methods) { if (method.getName().equals("setName")) { - log.println(" Modified "+method.getModifiers()); + log.display(" Modified "+method.getModifiers()); if ( (Modifier.SYNCHRONIZED & method.getModifiers())==Modifier.SYNCHRONIZED ) { - log.println("...Synchronized.."); + log.display("...Synchronized.."); pass = true; } } @@ -76,9 +76,9 @@ public boolean agentMethod() { }catch(Exception exp) { if ( redefineAttempted() && !isRedefined()) { pass =true; - log.println(" Passed .."); + log.display(" Passed .."); } else { - log.println(" Failed .."); + log.display(" Failed .."); } } return pass; diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t008/hs302t008.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t008/hs302t008.java index e7d0323dcc8d..8dc396a346cb 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t008/hs302t008.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t008/hs302t008.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, 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 @@ -75,18 +75,18 @@ public boolean agentMethod(){ } }catch(Exception exp) { exp.printStackTrace(); - log.println(" Exception "+exp.getMessage()); + log.display(" Exception "+exp.getMessage()); if ( isRedefined() ) { pass =true; - log.println(" Passed .."); + log.display(" Passed .."); } else { - log.println(" Failed .."); + log.display(" Failed .."); } } if ( redefineAttempted() && !isRedefined() ) { pass = true; } - log.println(" PASS = "+pass); + log.display(" PASS = "+pass); return pass; } } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t011/hs302t011.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t011/hs302t011.java index 442e86b9bd75..018113a4daba 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t011/hs302t011.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t011/hs302t011.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, 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 @@ -72,12 +72,12 @@ public boolean agentMethod(){ } }catch(Exception exp) { exp.printStackTrace(); - log.println(" Exception "+exp.getMessage()); + log.display(" Exception "+exp.getMessage()); } if ( redefineAttempted() && !isRedefined() ) { pass = true; } - log.println(" PASS = "+pass); + log.display(" PASS = "+pass); return pass; } } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t012/hs302t012.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t012/hs302t012.java index 96295e50b569..4a17898f3c48 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t012/hs302t012.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/hotswap/HS302/hs302t012/hs302t012.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, 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 @@ -77,12 +77,12 @@ public boolean agentMethod(){ } }catch(Exception exp) { exp.printStackTrace(); - log.println(" Exception "+exp.getMessage()); + log.display(" Exception "+exp.getMessage()); } if ( redefineAttempted() && !isRedefined() ) { pass = true; } - log.println(" PASS = "+pass); + log.display(" PASS = "+pass); return pass; } } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/IORedirector.java b/test/hotspot/jtreg/vmTestbase/nsk/share/IORedirector.java index 19a58780a951..4b2e3b96d4c5 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/IORedirector.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/IORedirector.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -71,7 +71,7 @@ public IORedirector(InputStream in, OutputStream out, String prefix) { public IORedirector(BufferedReader in, Log log, String prefix) { this(); this.bin = in; - addProcessor(s -> log.println(prefix + s)); + addProcessor(s -> log.display(prefix + s)); } public void addProcessor(Consumer lineProcessor) { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/Log.java b/test/hotspot/jtreg/vmTestbase/nsk/share/Log.java index 24711527606f..3bcf00603271 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/Log.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/Log.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,6 @@ import java.util.HashSet; import java.util.Vector; - /** * This class helps to print test-execution trace messages. *

@@ -198,32 +197,6 @@ public static String printExceptionToString(Object prefix, Throwable exception) return bos.toString(); } - /** - * Print message to the assigned output stream. - * - * @deprecated Test ought to be quiet if log mode is non-verbose - * and there is no errors found by the test. Methods - * display() and complain() - * are enough for testing purposes. - */ - @Deprecated - public synchronized void println(String message) { - doPrint(message); - } - - /** - * Print message to the assigned output stream, - * if log mode is non-verbose. - * - * @deprecated Test ought to be quiet if log mode is non-verbose - * and there is no errors found by the test. Methods - * display() and complain() - * are enough for testing purposes. - */ - @Deprecated - public synchronized void comment(String message) { - } - /** * Print trace message to the assigned output stream, * only if specified level is less or equal for the @@ -496,12 +469,12 @@ public void trace(int level, String message) { } /** - * Print message by invoking Log.println(). + * Print message by invoking Log.display(). * - * @see Log#println + * @see Log#display */ public void println(String message) { - log.println(makeLogMessage(message)); + log.display(makeLogMessage(message)); } /** diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/jdb/JdbTest.java b/test/hotspot/jtreg/vmTestbase/nsk/share/jdb/JdbTest.java index 11def178cdc5..c8fd40c2d4a0 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/jdb/JdbTest.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/jdb/JdbTest.java @@ -177,7 +177,7 @@ protected void runTest(String argv[]) { log = new Log(out, argumentHandler); if (shouldPass()) { - log.println("TEST PASSED"); + log.display("TEST PASSED"); return; } diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 18fcfc898fe9..ccb8938ba16f 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -453,17 +453,14 @@ javax/management/remote/mandatory/connection/BrokenConnectionTest.java 8262312 l java/net/DatagramSocket/DatagramSocketExample.java 8308807 aix-ppc64 java/net/DatagramSocket/DatagramSocketMulticasting.java 8308807 aix-ppc64 -java/net/MulticastSocket/B6427403.java 8308807 aix-ppc64 java/net/MulticastSocket/IPMulticastIF.java 8308807 aix-ppc64 java/net/MulticastSocket/JoinLeave.java 8308807 aix-ppc64 -java/net/MulticastSocket/MulticastAddresses.java 8308807 aix-ppc64 java/net/MulticastSocket/NoLoopbackPackets.java 7122846,8308807 macosx-all,aix-ppc64 java/net/MulticastSocket/NoSetNetworkInterface.java 8308807 aix-ppc64 -java/net/MulticastSocket/Promiscuous.java 8308807 aix-ppc64 java/net/MulticastSocket/SetGetNetworkInterfaceTest.java 8308807 aix-ppc64 java/net/MulticastSocket/SetLoopbackMode.java 7122846,8308807 macosx-all,aix-ppc64 java/net/MulticastSocket/SetOutgoingIf.java 8308807 aix-ppc64 -java/net/MulticastSocket/Test.java 7145658,8308807 macosx-all,aix-ppc64 +java/net/MulticastSocket/Test.java 7145658 macosx-all ############################################################################ @@ -580,8 +577,6 @@ javax/swing/plaf/synth/7158712/bug7158712.java 8324782 macosx-all # jdk_valhalla -valhalla/valuetypes/NullRestrictedTest.java 8390260 generic-all - ############################################################################ # core_tools @@ -636,6 +631,8 @@ jdk/jfr/event/oldobject/TestZ.java 8375615 generic- # jdk_internal +jdk/internal/platform/docker/TestLimitsUpdating.java 8390823 linux-all + ############################################################################ # jdk_jpackage diff --git a/test/jdk/java/awt/Mixing/AWT_Mixing/JPopupMenuOverlapping.java b/test/jdk/java/awt/Mixing/AWT_Mixing/JPopupMenuOverlapping.java index 003977e233ad..93346d997c51 100644 --- a/test/jdk/java/awt/Mixing/AWT_Mixing/JPopupMenuOverlapping.java +++ b/test/jdk/java/awt/Mixing/AWT_Mixing/JPopupMenuOverlapping.java @@ -58,7 +58,6 @@ public class JPopupMenuOverlapping extends OverlappingTestBase { {testEmbeddedFrame = true;} private boolean lwClicked = false; - private Point loc; private JPopupMenu popup; private JFrame frame=null; @@ -86,11 +85,12 @@ public void actionPerformed(ActionEvent event) { } propagateAWTControls(frame); frame.setVisible(true); - loc = frame.getContentPane().getLocationOnScreen(); } @Override protected boolean performTest() { + Point loc = frame.getContentPane().getLocationOnScreen(); + // run robot Robot robot = Util.createRobot(); robot.setAutoDelay(ROBOT_DELAY); diff --git a/test/jdk/java/net/httpclient/Http1RequestEmptyBufferTest.java b/test/jdk/java/net/httpclient/Http1RequestEmptyBufferTest.java new file mode 100644 index 000000000000..abffd8a7d2a7 --- /dev/null +++ b/test/jdk/java/net/httpclient/Http1RequestEmptyBufferTest.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.test.lib.Utils; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.CompletableFuture; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @test + * @bug 8308024 + * @summary Verify that the server observes the terminal chunk exactly once + * when the HTTP/1.1 client request body publisher supplies an empty buffer. + * @library /test/lib + * @run junit/othervm ${test.main.class} + */ +public class Http1RequestEmptyBufferTest { + + static final byte[] HEADER_END = new byte[] {'\r', '\n', '\r', '\n'}; + + static byte[] readRequestHeaders(InputStream input) throws IOException { + ByteArrayOutputStream headerBytes = new ByteArrayOutputStream(); + int headerEndMatch = 0, nextByte; + while ((nextByte = input.read()) != -1) { + headerBytes.write(nextByte); + if (nextByte == HEADER_END[headerEndMatch]) { + headerEndMatch++; + if (headerEndMatch == 4) { + return headerBytes.toByteArray(); + } + } else { + headerEndMatch = 0; + } + } + throw new IOException("EOF reached before reaching end of headers"); + } + + static final String TERMINAL_CHUNK = "0\r\n\r\n"; + + static final String RESPONSE_HEADERS = "HTTP/1.1 200 OK\r\n" + + "Content-Length: 0\r\n" + + "Connection: close\r\n\r\n"; + + static String escape(String value) { + return value.replace("\r", "\\r").replace("\n", "\\n"); + } + + @Test + void test() throws Exception { + try (ServerSocket server = new ServerSocket()) { + server.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0)); + String path = "/testChunkEmptyBuffer/"; + URI uri = new URI("http", + null, + server.getInetAddress().getHostAddress(), + server.getLocalPort(), + path, + null, + null); + try (HttpClient client = HttpClient.newBuilder() + .proxy(HttpClient.Builder.NO_PROXY) + .version(HttpClient.Version.HTTP_1_1) + .build()) { + HttpRequest request = HttpRequest.newBuilder(uri) + .PUT(HttpRequest.BodyPublishers.ofByteArrays(List.of(new byte[0]))) + .build(); + CompletableFuture> responseFuture = + client.sendAsync(request, HttpResponse.BodyHandlers.discarding()); + try (Socket connection = server.accept()) { + connection.setSoTimeout((int)Utils.adjustTimeout(1000)); + InputStream input = connection.getInputStream(); + byte[] headerBytes = readRequestHeaders(input); + String headerText = new String(headerBytes, StandardCharsets.US_ASCII) + .toLowerCase(Locale.ROOT); + assertTrue(headerText.contains("transfer-encoding: chunked"), + "Expected Transfer-Encoding: chunked header, got: " + + headerText); + byte[] firstChunkBytes = input.readNBytes(TERMINAL_CHUNK.length()); + OutputStream os = connection.getOutputStream(); + os.write(RESPONSE_HEADERS.getBytes(StandardCharsets.US_ASCII)); + os.flush(); + String requestBody = new String(firstChunkBytes, StandardCharsets.US_ASCII) + + new String(input.readAllBytes(), StandardCharsets.US_ASCII); + assertEquals(escape(TERMINAL_CHUNK), escape(requestBody)); + HttpResponse response = responseFuture.join(); + assertEquals(200, response.statusCode()); + } + } + } + } +} diff --git a/test/jdk/java/util/concurrent/atomic/AtomicReferenceFieldUpdaterValueObjectTest.java b/test/jdk/java/util/concurrent/atomic/AtomicReferenceFieldUpdaterValueObjectTest.java new file mode 100644 index 000000000000..ee31ee8b0b3c --- /dev/null +++ b/test/jdk/java/util/concurrent/atomic/AtomicReferenceFieldUpdaterValueObjectTest.java @@ -0,0 +1,143 @@ +/* + * 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 8388310 + * @summary AtomicReferenceFieldUpdater does not perform a substitutability check + * @enablePreview + * @run junit ${test.main.class} + */ + +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class AtomicReferenceFieldUpdaterValueObjectTest { + volatile Integer x = null; + + static AtomicReferenceFieldUpdater updaterFor(String fieldName) { + return AtomicReferenceFieldUpdater.newUpdater + (AtomicReferenceFieldUpdaterValueObjectTest.class, Integer.class, fieldName); + } + + /** + * get returns the last value set or assigned + */ + @Test + public void testGetSet() { + AtomicReferenceFieldUpdater a; + a = updaterFor("x"); + x = new Integer(1); + assertSame(new Integer(1), a.get(this)); + a.set(this, new Integer(2)); + assertSame(new Integer(2), a.get(this)); + a.set(this, new Integer(-3)); + assertSame(new Integer(-3), a.get(this)); + } + + /** + * get returns the last value lazySet by same thread + */ + @Test + public void testGetLazySet() { + AtomicReferenceFieldUpdater a; + a = updaterFor("x"); + x = new Integer(1); + assertSame(new Integer(1), a.get(this)); + a.lazySet(this, new Integer(2)); + assertSame(new Integer(2), a.get(this)); + a.lazySet(this, new Integer(-3)); + assertSame(new Integer(-3), a.get(this)); + } + + /** + * compareAndSet succeeds in changing value if same as expected else fails + */ + @Test + public void testCompareAndSet() { + AtomicReferenceFieldUpdater a; + a = updaterFor("x"); + x = new Integer(1); + assertTrue(a.compareAndSet(this, new Integer(1), new Integer(2))); + assertTrue(a.compareAndSet(this, new Integer(2), new Integer(-4))); + assertSame(new Integer(-4), a.get(this)); + assertFalse(a.compareAndSet(this, new Integer(-5), new Integer(7))); + assertNotSame(new Integer(7), a.get(this)); + assertSame(new Integer(-4), a.get(this)); + assertTrue(a.compareAndSet(this, new Integer(-4), new Integer(7))); + assertSame(new Integer(7), a.get(this)); + } + + /** + * compareAndSet in new Integer(1) thread enables another waiting for value + * to succeed + */ + @Test + public void testCompareAndSetInMultipleThreads() throws Exception { + x = new Integer(1); + final AtomicReferenceFieldUpdater a; + a = updaterFor("x"); + + Thread t = Thread.startVirtualThread(() -> { + while (!a.compareAndSet(AtomicReferenceFieldUpdaterValueObjectTest.this, new Integer(2), new Integer(3))) + Thread.yield(); + }); + + assertTrue(a.compareAndSet(this, new Integer(1), new Integer(2))); + t.join(); + assertFalse(t.isAlive()); + assertSame(new Integer(3), a.get(this)); + } + + /** + * repeated weakCompareAndSet succeeds in changing value when same as expected + */ + @Test + public void testWeakCompareAndSet() { + AtomicReferenceFieldUpdater a; + a = updaterFor("x"); + x = new Integer(1); + do {} while (!a.weakCompareAndSet(this, new Integer(1), new Integer(2))); + do {} while (!a.weakCompareAndSet(this, new Integer(2), new Integer(-4))); + assertSame(new Integer(-4), a.get(this)); + do {} while (!a.weakCompareAndSet(this, new Integer(-4), new Integer(7))); + assertSame(new Integer(7), a.get(this)); + } + + /** + * getAndSet returns previous value and sets to given value + */ + @Test + public void testGetAndSet() { + AtomicReferenceFieldUpdater a; + a = updaterFor("x"); + x = new Integer(1); + assertSame(new Integer(1), a.getAndSet(this, new Integer(0))); + assertSame(new Integer(0), a.getAndSet(this, new Integer(-10))); + assertSame(new Integer(-10), a.getAndSet(this, new Integer(1))); + } + +} diff --git a/test/jdk/javax/sound/sampled/Clip/SetPositionHang.java b/test/jdk/javax/sound/sampled/Clip/SetPositionHang.java index 0805dfc45a03..d893c60a5c64 100644 --- a/test/jdk/javax/sound/sampled/Clip/SetPositionHang.java +++ b/test/jdk/javax/sound/sampled/Clip/SetPositionHang.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ /** * @test * @bug 8266421 8269091 + * @key sound * @summary Tests that Clip.setFramePosition/setMicrosecondPosition do not hang. */ public final class SetPositionHang implements Runnable { diff --git a/test/jdk/javax/swing/JTable/7124218/SelectEditTableCell.java b/test/jdk/javax/swing/JTable/7124218/SelectEditTableCell.java index 455c29c4696b..e319dd4fbb48 100644 --- a/test/jdk/javax/swing/JTable/7124218/SelectEditTableCell.java +++ b/test/jdk/javax/swing/JTable/7124218/SelectEditTableCell.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,11 +26,10 @@ * @key headful * @bug 7124218 * @summary verifies different behaviour of SPACE and ENTER in JTable - * @library ../../regtesthelpers - * @build Util * @run main SelectEditTableCell */ import java.awt.Point; +import java.awt.Rectangle; import java.awt.Robot; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; @@ -99,13 +98,16 @@ public void run() { } private static void runTestCase() throws Exception { - Point centerPoint; - centerPoint = Util.getCenterPoint(table); - LookAndFeel lookAndFeel = UIManager.getLookAndFeel(); + Rectangle cellRect = table.getCellRect(0, 0, true); + Point centerPoint = new Point(cellRect.x + cellRect.width / 2,cellRect.y + cellRect.height / 2); + SwingUtilities.convertPointToScreen(centerPoint, table); + robot.mouseMove(centerPoint.x, centerPoint.y); - robot.mousePress(InputEvent.BUTTON1_MASK); - robot.mouseRelease(InputEvent.BUTTON1_MASK); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); robot.waitForIdle(); + robot.delay(500); + SwingUtilities.invokeAndWait(new Runnable() { @Override public void run() { @@ -120,7 +122,7 @@ public void run() { }); int fetchKeyCode; - keyTap(fetchKeyCode = isMac(lookAndFeel) + keyTap(fetchKeyCode = isMac(UIManager.getLookAndFeel()) ? KeyEvent.VK_ENTER : KeyEvent.VK_SPACE); final int keyCode = fetchKeyCode; robot.waitForIdle(); diff --git a/test/jdk/tools/jlink/JLinkToolProviderTest.java b/test/jdk/tools/jlink/JLinkToolProviderTest.java index cb1f2f90c439..94959dcc8d80 100644 --- a/test/jdk/tools/jlink/JLinkToolProviderTest.java +++ b/test/jdk/tools/jlink/JLinkToolProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,12 @@ import java.io.PrintWriter; import java.io.StringWriter; +import java.nio.file.Path; +import java.util.List; +import java.util.concurrent.Phaser; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.stream.IntStream; import java.util.spi.ToolProvider; /* @@ -42,8 +48,33 @@ private static void checkJlinkOptions(String... options) { JLINK_TOOL.run(pw, pw, options); } + private static void checkConcurrentAccess(int count) throws Exception { + Phaser startBarrier = new Phaser(count); + + try (var executor = Executors.newFixedThreadPool(count)) { + List> futures = IntStream.range(0, count).mapToObj(idx -> executor.submit(() -> { + startBarrier.arriveAndAwaitAdvance(); + + StringWriter out = new StringWriter(); + StringWriter err = new StringWriter(); + int code = JLINK_TOOL.run(new PrintWriter(out), new PrintWriter(err), + "--add-modules", "java.base", + "--output", Path.of(".").resolve("image-" + idx).toString()); + return code + ":" + out.toString().trim() + ":" + err.toString().trim(); + })).toList(); + + for (Future future : futures) { + String result = future.get(); + if (!"0::".equals(result)) { + throw new AssertionError(result); + } + } + } + } + public static void main(String[] args) throws Exception { checkJlinkOptions("--help"); checkJlinkOptions("--list-plugins"); + checkConcurrentAccess(Math.max(2, Runtime.getRuntime().availableProcessors() / 4)); } } diff --git a/test/lib-test/jdk/test/whitebox/CPUInfoTest.java b/test/lib-test/jdk/test/whitebox/CPUInfoTest.java index 809953a12636..3541d64aaf55 100644 --- a/test/lib-test/jdk/test/whitebox/CPUInfoTest.java +++ b/test/lib-test/jdk/test/whitebox/CPUInfoTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -67,7 +67,7 @@ public class CPUInfoTest { "f16c", "pku", "ospke", "cet_ibt", "cet_ss", "avx512_ifma", "serialize", "avx_ifma", "apx_f", "avx10_1", "avx10_2", "avx512_fp16", - "sha512", "hybrid" + "sha512", "hybrid", "fast_bmi2" ); // @formatter:on // Checkstyle: resume diff --git a/test/lib/jdk/test/lib/cds/CDSAppTester.java b/test/lib/jdk/test/lib/cds/CDSAppTester.java index 0be21b9ec274..07b2981d4d39 100644 --- a/test/lib/jdk/test/lib/cds/CDSAppTester.java +++ b/test/lib/jdk/test/lib/cds/CDSAppTester.java @@ -542,7 +542,7 @@ public void runAOTWorkflow(String... args) throws Exception { } // See JEP 483; stop at the assembly run; do not execute production run - public void runAOTAssemblyWorkflow() throws Exception { + public void runAOTTrainingAndAssemblyWorkflow() throws Exception { this.workflow = Workflow.AOT; recordAOTConfiguration(); createAOTCache(); diff --git a/test/lib/jdk/test/lib/cds/SimpleCDSAppTester.java b/test/lib/jdk/test/lib/cds/SimpleCDSAppTester.java index 73e45711fe91..ab8f85961b65 100644 --- a/test/lib/jdk/test/lib/cds/SimpleCDSAppTester.java +++ b/test/lib/jdk/test/lib/cds/SimpleCDSAppTester.java @@ -98,6 +98,12 @@ public SimpleCDSAppTester addVmArgs(String... args) { return this; } + // Replace VM args with new list + public SimpleCDSAppTester setVmArgs(String... args) { + vmArgs = args; + return this; + } + public SimpleCDSAppTester appCommandLine(String... args) { this.appCommandLine = args; return this; @@ -207,6 +213,16 @@ public SimpleCDSAppTester runAOTWorkflow() throws Exception { return this; } + public SimpleCDSAppTester runAOTTrainingAndAssemblyWorkflow() throws Exception { + tester.runAOTTrainingAndAssemblyWorkflow(); + return this; + } + + public SimpleCDSAppTester productionRun() throws Exception { + tester.productionRun(); + return this; + } + public SimpleCDSAppTester run(String args[]) throws Exception { tester.run(args); return this; @@ -226,7 +242,8 @@ public String aotCacheFile() { return tester.aotCacheFile(); } - public void setCheckExitValue(boolean b) { + public SimpleCDSAppTester setCheckExitValue(boolean b) { tester.setCheckExitValue(b); + return this; } } diff --git a/test/lib/jdk/test/lib/containers/systemd/SystemdTestUtils.java b/test/lib/jdk/test/lib/containers/systemd/SystemdTestUtils.java index 5a868b8cd2a7..724c6a5a535c 100644 --- a/test/lib/jdk/test/lib/containers/systemd/SystemdTestUtils.java +++ b/test/lib/jdk/test/lib/containers/systemd/SystemdTestUtils.java @@ -249,7 +249,7 @@ private static String getMemorySlice(SystemdRunOptions runOpts, String sliceName private static String getMemoryDSliceContent(SystemdRunOptions runOpts) { String format = "[Slice]\n" + basicMemoryContentFormat(); - return String.format(format, runOpts.sliceDMemoryLimit); + return String.format(format, runOpts.sliceDMemoryLimit, runOpts.sliceDMemoryLimit); } private static String getCPUDSliceContent(SystemdRunOptions runOpts) { @@ -268,13 +268,14 @@ private static String basicMemoryContentFormat() { return """ MemoryAccounting=true MemoryLimit=%s + MemoryMax=%s """; } private static String getMemorySliceContent(SystemdRunOptions runOpts) { String format = basicMemoryContentFormat(); - return String.format(format, runOpts.memoryLimit); + return String.format(format, runOpts.memoryLimit, runOpts.memoryLimit); } private static String getBasicSliceFormat() { diff --git a/test/lib/jdk/test/lib/util/FileUtils.java b/test/lib/jdk/test/lib/util/FileUtils.java index b5b49350389b..585d156999c1 100644 --- a/test/lib/jdk/test/lib/util/FileUtils.java +++ b/test/lib/jdk/test/lib/util/FileUtils.java @@ -263,7 +263,7 @@ public static boolean areFileSystemsAccessible() throws IOException { * File systems are considered to be accessible if this process completes * successfully before a given fixed duration has elapsed. * - * @implNote On Unix this executes the {@code df} command in a separate + * @implNote On Unix this executes the {@code df -a} command in a separate * process and on Windows always returns {@code true}. * * @return whether file systems appear to be accessible and duplicate-free @@ -274,7 +274,7 @@ public static boolean areMountPointsAccessibleAndUnique() { final AtomicBoolean areMountPointsOK = new AtomicBoolean(true); Thread thr = new Thread(() -> { try { - Process proc = new ProcessBuilder("df").start(); + Process proc = new ProcessBuilder("df", "-a").start(); BufferedReader reader = new BufferedReader (new InputStreamReader(proc.getInputStream())); // Skip the first line as it is the "df" output header.