diff --git a/.gitignore b/.gitignore index a45e21137561..4a721ec0954d 100644 --- a/.gitignore +++ b/.gitignore @@ -17,8 +17,10 @@ NashornProfile.txt **/JTwork/** /src/utils/LogCompilation/target/ /src/utils/LogCompilation/logc.jar -/.project/ -/.settings/ +**/.classpath +**/.factorypath +**/.project +**/.settings/ /compile_commands.json /.cache /.gdbinit diff --git a/make/hotspot/lib/CompileJvm.gmk b/make/hotspot/lib/CompileJvm.gmk index fd1591897c54..6d39cef9cdc6 100644 --- a/make/hotspot/lib/CompileJvm.gmk +++ b/make/hotspot/lib/CompileJvm.gmk @@ -207,6 +207,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJVM, \ DISABLED_WARNINGS_gcc_jvmFlag.cpp := unused-const-variable, \ DISABLED_WARNINGS_gcc_jvmtiTagMap.cpp := stringop-overflow, \ DISABLED_WARNINGS_gcc_macroAssembler_ppc_sha.cpp := unused-const-variable, \ + DISABLED_WARNINGS_gcc_mountUnmountDisabler.cpp := stringop-overflow, \ DISABLED_WARNINGS_gcc_postaloc.cpp := address, \ DISABLED_WARNINGS_gcc_safepointMechanism.cpp := stringop-overflow, \ DISABLED_WARNINGS_gcc_shenandoahGenerationalHeap.cpp := stringop-overflow, \ diff --git a/make/hotspot/lib/JvmFeatures.gmk b/make/hotspot/lib/JvmFeatures.gmk index 7097bce89e1d..5b6e5c423e45 100644 --- a/make/hotspot/lib/JvmFeatures.gmk +++ b/make/hotspot/lib/JvmFeatures.gmk @@ -121,10 +121,7 @@ ifneq ($(call check-jvm-feature, cds), true) JVM_CFLAGS_FEATURES += -DINCLUDE_CDS=0 JVM_EXCLUDE_FILES += \ aotCodeCache.cpp \ - classLoaderDataShared.cpp \ - classLoaderExt.cpp \ aotCompileBroker.cpp \ - systemDictionaryShared.cpp \ trainingData.cpp JVM_EXCLUDE_PATTERNS += cds/ endif diff --git a/make/jdk/src/classes/build/tools/intpoly/FieldGen.java b/make/jdk/src/classes/build/tools/intpoly/FieldGen.java index c29a315f368b..4648ce0cd77e 100644 --- a/make/jdk/src/classes/build/tools/intpoly/FieldGen.java +++ b/make/jdk/src/classes/build/tools/intpoly/FieldGen.java @@ -88,24 +88,6 @@ private static List P256CrSequence() { return result; } - static FieldParams P384 = new FieldParams( - "IntegerPolynomialP384", 28, 14, 2, 384, - Arrays.asList( - new Term(128, -1), - new Term(96, -1), - new Term(32, 1), - new Term(0, -1) - ), - P384CrSequence(), simpleSmallCrSequence(14) - ); - - private static List P384CrSequence() { - List result = new ArrayList(); - result.addAll(fullReduce(14)); - result.addAll(simpleSmallCrSequence(14)); - return result; - } - static FieldParams P521 = new FieldParams( "IntegerPolynomialP521", 28, 19, 2, 521, Arrays.asList( @@ -194,7 +176,7 @@ private static List orderFieldSmallCrSequence(int numLimbs) { } static final FieldParams[] ALL_FIELDS = { - Curve448, P256, P384, P521, O256, O384, O521, O25519, O448 + Curve448, P256, P521, O256, O384, O521, O25519, O448 }; public static class Term { diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 4d0e769042a5..0a4b2d479ae5 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -1701,6 +1701,34 @@ int MachCallRuntimeNode::ret_addr_offset() const { //============================================================================= +void MachUncommonTrapNode::emit_impl(C2_MacroAssembler* masm, PhaseRegAlloc* ra_) const { + address entry_point = OptoRuntime::uncommon_trap_blob()->entry_point(); + address runtime_call = __ trampoline_call(Address(entry_point, relocInfo::runtime_call_type)); + if (runtime_call == nullptr) { + ciEnv::current()->record_failure("Failed to emit uncommon trap trampoline call"); + return; + } + + __ emit_int32(trap_request()); +} + +uint MachUncommonTrapNode::size_impl(PhaseRegAlloc* ra_) const { + // BL followed by 32-bit integer constant. + // + // If target address cannot be directly encoded into the BL, + // a trampoline in the stub code will be emitted and used to reach + // the uncommon trap blob, similarly to trampolines for static calls + // into compiled Java methods. The size of the trampoline is + // accounted for separately. + return NativeInstruction::instruction_size + sizeof(int32_t); +} + +int MachUncommonTrapNode::ret_addr_offset_impl() const { + return NativeInstruction::instruction_size; +} + +//============================================================================= + #ifndef PRODUCT void MachBreakpointNode::format(PhaseRegAlloc *ra_, outputStream *st) const { st->print("BREAKPOINT"); @@ -3657,10 +3685,16 @@ encode %{ } } - __ post_call_nop(); + int trap_request; + bool is_trap = is_uncommon_trap(&trap_request); + if (is_trap) { + __ emit_int32(trap_request); + } else { + __ post_call_nop(); + } // Only non uncommon_trap calls need to reinitialize ptrue. - if (Compile::current()->max_vector_size() > 0 && uncommon_trap_request() == 0) { + if (Compile::current()->max_vector_size() > 0 && !is_trap) { __ reinitialize_ptrue(); } %} @@ -9315,21 +9349,6 @@ instruct subI_reg_reg(iRegINoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{ ins_pipe(ialu_reg_reg); %} -// Immediate Subtraction -instruct subI_reg_imm(iRegINoSp dst, iRegIorL2I src1, immIAddSub src2) %{ - match(Set dst (SubI src1 src2)); - - ins_cost(INSN_COST); - format %{ "subw $dst, $src1, $src2" %} - - // use opcode to indicate that this is a sub not an add - opcode(0x1); - - ins_encode(aarch64_enc_addsubw_imm(dst, src1, src2)); - - ins_pipe(ialu_reg_imm); -%} - // Long Subtraction instruct subL_reg_reg(iRegLNoSp dst, iRegL src1, iRegL src2) %{ @@ -9347,21 +9366,6 @@ instruct subL_reg_reg(iRegLNoSp dst, iRegL src1, iRegL src2) %{ ins_pipe(ialu_reg_reg); %} -// No constant pool entries requiredLong Immediate Subtraction. -instruct subL_reg_imm(iRegLNoSp dst, iRegL src1, immLAddSub src2) %{ - match(Set dst (SubL src1 src2)); - - ins_cost(INSN_COST); - format %{ "sub$dst, $src1, $src2" %} - - // use opcode to indicate that this is a sub not an add - opcode(0x1); - - ins_encode( aarch64_enc_addsub_imm(dst, src1, src2) ); - - ins_pipe(ialu_reg_imm); -%} - // Integer Negation (special case for sub) instruct negI_reg(iRegINoSp dst, iRegIorL2I src, immI0 zero, rFlagsReg cr) %{ diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index c9d5d9b4dedb..fc571f369a13 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -498,6 +498,7 @@ void LIR_Assembler::return_op(LIR_Opr result, C1SafepointPollStub* code_stub) { Label not_null; __ cbnz(r0, not_null); // Returned value is null, zero all return registers because they may belong to oop fields + __ mov(j_rarg0, zr); __ mov(j_rarg1, zr); __ mov(j_rarg2, zr); __ mov(j_rarg3, zr); @@ -2415,7 +2416,7 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { } // if we don't know anything, just go through the generic arraycopy - if (default_type == nullptr // || basic_type == T_OBJECT + if (default_type == nullptr || !default_type->is_loaded() // || basic_type == T_OBJECT ) { Label done; assert(src == r1 && src_pos == r2, "mismatch in calling convention"); diff --git a/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp index 674f79afbb32..e00a2a459e6c 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp @@ -1173,7 +1173,8 @@ void LIRGenerator::do_NewObjectArray(NewObjectArray* x) { // in case of patching (i.e., object class is not yet loaded), we need to reexecute the instruction // and therefore provide the state before the parameters have been consumed CodeEmitInfo* patching_info = nullptr; - if (!x->klass()->is_loaded() || PatchALot) { + ciKlass* obj = ciObjArrayKlass::make(x->klass()); + if (!x->klass()->is_loaded() || !obj->is_loaded() || PatchALot) { patching_info = state_for(x, x->state_before()); } @@ -1189,15 +1190,13 @@ void LIRGenerator::do_NewObjectArray(NewObjectArray* x) { length.load_item_force(FrameMap::r19_opr); LIR_Opr len = length.result(); - ciKlass* obj = ciObjArrayKlass::make(x->klass()); - // TODO 8265122 Implement a fast path for this bool is_flat = obj->is_loaded() && obj->is_flat_array_klass(); bool is_null_free = obj->is_loaded() && obj->as_array_klass()->is_elem_null_free(); CodeStub* slow_path = new NewObjectArrayStub(klass_reg, len, reg, info, is_null_free); - if (obj == ciEnv::unloaded_ciobjarrayklass()) { - BAILOUT("encountered unloaded_ciobjarrayklass due to out of memory error"); + if (x->klass()->is_loaded() && !obj->is_loaded()) { + BAILOUT("encountered unloaded ciobjarrayklass due to out of memory error"); } klass2reg_with_patching(klass_reg, obj, patching_info); diff --git a/src/hotspot/cpu/aarch64/frame_aarch64.cpp b/src/hotspot/cpu/aarch64/frame_aarch64.cpp index e2fc296a7abe..0830605a6567 100644 --- a/src/hotspot/cpu/aarch64/frame_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/frame_aarch64.cpp @@ -106,6 +106,13 @@ bool frame::safe_for_sender(JavaThread *thread) { if (_cb->is_nmethod() || _cb->is_adapter_blob() || _cb->is_runtime_stub()) { return false; } + + if (_cb->is_uncommon_trap_stub() && + !(thread->has_last_Java_frame() && thread->frame_anchor()->walkable())) { + // The frame might not be safe to walk when the frame anchor is missing. + // For example, RegisterSaver adjusts the stack pointer as it spills the registers. + return false; + } } // Could just be some random pointer within the codeBlob diff --git a/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.ad b/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.ad index c4af02102b86..c68b8797b0c5 100644 --- a/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.ad +++ b/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.ad @@ -633,33 +633,6 @@ instruct g1CompareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN newval, iRegPNo ins_pipe(pipe_slow); %} -// This pattern is generated automatically from g1_aarch64.m4. -// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE -instruct g1GetAndSetP(indirect mem, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp preval, rFlagsReg cr) -%{ - predicate(UseG1GC && !needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() != 0); - match(Set preval (GetAndSetP mem newval)); - effect(TEMP preval, TEMP tmp1, TEMP tmp2, KILL cr); - ins_cost(2 * VOLATILE_REF_COST); - format %{ "atomic_xchg $preval, $newval, [$mem]" %} - ins_encode %{ - assert_different_registers($mem$$Register, $newval$$Register); - write_barrier_pre(masm, this, - $mem$$Register /* obj */, - $preval$$Register /* pre_val (as a temporary register) */, - $tmp1$$Register /* tmp1 */, - $tmp2$$Register /* tmp2 */, - RegSet::of($mem$$Register, $preval$$Register, $newval$$Register) /* preserve */); - __ atomic_xchg($preval$$Register, $newval$$Register, $mem$$Register); - write_barrier_post(masm, this, - $mem$$Register /* store_addr */, - $newval$$Register /* new_val */, - $tmp1$$Register /* tmp1 */, - $tmp2$$Register /* tmp2 */); - %} - ins_pipe(pipe_serial); -%} - // This pattern is generated automatically from g1_aarch64.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE instruct g1GetAndSetPAcq(indirect mem, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp preval, rFlagsReg cr) @@ -687,34 +660,6 @@ instruct g1GetAndSetPAcq(indirect mem, iRegP newval, iRegPNoSp tmp1, iRegPNoSp t ins_pipe(pipe_serial); %} -// This pattern is generated automatically from g1_aarch64.m4. -// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE -instruct g1GetAndSetN(indirect mem, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegNNoSp preval, rFlagsReg cr) -%{ - predicate(UseG1GC && !needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() != 0); - match(Set preval (GetAndSetN mem newval)); - effect(TEMP preval, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); - ins_cost(2 * VOLATILE_REF_COST); - format %{ "atomic_xchgw $preval, $newval, [$mem]" %} - ins_encode %{ - assert_different_registers($mem$$Register, $newval$$Register); - write_barrier_pre(masm, this, - $mem$$Register /* obj */, - $tmp1$$Register /* pre_val */, - $tmp2$$Register /* tmp1 */, - $tmp3$$Register /* tmp2 */, - RegSet::of($mem$$Register, $preval$$Register, $newval$$Register) /* preserve */); - __ atomic_xchgw($preval$$Register, $newval$$Register, $mem$$Register); - __ decode_heap_oop($tmp1$$Register, $newval$$Register); - write_barrier_post(masm, this, - $mem$$Register /* store_addr */, - $tmp1$$Register /* new_val */, - $tmp2$$Register /* tmp1 */, - $tmp3$$Register /* tmp2 */); - %} - ins_pipe(pipe_serial); -%} - // This pattern is generated automatically from g1_aarch64.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE instruct g1GetAndSetNAcq(indirect mem, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegNNoSp preval, rFlagsReg cr) diff --git a/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.m4 b/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.m4 index 63b464ceb8c3..b4b1d61c4228 100644 --- a/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.m4 +++ b/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.m4 @@ -1,4 +1,4 @@ -dnl Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. +dnl Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. dnl DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. dnl dnl This code is free software; you can redistribute it and/or modify it @@ -299,7 +299,6 @@ instruct g1GetAndSetP$1(indirect mem, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tm %} ins_pipe(pipe_serial); %}')dnl -XCHGP_INSN(,,atomic_xchg) XCHGP_INSN(Acq,_acq,atomic_xchgal) dnl define(`XCHGN_INSN', @@ -331,7 +330,6 @@ instruct g1GetAndSetN$1(indirect mem, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tm %} ins_pipe(pipe_serial); %}')dnl -XCHGN_INSN(,atomic_xchgw,atomic_xchgw) XCHGN_INSN(Acq,atomic_xchgw_acq,atomic_xchgalw) // This pattern is generated automatically from g1_aarch64.m4. diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoah_aarch64.ad b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoah_aarch64.ad index fa18bc46bda3..e936eae532e1 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoah_aarch64.ad +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoah_aarch64.ad @@ -246,34 +246,6 @@ instruct encodePAndStoreN_Volatile_shenandoah(indirect mem, iRegP src, iRegPNoSp // ---------------------- LOAD-STORES ----------------------------------- // -instruct compareAndSwap_P_shenandoah(iRegINoSp res, indirect mem, iRegPNoSp oldval, iRegP newval, iRegPNoSp tmp, rFlagsReg cr) -%{ - match(Set res (CompareAndSwapP mem (Binary oldval newval))); - predicate(UseShenandoahGC && !needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() != 0); - effect(TEMP_DEF res, TEMP tmp, KILL cr); - format %{ - "cmpxchg_P_shenandoah $mem, $oldval, $newval\t# (ptr) if $mem == $oldval then $mem <-- $newval" - %} - ins_encode %{ - assert($mem$$index == -1 && $mem$$disp == 0, "impossible encoding"); - - ShenandoahBarrierSet::assembler()->compare_and_set_c2(this, masm, - $res$$Register, - $mem$$base$$Register, - $oldval$$Register, - $newval$$Register, - $tmp$$Register, - rscratch1, - rscratch2, - /* exchange = */ false, - /* narrow = */ false, - /* weak = */ false, - /* acquire = */ false); - %} - ins_cost(VOLATILE_REF_COST); - ins_pipe(pipe_slow); -%} - instruct compareAndSwap_P_A_shenandoah(iRegINoSp res, indirect mem, iRegPNoSp oldval, iRegP newval, iRegPNoSp tmp, rFlagsReg cr) %{ match(Set res (CompareAndSwapP mem (Binary oldval newval))); @@ -302,34 +274,6 @@ instruct compareAndSwap_P_A_shenandoah(iRegINoSp res, indirect mem, iRegPNoSp ol ins_pipe(pipe_slow); %} -instruct compareAndSwap_N_shenandoah(iRegINoSp res, indirect mem, iRegNNoSp oldval, iRegN newval, iRegPNoSp tmp, rFlagsReg cr) -%{ - match(Set res (CompareAndSwapN mem (Binary oldval newval))); - predicate(UseShenandoahGC && !needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() != 0); - effect(TEMP_DEF res, TEMP tmp, KILL cr); - format %{ - "cmpxchg_N_shenandoah $mem, $oldval, $newval\t# (ptr) if $mem == $oldval then $mem <-- $newval" - %} - ins_encode %{ - assert($mem$$index == -1 && $mem$$disp == 0, "impossible encoding"); - - ShenandoahBarrierSet::assembler()->compare_and_set_c2(this, masm, - $res$$Register, - $mem$$base$$Register, - $oldval$$Register, - $newval$$Register, - $tmp$$Register, - rscratch1, - rscratch2, - /* exchange = */ false, - /* narrow = */ true, - /* weak = */ false, - /* acquire = */ false); - %} - ins_cost(VOLATILE_REF_COST); - ins_pipe(pipe_slow); -%} - instruct compareAndSwap_N_A_shenandoah(iRegINoSp res, indirect mem, iRegNNoSp oldval, iRegN newval, iRegPNoSp tmp, rFlagsReg cr) %{ match(Set res (CompareAndSwapN mem (Binary oldval newval))); @@ -586,26 +530,6 @@ instruct weakCompareAndSwap_P_A_shenandoah(iRegINoSp res, indirect mem, iRegPNoS ins_pipe(pipe_slow); %} -instruct getAndSet_P_shenandoah(indirect mem, iRegP newval, iRegPNoSp preval, iRegPNoSp tmp, rFlagsReg cr) -%{ - match(Set preval (GetAndSetP mem newval)); - predicate(UseShenandoahGC && !needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() != 0); - effect(TEMP_DEF preval, TEMP tmp, KILL cr); - format %{ "atomic_xchg $preval, $newval, [$mem]" %} - ins_encode %{ - ShenandoahBarrierSet::assembler()->get_and_set_c2(this, masm, - $preval$$Register, - $newval$$Register, - $mem$$Register, - $tmp$$Register, - rscratch1, - rscratch2, - /* acquire = */ false); - %} - ins_cost(2*VOLATILE_REF_COST); - ins_pipe(pipe_serial); -%} - instruct getAndSet_P_A_shenandoah(indirect mem, iRegP newval, iRegPNoSp preval, iRegPNoSp tmp, rFlagsReg cr) %{ match(Set preval (GetAndSetP mem newval)); @@ -626,26 +550,6 @@ instruct getAndSet_P_A_shenandoah(indirect mem, iRegP newval, iRegPNoSp preval, ins_pipe(pipe_serial); %} -instruct getAndSet_N_shenandoah(indirect mem, iRegN newval, iRegNNoSp preval, iRegPNoSp tmp, rFlagsReg cr) -%{ - match(Set preval (GetAndSetN mem newval)); - predicate(UseShenandoahGC && !needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() != 0); - effect(TEMP_DEF preval, TEMP tmp, KILL cr); - format %{ "atomic_xchgw $preval, $newval, [$mem]" %} - ins_encode %{ - ShenandoahBarrierSet::assembler()->get_and_set_c2(this, masm, - $preval$$Register, - $newval$$Register, - $mem$$Register, - $tmp$$Register, - rscratch1, - rscratch2, - /* acquire = */ false); - %} - ins_cost(2*VOLATILE_REF_COST); - ins_pipe(pipe_serial); -%} - instruct getAndSet_N_A_shenandoah(indirect mem, iRegN newval, iRegNNoSp preval, iRegPNoSp tmp, rFlagsReg cr) %{ match(Set preval (GetAndSetN mem newval)); diff --git a/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad b/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad index 11b70e4e8384..36b8463d3cc8 100644 --- a/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad +++ b/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad @@ -1,5 +1,5 @@ // -// Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. // // This code is free software; you can redistribute it and/or modify it @@ -283,24 +283,6 @@ instruct zCompareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iReg ins_pipe(pipe_slow); %} -instruct zGetAndSetP(indirect mem, iRegP newv, iRegPNoSp prev, rFlagsReg cr) %{ - match(Set prev (GetAndSetP mem newv)); - predicate(UseZGC && !needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() != 0); - effect(TEMP prev, KILL cr); - - ins_cost(2 * VOLATILE_REF_COST); - - format %{ "atomic_xchg $prev, $newv, [$mem]" %} - - ins_encode %{ - z_store_barrier(masm, this, Address($mem$$Register), $newv$$Register, $prev$$Register, rscratch2, true /* is_atomic */); - __ atomic_xchg($prev$$Register, $prev$$Register, $mem$$Register); - z_uncolor(masm, $prev$$Register); - %} - - ins_pipe(pipe_serial); -%} - instruct zGetAndSetPAcq(indirect mem, iRegP newv, iRegPNoSp prev, rFlagsReg cr) %{ match(Set prev (GetAndSetP mem newv)); predicate(UseZGC && needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() != 0); diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp index caa75144893c..10124ef0ce52 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp @@ -692,6 +692,7 @@ void InterpreterMacroAssembler::remove_activation(TosState state, Label not_null; cbnz(r0, not_null); // Returned value is null, zero all return registers because they may belong to oop fields + mov(j_rarg0, zr); mov(j_rarg1, zr); mov(j_rarg2, zr); mov(j_rarg3, zr); diff --git a/src/hotspot/cpu/aarch64/matcher_aarch64.hpp b/src/hotspot/cpu/aarch64/matcher_aarch64.hpp index ede5a4814950..14143600d678 100644 --- a/src/hotspot/cpu/aarch64/matcher_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/matcher_aarch64.hpp @@ -115,6 +115,10 @@ // C code as the Java calling convention forces doubles to be aligned. static const bool misaligned_doubles_ok = true; + // Use MachUncommonTrapNode for representing uncommon trap sequences. + // This is an alternative to representing uncommon traps as runtime calls. + static const bool use_mach_uncommon_trap_node = true; + // Are floats converted to double when stored to stack during // deoptimization? static constexpr bool float_in_double() { return false; } diff --git a/src/hotspot/cpu/aarch64/registerSaver_aarch64.hpp b/src/hotspot/cpu/aarch64/registerSaver_aarch64.hpp new file mode 100644 index 000000000000..f10681287291 --- /dev/null +++ b/src/hotspot/cpu/aarch64/registerSaver_aarch64.hpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2021, Red Hat Inc. All rights reserved. + * Copyright (c) 2021, Azul Systems, Inc. All rights reserved. + * 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 CPU_AARCH64_REGISTER_SAVER_AARCH64_HPP +#define CPU_AARCH64_REGISTER_SAVER_AARCH64_HPP + +#include "asm/macroAssembler.hpp" +#include "compiler/oopMap.hpp" + +class RegisterSaver { + const bool _save_vectors; + public: + RegisterSaver(bool save_vectors) : _save_vectors(save_vectors) {} + + OopMap* save_live_registers(MacroAssembler* masm, int additional_frame_words, int* total_frame_words); + void restore_live_registers(MacroAssembler* masm); + + // Offsets into the register save area + // Used by deoptimization when it is managing result register + // values on its own + + int reg_offset_in_bytes(Register r); + int r0_offset_in_bytes() { return reg_offset_in_bytes(r0); } + int rscratch1_offset_in_bytes() { return reg_offset_in_bytes(rscratch1); } + int v0_offset_in_bytes(); + + // Total stack size in bytes for saving SVE predicate registers. + int total_sve_predicate_in_bytes(); + + // Capture info about frame layout + // Note this is only correct when not saving full vectors. + enum layout { + fpu_state_off = 0, + fpu_state_end = fpu_state_off + FPUStateSizeInWords - 1, + // The frame sender code expects that rfp will be in + // the "natural" place and will override any oopMap + // setting for it. We must therefore force the layout + // so that it agrees with the frame sender code. + r0_off = fpu_state_off + FPUStateSizeInWords, + rfp_off = r0_off + (Register::number_of_registers - 2) * Register::max_slots_per_register, + return_off = rfp_off + Register::max_slots_per_register, // slot for return address + reg_save_size = return_off + Register::max_slots_per_register}; + +}; + +#endif // CPU_AARCH64_REGISTER_SAVER_AARCH64_HPP diff --git a/src/hotspot/cpu/aarch64/runtime_aarch64.cpp b/src/hotspot/cpu/aarch64/runtime_aarch64.cpp index 9620aba88f8c..00c8d175d10d 100644 --- a/src/hotspot/cpu/aarch64/runtime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/runtime_aarch64.cpp @@ -30,6 +30,7 @@ #include "code/vmreg.hpp" #include "interpreter/interpreter.hpp" #include "opto/runtime.hpp" +#include "registerSaver_aarch64.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/stubRoutines.hpp" @@ -75,25 +76,18 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() { return nullptr; } MacroAssembler* masm = new MacroAssembler(&buffer); - - assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); + OopMap* map = nullptr; + int frame_size_in_words; + int frame_size_in_bytes; + RegisterSaver reg_save(true); address start = __ pc(); - // Push self-frame. We get here with a return address in LR - // and sp should be 16 byte aligned - // push rfp and retaddr by hand - __ protect_return_address(); - __ stp(rfp, lr, Address(__ pre(sp, -2 * wordSize))); - // we don't expect an arg reg save area -#ifndef PRODUCT - assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); -#endif - // compiler left unloaded_class_index in j_rarg0 move to where the - // runtime expects it. - if (c_rarg1 != j_rarg0) { - __ movw(c_rarg1, j_rarg0); - } + map = reg_save.save_live_registers(masm, 0, &frame_size_in_words); + frame_size_in_bytes = frame_size_in_words * wordSize; + + __ authenticate_return_address(); + __ ldrw(c_rarg1, Address(lr)); // we need to set the past SP to the stack pointer of the stub frame // and the pc to the address where this runtime call will return @@ -119,7 +113,6 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() { // Set an oopmap for the call site OopMapSet* oop_maps = new OopMapSet(); - OopMap* map = new OopMap(SimpleRuntimeFrame::framesize, 0); // location of rfp is known implicitly by the frame sender code @@ -148,7 +141,7 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() { // 3: caller of deopting frame (could be compiled/interpreted). // Pop self-frame. We have no frame, and must rely only on r0 and sp. - __ add(sp, sp, (SimpleRuntimeFrame::framesize) << LogBytesPerInt); // Epilog! + __ add(sp, sp, frame_size_in_bytes); // Pop deoptimized frame (int) __ ldrw(r2, Address(r4, @@ -212,8 +205,12 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() { __ subsw(r3, r3, 1); // Decrement counter __ br(Assembler::GT, loop); __ ldr(lr, Address(r2, 0)); // save final return address + // Re-push self-frame + uint64_t frame_size_diff = frame_size_in_bytes; __ enter(); // & old rfp & set new rfp + frame_size_diff -= 2 * wordSize; + __ sub(sp, sp, frame_size_diff); // Use rfp because the frames look interpreted now // Save "the_pc" since it cannot easily be retrieved using the last_java_SP after we aligned SP. @@ -236,9 +233,9 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() { __ lea(rscratch1, RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames))); __ blr(rscratch1); - // Set an oopmap for the call site - // Use the same PC we used for the last java frame - oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); + // OopMap frame size is in compiler stack slots (jint) not bytes or words + int frame_size_in_slots = frame_size_in_bytes / BytesPerInt; + oop_maps->add_gc_map(the_pc - start, new OopMap(frame_size_in_slots, 0)); // Clear fp AND pc __ reset_last_Java_frame(true); @@ -252,7 +249,7 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() { // Code will be copied. No ICache sync required. UncommonTrapBlob *ut_blob = UncommonTrapBlob::create(&buffer, oop_maps, - SimpleRuntimeFrame::framesize >> 1); + frame_size_in_words); AOTCodeCache::store_code_blob(*ut_blob, AOTCodeEntry::C2Blob, BlobId::c2_uncommon_trap_id); return ut_blob; } diff --git a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp index bac01aecdedc..d194906f7cf9 100644 --- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp @@ -42,6 +42,7 @@ #include "oops/klass.inline.hpp" #include "oops/method.inline.hpp" #include "prims/methodHandles.hpp" +#include "registerSaver_aarch64.hpp" #include "runtime/continuation.hpp" #include "runtime/continuationEntry.inline.hpp" #include "runtime/globals.hpp" @@ -73,43 +74,6 @@ const int StackAlignmentInSlots = StackAlignmentInBytes / VMRegImpl::stack_slot_size; -// FIXME -- this is used by C1 -class RegisterSaver { - const bool _save_vectors; - public: - RegisterSaver(bool save_vectors) : _save_vectors(save_vectors) {} - - OopMap* save_live_registers(MacroAssembler* masm, int additional_frame_words, int* total_frame_words); - void restore_live_registers(MacroAssembler* masm); - - // Offsets into the register save area - // Used by deoptimization when it is managing result register - // values on its own - - int reg_offset_in_bytes(Register r); - int r0_offset_in_bytes() { return reg_offset_in_bytes(r0); } - int rscratch1_offset_in_bytes() { return reg_offset_in_bytes(rscratch1); } - int v0_offset_in_bytes(); - - // Total stack size in bytes for saving sve predicate registers. - int total_sve_predicate_in_bytes(); - - // Capture info about frame layout - // Note this is only correct when not saving full vectors. - enum layout { - fpu_state_off = 0, - fpu_state_end = fpu_state_off + FPUStateSizeInWords - 1, - // The frame sender code expects that rfp will be in - // the "natural" place and will override any oopMap - // setting for it. We must therefore force the layout - // so that it agrees with the frame sender code. - r0_off = fpu_state_off + FPUStateSizeInWords, - rfp_off = r0_off + (Register::number_of_registers - 2) * Register::max_slots_per_register, - return_off = rfp_off + Register::max_slots_per_register, // slot for return address - reg_save_size = return_off + Register::max_slots_per_register}; - -}; - int RegisterSaver::reg_offset_in_bytes(Register r) { // The integer registers are located above the floating point // registers in the stack frame pushed by save_live_registers() so the diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index b59c23489bea..abd93c1c74b2 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -8468,21 +8468,21 @@ class StubGenerator: public StubCodeGenerator { address generate_intpoly_assign() { // KNOWN Lengths: - // MontgomeryIntPolynP256: 5 = 4 + 1 - // IntegerPolynomial1305: 5 = 4 + 1 - // IntegerPolynomial25519: 10 = 8 + 2 + // MontgomeryIntPolynP256: 5 = 0 + 5 + // IntegerPolynomial1305: 5 = 0 + 5 + // IntegerPolynomial25519: 5 = 0 + 5 + // IntegerPolynomialP384: 7 = 6 + 1 // IntegerPolynomialP256: 10 = 8 + 2 // Curve25519OrderField: 10 = 8 + 2 // Curve25519OrderField: 10 = 8 + 2 // P256OrderField: 10 = 8 + 2 - // IntegerPolynomialP384: 14 = 8 + 4 + 2 - // P384OrderField: 14 = 8 + 4 + 2 - // IntegerPolynomial448: 16 = 8 + 8 - // Curve448OrderField: 16 = 8 + 8 - // Curve448OrderField: 16 = 8 + 8 - // IntegerPolynomialP521: 19 = 8 + 8 + 2 + 1 - // P521OrderField: 19 = 8 + 8 + 2 + 1 - // Special Cases 5, 10, 14, 16, 19 + // P384OrderField: 14 = 10 + 4 + // IntegerPolynomial448: 16 = 16 + 0 + // Curve448OrderField: 16 = 16 + 0 + // Curve448OrderField: 16 = 16 + 0 + // IntegerPolynomialP521: 19 = 16 + 3 + // P521OrderField: 19 = 16 + 3 + // Special Cases 5, 7, 10, 14, 16, 19 assert(UseIntPolyIntrinsics, "what are we doing here?"); StubId stub_id = StubId::stubgen_intpoly_assign_id; int entry_count = StubInfo::entry_count(stub_id); @@ -8503,7 +8503,7 @@ class StubGenerator: public StubCodeGenerator { const Register bLimbs = c_rarg2; const Register length = c_rarg3; - Label L_Length5, L_Length10, L_Length14, L_Length16, L_Length19, L_Default, L_Done; + Label L_Length5, L_Length7, L_Length10, L_Length14, L_Length16, L_Length19, L_Default, L_Done; /* int maskValue = -set; @@ -8520,6 +8520,8 @@ class StubGenerator: public StubCodeGenerator { __ cmp(length, (u1)5); __ br(Assembler::EQ, L_Length5); + __ cmp(length, (u1)7); + __ br(Assembler::EQ, L_Length7); __ cmp(length, (u1)10); __ br(Assembler::EQ, L_Length10); __ cmp(length, (u1)14); @@ -8588,6 +8590,47 @@ class StubGenerator: public StubCodeGenerator { __ b(L_Done); } + // Length = 7 + // Split into 3 neon regs and 1 GPR + __ BIND(L_Length7); + { + Register a7 = r10; + Register b7 = r11; + + VSeq<3> a_vec(16); + VSeq<3> b_vec(20); + + int offsets[1] = { 0 }; + + __ ldr(a7, Address(aLimbs, 6 * BytesPerLong)); + __ ldr(b7, Address(bLimbs, 6 * BytesPerLong)); + + __ ld1(a_vec[0], __ T2D, aLimbs); + vs_ldpq_indexed(vs_tail(a_vec), aLimbs, 16, offsets); + + __ eor(b7, b7, a7); + + __ ld1(b_vec[0], __ T2D, bLimbs); + vs_ldpq_indexed(vs_tail(b_vec), bLimbs, 16, offsets); + + __ andr(b7, b7, mask_scalar); + + vs_eor(b_vec, b_vec, a_vec); + + __ eor(a7, a7, b7); + + vs_andr(b_vec, b_vec, mask_vec); + + __ str(a7, Address(aLimbs, 6 * BytesPerLong)); + + vs_eor(a_vec, a_vec, b_vec); + + __ st1(a_vec[0], __ T2D, aLimbs); + vs_stpq_indexed(vs_tail(a_vec), aLimbs, 16, offsets); + + __ b(L_Done); + } + // Length = 10 // Split into 4 neon regs and 2 GPRs __ BIND(L_Length10); diff --git a/src/hotspot/cpu/arm/arm.ad b/src/hotspot/cpu/arm/arm.ad index 6b8b37f53b59..68180d5fa552 100644 --- a/src/hotspot/cpu/arm/arm.ad +++ b/src/hotspot/cpu/arm/arm.ad @@ -5799,18 +5799,6 @@ instruct rsbshrI_reg_imm_reg(iRegI dst, iRegI src1, immU5 src2, iRegI src3) %{ ins_pipe(ialu_reg_reg); %} -// Immediate Subtraction -instruct subI_reg_aimmI(iRegI dst, iRegI src1, aimmI src2) %{ - match(Set dst (SubI src1 src2)); - - size(4); - format %{ "sub_32 $dst,$src1,$src2\t! int" %} - ins_encode %{ - __ sub_32($dst$$Register, $src1$$Register, $src2$$constant); - %} - ins_pipe(ialu_reg_imm); -%} - instruct subI_reg_immRotneg(iRegI dst, iRegI src1, aimmIneg src2) %{ match(Set dst (AddI src1 src2)); @@ -5848,25 +5836,6 @@ instruct subL_reg_reg(iRegL dst, iRegL src1, iRegL src2, flagsReg icc ) %{ ins_pipe(ialu_reg_reg); %} -// TODO - -// Immediate Subtraction -// TODO: try immLRot2 instead, (0, $con$$constant) becomes -// (hi($con$$constant), lo($con$$constant)) becomes -instruct subL_reg_immRot(iRegL dst, iRegL src1, immLlowRot con, flagsReg icc) %{ - match(Set dst (SubL src1 con)); - effect (KILL icc); - - size(8); - format %{ "SUB $dst.lo,$src1.lo,$con\t! long\n\t" - "SBC $dst.hi,$src1.hi,0" %} - ins_encode %{ - __ subs($dst$$Register, $src1$$Register, $con$$constant); - __ sbc($dst$$Register->successor(), $src1$$Register->successor(), 0); - %} - ins_pipe(ialu_reg_imm); -%} - // Long negation instruct negL_reg_reg(iRegL dst, immL0 zero, iRegL src2, flagsReg icc) %{ match(Set dst (SubL zero src2)); diff --git a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp index 246ee211973a..4336fb6ac9d1 100644 --- a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp +++ b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp @@ -2115,7 +2115,7 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { if (basic_type == T_ARRAY) basic_type = T_OBJECT; // If we don't know anything or it's an object array, just go through the generic arraycopy - if (default_type == nullptr) { + if (default_type == nullptr || !default_type->is_loaded()) { // save arguments, because they will be killed by a runtime call save_in_reserved_area(R0, R1, R2, R3); diff --git a/src/hotspot/cpu/arm/c1_LIRGenerator_arm.cpp b/src/hotspot/cpu/arm/c1_LIRGenerator_arm.cpp index fd6f2979333e..ae18f80c52bf 100644 --- a/src/hotspot/cpu/arm/c1_LIRGenerator_arm.cpp +++ b/src/hotspot/cpu/arm/c1_LIRGenerator_arm.cpp @@ -982,7 +982,8 @@ void LIRGenerator::do_NewObjectArray(NewObjectArray* x) { LIR_Opr len = length.result(); CodeEmitInfo* patching_info = nullptr; - if (!x->klass()->is_loaded() || PatchALot) { + ciMetadata* obj = ciObjArrayKlass::make(x->klass()); + if (!x->klass()->is_loaded() || !obj->is_loaded() || PatchALot) { patching_info = state_for(x, x->state_before()); } @@ -995,8 +996,8 @@ void LIRGenerator::do_NewObjectArray(NewObjectArray* x) { LIR_Opr tmp4 = LIR_OprFact::illegalOpr; CodeStub* slow_path = new NewObjectArrayStub(klass_reg, len, reg, info); - ciMetadata* obj = ciObjArrayKlass::make(x->klass()); - if (obj == ciEnv::unloaded_ciobjarrayklass()) { + + if (x->klass()->is_loaded() && !obj->is_loaded()) { BAILOUT("encountered unloaded_ciobjarrayklass due to out of memory error"); } klass2reg_with_patching(klass_reg, obj, patching_info); diff --git a/src/hotspot/cpu/arm/matcher_arm.hpp b/src/hotspot/cpu/arm/matcher_arm.hpp index 7980838c5cca..5d3539b1e5d1 100644 --- a/src/hotspot/cpu/arm/matcher_arm.hpp +++ b/src/hotspot/cpu/arm/matcher_arm.hpp @@ -100,6 +100,10 @@ // Java calling convention forces doubles to be aligned. static const bool misaligned_doubles_ok = false; + // Use MachUncommonTrapNode for representing uncommon trap sequences. + // This is an alternative to representing uncommon traps as runtime calls. + static const bool use_mach_uncommon_trap_node = false; + // Are floats converted to double when stored to stack during deoptimization? // ARM does not handle callee-save floats. static constexpr bool float_in_double() { diff --git a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp index 6834cf1a6870..5d3cc8b7f607 100644 --- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp @@ -1819,7 +1819,7 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { // the known type isn't loaded since the code sanity checks // in debug mode and the type isn't required when we know the exact type // also check that the type is an array type. - if (default_type == nullptr) { + if (default_type == nullptr || !default_type->is_loaded()) { assert(src->is_nonvolatile() && src_pos->is_nonvolatile() && dst->is_nonvolatile() && dst_pos->is_nonvolatile() && length->is_nonvolatile(), "must preserve"); address copyfunc_addr = StubRoutines::generic_arraycopy(); @@ -2197,7 +2197,7 @@ void LIR_Assembler::shift_op(LIR_Code code, LIR_Opr left, jint count, LIR_Opr de #endif if (dest->is_single_cpu()) { - count = count & 0x1F; // Java spec + count &= right_n_bits(LogBitsPerInt); // Java spec if (count == 0) { __ mr_if_needed(dest->as_register(), left->as_register()); } else { switch (code) { @@ -2208,7 +2208,7 @@ void LIR_Assembler::shift_op(LIR_Code code, LIR_Opr left, jint count, LIR_Opr de } } } else if (dest->is_double_cpu()) { - count = count & 63; // Java spec + count &= right_n_bits(LogBitsPerLong); // Java spec if (count == 0) { __ mr_if_needed(dest->as_pointer_register(), left->as_pointer_register()); } else { switch (code) { diff --git a/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp index 3c87869879a3..3d639f63aee5 100644 --- a/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp @@ -886,7 +886,8 @@ void LIRGenerator::do_NewObjectArray(NewObjectArray* x) { // we need to reexecute the instruction and therefore provide // the state before the parameters have been consumed. CodeEmitInfo* patching_info = nullptr; - if (!x->klass()->is_loaded() || PatchALot) { + ciKlass* obj = ciObjArrayKlass::make(x->klass()); + if (!x->klass()->is_loaded() || !obj->is_loaded() || PatchALot) { patching_info = state_for(x, x->state_before()); } @@ -902,15 +903,13 @@ void LIRGenerator::do_NewObjectArray(NewObjectArray* x) { LIR_Opr tmp4 = FrameMap::R8_oop_opr; LIR_Opr len = length.result(); - ciKlass* obj = ciObjArrayKlass::make(x->klass()); - // TODO 8265122 Implement a fast path for this bool is_flat = obj->is_loaded() && obj->is_flat_array_klass(); bool is_null_free = obj->is_loaded() && obj->as_array_klass()->is_elem_null_free(); CodeStub* slow_path = new NewObjectArrayStub(klass_reg, len, reg, info, is_null_free); - if (obj == ciEnv::unloaded_ciobjarrayklass()) { - BAILOUT("encountered unloaded_ciobjarrayklass due to out of memory error"); + if (x->klass()->is_loaded() && !obj->is_loaded()) { + BAILOUT("encountered unloaded ciobjarrayklass due to out of memory error"); } klass2reg_with_patching(klass_reg, obj, patching_info); bool always_slow_path = is_null_free || is_flat; diff --git a/src/hotspot/cpu/ppc/matcher_ppc.hpp b/src/hotspot/cpu/ppc/matcher_ppc.hpp index 356682bb760b..c2e7c6b2fcdd 100644 --- a/src/hotspot/cpu/ppc/matcher_ppc.hpp +++ b/src/hotspot/cpu/ppc/matcher_ppc.hpp @@ -113,6 +113,10 @@ // Java calling convention forces doubles to be aligned. static const bool misaligned_doubles_ok = true; + // Use MachUncommonTrapNode for representing uncommon trap sequences. + // This is an alternative to representing uncommon traps as runtime calls. + static const bool use_mach_uncommon_trap_node = false; + // Do floats take an entire double register or just half? // // A float occupies a ppc64 double register. For the allocator, a diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index a3c36163e466..fd0ea4685372 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -2918,88 +2918,6 @@ encode %{ __ stfd($src$$FloatRegister, Idisp, $mem$$base$$Register); %} - enc_class postalloc_expand_encode_oop(iRegNdst dst, iRegPdst src, flagsReg crx) %{ - cmpP_reg_imm16Node *n_compare = new cmpP_reg_imm16Node(); - encodeP_subNode *n_sub_base = new encodeP_subNode(); - encodeP_shiftNode *n_shift = new encodeP_shiftNode(); - cond_set_0_oopNode *n_cond_set = new cond_set_0_oopNode(); - - n_compare->add_req(n_region, n_src); - n_compare->_opnds[0] = op_crx; - n_compare->_opnds[1] = op_src; - n_compare->_opnds[2] = new immL16Oper(0); - - n_sub_base->add_req(n_region, n_src); - n_sub_base->_opnds[0] = op_dst; - n_sub_base->_opnds[1] = op_src; - n_sub_base->_bottom_type = _bottom_type; - - n_shift->add_req(n_region, n_sub_base); - n_shift->_opnds[0] = op_dst; - n_shift->_opnds[1] = op_dst; - n_shift->_bottom_type = _bottom_type; - - n_cond_set->add_req(n_region, n_compare, n_shift); - n_cond_set->_opnds[0] = op_dst; - n_cond_set->_opnds[1] = op_crx; - n_cond_set->_opnds[2] = op_dst; - n_cond_set->_bottom_type = _bottom_type; - - ra_->set_pair(n_compare->_idx, ra_->get_reg_second(n_crx), ra_->get_reg_first(n_crx)); - ra_->set_pair(n_sub_base->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); - ra_->set_pair(n_shift->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); - ra_->set_pair(n_cond_set->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); - - nodes->push(n_compare); - nodes->push(n_sub_base); - nodes->push(n_shift); - nodes->push(n_cond_set); - - assert(!(ra_->is_oop(this)), "sanity"); // This is not supposed to be GC'ed. - %} - - enc_class postalloc_expand_decode_oop(iRegPdst dst, iRegNsrc src, flagsReg crx) %{ - decodeN_shiftNode *n_shift = new decodeN_shiftNode(); - cmpN_reg_imm0Node *n_compare = new cmpN_reg_imm0Node(); - - n_compare->add_req(n_region, n_src); - n_compare->_opnds[0] = op_crx; - n_compare->_opnds[1] = op_src; - n_compare->_opnds[2] = new immN_0Oper(TypeNarrowOop::NULL_PTR); - - n_shift->add_req(n_region, n_src); - n_shift->_opnds[0] = op_dst; - n_shift->_opnds[1] = op_src; - n_shift->_bottom_type = _bottom_type; - - decodeN_addNode *n_add_base = new decodeN_addNode(); - n_add_base->add_req(n_region, n_shift); - n_add_base->_opnds[0] = op_dst; - n_add_base->_opnds[1] = op_dst; - n_add_base->_bottom_type = _bottom_type; - - cond_set_0_ptrNode *n_cond_set = new cond_set_0_ptrNode(); - n_cond_set->add_req(n_region, n_compare, n_add_base); - n_cond_set->_opnds[0] = op_dst; - n_cond_set->_opnds[1] = op_crx; - n_cond_set->_opnds[2] = op_dst; - n_cond_set->_bottom_type = _bottom_type; - - assert(ra_->is_oop(this) == true, "A decodeN node must produce an oop!"); - ra_->set_oop(n_cond_set, true); - - ra_->set_pair(n_shift->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); - ra_->set_pair(n_compare->_idx, ra_->get_reg_second(n_crx), ra_->get_reg_first(n_crx)); - ra_->set_pair(n_add_base->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); - ra_->set_pair(n_cond_set->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); - - nodes->push(n_compare); - nodes->push(n_shift); - nodes->push(n_add_base); - nodes->push(n_cond_set); - - %} - // This enc_class is needed so that scheduler gets proper // input mapping for latency computation. enc_class enc_andc(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ @@ -5675,18 +5593,6 @@ instruct rldicl(iRegLdst dst, iRegLsrc src, immI16 shift, immI16 mask_begin) %{ ins_pipe(pipe_class_default); %} -// Optimize DecodeN for disjoint base. -// Load base of compressed oops into a register -instruct loadBase(iRegLdst dst) %{ - effect(DEF dst); - - format %{ "LoadConst $dst, heapbase" %} - ins_encode %{ - __ load_const_optimized($dst$$Register, CompressedOops::base(), R0); - %} - ins_pipe(pipe_class_default); -%} - instruct loadConN_31bit(iRegNdst dst, immN_31bit src) %{ match(Set dst src); ins_cost(DEFAULT_COST*2); @@ -6125,66 +6031,6 @@ instruct storeD(memory mem, regD src) %{ // Convert oop pointer into compressed form. -// Nodes for postalloc expand. - -// Shift node for expand. -instruct encodeP_shift(iRegNdst dst, iRegNsrc src) %{ - // The match rule is needed to make it a 'MachTypeNode'! - match(Set dst (EncodeP src)); - predicate(false); - - format %{ "SRDI $dst, $src, 3 \t// encode" %} - size(4); - ins_encode %{ - __ srdi($dst$$Register, $src$$Register, CompressedOops::shift()); - %} - ins_pipe(pipe_class_default); -%} - -// Add node for expand. -instruct encodeP_sub(iRegPdst dst, iRegPdst src) %{ - // The match rule is needed to make it a 'MachTypeNode'! - match(Set dst (EncodeP src)); - predicate(false); - - format %{ "SUB $dst, $src, oop_base \t// encode" %} - ins_encode %{ - __ sub_const_optimized($dst$$Register, $src$$Register, CompressedOops::base(), R0); - %} - ins_pipe(pipe_class_default); -%} - -// Conditional sub base. -instruct cond_sub_base(iRegNdst dst, flagsRegSrc crx, iRegPsrc src1) %{ - // The match rule is needed to make it a 'MachTypeNode'! - match(Set dst (EncodeP (Binary crx src1))); - predicate(false); - - format %{ "BEQ $crx, done\n\t" - "SUB $dst, $src1, heapbase \t// encode: subtract base if != nullptr\n" - "done:" %} - ins_encode %{ - Label done; - __ beq($crx$$CondRegister, done); - __ sub_const_optimized($dst$$Register, $src1$$Register, CompressedOops::base(), R0); - __ bind(done); - %} - ins_pipe(pipe_class_default); -%} - -instruct cond_set_0_oop(iRegNdst dst, flagsRegSrc crx, iRegPsrc src1) %{ - // The match rule is needed to make it a 'MachTypeNode'! - match(Set dst (EncodeP (Binary crx src1))); - predicate(false); - - format %{ "CMOVE $dst, $crx eq, 0, $src1 \t// encode: preserve 0" %} - size(4); - ins_encode %{ - __ isel_0($dst$$Register, $crx$$CondRegister, Assembler::equal, $src1$$Register); - %} - ins_pipe(pipe_class_default); -%} - // Disjoint narrow oop base. instruct encodeP_Disjoint(iRegNdst dst, iRegPsrc src) %{ match(Set dst (EncodeP src)); @@ -6199,15 +6045,21 @@ instruct encodeP_Disjoint(iRegNdst dst, iRegPsrc src) %{ %} // shift != 0, base != 0 -instruct encodeP_Ex(iRegNdst dst, flagsReg crx, iRegPsrc src) %{ +instruct encodeP(iRegNdst dst, flagsReg crx, iRegPsrc src) %{ match(Set dst (EncodeP src)); effect(TEMP crx); predicate(n->bottom_type()->make_ptr()->ptr() != TypePtr::NotNull && CompressedOops::shift() != 0 && CompressedOops::base_overlaps()); - format %{ "EncodeP $dst, $crx, $src \t// postalloc expanded" %} - postalloc_expand( postalloc_expand_encode_oop(dst, src, crx)); + format %{ "EncodeP $dst, $crx, $src" %} + ins_encode %{ + __ cmpdi($crx$$CondRegister, $src$$Register, 0); + __ sub_const_optimized($dst$$Register, $src$$Register, CompressedOops::base(), R0); + __ srdi($dst$$Register, $dst$$Register, CompressedOops::shift()); + __ isel_0($dst$$Register, $crx$$CondRegister, Assembler::equal); + %} + ins_pipe(pipe_class_default); %} // shift != 0, base != 0 @@ -6226,7 +6078,6 @@ instruct encodeP_not_null(iRegNdst dst, iRegPsrc src) %{ %} // shift != 0, base == 0 -// TODO: This is the same as encodeP_shift. Merge! instruct encodeP_not_null_base_null(iRegNdst dst, iRegPsrc src) %{ match(Set dst (EncodeP src)); predicate(CompressedOops::shift() != 0 && @@ -6256,82 +6107,23 @@ instruct encodeP_narrow_oop_shift_0(iRegNdst dst, iRegPsrc src) %{ // Decode nodes. -// Shift node for expand. -instruct decodeN_shift(iRegPdst dst, iRegPsrc src) %{ - // The match rule is needed to make it a 'MachTypeNode'! - match(Set dst (DecodeN src)); - predicate(false); - - format %{ "SLDI $dst, $src, #3 \t// DecodeN" %} - size(4); - ins_encode %{ - __ sldi($dst$$Register, $src$$Register, CompressedOops::shift()); - %} - ins_pipe(pipe_class_default); -%} - -// Add node for expand. -instruct decodeN_add(iRegPdst dst, iRegPdst src) %{ - // The match rule is needed to make it a 'MachTypeNode'! - match(Set dst (DecodeN src)); - predicate(false); - - format %{ "ADD $dst, $src, heapbase \t// DecodeN, add oop base" %} - ins_encode %{ - __ add_const_optimized($dst$$Register, $src$$Register, CompressedOops::base(), R0); - %} - ins_pipe(pipe_class_default); -%} - -// conditianal add base for expand -instruct cond_add_base(iRegPdst dst, flagsRegSrc crx, iRegPsrc src) %{ - // The match rule is needed to make it a 'MachTypeNode'! - // NOTICE that the rule is nonsense - we just have to make sure that: - // - _matrule->_rChild->_opType == "DecodeN" (see InstructForm::captures_bottom_type() in formssel.cpp) - // - we have to match 'crx' to avoid an "illegal USE of non-input: flagsReg crx" error in ADLC. - match(Set dst (DecodeN (Binary crx src))); - predicate(false); - - format %{ "BEQ $crx, done\n\t" - "ADD $dst, $src, heapbase \t// DecodeN: add oop base if $src != nullptr\n" - "done:" %} - ins_encode %{ - Label done; - __ beq($crx$$CondRegister, done); - __ add_const_optimized($dst$$Register, $src$$Register, CompressedOops::base(), R0); - __ bind(done); - %} - ins_pipe(pipe_class_default); -%} - -instruct cond_set_0_ptr(iRegPdst dst, flagsRegSrc crx, iRegPsrc src1) %{ - // The match rule is needed to make it a 'MachTypeNode'! - // NOTICE that the rule is nonsense - we just have to make sure that: - // - _matrule->_rChild->_opType == "DecodeN" (see InstructForm::captures_bottom_type() in formssel.cpp) - // - we have to match 'crx' to avoid an "illegal USE of non-input: flagsReg crx" error in ADLC. - match(Set dst (DecodeN (Binary crx src1))); - predicate(false); - - format %{ "CMOVE $dst, $crx eq, 0, $src1 \t// decode: preserve 0" %} - size(4); - ins_encode %{ - __ isel_0($dst$$Register, $crx$$CondRegister, Assembler::equal, $src1$$Register); - %} - ins_pipe(pipe_class_default); -%} - // shift != 0, base != 0 -instruct decodeN_Ex(iRegPdst dst, iRegNsrc src, flagsReg crx) %{ +instruct decodeN(iRegPdst dst, iRegNsrc src, flagsRegCR0 cr0) %{ match(Set dst (DecodeN src)); predicate((n->bottom_type()->is_oopptr()->ptr() != TypePtr::NotNull && n->bottom_type()->is_oopptr()->ptr() != TypePtr::Constant) && CompressedOops::shift() != 0 && CompressedOops::base() != nullptr); - ins_cost(4 * DEFAULT_COST); // Should be more expensive than decodeN_Disjoint_isel_Ex. - effect(TEMP crx); + ins_cost(4 * DEFAULT_COST); // Should be more expensive than decodeN_Disjoint_isel. + effect(TEMP cr0); - format %{ "DecodeN $dst, $src \t// Kills $crx, postalloc expanded" %} - postalloc_expand( postalloc_expand_decode_oop(dst, src, crx) ); + format %{ "DecodeN $dst, $src \t// decode nullable narrow oop" %} + ins_encode %{ + __ sldi_($dst$$Register, $src$$Register, CompressedOops::shift()); + __ add_const_optimized($dst$$Register, $dst$$Register, CompressedOops::base(), R0); + __ isel_0($dst$$Register, CR0, Assembler::equal); + %} + ins_pipe(pipe_class_default); %} // shift != 0, base == 0 @@ -6348,27 +6140,9 @@ instruct decodeN_nullBase(iRegPdst dst, iRegNsrc src) %{ ins_pipe(pipe_class_default); %} -// Optimize DecodeN for disjoint base. -// Shift narrow oop and or it into register that already contains the heap base. -// Base == dst must hold, and is assured by construction in postaloc_expand. -instruct decodeN_mergeDisjoint(iRegPdst dst, iRegNsrc src, iRegLsrc base) %{ - match(Set dst (DecodeN src)); - effect(TEMP base); - predicate(false); - - format %{ "RLDIMI $dst, $src, shift, 32-shift \t// DecodeN (disjoint base)" %} - size(4); - ins_encode %{ - __ rldimi($dst$$Register, $src$$Register, CompressedOops::shift(), 32-CompressedOops::shift()); - %} - ins_pipe(pipe_class_default); -%} - // Optimize DecodeN for disjoint base. // This node requires only one cycle on the critical path. -// We must postalloc_expand as we can not express use_def effects where -// the used register is L and the def'ed register P. -instruct decodeN_Disjoint_notNull_Ex(iRegPdst dst, iRegNsrc src) %{ +instruct decodeN_Disjoint_notNull(iRegPdst dst, iRegNsrc src) %{ match(Set dst (DecodeN src)); effect(TEMP_DEF dst); predicate((n->bottom_type()->is_oopptr()->ptr() == TypePtr::NotNull || @@ -6378,76 +6152,29 @@ instruct decodeN_Disjoint_notNull_Ex(iRegPdst dst, iRegNsrc src) %{ format %{ "MOV $dst, heapbase \t\n" "RLDIMI $dst, $src, shift, 32-shift \t// decode with disjoint base" %} - postalloc_expand %{ - loadBaseNode *n1 = new loadBaseNode(); - n1->add_req(nullptr); - n1->_opnds[0] = op_dst; - - decodeN_mergeDisjointNode *n2 = new decodeN_mergeDisjointNode(); - n2->add_req(n_region, n_src, n1); - n2->_opnds[0] = op_dst; - n2->_opnds[1] = op_src; - n2->_opnds[2] = op_dst; - n2->_bottom_type = _bottom_type; - - assert(ra_->is_oop(this) == true, "A decodeN node must produce an oop!"); - ra_->set_oop(n2, true); - - ra_->set_pair(n1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); - ra_->set_pair(n2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); - - nodes->push(n1); - nodes->push(n2); + ins_encode %{ + __ load_const_optimized($dst$$Register, CompressedOops::base(), R0); + __ rldimi($dst$$Register, $src$$Register, CompressedOops::shift(), 32-CompressedOops::shift()); %} + ins_pipe(pipe_class_default); %} -instruct decodeN_Disjoint_isel_Ex(iRegPdst dst, iRegNsrc src, flagsReg crx) %{ +instruct decodeN_Disjoint_isel(iRegPdst dst, iRegNsrc src, flagsReg crx) %{ match(Set dst (DecodeN src)); effect(TEMP_DEF dst, TEMP crx); predicate((n->bottom_type()->is_oopptr()->ptr() != TypePtr::NotNull && n->bottom_type()->is_oopptr()->ptr() != TypePtr::Constant) && CompressedOops::base_disjoint()); - ins_cost(3 * DEFAULT_COST); + ins_cost(3 * DEFAULT_COST); // Has a lower latency than decodeN because of a better ILP. format %{ "DecodeN $dst, $src \t// decode with disjoint base using isel" %} - postalloc_expand %{ - loadBaseNode *n1 = new loadBaseNode(); - n1->add_req(nullptr); - n1->_opnds[0] = op_dst; - - cmpN_reg_imm0Node *n_compare = new cmpN_reg_imm0Node(); - n_compare->add_req(n_region, n_src); - n_compare->_opnds[0] = op_crx; - n_compare->_opnds[1] = op_src; - n_compare->_opnds[2] = new immN_0Oper(TypeNarrowOop::NULL_PTR); - - decodeN_mergeDisjointNode *n2 = new decodeN_mergeDisjointNode(); - n2->add_req(n_region, n_src, n1); - n2->_opnds[0] = op_dst; - n2->_opnds[1] = op_src; - n2->_opnds[2] = op_dst; - n2->_bottom_type = _bottom_type; - - cond_set_0_ptrNode *n_cond_set = new cond_set_0_ptrNode(); - n_cond_set->add_req(n_region, n_compare, n2); - n_cond_set->_opnds[0] = op_dst; - n_cond_set->_opnds[1] = op_crx; - n_cond_set->_opnds[2] = op_dst; - n_cond_set->_bottom_type = _bottom_type; - - assert(ra_->is_oop(this) == true, "A decodeN node must produce an oop!"); - ra_->set_oop(n_cond_set, true); - - ra_->set_pair(n1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); - ra_->set_pair(n_compare->_idx, ra_->get_reg_second(n_crx), ra_->get_reg_first(n_crx)); - ra_->set_pair(n2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); - ra_->set_pair(n_cond_set->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); - - nodes->push(n1); - nodes->push(n_compare); - nodes->push(n2); - nodes->push(n_cond_set); + ins_encode %{ + __ load_const_optimized($dst$$Register, CompressedOops::base(), R0); + __ cmplwi($crx$$CondRegister, $src$$Register, 0); + __ rldimi($dst$$Register, $src$$Register, CompressedOops::shift(), 32-CompressedOops::shift()); + __ isel_0($dst$$Register, $crx$$CondRegister, Assembler::equal); %} + ins_pipe(pipe_class_default); %} // src != 0, shift != 0, base != 0 @@ -10409,22 +10136,6 @@ instruct cmpP_reg_null(flagsReg crx, iRegP_N2P src1, immP_0or1 src2) %{ ins_pipe(pipe_class_compare); %} -// Used in postalloc expand. -instruct cmpP_reg_imm16(flagsReg crx, iRegPsrc src1, immL16 src2) %{ - // This match rule prevents reordering of node before a safepoint. - // This only makes sense if this instructions is used exclusively - // for the expansion of EncodeP! - match(Set crx (CmpP src1 src2)); - predicate(false); - - format %{ "CMPDI $crx, $src1, $src2" %} - size(4); - ins_encode %{ - __ cmpdi($crx$$CondRegister, $src1$$Register, $src2$$constant); - %} - ins_pipe(pipe_class_compare); -%} - //----------Float Compares---------------------------------------------------- instruct cmpFUnordered_reg_reg(flagsReg crx, regF src1, regF src2) %{ diff --git a/src/hotspot/cpu/riscv/assembler_riscv.hpp b/src/hotspot/cpu/riscv/assembler_riscv.hpp index 3483fc42861f..0b852e4758e8 100644 --- a/src/hotspot/cpu/riscv/assembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/assembler_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved. * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -2896,6 +2896,10 @@ enum Nf { INSN(andn, 0b0110011, 0b111, 0b0100000); INSN(orn, 0b0110011, 0b110, 0b0100000); INSN(xnor, 0b0110011, 0b100, 0b0100000); + INSN(bset, 0b0110011, 0b001, 0b0010100); + INSN(bclr, 0b0110011, 0b001, 0b0100100); + INSN(binv, 0b0110011, 0b001, 0b0110100); + INSN(bext, 0b0110011, 0b101, 0b0100100); INSN(max, 0b0110011, 0b110, 0b0000101); INSN(maxu, 0b0110011, 0b111, 0b0000101); INSN(min, 0b0110011, 0b100, 0b0000101); @@ -2946,6 +2950,9 @@ enum Nf { INSN(rori, 0b0010011, 0b101, 0b011000); INSN(slli_uw, 0b0011011, 0b001, 0b000010); + INSN(bseti, 0b0010011, 0b001, 0b001010); + INSN(bclri, 0b0010011, 0b001, 0b010010); + INSN(binvi, 0b0010011, 0b001, 0b011010); INSN(bexti, 0b0010011, 0b101, 0b010010); #undef INSN @@ -3906,37 +3913,6 @@ enum Nf { } -// Cache Management Operations -// These instruction may be turned off for user space. - private: - enum CBO_FUNCT : unsigned int { - CBO_INVAL = 0b0000000000000, - CBO_CLEAN = 0b0000000000001, - CBO_FLUSH = 0b0000000000010, - CBO_ZERO = 0b0000000000100 - }; - - template - void cbo_base(Register Rs1) { - assert((UseZicbom && FUNCT != CBO_ZERO) || UseZicboz, "sanity"); - unsigned insn = 0; - patch((address)&insn, 6, 0, 0b0001111); - patch((address)&insn, 14, 12, 0b010); - patch_reg((address)&insn, 15, Rs1); - patch((address)&insn, 31, 20, FUNCT); - emit(insn); - } - - // This instruction have some security implication. - // At this time it's not likely to be enabled for user mode. - void cbo_inval(Register Rs1) { cbo_base(Rs1); } - public: - // Zicbom - void cbo_clean(Register Rs1) { cbo_base(Rs1); } - void cbo_flush(Register Rs1) { cbo_base(Rs1); } - // Zicboz - void cbo_zero(Register Rs1) { cbo_base(Rs1); } - private: enum PREFETCH_FUNCT : unsigned int { PREFETCH_I = 0b0000000000000, diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_arraycopy_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_arraycopy_riscv.cpp index 70cccefb2ea5..0098b9333dec 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_arraycopy_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_arraycopy_riscv.cpp @@ -274,7 +274,7 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { } // if we don't know anything, just go through the generic arraycopy - if (default_type == nullptr) { + if (default_type == nullptr || !default_type->is_loaded()) { generic_arraycopy(src, src_pos, length, dst, dst_pos, stub); return; } diff --git a/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp index e37726b6632a..7e6e430e1064 100644 --- a/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp @@ -177,7 +177,8 @@ LIR_Address* LIRGenerator::generate_address(LIR_Opr base, LIR_Opr index, return new LIR_Address(base, (jint)c, type); } else { LIR_Opr tmp = new_register(T_LONG); - __ move(index, tmp); + // A T_INT index can still require a 64-bit byte offset. + __ move(LIR_OprFact::longConst(c), tmp); return new LIR_Address(base, tmp, type); } } @@ -1041,7 +1042,8 @@ void LIRGenerator::do_NewObjectArray(NewObjectArray* x) { // in case of patching (i.e., object class is not yet loaded), we need to reexecute the instruction // and therefore provide the state before the parameters have been consumed CodeEmitInfo* patching_info = nullptr; - if (!x->klass()->is_loaded() || PatchALot) { + ciKlass* obj = ciObjArrayKlass::make(x->klass()); + if (!x->klass()->is_loaded() || !obj->is_loaded() || PatchALot) { patching_info = state_for(x, x->state_before()); } @@ -1057,16 +1059,14 @@ void LIRGenerator::do_NewObjectArray(NewObjectArray* x) { length.load_item_force(FrameMap::r9_opr); LIR_Opr len = length.result(); - ciKlass* obj = ciObjArrayKlass::make(x->klass()); - // TODO 8265122 Implement a fast path for this bool is_flat = obj->is_loaded() && obj->is_flat_array_klass(); bool is_null_free = obj->is_loaded() && obj->as_array_klass()->is_elem_null_free(); CodeStub* slow_path = new NewObjectArrayStub(klass_reg, len, reg, info, is_null_free); - if (obj == ciEnv::unloaded_ciobjarrayklass()) { - BAILOUT("encountered unloaded_ciobjarrayklass due to out of memory error"); + if (x->klass()->is_loaded() && !obj->is_loaded()) { + BAILOUT("encountered unloaded ciobjarrayklass due to out of memory error"); } klass2reg_with_patching(klass_reg, obj, patching_info); bool always_slow_path = is_null_free || is_flat; diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp index 7d412087753e..c81883ae38b8 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp @@ -165,8 +165,7 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, srli(tmp2_hash, tmp2_hash, markWord::hash_shift); // Get the table and calculate the bucket's address. - la(tmp3_t, ExternalAddress(ObjectMonitorTable::current_table_address())); - ld(tmp3_t, Address(tmp3_t)); + ld(tmp3_t, ExternalAddress(ObjectMonitorTable::current_table_address())); ld(tmp1, Address(tmp3_t, ObjectMonitorTable::table_capacity_mask_offset())); andr(tmp2_hash, tmp2_hash, tmp1); ld(tmp3_t, Address(tmp3_t, ObjectMonitorTable::table_buckets_offset())); diff --git a/src/hotspot/cpu/riscv/gc/g1/g1_riscv.ad b/src/hotspot/cpu/riscv/gc/g1/g1_riscv.ad index 784e9629e36e..9242d0f11bb2 100644 --- a/src/hotspot/cpu/riscv/gc/g1/g1_riscv.ad +++ b/src/hotspot/cpu/riscv/gc/g1/g1_riscv.ad @@ -78,7 +78,7 @@ static void write_barrier_post(MacroAssembler* masm, // TODO 8350865 (same applies to g1StoreLSpecialTwoOops) // - Do not set/overwrite barrier data here, also handle G1C2BarrierPostNotNull -instruct g1StoreLSpecialOneOopOff0(indirect mem, iRegLNoSp src, immI0 off, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) +instruct g1StoreLSpecialOneOopOff0(indirect mem, iRegLNoSp src, immI_0 off, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) %{ predicate(UseG1GC); match(Set mem (StoreLSpecial mem (Binary src off))); diff --git a/src/hotspot/cpu/riscv/globals_riscv.hpp b/src/hotspot/cpu/riscv/globals_riscv.hpp index 50bbc7e2e994..567cd9eb7ed9 100644 --- a/src/hotspot/cpu/riscv/globals_riscv.hpp +++ b/src/hotspot/cpu/riscv/globals_riscv.hpp @@ -84,8 +84,6 @@ define_pd_global(bool, ValueTypeReturnedAsFields, false); range, \ constraint) \ \ - product(bool, UseBlockZeroing, false, \ - "Use Zicboz for block zeroing") \ product(intx, BlockZeroingLowLimit, 256, \ "Minimum size in bytes when block zeroing will be used") \ range(1, max_jint) \ @@ -116,9 +114,7 @@ define_pd_global(bool, ValueTypeReturnedAsFields, false); product(bool, UseZawrs, false, EXPERIMENTAL, "Use Zawrs instructions") \ product(bool, UseZcb, false, DIAGNOSTIC, "Use Zcb instructions") \ product(bool, UseZic64b, false, EXPERIMENTAL, "Use Zic64b instructions") \ - product(bool, UseZicbom, false, EXPERIMENTAL, "Use Zicbom instructions") \ - product(bool, UseZicbop, false, EXPERIMENTAL, "Use Zicbop instructions") \ - product(bool, UseZicboz, false, EXPERIMENTAL, "Use Zicboz instructions") \ + product(bool, UseZicbop, false, DIAGNOSTIC, "Use Zicbop instructions") \ product(bool, UseZiccid, false, EXPERIMENTAL, \ "Use Ziccid to omit icache flushes for atomic call patching") \ product(bool, UseZicond, false, DIAGNOSTIC, "Use Zicond instructions") \ diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index 8e383ea9e544..0177288a0770 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -6740,44 +6740,6 @@ void MacroAssembler::fill_words(Register base, Register cnt, Register value) { bind(fini); } -// Zero blocks of memory by using CBO.ZERO. -// -// Aligns the base address first sufficiently for CBO.ZERO, then uses -// CBO.ZERO repeatedly for every full block. cnt is the size to be -// zeroed in HeapWords. Returns the count of words left to be zeroed -// in cnt. -// -// NOTE: This is intended to be used in the zero_blocks() stub. If -// you want to use it elsewhere, note that cnt must be >= zicboz_block_size. -void MacroAssembler::zero_dcache_blocks(Register base, Register cnt, Register tmp1, Register tmp2) { - int zicboz_block_size = VM_Version::zicboz_block_size.value(); - Label initial_table_end, loop; - - // Align base with cache line size. - neg(tmp1, base); - andi(tmp1, tmp1, zicboz_block_size - 1); - - // tmp1: the number of bytes to be filled to align the base with cache line size. - add(base, base, tmp1); - srai(tmp2, tmp1, 3); - sub(cnt, cnt, tmp2); - srli(tmp2, tmp1, 1); - la(tmp1, initial_table_end); - sub(tmp2, tmp1, tmp2); - jr(tmp2); - for (int i = -zicboz_block_size + wordSize; i < 0; i += wordSize) { - sd(zr, Address(base, i)); - } - bind(initial_table_end); - - mv(tmp1, zicboz_block_size / wordSize); - bind(loop); - cbo_zero(base); - sub(cnt, cnt, tmp1); - addi(base, base, zicboz_block_size); - bge(cnt, tmp1, loop); -} - // java.lang.Math.round(float a) // Returns the closest int to the argument, with ties rounding to positive infinity. void MacroAssembler::java_round_float(Register dst, FloatRegister src, FloatRegister ftmp) { diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index de1352a1215a..62267fe51676 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -1535,8 +1535,6 @@ INSN(sd_release, sd, sd_rl); address zero_words(Register ptr, Register cnt); void fill_words(Register base, Register cnt, Register value); void zero_memory(Register addr, Register len, Register tmp); - void zero_dcache_blocks(Register base, Register cnt, Register tmp1, Register tmp2); - void shift_left_add(Register Rd, Register Rs1, Register Rs2, int shamt); void shift_left_add(Register Rd, Register Rs1, Register Rs2, int shamt, Register tmp); diff --git a/src/hotspot/cpu/riscv/matcher_riscv.hpp b/src/hotspot/cpu/riscv/matcher_riscv.hpp index 570bfbf711ad..b92b746ccc4c 100644 --- a/src/hotspot/cpu/riscv/matcher_riscv.hpp +++ b/src/hotspot/cpu/riscv/matcher_riscv.hpp @@ -114,6 +114,10 @@ // C code as the Java calling convention forces doubles to be aligned. static const bool misaligned_doubles_ok = true; + // Use MachUncommonTrapNode for representing uncommon trap sequences. + // This is an alternative to representing uncommon traps as runtime calls. + static const bool use_mach_uncommon_trap_node = false; + // Are floats converted to double when stored to stack during // deoptimization? static constexpr bool float_in_double() { return false; } diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 5513eafa7762..bb9d0e676762 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -730,10 +730,6 @@ reg_class r30_reg( R30, R30_H ); -reg_class r31_reg( - R31, R31_H -); - // Class for zero registesr reg_class zr_reg( R0, R0_H @@ -2002,10 +1998,6 @@ bool Matcher::match_rule_supported(int opcode) { case Op_SqrtHF: case Op_SubHF: return UseZfh; - - case Op_CMoveP: - case Op_CMoveN: - return false; } return true; // Per default match rules are supported. @@ -2748,7 +2740,7 @@ operand immI() %} // 32 bit zero -operand immI0() +operand immI_0() %{ predicate(n->get_int() == 0); match(ConI); @@ -2780,9 +2772,10 @@ operand immI_M1() interface(CONST_INTER); %} -// Unsigned Integer Immediate: 6-bit int, greater than 32 -operand uimmI6_ge32() %{ - predicate(((unsigned int)(n->get_int()) < 64) && (n->get_int() >= 32)); +// Integer Immediate: the values 32-63 +operand immI_32_63() +%{ + predicate(n->get_int() >= 32 && n->get_int() < 64); match(ConI); op_cost(0); format %{ %} @@ -2847,8 +2840,8 @@ operand immI_63() interface(CONST_INTER); %} -// 32 bit integer valid for add immediate -operand immIAdd() +// 12 bit signed immediate +operand immI_simm12() %{ predicate(Assembler::is_simm12((int64_t)n->get_int())); match(ConI); @@ -2857,31 +2850,32 @@ operand immIAdd() interface(CONST_INTER); %} -// 32 bit integer valid for sub immediate -operand immISub() +// 5 bit signed value. +operand immI_simm5() %{ - predicate(Assembler::is_simm12(-(int64_t)n->get_int())); + predicate(n->get_int() <= 15 && n->get_int() >= -16); match(ConI); + op_cost(0); format %{ %} interface(CONST_INTER); %} -// 5 bit signed value. -operand immI5() +// Integer operands 64 bit +// 64 bit immediate +operand immL() %{ - predicate(n->get_int() <= 15 && n->get_int() >= -16); - match(ConI); + match(ConL); op_cost(0); format %{ %} interface(CONST_INTER); %} -// 5 bit signed value (simm5) -operand immL5() +// 64 bit zero +operand immL_0() %{ - predicate(n->get_long() <= 15 && n->get_long() >= -16); + predicate(n->get_long() == 0); match(ConL); op_cost(0); @@ -2889,10 +2883,10 @@ operand immL5() interface(CONST_INTER); %} -// Integer operands 64 bit -// 64 bit immediate -operand immL() +// 64 bit unit decrement +operand immL_M1() %{ + predicate(n->get_long() == -1); match(ConL); op_cost(0); @@ -2900,10 +2894,9 @@ operand immL() interface(CONST_INTER); %} -// 64 bit zero -operand immL0() +operand immL_1() %{ - predicate(n->get_long() == 0); + predicate(n->get_long() == 1); match(ConL); op_cost(0); @@ -2911,6 +2904,27 @@ operand immL0() interface(CONST_INTER); %} +// 5 bit signed value (simm5) +operand immL_simm5() +%{ + predicate(n->get_long() <= 15 && n->get_long() >= -16); + match(ConL); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// 12 bit signed immediate +operand immL_simm12() +%{ + predicate(Assembler::is_simm12(n->get_long())); + match(ConL); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + // Pointer operands // Pointer Immediate operand immP() @@ -2923,7 +2937,7 @@ operand immP() %} // Null Pointer Immediate -operand immP0() +operand immP_0() %{ predicate(n->get_ptr() == 0); match(ConP); @@ -2967,7 +2981,7 @@ operand immI_16bits() interface(CONST_INTER); %} -operand immIpowerOf2() %{ +operand immI_single_set_bit() %{ predicate(is_power_of_2((juint)(n->get_int()))); match(ConI); op_cost(0); @@ -3019,10 +3033,9 @@ operand immL_bitmask() %{ interface(CONST_INTER); %} -// 64 bit unit decrement -operand immL_M1() +operand immL_single_set_bit() %{ - predicate(n->get_long() == -1); + predicate(is_power_of_2((julong)n->get_long())); match(ConL); op_cost(0); @@ -3030,22 +3043,11 @@ operand immL_M1() interface(CONST_INTER); %} - -// 64 bit integer valid for add immediate -operand immLAdd() +operand immL_single_clear_bit() %{ - predicate(Assembler::is_simm12(n->get_long())); + predicate(is_power_of_2((julong)~n->get_long())); match(ConL); - op_cost(0); - format %{ %} - interface(CONST_INTER); -%} -// 64 bit integer valid for sub immediate -operand immLSub() -%{ - predicate(Assembler::is_simm12(-(n->get_long()))); - match(ConL); op_cost(0); format %{ %} interface(CONST_INTER); @@ -3063,7 +3065,7 @@ operand immN() %} // Narrow Null Pointer Immediate -operand immN0() +operand immN_0() %{ predicate(n->get_narrowcon() == 0); match(ConN); @@ -3093,7 +3095,7 @@ operand immD() %} // Double Immediate: +0.0d -operand immD0() +operand immD_0() %{ predicate(jlong_cast(n->getd()) == 0); match(ConD); @@ -3113,7 +3115,7 @@ operand immF() %} // Float Immediate: +0.0f. -operand immF0() +operand immF_0() %{ predicate(jint_cast(n->getf()) == 0); match(ConF); @@ -3134,7 +3136,7 @@ operand immH() %} // Half Float Immediate: +0.0f. -operand immH0() +operand immH_0() %{ predicate(jint_cast(n->geth()) == 0); match(ConH); @@ -3144,26 +3146,8 @@ operand immH0() interface(CONST_INTER); %} -operand immIOffset() -%{ - predicate(Assembler::is_simm12(n->get_int())); - match(ConI); - op_cost(0); - format %{ %} - interface(CONST_INTER); -%} - -operand immLOffset() -%{ - predicate(Assembler::is_simm12(n->get_long())); - match(ConL); - op_cost(0); - format %{ %} - interface(CONST_INTER); -%} - // Scale values -operand immIScale() +operand immI_scale() %{ predicate(1 <= n->get_int() && (n->get_int() <= 3)); match(ConI); @@ -3416,17 +3400,6 @@ operand iRegP_R30() interface(REG_INTER); %} -// Pointer 64 bit Register R31 only -operand iRegP_R31() -%{ - constraint(ALLOC_IN_RC(r31_reg)); - match(RegP); - match(iRegPNoSp); - op_cost(0); - format %{ %} - interface(REG_INTER); -%} - // Pointer Register Operands // Narrow Pointer Register operand iRegN() @@ -3654,7 +3627,7 @@ operand indirect(iRegP reg) %} %} -operand indOffI(iRegP reg, immIOffset off) +operand indOffI(iRegP reg, immI_simm12 off) %{ constraint(ALLOC_IN_RC(ptr_reg)); match(AddP reg off); @@ -3668,7 +3641,7 @@ operand indOffI(iRegP reg, immIOffset off) %} %} -operand indOffL(iRegP reg, immLOffset off) +operand indOffL(iRegP reg, immL_simm12 off) %{ constraint(ALLOC_IN_RC(ptr_reg)); match(AddP reg off); @@ -3697,7 +3670,7 @@ operand indirectN(iRegN reg) %} %} -operand indOffIN(iRegN reg, immIOffset off) +operand indOffIN(iRegN reg, immI_simm12 off) %{ predicate(CompressedOops::shift() == 0); constraint(ALLOC_IN_RC(ptr_reg)); @@ -3712,7 +3685,7 @@ operand indOffIN(iRegN reg, immIOffset off) %} %} -operand indOffLN(iRegN reg, immLOffset off) +operand indOffLN(iRegN reg, immL_simm12 off) %{ predicate(CompressedOops::shift() == 0); constraint(ALLOC_IN_RC(ptr_reg)); @@ -3727,6 +3700,34 @@ operand indOffLN(iRegN reg, immLOffset off) %} %} +operand indirectX2P(iRegL reg) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(CastX2P reg); + op_cost(0); + format %{ "[$reg]\t# long -> ptr" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0xffffffff); + scale(0x0); + disp(0x0); + %} +%} + +operand indOffX2P(iRegL reg, immL_simm12 off) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP (CastX2P reg) off); + op_cost(0); + format %{ "[$reg, $off]\t# long -> ptr" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0xffffffff); + scale(0x0); + disp($off); + %} +%} + //----------Special Memory Operands-------------------------------------------- // Stack Slot Operand - This operand is used for loading and storing temporary // values on the stack where a match requires a value to @@ -3800,6 +3801,13 @@ operand iRegL2I(iRegL reg) %{ interface(REG_INTER) %} +operand iRegL2P(iRegL reg) %{ + op_cost(0); + match(CastX2P reg); + format %{ "l2p($reg)" %} + interface(REG_INTER) +%} + // Comparison Operands // NOTE: Label is a predefined operand which should not be redefined in @@ -3958,7 +3966,7 @@ operand inline_cache_RegP(iRegP reg) // memory is used to define read/write location for load/store // instruction defs. we can turn a memory op into an Address -opclass memory(indirect, indOffI, indOffL, indirectN, indOffIN, indOffLN); +opclass memory(indirect, indOffI, indOffL, indirectN, indOffIN, indOffLN, indirectX2P, indOffX2P); // iRegIorL2I is used for src inputs in rules for 32 bit int (I) // operations. it allows the src to be either an iRegI or a (ConvL2I @@ -3977,6 +3985,7 @@ opclass iRegIorL2I(iRegI, iRegL2I); opclass iRegIorL(iRegI, iRegL); opclass iRegNorP(iRegN, iRegP); opclass iRegILNP(iRegI, iRegL, iRegN, iRegP); +opclass iRegPorL2P(iRegP, iRegL2P); opclass iRegILNPNoSp(iRegINoSp, iRegLNoSp, iRegNNoSp, iRegPNoSp); opclass immIorL(immI, immL); @@ -5218,7 +5227,7 @@ instruct loadConP(iRegPNoSp dst, immP con) %} // Load Null Pointer Constant -instruct loadConP0(iRegPNoSp dst, immP0 con) +instruct loadConP0(iRegPNoSp dst, immP_0 con) %{ match(Set dst con); @@ -5271,7 +5280,7 @@ instruct loadConN(iRegNNoSp dst, immN con) %} // Load Narrow Null Pointer Constant -instruct loadConN0(iRegNNoSp dst, immN0 con) +instruct loadConN0(iRegNNoSp dst, immN_0 con) %{ match(Set dst con); @@ -5317,7 +5326,7 @@ instruct loadConH(fRegF dst, immH con) %{ ins_pipe(fp_load_constant_s); %} -instruct loadConH0(fRegF dst, immH0 con) %{ +instruct loadConH0(fRegF dst, immH_0 con) %{ match(Set dst con); ins_cost(XFER_COST); @@ -5352,7 +5361,7 @@ instruct loadConF(fRegF dst, immF con) %{ ins_pipe(fp_load_constant_s); %} -instruct loadConF0(fRegF dst, immF0 con) %{ +instruct loadConF0(fRegF dst, immF_0 con) %{ match(Set dst con); ins_cost(XFER_COST); @@ -5386,7 +5395,7 @@ instruct loadConD(fRegD dst, immD con) %{ ins_pipe(fp_load_constant_d); %} -instruct loadConD0(fRegD dst, immD0 con) %{ +instruct loadConD0(fRegD dst, immD_0 con) %{ match(Set dst con); ins_cost(XFER_COST); @@ -5431,7 +5440,7 @@ instruct storeB_volatile(iRegIorL2I src, indirect mem) ins_pipe(pipe_serial); %} -instruct storeimmB0(immI0 zero, memory mem) +instruct storeimmB0(immI_0 zero, memory mem) %{ predicate(!needs_releasing_store(n)); match(Set mem (StoreB mem zero)); @@ -5446,7 +5455,7 @@ instruct storeimmB0(immI0 zero, memory mem) ins_pipe(istore_mem); %} -instruct storeimmB0_volatile(immI0 zero, indirect mem) +instruct storeimmB0_volatile(immI_0 zero, indirect mem) %{ predicate(needs_releasing_store(n)); match(Set mem (StoreB mem zero)); @@ -5492,7 +5501,7 @@ instruct storeC_volatile(iRegIorL2I src, indirect mem) ins_pipe(pipe_serial); %} -instruct storeimmC0(immI0 zero, memory mem) +instruct storeimmC0(immI_0 zero, memory mem) %{ predicate(!needs_releasing_store(n)); match(Set mem (StoreC mem zero)); @@ -5507,7 +5516,7 @@ instruct storeimmC0(immI0 zero, memory mem) ins_pipe(istore_mem); %} -instruct storeimmC0_volatile(immI0 zero, indirect mem) +instruct storeimmC0_volatile(immI_0 zero, indirect mem) %{ predicate(needs_releasing_store(n)); match(Set mem (StoreC mem zero)); @@ -5553,7 +5562,7 @@ instruct storeI_volatile(iRegIorL2I src, indirect mem) ins_pipe(pipe_serial); %} -instruct storeimmI0(immI0 zero, memory mem) +instruct storeimmI0(immI_0 zero, memory mem) %{ predicate(!needs_releasing_store(n)); match(Set mem(StoreI mem zero)); @@ -5568,7 +5577,7 @@ instruct storeimmI0(immI0 zero, memory mem) ins_pipe(istore_mem); %} -instruct storeimmI0_volatile(immI0 zero, indirect mem) +instruct storeimmI0_volatile(immI_0 zero, indirect mem) %{ predicate(needs_releasing_store(n)); match(Set mem (StoreI mem zero)); @@ -5615,7 +5624,7 @@ instruct storeL_volatile(iRegL src, indirect mem) %} // Store Long (64 bit signed) -instruct storeimmL0(immL0 zero, memory mem) +instruct storeimmL0(immL_0 zero, memory mem) %{ predicate(!needs_releasing_store(n)); match(Set mem (StoreL mem zero)); @@ -5630,7 +5639,7 @@ instruct storeimmL0(immL0 zero, memory mem) ins_pipe(istore_mem); %} -instruct storeimmL0_volatile(immL0 zero, indirect mem) +instruct storeimmL0_volatile(immL_0 zero, indirect mem) %{ predicate(needs_releasing_store(n)); match(Set mem (StoreL mem zero)); @@ -5677,7 +5686,7 @@ instruct storeP_volatile(iRegP src, indirect mem) %} // Store Pointer -instruct storeimmP0(immP0 zero, memory mem) +instruct storeimmP0(immP_0 zero, memory mem) %{ match(Set mem (StoreP mem zero)); predicate(n->as_Store()->barrier_data() == 0 && !needs_releasing_store(n)); @@ -5692,7 +5701,7 @@ instruct storeimmP0(immP0 zero, memory mem) ins_pipe(istore_mem); %} -instruct storeimmP0_volatile(immP0 zero, indirect mem) +instruct storeimmP0_volatile(immP_0 zero, indirect mem) %{ predicate(n->as_Store()->barrier_data() == 0 && needs_releasing_store(n)); match(Set mem (StoreP mem zero)); @@ -5738,7 +5747,7 @@ instruct storeN_volatile(iRegN src, indirect mem) ins_pipe(pipe_serial); %} -instruct storeImmN0(immN0 zero, memory mem) +instruct storeImmN0(immN_0 zero, memory mem) %{ predicate(n->as_Store()->barrier_data() == 0 && !needs_releasing_store(n)); match(Set mem (StoreN mem zero)); @@ -5753,7 +5762,7 @@ instruct storeImmN0(immN0 zero, memory mem) ins_pipe(istore_reg_mem); %} -instruct storeImmN0_volatile(immN0 zero, indirect mem) +instruct storeImmN0_volatile(immN_0 zero, indirect mem) %{ predicate(n->as_Store()->barrier_data() == 0 && needs_releasing_store(n)); match(Set mem (StoreN mem zero)); @@ -6875,7 +6884,7 @@ instruct get_and_addL_no_resAcq(indirect mem, Universe dummy, iRegL incr) %{ ins_pipe(pipe_serial); %} -instruct get_and_addLiAcq(indirect mem, iRegLNoSp newval, immLAdd incr) +instruct get_and_addLiAcq(indirect mem, iRegLNoSp newval, immL_simm12 incr) %{ predicate(needs_acquiring_load_reserved(n)); @@ -6892,7 +6901,7 @@ instruct get_and_addLiAcq(indirect mem, iRegLNoSp newval, immLAdd incr) ins_pipe(pipe_serial); %} -instruct get_and_addLi_no_resAcq(indirect mem, Universe dummy, immLAdd incr) +instruct get_and_addLi_no_resAcq(indirect mem, Universe dummy, immL_simm12 incr) %{ predicate(n->as_LoadStore()->result_not_used() && needs_acquiring_load_reserved(n)); @@ -6943,7 +6952,7 @@ instruct get_and_addI_no_resAcq(indirect mem, Universe dummy, iRegIorL2I incr) ins_pipe(pipe_serial); %} -instruct get_and_addIiAcq(indirect mem, iRegINoSp newval, immIAdd incr) +instruct get_and_addIiAcq(indirect mem, iRegINoSp newval, immI_simm12 incr) %{ predicate(needs_acquiring_load_reserved(n)); @@ -6960,7 +6969,7 @@ instruct get_and_addIiAcq(indirect mem, iRegINoSp newval, immIAdd incr) ins_pipe(pipe_serial); %} -instruct get_and_addIi_no_resAcq(indirect mem, Universe dummy, immIAdd incr) +instruct get_and_addIi_no_resAcq(indirect mem, Universe dummy, immI_simm12 incr) %{ predicate(n->as_LoadStore()->result_not_used() && needs_acquiring_load_reserved(n)); @@ -7003,7 +7012,7 @@ instruct addI_reg_reg(iRegINoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{ ins_pipe(ialu_reg_reg); %} -instruct addI_reg_imm(iRegINoSp dst, iRegIorL2I src1, immIAdd src2) %{ +instruct addI_reg_imm(iRegINoSp dst, iRegIorL2I src1, immI_simm12 src2) %{ match(Set dst (AddI src1 src2)); ins_cost(ALU_COST); @@ -7018,7 +7027,7 @@ instruct addI_reg_imm(iRegINoSp dst, iRegIorL2I src1, immIAdd src2) %{ ins_pipe(ialu_reg_imm); %} -instruct addI_reg_imm_l2i(iRegINoSp dst, iRegL src1, immIAdd src2) %{ +instruct addI_reg_imm_l2i(iRegINoSp dst, iRegL src1, immI_simm12 src2) %{ match(Set dst (AddI (ConvL2I src1) src2)); ins_cost(ALU_COST); @@ -7034,7 +7043,7 @@ instruct addI_reg_imm_l2i(iRegINoSp dst, iRegL src1, immIAdd src2) %{ %} // Pointer Addition -instruct addP_reg_reg(iRegPNoSp dst, iRegP src1, iRegL src2) %{ +instruct addP_reg_reg(iRegPNoSp dst, iRegPorL2P src1, iRegL src2) %{ match(Set dst (AddP src1 src2)); ins_cost(ALU_COST); @@ -7050,7 +7059,7 @@ instruct addP_reg_reg(iRegPNoSp dst, iRegP src1, iRegL src2) %{ %} // If we shift more than 32 bits, we need not convert I2L. -instruct lShiftL_regI_immGE32(iRegLNoSp dst, iRegI src, uimmI6_ge32 scale) %{ +instruct lShiftL_regI_immGE32(iRegLNoSp dst, iRegI src, immI_32_63 scale) %{ match(Set dst (LShiftL (ConvI2L src) scale)); ins_cost(ALU_COST); format %{ "slli $dst, $src, $scale & 63\t#@lShiftL_regI_immGE32" %} @@ -7065,7 +7074,7 @@ instruct lShiftL_regI_immGE32(iRegLNoSp dst, iRegI src, uimmI6_ge32 scale) %{ // Pointer Immediate Addition // n.b. this needs to be more expensive than using an indirect memory // operand -instruct addP_reg_imm(iRegPNoSp dst, iRegP src1, immLAdd src2) %{ +instruct addP_reg_imm(iRegPNoSp dst, iRegPorL2P src1, immL_simm12 src2) %{ match(Set dst (AddP src1 src2)); ins_cost(ALU_COST); format %{ "addi $dst, $src1, $src2\t# ptr, #@addP_reg_imm" %} @@ -7095,7 +7104,7 @@ instruct addL_reg_reg(iRegLNoSp dst, iRegL src1, iRegL src2) %{ %} // No constant pool entries requiredLong Immediate Addition. -instruct addL_reg_imm(iRegLNoSp dst, iRegL src1, immLAdd src2) %{ +instruct addL_reg_imm(iRegLNoSp dst, iRegL src1, immL_simm12 src2) %{ match(Set dst (AddL src1 src2)); ins_cost(ALU_COST); format %{ "addi $dst, $src1, $src2\t#@addL_reg_imm" %} @@ -7126,23 +7135,6 @@ instruct subI_reg_reg(iRegINoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{ ins_pipe(ialu_reg_reg); %} -// Immediate Subtraction -instruct subI_reg_imm(iRegINoSp dst, iRegIorL2I src1, immISub src2) %{ - match(Set dst (SubI src1 src2)); - - ins_cost(ALU_COST); - format %{ "addiw $dst, $src1, -$src2\t#@subI_reg_imm" %} - - ins_encode %{ - // src2 is imm, so actually call the addiw - __ subiw(as_Register($dst$$reg), - as_Register($src1$$reg), - $src2$$constant); - %} - - ins_pipe(ialu_reg_imm); -%} - // Long Subtraction instruct subL_reg_reg(iRegLNoSp dst, iRegL src1, iRegL src2) %{ match(Set dst (SubL src1 src2)); @@ -7158,25 +7150,9 @@ instruct subL_reg_reg(iRegLNoSp dst, iRegL src1, iRegL src2) %{ ins_pipe(ialu_reg_reg); %} -// No constant pool entries requiredLong Immediate Subtraction. -instruct subL_reg_imm(iRegLNoSp dst, iRegL src1, immLSub src2) %{ - match(Set dst (SubL src1 src2)); - ins_cost(ALU_COST); - format %{ "addi $dst, $src1, -$src2\t#@subL_reg_imm" %} - - ins_encode %{ - // src2 is imm, so actually call the addi - __ subi(as_Register($dst$$reg), - as_Register($src1$$reg), - $src2$$constant); - %} - - ins_pipe(ialu_reg_imm); -%} - // Integer Negation (special case for sub) -instruct negI_reg(iRegINoSp dst, iRegIorL2I src, immI0 zero) %{ +instruct negI_reg(iRegINoSp dst, iRegIorL2I src, immI_0 zero) %{ match(Set dst (SubI zero src)); ins_cost(ALU_COST); format %{ "subw $dst, x0, $src\t# int, #@negI_reg" %} @@ -7192,7 +7168,7 @@ instruct negI_reg(iRegINoSp dst, iRegIorL2I src, immI0 zero) %{ // Long Negation -instruct negL_reg(iRegLNoSp dst, iRegL src, immL0 zero) %{ +instruct negL_reg(iRegLNoSp dst, iRegL src, immL_0 zero) %{ match(Set dst (SubL zero src)); ins_cost(ALU_COST); format %{ "sub $dst, x0, $src\t# long, #@negL_reg" %} @@ -8372,7 +8348,7 @@ instruct andI_reg_reg(iRegINoSp dst, iRegI src1, iRegI src2) %{ %} // Immediate And -instruct andI_reg_imm(iRegINoSp dst, iRegI src1, immIAdd src2) %{ +instruct andI_reg_imm(iRegINoSp dst, iRegI src1, immI_simm12 src2) %{ match(Set dst (AndI src1 src2)); format %{ "andi $dst, $src1, $src2\t#@andI_reg_imm" %} @@ -8404,7 +8380,7 @@ instruct orI_reg_reg(iRegINoSp dst, iRegI src1, iRegI src2) %{ %} // Immediate Or -instruct orI_reg_imm(iRegINoSp dst, iRegI src1, immIAdd src2) %{ +instruct orI_reg_imm(iRegINoSp dst, iRegI src1, immI_simm12 src2) %{ match(Set dst (OrI src1 src2)); format %{ "ori $dst, $src1, $src2\t#@orI_reg_imm" %} @@ -8436,7 +8412,7 @@ instruct xorI_reg_reg(iRegINoSp dst, iRegI src1, iRegI src2) %{ %} // Immediate Xor -instruct xorI_reg_imm(iRegINoSp dst, iRegI src1, immIAdd src2) %{ +instruct xorI_reg_imm(iRegINoSp dst, iRegI src1, immI_simm12 src2) %{ match(Set dst (XorI src1 src2)); format %{ "xori $dst, $src1, $src2\t#@xorI_reg_imm" %} @@ -8468,7 +8444,7 @@ instruct andL_reg_reg(iRegLNoSp dst, iRegL src1, iRegL src2) %{ %} // Immediate And Long -instruct andL_reg_imm(iRegLNoSp dst, iRegL src1, immLAdd src2) %{ +instruct andL_reg_imm(iRegLNoSp dst, iRegL src1, immL_simm12 src2) %{ match(Set dst (AndL src1 src2)); format %{ "andi $dst, $src1, $src2\t#@andL_reg_imm" %} @@ -8500,7 +8476,7 @@ instruct orL_reg_reg(iRegLNoSp dst, iRegL src1, iRegL src2) %{ %} // Immediate Or Long -instruct orL_reg_imm(iRegLNoSp dst, iRegL src1, immLAdd src2) %{ +instruct orL_reg_imm(iRegLNoSp dst, iRegL src1, immL_simm12 src2) %{ match(Set dst (OrL src1 src2)); format %{ "ori $dst, $src1, $src2\t#@orL_reg_imm" %} @@ -8532,7 +8508,7 @@ instruct xorL_reg_reg(iRegLNoSp dst, iRegL src1, iRegL src2) %{ %} // Immediate Xor Long -instruct xorL_reg_imm(iRegLNoSp dst, iRegL src1, immLAdd src2) %{ +instruct xorL_reg_imm(iRegLNoSp dst, iRegL src1, immL_simm12 src2) %{ match(Set dst (XorL src1 src2)); ins_cost(ALU_COST); @@ -9038,7 +9014,7 @@ instruct convI2L_addI_reg_reg(iRegLNoSp dst, iRegIorL2I src1, iRegIorL2I src2) % ins_pipe(ialu_reg_reg); %} -instruct convI2L_addI_reg_imm(iRegLNoSp dst, iRegIorL2I src1, immIAdd src2) %{ +instruct convI2L_addI_reg_imm(iRegLNoSp dst, iRegIorL2I src1, immI_simm12 src2) %{ match(Set dst (ConvI2L (AddI src1 src2))); ins_cost(ALU_COST); @@ -9985,7 +9961,7 @@ instruct cmpLTMask_reg_reg(iRegINoSp dst, iRegI p, iRegI q) ins_pipe(ialu_reg_reg); %} -instruct cmpLTMask_reg_zero(iRegINoSp dst, iRegIorL2I op, immI0 zero) +instruct cmpLTMask_reg_zero(iRegINoSp dst, iRegIorL2I op, immI_0 zero) %{ match(Set dst (CmpLTMask op zero)); @@ -10038,7 +10014,7 @@ instruct maxI_reg_reg(iRegINoSp dst, iRegI src) // n.b. this is selected in preference to the rule above because it // avoids loading constant 0 into a source register -instruct minI_reg_zero(iRegINoSp dst, immI0 zero) +instruct minI_reg_zero(iRegINoSp dst, immI_0 zero) %{ match(Set dst (MinI dst zero)); match(Set dst (MinI zero dst)); @@ -10054,7 +10030,7 @@ instruct minI_reg_zero(iRegINoSp dst, immI0 zero) ins_pipe(pipe_class_compare); %} -instruct maxI_reg_zero(iRegINoSp dst, immI0 zero) +instruct maxI_reg_zero(iRegINoSp dst, immI_0 zero) %{ match(Set dst (MaxI dst zero)); match(Set dst (MaxI zero dst)); @@ -10343,7 +10319,7 @@ instruct cmpD_branch(cmpOp cmp, fRegD op1, fRegD op2, label lbl) %} // Compare signed int with zero and branch near instructions -instruct cmpI_reg_imm0_branch(cmpOp cmp, iRegI op1, immI0 zero, label lbl) +instruct cmpI_reg_imm0_branch(cmpOp cmp, iRegI op1, immI_0 zero, label lbl) %{ // Same match rule as `far_cmpI_reg_imm0_branch'. match(If cmp (CmpI op1 zero)); @@ -10361,7 +10337,7 @@ instruct cmpI_reg_imm0_branch(cmpOp cmp, iRegI op1, immI0 zero, label lbl) ins_short_branch(1); %} -instruct cmpI_reg_imm0_loop(cmpOp cmp, iRegI op1, immI0 zero, label lbl) +instruct cmpI_reg_imm0_loop(cmpOp cmp, iRegI op1, immI_0 zero, label lbl) %{ // Same match rule as `far_cmpI_reg_imm0_loop'. match(CountedLoopEnd cmp (CmpI op1 zero)); @@ -10381,7 +10357,7 @@ instruct cmpI_reg_imm0_loop(cmpOp cmp, iRegI op1, immI0 zero, label lbl) %} // Compare unsigned int with zero and branch near instructions -instruct cmpUEqNeLeGt_reg_imm0_branch(cmpOpUEqNeLeGt cmp, iRegI op1, immI0 zero, label lbl) +instruct cmpUEqNeLeGt_reg_imm0_branch(cmpOpUEqNeLeGt cmp, iRegI op1, immI_0 zero, label lbl) %{ // Same match rule as `far_cmpUEqNeLeGt_reg_imm0_branch'. match(If cmp (CmpU op1 zero)); @@ -10401,7 +10377,7 @@ instruct cmpUEqNeLeGt_reg_imm0_branch(cmpOpUEqNeLeGt cmp, iRegI op1, immI0 zero, %} // Compare signed long with zero and branch near instructions -instruct cmpL_reg_imm0_branch(cmpOp cmp, iRegL op1, immL0 zero, label lbl) +instruct cmpL_reg_imm0_branch(cmpOp cmp, iRegL op1, immL_0 zero, label lbl) %{ // Same match rule as `far_cmpL_reg_imm0_branch'. match(If cmp (CmpL op1 zero)); @@ -10420,7 +10396,7 @@ instruct cmpL_reg_imm0_branch(cmpOp cmp, iRegL op1, immL0 zero, label lbl) ins_short_branch(1); %} -instruct cmpL_reg_imm0_loop(cmpOp cmp, iRegL op1, immL0 zero, label lbl) +instruct cmpL_reg_imm0_loop(cmpOp cmp, iRegL op1, immL_0 zero, label lbl) %{ // Same match rule as `far_cmpL_reg_imm0_loop'. match(CountedLoopEnd cmp (CmpL op1 zero)); @@ -10440,7 +10416,7 @@ instruct cmpL_reg_imm0_loop(cmpOp cmp, iRegL op1, immL0 zero, label lbl) %} // Compare unsigned long with zero and branch near instructions -instruct cmpULEqNeLeGt_reg_imm0_branch(cmpOpUEqNeLeGt cmp, iRegL op1, immL0 zero, label lbl) +instruct cmpULEqNeLeGt_reg_imm0_branch(cmpOpUEqNeLeGt cmp, iRegL op1, immL_0 zero, label lbl) %{ // Same match rule as `far_cmpULEqNeLeGt_reg_imm0_branch'. match(If cmp (CmpUL op1 zero)); @@ -10460,7 +10436,7 @@ instruct cmpULEqNeLeGt_reg_imm0_branch(cmpOpUEqNeLeGt cmp, iRegL op1, immL0 zero %} // Compare pointer with zero and branch near instructions -instruct cmpP_imm0_branch(cmpOpEqNe cmp, iRegP op1, immP0 zero, label lbl) %{ +instruct cmpP_imm0_branch(cmpOpEqNe cmp, iRegP op1, immP_0 zero, label lbl) %{ // Same match rule as `far_cmpP_reg_imm0_branch'. match(If cmp (CmpP op1 zero)); effect(USE lbl); @@ -10477,7 +10453,7 @@ instruct cmpP_imm0_branch(cmpOpEqNe cmp, iRegP op1, immP0 zero, label lbl) %{ %} // Compare narrow pointer with zero and branch near instructions -instruct cmpN_imm0_branch(cmpOpEqNe cmp, iRegN op1, immN0 zero, label lbl) %{ +instruct cmpN_imm0_branch(cmpOpEqNe cmp, iRegN op1, immN_0 zero, label lbl) %{ // Same match rule as `far_cmpN_reg_imm0_branch'. match(If cmp (CmpN op1 zero)); effect(USE lbl); @@ -10495,7 +10471,7 @@ instruct cmpN_imm0_branch(cmpOpEqNe cmp, iRegN op1, immN0 zero, label lbl) %{ %} // Compare narrow pointer with pointer zero and branch near instructions -instruct cmpP_narrowOop_imm0_branch(cmpOpEqNe cmp, iRegN op1, immP0 zero, label lbl) %{ +instruct cmpP_narrowOop_imm0_branch(cmpOpEqNe cmp, iRegN op1, immP_0 zero, label lbl) %{ // Same match rule as `far_cmpP_narrowOop_imm0_branch'. match(If cmp (CmpP (DecodeN op1) zero)); effect(USE lbl); @@ -10688,7 +10664,7 @@ instruct far_cmpD_branch(cmpOp cmp, fRegD op1, fRegD op2, label lbl) ins_pipe(pipe_class_compare); %} -instruct far_cmpI_reg_imm0_branch(cmpOp cmp, iRegI op1, immI0 zero, label lbl) +instruct far_cmpI_reg_imm0_branch(cmpOp cmp, iRegI op1, immI_0 zero, label lbl) %{ match(If cmp (CmpI op1 zero)); @@ -10705,7 +10681,7 @@ instruct far_cmpI_reg_imm0_branch(cmpOp cmp, iRegI op1, immI0 zero, label lbl) ins_pipe(pipe_cmpz_branch); %} -instruct far_cmpI_reg_imm0_loop(cmpOp cmp, iRegI op1, immI0 zero, label lbl) +instruct far_cmpI_reg_imm0_loop(cmpOp cmp, iRegI op1, immI_0 zero, label lbl) %{ match(CountedLoopEnd cmp (CmpI op1 zero)); @@ -10722,7 +10698,7 @@ instruct far_cmpI_reg_imm0_loop(cmpOp cmp, iRegI op1, immI0 zero, label lbl) ins_pipe(pipe_cmpz_branch); %} -instruct far_cmpUEqNeLeGt_imm0_branch(cmpOpUEqNeLeGt cmp, iRegI op1, immI0 zero, label lbl) +instruct far_cmpUEqNeLeGt_imm0_branch(cmpOpUEqNeLeGt cmp, iRegI op1, immI_0 zero, label lbl) %{ match(If cmp (CmpU op1 zero)); @@ -10740,7 +10716,7 @@ instruct far_cmpUEqNeLeGt_imm0_branch(cmpOpUEqNeLeGt cmp, iRegI op1, immI0 zero, %} // compare lt/ge unsigned instructs has no short instruct with same match -instruct far_cmpULtGe_reg_imm0_branch(cmpOpULtGe cmp, iRegI op1, immI0 zero, label lbl) +instruct far_cmpULtGe_reg_imm0_branch(cmpOpULtGe cmp, iRegI op1, immI_0 zero, label lbl) %{ match(If cmp (CmpU op1 zero)); @@ -10755,7 +10731,7 @@ instruct far_cmpULtGe_reg_imm0_branch(cmpOpULtGe cmp, iRegI op1, immI0 zero, lab ins_pipe(pipe_cmpz_branch); %} -instruct far_cmpL_reg_imm0_branch(cmpOp cmp, iRegL op1, immL0 zero, label lbl) +instruct far_cmpL_reg_imm0_branch(cmpOp cmp, iRegL op1, immL_0 zero, label lbl) %{ match(If cmp (CmpL op1 zero)); @@ -10772,7 +10748,7 @@ instruct far_cmpL_reg_imm0_branch(cmpOp cmp, iRegL op1, immL0 zero, label lbl) ins_pipe(pipe_cmpz_branch); %} -instruct far_cmpL_reg_imm0_loop(cmpOp cmp, iRegL op1, immL0 zero, label lbl) +instruct far_cmpL_reg_imm0_loop(cmpOp cmp, iRegL op1, immL_0 zero, label lbl) %{ match(CountedLoopEnd cmp (CmpL op1 zero)); @@ -10789,7 +10765,7 @@ instruct far_cmpL_reg_imm0_loop(cmpOp cmp, iRegL op1, immL0 zero, label lbl) ins_pipe(pipe_cmpz_branch); %} -instruct far_cmpULEqNeLeGt_reg_imm0_branch(cmpOpUEqNeLeGt cmp, iRegL op1, immL0 zero, label lbl) +instruct far_cmpULEqNeLeGt_reg_imm0_branch(cmpOpUEqNeLeGt cmp, iRegL op1, immL_0 zero, label lbl) %{ match(If cmp (CmpUL op1 zero)); @@ -10807,7 +10783,7 @@ instruct far_cmpULEqNeLeGt_reg_imm0_branch(cmpOpUEqNeLeGt cmp, iRegL op1, immL0 %} // compare lt/ge unsigned instructs has no short instruct with same match -instruct far_cmpULLtGe_reg_imm0_branch(cmpOpULtGe cmp, iRegL op1, immL0 zero, label lbl) +instruct far_cmpULLtGe_reg_imm0_branch(cmpOpULtGe cmp, iRegL op1, immL_0 zero, label lbl) %{ match(If cmp (CmpUL op1 zero)); @@ -10822,7 +10798,7 @@ instruct far_cmpULLtGe_reg_imm0_branch(cmpOpULtGe cmp, iRegL op1, immL0 zero, la ins_pipe(pipe_cmpz_branch); %} -instruct far_cmpP_imm0_branch(cmpOpEqNe cmp, iRegP op1, immP0 zero, label lbl) %{ +instruct far_cmpP_imm0_branch(cmpOpEqNe cmp, iRegP op1, immP_0 zero, label lbl) %{ match(If cmp (CmpP op1 zero)); effect(USE lbl); @@ -10836,7 +10812,7 @@ instruct far_cmpP_imm0_branch(cmpOpEqNe cmp, iRegP op1, immP0 zero, label lbl) % ins_pipe(pipe_cmpz_branch); %} -instruct far_cmpN_imm0_branch(cmpOpEqNe cmp, iRegN op1, immN0 zero, label lbl) %{ +instruct far_cmpN_imm0_branch(cmpOpEqNe cmp, iRegN op1, immN_0 zero, label lbl) %{ match(If cmp (CmpN op1 zero)); effect(USE lbl); @@ -10851,7 +10827,7 @@ instruct far_cmpN_imm0_branch(cmpOpEqNe cmp, iRegN op1, immN0 zero, label lbl) % ins_pipe(pipe_cmpz_branch); %} -instruct far_cmpP_narrowOop_imm0_branch(cmpOpEqNe cmp, iRegN op1, immP0 zero, label lbl) %{ +instruct far_cmpP_narrowOop_imm0_branch(cmpOpEqNe cmp, iRegN op1, immP_0 zero, label lbl) %{ match(If cmp (CmpP (DecodeN op1) zero)); effect(USE lbl); @@ -10870,81 +10846,84 @@ instruct far_cmpP_narrowOop_imm0_branch(cmpOpEqNe cmp, iRegN op1, immP0 zero, la // --------- CMoveI --------- +// Conditions are grouped by how the compare is encoded: +// signed integral : CmpI, CmpL -> cmpOp, no mask +// unsigned integral : CmpU, CmpUL -> cmpOpU, unsigned_branch_mask +// pointer : CmpN, CmpP -> cmpOpU, unsigned_branch_mask +// floating point : CmpF, CmpD -> single/double variants of the fp encoder + instruct cmovI_cmpI_cmpL_zero_one(iRegINoSp dst, iRegIorL op1, iRegIorL op2, - cmpOp cop, immI_1 one, immI0 zero) %{ + cmpOp cop, immI_1 one, immI_0 zero) %{ match(Set dst (CMoveI (Binary cop (CmpI op1 op2)) (Binary one zero))); match(Set dst (CMoveI (Binary cop (CmpL op1 op2)) (Binary one zero))); - ins_cost(ALU_COST * 2); + format %{ "CMoveI $dst, ($op1 $cop $op2), 1, 0\t#@cmovI_cmpI_cmpL_zero_one" %} ins_encode %{ __ enc_cmove_zero_one($cop$$cmpcode, - as_Register($op1$$reg), as_Register($op2$$reg), - as_Register($dst$$reg)); + as_Register($op1$$reg), as_Register($op2$$reg), + as_Register($dst$$reg)); %} ins_pipe(pipe_class_compare); %} -instruct cmovI_cmpI(iRegINoSp dst, iRegI src, iRegI op1, iRegI op2, cmpOp cop) %{ - match(Set dst (CMoveI (Binary cop (CmpI op1 op2)) (Binary dst src))); - ins_cost(ALU_COST + BRANCH_COST); +instruct cmovI_cmpU_cmpUL_zero_one(iRegINoSp dst, iRegIorL op1, iRegIorL op2, + cmpOpU cop, immI_1 one, immI_0 zero) %{ + match(Set dst (CMoveI (Binary cop (CmpU op1 op2)) (Binary one zero))); + match(Set dst (CMoveI (Binary cop (CmpUL op1 op2)) (Binary one zero))); + ins_cost(ALU_COST * 2); - format %{ - "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpI\n\t" - %} + format %{ "CMoveI $dst, ($op1 $cop $op2), 1, 0\t#@cmovI_cmpU_cmpUL_zero_one" %} ins_encode %{ - __ enc_cmove($cop$$cmpcode, - as_Register($op1$$reg), as_Register($op2$$reg), - as_Register($dst$$reg), as_Register($src$$reg)); + __ enc_cmove_zero_one($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, + as_Register($op1$$reg), as_Register($op2$$reg), + as_Register($dst$$reg)); %} ins_pipe(pipe_class_compare); %} -instruct cmovI_cmpU_cmpUL_zero_one(iRegINoSp dst, iRegIorL op1, iRegIorL op2, - cmpOpU cop, immI_1 one, immI0 zero) %{ - match(Set dst (CMoveI (Binary cop (CmpU op1 op2)) (Binary one zero))); - match(Set dst (CMoveI (Binary cop (CmpUL op1 op2)) (Binary one zero))); - +instruct cmovI_cmpF_zero_one(iRegINoSp dst, fRegF op1, fRegF op2, + cmpOp cop, immI_1 one, immI_0 zero) %{ + match(Set dst (CMoveI (Binary cop (CmpF op1 op2)) (Binary one zero))); ins_cost(ALU_COST * 2); - format %{ "CMoveI $dst, ($op1 $cop $op2), 1, 0\t#@cmovI_cmpU_cmpUL_zero_one" %} + + format %{ "CMoveI $dst, ($op1 $cop $op2), 1, 0\t#@cmovI_cmpF_zero_one" %} ins_encode %{ - __ enc_cmove_zero_one($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, - as_Register($op1$$reg), as_Register($op2$$reg), - as_Register($dst$$reg)); + __ enc_cmove_cmp_fp_zero_one($cop$$cmpcode, + as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), + as_Register($dst$$reg)); %} ins_pipe(pipe_class_compare); %} -instruct cmovI_cmpU(iRegINoSp dst, iRegI src, iRegI op1, iRegI op2, cmpOpU cop) %{ - match(Set dst (CMoveI (Binary cop (CmpU op1 op2)) (Binary dst src))); - ins_cost(ALU_COST + BRANCH_COST); +instruct cmovI_cmpD_zero_one(iRegINoSp dst, fRegD op1, fRegD op2, + cmpOp cop, immI_1 one, immI_0 zero) %{ + match(Set dst (CMoveI (Binary cop (CmpD op1 op2)) (Binary one zero))); + ins_cost(ALU_COST * 2); - format %{ - "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpU\n\t" - %} + format %{ "CMoveI $dst, ($op1 $cop $op2), 1, 0\t#@cmovI_cmpD_zero_one" %} ins_encode %{ - __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, - as_Register($op1$$reg), as_Register($op2$$reg), - as_Register($dst$$reg), as_Register($src$$reg)); + __ enc_cmove_cmp_fp_zero_one($cop$$cmpcode | C2_MacroAssembler::double_branch_mask, + as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), + as_Register($dst$$reg)); %} ins_pipe(pipe_class_compare); %} -instruct cmovI_cmpL(iRegINoSp dst, iRegI src, iRegL op1, iRegL op2, cmpOp cop) %{ +instruct cmovI_cmpI_cmpL(iRegINoSp dst, iRegI src, iRegIorL op1, iRegIorL op2, cmpOp cop) %{ + match(Set dst (CMoveI (Binary cop (CmpI op1 op2)) (Binary dst src))); match(Set dst (CMoveI (Binary cop (CmpL op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpL\n\t" - %} + format %{ "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpI_cmpL" %} ins_encode %{ __ enc_cmove($cop$$cmpcode, @@ -10955,13 +10934,12 @@ instruct cmovI_cmpL(iRegINoSp dst, iRegI src, iRegL op1, iRegL op2, cmpOp cop) % ins_pipe(pipe_class_compare); %} -instruct cmovI_cmpUL(iRegINoSp dst, iRegI src, iRegL op1, iRegL op2, cmpOpU cop) %{ +instruct cmovI_cmpU_cmpUL(iRegINoSp dst, iRegI src, iRegIorL op1, iRegIorL op2, cmpOpU cop) %{ + match(Set dst (CMoveI (Binary cop (CmpU op1 op2)) (Binary dst src))); match(Set dst (CMoveI (Binary cop (CmpUL op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpUL\n\t" - %} + format %{ "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpU_cmpUL" %} ins_encode %{ __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, @@ -10972,17 +10950,17 @@ instruct cmovI_cmpUL(iRegINoSp dst, iRegI src, iRegL op1, iRegL op2, cmpOpU cop) ins_pipe(pipe_class_compare); %} -instruct cmovI_cmpF_zero_one(iRegINoSp dst, fRegF op1, fRegF op2, - cmpOp cop, immI_1 one, immI0 zero) %{ - match(Set dst (CMoveI (Binary cop (CmpF op1 op2)) (Binary one zero))); +instruct cmovI_cmpN_cmpP(iRegINoSp dst, iRegI src, iRegNorP op1, iRegNorP op2, cmpOpU cop) %{ + match(Set dst (CMoveI (Binary cop (CmpN op1 op2)) (Binary dst src))); + match(Set dst (CMoveI (Binary cop (CmpP op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); - ins_cost(ALU_COST * 2); - format %{ "CMoveI $dst, ($op1 $cop $op2), 1, 0\t#@cmovI_cmpF_zero_one" %} + format %{ "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpN_cmpP" %} ins_encode %{ - __ enc_cmove_cmp_fp_zero_one($cop$$cmpcode, - as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), - as_Register($dst$$reg)); + __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, + as_Register($op1$$reg), as_Register($op2$$reg), + as_Register($dst$$reg), as_Register($src$$reg)); %} ins_pipe(pipe_class_compare); @@ -10992,30 +10970,12 @@ instruct cmovI_cmpF(iRegINoSp dst, iRegI src, fRegF op1, fRegF op2, cmpOp cop) % match(Set dst (CMoveI (Binary cop (CmpF op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpF\n\t" - %} + format %{ "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpF" %} ins_encode %{ __ enc_cmove_cmp_fp($cop$$cmpcode, as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), - as_Register($dst$$reg), as_Register($src$$reg), true /* is_single */); - %} - - ins_pipe(pipe_class_compare); -%} - -instruct cmovI_cmpD_zero_one(iRegINoSp dst, fRegD op1, fRegD op2, - cmpOp cop, immI_1 one, immI0 zero) %{ - match(Set dst (CMoveI (Binary cop (CmpD op1 op2)) (Binary one zero))); - - ins_cost(ALU_COST * 2); - format %{ "CMoveI $dst, ($op1 $cop $op2), 1, 0\t#@cmovI_cmpD_zero_one" %} - - ins_encode %{ - __ enc_cmove_cmp_fp_zero_one($cop$$cmpcode | C2_MacroAssembler::double_branch_mask, - as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), - as_Register($dst$$reg)); + as_Register($dst$$reg), as_Register($src$$reg), /* is_single */ true); %} ins_pipe(pipe_class_compare); @@ -11025,48 +10985,12 @@ instruct cmovI_cmpD(iRegINoSp dst, iRegI src, fRegD op1, fRegD op2, cmpOp cop) % match(Set dst (CMoveI (Binary cop (CmpD op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpD\n\t" - %} + format %{ "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpD" %} ins_encode %{ __ enc_cmove_cmp_fp($cop$$cmpcode | C2_MacroAssembler::double_branch_mask, as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), - as_Register($dst$$reg), as_Register($src$$reg), false /* is_single */); - %} - - ins_pipe(pipe_class_compare); -%} - -instruct cmovI_cmpN(iRegINoSp dst, iRegI src, iRegN op1, iRegN op2, cmpOpU cop) %{ - match(Set dst (CMoveI (Binary cop (CmpN op1 op2)) (Binary dst src))); - ins_cost(ALU_COST + BRANCH_COST); - - format %{ - "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpN\n\t" - %} - - ins_encode %{ - __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, - as_Register($op1$$reg), as_Register($op2$$reg), - as_Register($dst$$reg), as_Register($src$$reg)); - %} - - ins_pipe(pipe_class_compare); -%} - -instruct cmovI_cmpP(iRegINoSp dst, iRegI src, iRegP op1, iRegP op2, cmpOpU cop) %{ - match(Set dst (CMoveI (Binary cop (CmpP op1 op2)) (Binary dst src))); - ins_cost(ALU_COST + BRANCH_COST); - - format %{ - "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpP\n\t" - %} - - ins_encode %{ - __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, - as_Register($op1$$reg), as_Register($op2$$reg), - as_Register($dst$$reg), as_Register($src$$reg)); + as_Register($dst$$reg), as_Register($src$$reg), /* is_single */ false); %} ins_pipe(pipe_class_compare); @@ -11076,9 +11000,7 @@ instruct cmovI_cmpP(iRegINoSp dst, iRegI src, iRegP op1, iRegP op2, cmpOpU cop) // These are selected in preference to the rules above because they // avoid loading constant 0 into a source register -// CMoveI (signed compare) with zero as second operand -// Pattern: dst = cond ? dst : 0 (conditional clear) -instruct cmovI_cmpI_zero(iRegINoSp dst, immI0 src, iRegI op1, iRegI op2, cmpOp cop) %{ +instruct cmovI_cmpI_zero(iRegINoSp dst, immI_0 src, iRegI op1, iRegI op2, cmpOp cop) %{ match(Set dst (CMoveI (Binary cop (CmpI op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); @@ -11093,8 +11015,7 @@ instruct cmovI_cmpI_zero(iRegINoSp dst, immI0 src, iRegI op1, iRegI op2, cmpOp c ins_pipe(pipe_class_compare); %} -// CMoveI (unsigned compare) with zero as second operand -instruct cmovI_cmpU_zero(iRegINoSp dst, immI0 src, iRegI op1, iRegI op2, cmpOpU cop) %{ +instruct cmovI_cmpU_zero(iRegINoSp dst, immI_0 src, iRegI op1, iRegI op2, cmpOpU cop) %{ match(Set dst (CMoveI (Binary cop (CmpU op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); @@ -11109,26 +11030,12 @@ instruct cmovI_cmpU_zero(iRegINoSp dst, immI0 src, iRegI op1, iRegI op2, cmpOpU ins_pipe(pipe_class_compare); %} -instruct cmovI_cmpN_zero(iRegINoSp dst, immI0 src, iRegN op1, iRegN op2, cmpOpU cop) %{ +instruct cmovI_cmpN_cmpP_zero(iRegINoSp dst, immI_0 src, iRegNorP op1, iRegNorP op2, cmpOpU cop) %{ match(Set dst (CMoveI (Binary cop (CmpN op1 op2)) (Binary dst src))); - ins_cost(ALU_COST + BRANCH_COST); - - format %{ "CMoveI $dst, ($op1 $cop $op2), $dst, zr\t#@cmovI_cmpN_zero" %} - - ins_encode %{ - __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, - as_Register($op1$$reg), as_Register($op2$$reg), - as_Register($dst$$reg), zr); - %} - - ins_pipe(pipe_class_compare); -%} - -instruct cmovI_cmpP_zero(iRegINoSp dst, immI0 src, iRegP op1, iRegP op2, cmpOpU cop) %{ match(Set dst (CMoveI (Binary cop (CmpP op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ "CMoveI $dst, ($op1 $cop $op2), $dst, zr\t#@cmovI_cmpP_zero" %} + format %{ "CMoveI $dst, ($op1 $cop $op2), $dst, zr\t#@cmovI_cmpN_cmpP_zero" %} ins_encode %{ __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, @@ -11141,13 +11048,12 @@ instruct cmovI_cmpP_zero(iRegINoSp dst, immI0 src, iRegP op1, iRegP op2, cmpOpU // --------- CMoveL --------- -instruct cmovL_cmpL(iRegLNoSp dst, iRegL src, iRegL op1, iRegL op2, cmpOp cop) %{ +instruct cmovL_cmpI_cmpL(iRegLNoSp dst, iRegL src, iRegIorL op1, iRegIorL op2, cmpOp cop) %{ + match(Set dst (CMoveL (Binary cop (CmpI op1 op2)) (Binary dst src))); match(Set dst (CMoveL (Binary cop (CmpL op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpL\n\t" - %} + format %{ "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpI_cmpL" %} ins_encode %{ __ enc_cmove($cop$$cmpcode, @@ -11158,13 +11064,12 @@ instruct cmovL_cmpL(iRegLNoSp dst, iRegL src, iRegL op1, iRegL op2, cmpOp cop) % ins_pipe(pipe_class_compare); %} -instruct cmovL_cmpUL(iRegLNoSp dst, iRegL src, iRegL op1, iRegL op2, cmpOpU cop) %{ +instruct cmovL_cmpU_cmpUL(iRegLNoSp dst, iRegL src, iRegIorL op1, iRegIorL op2, cmpOpU cop) %{ + match(Set dst (CMoveL (Binary cop (CmpU op1 op2)) (Binary dst src))); match(Set dst (CMoveL (Binary cop (CmpUL op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpUL\n\t" - %} + format %{ "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpU_cmpUL" %} ins_encode %{ __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, @@ -11175,30 +11080,12 @@ instruct cmovL_cmpUL(iRegLNoSp dst, iRegL src, iRegL op1, iRegL op2, cmpOpU cop) ins_pipe(pipe_class_compare); %} -instruct cmovL_cmpI(iRegLNoSp dst, iRegL src, iRegI op1, iRegI op2, cmpOp cop) %{ - match(Set dst (CMoveL (Binary cop (CmpI op1 op2)) (Binary dst src))); - ins_cost(ALU_COST + BRANCH_COST); - - format %{ - "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpI\n\t" - %} - - ins_encode %{ - __ enc_cmove($cop$$cmpcode, - as_Register($op1$$reg), as_Register($op2$$reg), - as_Register($dst$$reg), as_Register($src$$reg)); - %} - - ins_pipe(pipe_class_compare); -%} - -instruct cmovL_cmpU(iRegLNoSp dst, iRegL src, iRegI op1, iRegI op2, cmpOpU cop) %{ - match(Set dst (CMoveL (Binary cop (CmpU op1 op2)) (Binary dst src))); +instruct cmovL_cmpN_cmpP(iRegLNoSp dst, iRegL src, iRegNorP op1, iRegNorP op2, cmpOpU cop) %{ + match(Set dst (CMoveL (Binary cop (CmpN op1 op2)) (Binary dst src))); + match(Set dst (CMoveL (Binary cop (CmpP op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpU\n\t" - %} + format %{ "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpN_cmpP" %} ins_encode %{ __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, @@ -11213,14 +11100,12 @@ instruct cmovL_cmpF(iRegLNoSp dst, iRegL src, fRegF op1, fRegF op2, cmpOp cop) % match(Set dst (CMoveL (Binary cop (CmpF op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpF\n\t" - %} + format %{ "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpF" %} ins_encode %{ __ enc_cmove_cmp_fp($cop$$cmpcode, as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), - as_Register($dst$$reg), as_Register($src$$reg), true /* is_single */); + as_Register($dst$$reg), as_Register($src$$reg), /* is_single */ true); %} ins_pipe(pipe_class_compare); @@ -11230,360 +11115,368 @@ instruct cmovL_cmpD(iRegLNoSp dst, iRegL src, fRegD op1, fRegD op2, cmpOp cop) % match(Set dst (CMoveL (Binary cop (CmpD op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpD\n\t" - %} + format %{ "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpD" %} ins_encode %{ __ enc_cmove_cmp_fp($cop$$cmpcode | C2_MacroAssembler::double_branch_mask, as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), - as_Register($dst$$reg), as_Register($src$$reg), false /* is_single */); + as_Register($dst$$reg), as_Register($src$$reg), /* is_single */ false); %} ins_pipe(pipe_class_compare); %} -instruct cmovL_cmpN(iRegLNoSp dst, iRegL src, iRegN op1, iRegN op2, cmpOpU cop) %{ - match(Set dst (CMoveL (Binary cop (CmpN op1 op2)) (Binary dst src))); +// Special cases where one arg is zero + +instruct cmovL_cmpL_zero(iRegLNoSp dst, immL_0 src, iRegL op1, iRegL op2, cmpOp cop) %{ + match(Set dst (CMoveL (Binary cop (CmpL op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpN\n\t" - %} + format %{ "CMoveL $dst, ($op1 $cop $op2), $dst, zr\t#@cmovL_cmpL_zero" %} ins_encode %{ - __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, + __ enc_cmove($cop$$cmpcode, as_Register($op1$$reg), as_Register($op2$$reg), - as_Register($dst$$reg), as_Register($src$$reg)); + as_Register($dst$$reg), zr); %} ins_pipe(pipe_class_compare); %} -instruct cmovL_cmpP(iRegLNoSp dst, iRegL src, iRegP op1, iRegP op2, cmpOpU cop) %{ - match(Set dst (CMoveL (Binary cop (CmpP op1 op2)) (Binary dst src))); +instruct cmovL_cmpUL_zero(iRegLNoSp dst, immL_0 src, iRegL op1, iRegL op2, cmpOpU cop) %{ + match(Set dst (CMoveL (Binary cop (CmpUL op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpP\n\t" - %} + format %{ "CMoveL $dst, ($op1 $cop $op2), $dst, zr\t#@cmovL_cmpUL_zero" %} ins_encode %{ __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg), as_Register($op2$$reg), - as_Register($dst$$reg), as_Register($src$$reg)); + as_Register($dst$$reg), zr); %} ins_pipe(pipe_class_compare); %} -// CMoveL (signed compare) with zero as second operand -instruct cmovL_cmpL_zero(iRegLNoSp dst, immL0 src, iRegL op1, iRegL op2, cmpOp cop) %{ - match(Set dst (CMoveL (Binary cop (CmpL op1 op2)) (Binary dst src))); +// --------- CMoveF --------- + +instruct cmovF_cmpI_cmpL(fRegF dst, fRegF src, iRegIorL op1, iRegIorL op2, cmpOp cop) %{ + match(Set dst (CMoveF (Binary cop (CmpI op1 op2)) (Binary dst src))); + match(Set dst (CMoveF (Binary cop (CmpL op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ "CMoveL $dst, ($op1 $cop $op2), $dst, zr\t#@cmovL_cmpL_zero" %} + format %{ "CMoveF $dst, ($op1 $cop $op2), $dst, $src\t#@cmovF_cmpI_cmpL" %} ins_encode %{ - __ enc_cmove($cop$$cmpcode, - as_Register($op1$$reg), as_Register($op2$$reg), - as_Register($dst$$reg), zr); + __ enc_cmove_fp_cmp($cop$$cmpcode, + as_Register($op1$$reg), as_Register($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), /* is_single */ true); %} ins_pipe(pipe_class_compare); %} -// CMoveL (unsigned compare) with zero as second operand -instruct cmovL_cmpUL_zero(iRegLNoSp dst, immL0 src, iRegL op1, iRegL op2, cmpOpU cop) %{ - match(Set dst (CMoveL (Binary cop (CmpUL op1 op2)) (Binary dst src))); +instruct cmovF_cmpU_cmpUL(fRegF dst, fRegF src, iRegIorL op1, iRegIorL op2, cmpOpU cop) %{ + match(Set dst (CMoveF (Binary cop (CmpU op1 op2)) (Binary dst src))); + match(Set dst (CMoveF (Binary cop (CmpUL op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ "CMoveL $dst, ($op1 $cop $op2), $dst, zr\t#@cmovL_cmpUL_zero" %} + format %{ "CMoveF $dst, ($op1 $cop $op2), $dst, $src\t#@cmovF_cmpU_cmpUL" %} ins_encode %{ - __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, - as_Register($op1$$reg), as_Register($op2$$reg), - as_Register($dst$$reg), zr); + __ enc_cmove_fp_cmp($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, + as_Register($op1$$reg), as_Register($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), /* is_single */ true); %} ins_pipe(pipe_class_compare); %} -// --------- CMoveF --------- - -instruct cmovF_cmpI(fRegF dst, fRegF src, iRegI op1, iRegI op2, cmpOp cop) %{ - match(Set dst (CMoveF (Binary cop (CmpI op1 op2)) (Binary dst src))); +instruct cmovF_cmpN_cmpP(fRegF dst, fRegF src, iRegNorP op1, iRegNorP op2, cmpOpU cop) %{ + match(Set dst (CMoveF (Binary cop (CmpN op1 op2)) (Binary dst src))); + match(Set dst (CMoveF (Binary cop (CmpP op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveF $dst, ($op1 $cop $op2), $dst, $src\t#@cmovF_cmpI\n\t" - %} + format %{ "CMoveF $dst, ($op1 $cop $op2), $dst, $src\t#@cmovF_cmpN_cmpP" %} ins_encode %{ - __ enc_cmove_fp_cmp($cop$$cmpcode, - as_Register($op1$$reg), as_Register($op2$$reg), - as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), true /* is_single */); + __ enc_cmove_fp_cmp($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, + as_Register($op1$$reg), as_Register($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), /* is_single */ true); %} ins_pipe(pipe_class_compare); %} -instruct cmovF_cmpU(fRegF dst, fRegF src, iRegI op1, iRegI op2, cmpOpU cop) %{ - match(Set dst (CMoveF (Binary cop (CmpU op1 op2)) (Binary dst src))); +instruct cmovF_cmpF(fRegF dst, fRegF src, fRegF op1, fRegF op2, cmpOp cop) %{ + match(Set dst (CMoveF (Binary cop (CmpF op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveF $dst, ($op1 $cop $op2), $dst, $src\t#@cmovF_cmpU\n\t" - %} + format %{ "CMoveF $dst, ($op1 $cop $op2), $dst, $src\t#@cmovF_cmpF" %} ins_encode %{ - __ enc_cmove_fp_cmp($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, - as_Register($op1$$reg), as_Register($op2$$reg), - as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), true /* is_single */); + __ enc_cmove_fp_cmp_fp($cop$$cmpcode, + as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), + /* cmp_single */ true, /* cmov_single */ true); %} ins_pipe(pipe_class_compare); %} -instruct cmovF_cmpL(fRegF dst, fRegF src, iRegL op1, iRegL op2, cmpOp cop) %{ - match(Set dst (CMoveF (Binary cop (CmpL op1 op2)) (Binary dst src))); +instruct cmovF_cmpD(fRegF dst, fRegF src, fRegD op1, fRegD op2, cmpOp cop) %{ + match(Set dst (CMoveF (Binary cop (CmpD op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveF $dst, ($op1 $cop $op2), $dst, $src\t#@cmovF_cmpL\n\t" + format %{ "CMoveF $dst, ($op1 $cop $op2), $dst, $src\t#@cmovF_cmpD" %} + + ins_encode %{ + __ enc_cmove_fp_cmp_fp($cop$$cmpcode | C2_MacroAssembler::double_branch_mask, + as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), + /* cmp_single */ false, /* cmov_single */ true); %} + ins_pipe(pipe_class_compare); +%} + +// --------- CMoveD --------- + +instruct cmovD_cmpI_cmpL(fRegD dst, fRegD src, iRegIorL op1, iRegIorL op2, cmpOp cop) %{ + match(Set dst (CMoveD (Binary cop (CmpI op1 op2)) (Binary dst src))); + match(Set dst (CMoveD (Binary cop (CmpL op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ "CMoveD $dst, ($op1 $cop $op2), $dst, $src\t#@cmovD_cmpI_cmpL" %} + ins_encode %{ __ enc_cmove_fp_cmp($cop$$cmpcode, - as_Register($op1$$reg), as_Register($op2$$reg), - as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), true /* is_single */); + as_Register($op1$$reg), as_Register($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), /* is_single */ false); %} ins_pipe(pipe_class_compare); %} -instruct cmovF_cmpUL(fRegF dst, fRegF src, iRegL op1, iRegL op2, cmpOpU cop) %{ - match(Set dst (CMoveF (Binary cop (CmpUL op1 op2)) (Binary dst src))); +instruct cmovD_cmpU_cmpUL(fRegD dst, fRegD src, iRegIorL op1, iRegIorL op2, cmpOpU cop) %{ + match(Set dst (CMoveD (Binary cop (CmpU op1 op2)) (Binary dst src))); + match(Set dst (CMoveD (Binary cop (CmpUL op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveF $dst, ($op1 $cop $op2), $dst, $src\t#@cmovF_cmpUL\n\t" - %} + format %{ "CMoveD $dst, ($op1 $cop $op2), $dst, $src\t#@cmovD_cmpU_cmpUL" %} ins_encode %{ __ enc_cmove_fp_cmp($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, - as_Register($op1$$reg), as_Register($op2$$reg), - as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), true /* is_single */); + as_Register($op1$$reg), as_Register($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), /* is_single */ false); %} ins_pipe(pipe_class_compare); %} -instruct cmovF_cmpF(fRegF dst, fRegF src, fRegF op1, fRegF op2, cmpOp cop) %{ - match(Set dst (CMoveF (Binary cop (CmpF op1 op2)) (Binary dst src))); +instruct cmovD_cmpN_cmpP(fRegD dst, fRegD src, iRegNorP op1, iRegNorP op2, cmpOpU cop) %{ + match(Set dst (CMoveD (Binary cop (CmpN op1 op2)) (Binary dst src))); + match(Set dst (CMoveD (Binary cop (CmpP op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveF $dst, ($op1 $cop $op2), $dst, $src\t#@cmovF_cmpF\n\t" + format %{ "CMoveD $dst, ($op1 $cop $op2), $dst, $src\t#@cmovD_cmpN_cmpP" %} + + ins_encode %{ + __ enc_cmove_fp_cmp($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, + as_Register($op1$$reg), as_Register($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), /* is_single */ false); %} + ins_pipe(pipe_class_compare); +%} + +instruct cmovD_cmpF(fRegD dst, fRegD src, fRegF op1, fRegF op2, cmpOp cop) %{ + match(Set dst (CMoveD (Binary cop (CmpF op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ "CMoveD $dst, ($op1 $cop $op2), $dst, $src\t#@cmovD_cmpF" %} + ins_encode %{ __ enc_cmove_fp_cmp_fp($cop$$cmpcode, - as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), - as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), - true /* cmp_single */, true /* cmov_single */); + as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), + /* cmp_single */ true, /* cmov_single */ false); %} ins_pipe(pipe_class_compare); %} -instruct cmovF_cmpD(fRegF dst, fRegF src, fRegD op1, fRegD op2, cmpOp cop) %{ - match(Set dst (CMoveF (Binary cop (CmpD op1 op2)) (Binary dst src))); +instruct cmovD_cmpD(fRegD dst, fRegD src, fRegD op1, fRegD op2, cmpOp cop) %{ + match(Set dst (CMoveD (Binary cop (CmpD op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveF $dst, ($op1 $cop $op2), $dst, $src\t#@cmovF_cmpD\n\t" - %} + format %{ "CMoveD $dst, ($op1 $cop $op2), $dst, $src\t#@cmovD_cmpD" %} ins_encode %{ __ enc_cmove_fp_cmp_fp($cop$$cmpcode | C2_MacroAssembler::double_branch_mask, - as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), - as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), - false /* cmp_single */, true /* cmov_single */); + as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), + /* cmp_single */ false, /* cmov_single */ false); %} ins_pipe(pipe_class_compare); %} -instruct cmovF_cmpN(fRegF dst, fRegF src, iRegN op1, iRegN op2, cmpOp cop) %{ - match(Set dst (CMoveF (Binary cop (CmpN op1 op2)) (Binary dst src))); +// --------- CMoveP --------- + +instruct cmovP_cmpI_cmpL(iRegPNoSp dst, iRegP src, iRegIorL op1, iRegIorL op2, cmpOp cop) %{ + match(Set dst (CMoveP (Binary cop (CmpI op1 op2)) (Binary dst src))); + match(Set dst (CMoveP (Binary cop (CmpL op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveF $dst, ($op1 $cop $op2), $dst, $src\t#@cmovF_cmpN\n\t" - %} + format %{ "CMoveP $dst, ($op1 $cop $op2), $dst, $src\t#@cmovP_cmpI_cmpL" %} ins_encode %{ - __ enc_cmove_fp_cmp($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, + __ enc_cmove($cop$$cmpcode, as_Register($op1$$reg), as_Register($op2$$reg), - as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), true /* is_single */); + as_Register($dst$$reg), as_Register($src$$reg)); %} ins_pipe(pipe_class_compare); %} -instruct cmovF_cmpP(fRegF dst, fRegF src, iRegP op1, iRegP op2, cmpOp cop) %{ - match(Set dst (CMoveF (Binary cop (CmpP op1 op2)) (Binary dst src))); +instruct cmovP_cmpU_cmpUL(iRegPNoSp dst, iRegP src, iRegIorL op1, iRegIorL op2, cmpOpU cop) %{ + match(Set dst (CMoveP (Binary cop (CmpU op1 op2)) (Binary dst src))); + match(Set dst (CMoveP (Binary cop (CmpUL op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveF $dst, ($op1 $cop $op2), $dst, $src\t#@cmovF_cmpP\n\t" - %} + format %{ "CMoveP $dst, ($op1 $cop $op2), $dst, $src\t#@cmovP_cmpU_cmpUL" %} ins_encode %{ - __ enc_cmove_fp_cmp($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, + __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg), as_Register($op2$$reg), - as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), true /* is_single */); + as_Register($dst$$reg), as_Register($src$$reg)); %} ins_pipe(pipe_class_compare); %} -// --------- CMoveD --------- - -instruct cmovD_cmpI(fRegD dst, fRegD src, iRegI op1, iRegI op2, cmpOp cop) %{ - match(Set dst (CMoveD (Binary cop (CmpI op1 op2)) (Binary dst src))); +instruct cmovP_cmpN_cmpP(iRegPNoSp dst, iRegP src, iRegNorP op1, iRegNorP op2, cmpOpU cop) %{ + match(Set dst (CMoveP (Binary cop (CmpN op1 op2)) (Binary dst src))); + match(Set dst (CMoveP (Binary cop (CmpP op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveD $dst, ($op1 $cop $op2), $dst, $src\t#@cmovD_cmpI\n\t" - %} + format %{ "CMoveP $dst, ($op1 $cop $op2), $dst, $src\t#@cmovP_cmpN_cmpP" %} ins_encode %{ - __ enc_cmove_fp_cmp($cop$$cmpcode, + __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg), as_Register($op2$$reg), - as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), false /* is_single */); + as_Register($dst$$reg), as_Register($src$$reg)); %} ins_pipe(pipe_class_compare); %} -instruct cmovD_cmpU(fRegD dst, fRegD src, iRegI op1, iRegI op2, cmpOpU cop) %{ - match(Set dst (CMoveD (Binary cop (CmpU op1 op2)) (Binary dst src))); +instruct cmovP_cmpF(iRegPNoSp dst, iRegP src, fRegF op1, fRegF op2, cmpOp cop) %{ + match(Set dst (CMoveP (Binary cop (CmpF op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveD $dst, ($op1 $cop $op2), $dst, $src\t#@cmovD_cmpU\n\t" - %} + format %{ "CMoveP $dst, ($op1 $cop $op2), $dst, $src\t#@cmovP_cmpF" %} ins_encode %{ - __ enc_cmove_fp_cmp($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, - as_Register($op1$$reg), as_Register($op2$$reg), - as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), false /* is_single */); + __ enc_cmove_cmp_fp($cop$$cmpcode, + as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), + as_Register($dst$$reg), as_Register($src$$reg), /* is_single */ true); %} ins_pipe(pipe_class_compare); %} -instruct cmovD_cmpL(fRegD dst, fRegD src, iRegL op1, iRegL op2, cmpOp cop) %{ - match(Set dst (CMoveD (Binary cop (CmpL op1 op2)) (Binary dst src))); +instruct cmovP_cmpD(iRegPNoSp dst, iRegP src, fRegD op1, fRegD op2, cmpOp cop) %{ + match(Set dst (CMoveP (Binary cop (CmpD op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveD $dst, ($op1 $cop $op2), $dst, $src\t#@cmovD_cmpL\n\t" - %} + format %{ "CMoveP $dst, ($op1 $cop $op2), $dst, $src\t#@cmovP_cmpD" %} ins_encode %{ - __ enc_cmove_fp_cmp($cop$$cmpcode, - as_Register($op1$$reg), as_Register($op2$$reg), - as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), false /* is_single */); + __ enc_cmove_cmp_fp($cop$$cmpcode | C2_MacroAssembler::double_branch_mask, + as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), + as_Register($dst$$reg), as_Register($src$$reg), /* is_single */ false); %} ins_pipe(pipe_class_compare); %} -instruct cmovD_cmpUL(fRegD dst, fRegD src, iRegL op1, iRegL op2, cmpOpU cop) %{ - match(Set dst (CMoveD (Binary cop (CmpUL op1 op2)) (Binary dst src))); +// --------- CMoveN --------- + +instruct cmovN_cmpI_cmpL(iRegNNoSp dst, iRegN src, iRegIorL op1, iRegIorL op2, cmpOp cop) %{ + match(Set dst (CMoveN (Binary cop (CmpI op1 op2)) (Binary dst src))); + match(Set dst (CMoveN (Binary cop (CmpL op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveD $dst, ($op1 $cop $op2), $dst, $src\t#@cmovD_cmpUL\n\t" - %} + format %{ "CMoveN $dst, ($op1 $cop $op2), $dst, $src\t#@cmovN_cmpI_cmpL" %} ins_encode %{ - __ enc_cmove_fp_cmp($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, + __ enc_cmove($cop$$cmpcode, as_Register($op1$$reg), as_Register($op2$$reg), - as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), false /* is_single */); + as_Register($dst$$reg), as_Register($src$$reg)); %} ins_pipe(pipe_class_compare); %} -instruct cmovD_cmpF(fRegD dst, fRegD src, fRegF op1, fRegF op2, cmpOp cop) %{ - match(Set dst (CMoveD (Binary cop (CmpF op1 op2)) (Binary dst src))); +instruct cmovN_cmpU_cmpUL(iRegNNoSp dst, iRegN src, iRegIorL op1, iRegIorL op2, cmpOpU cop) %{ + match(Set dst (CMoveN (Binary cop (CmpU op1 op2)) (Binary dst src))); + match(Set dst (CMoveN (Binary cop (CmpUL op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveD $dst, ($op1 $cop $op2), $dst, $src\t#@cmovD_cmpF\n\t" - %} + format %{ "CMoveN $dst, ($op1 $cop $op2), $dst, $src\t#@cmovN_cmpU_cmpUL" %} ins_encode %{ - __ enc_cmove_fp_cmp_fp($cop$$cmpcode, - as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), - as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), - true /* cmp_single */, false /* cmov_single */); + __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, + as_Register($op1$$reg), as_Register($op2$$reg), + as_Register($dst$$reg), as_Register($src$$reg)); %} ins_pipe(pipe_class_compare); %} -instruct cmovD_cmpD(fRegD dst, fRegD src, fRegD op1, fRegD op2, cmpOp cop) %{ - match(Set dst (CMoveD (Binary cop (CmpD op1 op2)) (Binary dst src))); +instruct cmovN_cmpN_cmpP(iRegNNoSp dst, iRegN src, iRegNorP op1, iRegNorP op2, cmpOpU cop) %{ + match(Set dst (CMoveN (Binary cop (CmpN op1 op2)) (Binary dst src))); + match(Set dst (CMoveN (Binary cop (CmpP op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveD $dst, ($op1 $cop $op2), $dst, $src\t#@cmovD_cmpD\n\t" - %} + format %{ "CMoveN $dst, ($op1 $cop $op2), $dst, $src\t#@cmovN_cmpN_cmpP" %} ins_encode %{ - __ enc_cmove_fp_cmp_fp($cop$$cmpcode | C2_MacroAssembler::double_branch_mask, - as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), - as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), - false /* cmp_single */, false /* cmov_single */); + __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, + as_Register($op1$$reg), as_Register($op2$$reg), + as_Register($dst$$reg), as_Register($src$$reg)); %} ins_pipe(pipe_class_compare); %} -instruct cmovD_cmpN(fRegD dst, fRegD src, iRegN op1, iRegN op2, cmpOp cop) %{ - match(Set dst (CMoveD (Binary cop (CmpN op1 op2)) (Binary dst src))); +instruct cmovN_cmpF(iRegNNoSp dst, iRegN src, fRegF op1, fRegF op2, cmpOp cop) %{ + match(Set dst (CMoveN (Binary cop (CmpF op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveD $dst, ($op1 $cop $op2), $dst, $src\t#@cmovD_cmpN\n\t" - %} + format %{ "CMoveN $dst, ($op1 $cop $op2), $dst, $src\t#@cmovN_cmpF" %} ins_encode %{ - __ enc_cmove_fp_cmp($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, - as_Register($op1$$reg), as_Register($op2$$reg), - as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), false /* is_single */); + __ enc_cmove_cmp_fp($cop$$cmpcode, + as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), + as_Register($dst$$reg), as_Register($src$$reg), /* is_single */ true); %} ins_pipe(pipe_class_compare); %} -instruct cmovD_cmpP(fRegD dst, fRegD src, iRegP op1, iRegP op2, cmpOp cop) %{ - match(Set dst (CMoveD (Binary cop (CmpP op1 op2)) (Binary dst src))); +instruct cmovN_cmpD(iRegNNoSp dst, iRegN src, fRegD op1, fRegD op2, cmpOp cop) %{ + match(Set dst (CMoveN (Binary cop (CmpD op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); - format %{ - "CMoveD $dst, ($op1 $cop $op2), $dst, $src\t#@cmovD_cmpP\n\t" - %} + format %{ "CMoveN $dst, ($op1 $cop $op2), $dst, $src\t#@cmovN_cmpD" %} ins_encode %{ - __ enc_cmove_fp_cmp($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, - as_Register($op1$$reg), as_Register($op2$$reg), - as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), false /* is_single */); + __ enc_cmove_cmp_fp($cop$$cmpcode | C2_MacroAssembler::double_branch_mask, + as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), + as_Register($dst$$reg), as_Register($src$$reg), /* is_single */ false); %} ins_pipe(pipe_class_compare); @@ -12047,14 +11940,14 @@ instruct stringL_indexof_char(iRegP_R11 str1, iRegI_R12 cnt1, iRegI_R13 ch, // ============================================================================ // clearing of an array -instruct clearArray_reg_reg_immL0(iRegL_R29 cnt, iRegP_R28 base, immL0 zero, - iRegP_R30 tmp1, iRegP_R31 tmp2, rFlagsReg cr, +instruct clearArray_reg_reg_immL0(iRegL_R29 cnt, iRegP_R28 base, immL_0 zero, + iRegP_R30 tmp1, rFlagsReg cr, Universe dummy) %{ // temp registers must match the one used in StubGenerator::generate_zero_blocks() - predicate(UseBlockZeroing || !UseRVV); + predicate(!UseRVV); match(Set dummy (ClearArray (Binary cnt base) zero)); - effect(USE_KILL cnt, USE_KILL base, TEMP tmp1, TEMP tmp2, KILL cr); + effect(USE_KILL cnt, USE_KILL base, TEMP tmp1, KILL cr); ins_cost(4 * DEFAULT_COST); format %{ "ClearArray $cnt, $base\t#@clearArray_reg_reg_immL0" %} @@ -12071,13 +11964,11 @@ instruct clearArray_reg_reg_immL0(iRegL_R29 cnt, iRegP_R28 base, immL0 zero, %} instruct clearArray_reg_reg(iRegL_R29 cnt, iRegP_R28 base, iRegL val, - iRegP_R30 tmp1, iRegP_R31 tmp2, rFlagsReg cr, - Universe dummy) + rFlagsReg cr, Universe dummy) %{ - // temp registers must match the one used in StubGenerator::generate_zero_blocks() predicate(((ClearArrayNode*)n)->requires_word_fill()); match(Set dummy (ClearArray (Binary cnt base) val)); - effect(USE_KILL cnt, USE_KILL base, TEMP tmp1, TEMP tmp2, KILL cr); + effect(USE_KILL cnt, USE_KILL base, KILL cr); ins_cost(4 * DEFAULT_COST); format %{ "ClearArray $cnt, $base, $val\t#@clearArray_reg_reg" %} @@ -12089,7 +11980,7 @@ instruct clearArray_reg_reg(iRegL_R29 cnt, iRegP_R28 base, iRegL val, ins_pipe(pipe_class_memory); %} -instruct clearArray_imm_reg(immL cnt, iRegP_R28 base, immL0 zero, Universe dummy, rFlagsReg cr) +instruct clearArray_imm_reg(immL cnt, iRegP_R28 base, immL_0 zero, Universe dummy, rFlagsReg cr) %{ predicate(!UseRVV && (uint64_t)n->in(2)->in(1)->get_long() diff --git a/src/hotspot/cpu/riscv/riscv_b.ad b/src/hotspot/cpu/riscv/riscv_b.ad index ad66ecfb5993..a4be56cb069e 100644 --- a/src/hotspot/cpu/riscv/riscv_b.ad +++ b/src/hotspot/cpu/riscv/riscv_b.ad @@ -329,7 +329,7 @@ instruct bits_reverse_L(iRegLNoSp dst, iRegL src) // Shift Add Pointer -instruct shaddP_reg_reg_b(iRegPNoSp dst, iRegP src1, iRegL src2, immIScale imm) %{ +instruct shaddP_reg_reg_b(iRegPNoSp dst, iRegPorL2P src1, iRegL src2, immI_scale imm) %{ predicate(UseZba); match(Set dst (AddP src1 (LShiftL src2 imm))); @@ -346,7 +346,7 @@ instruct shaddP_reg_reg_b(iRegPNoSp dst, iRegP src1, iRegL src2, immIScale imm) ins_pipe(ialu_reg_reg); %} -instruct shaddP_reg_reg_ext_b(iRegPNoSp dst, iRegP src1, iRegI src2, immIScale imm) %{ +instruct shaddP_reg_reg_ext_b(iRegPNoSp dst, iRegPorL2P src1, iRegI src2, immI_scale imm) %{ predicate(UseZba); match(Set dst (AddP src1 (LShiftL (ConvI2L src2) imm))); @@ -364,7 +364,7 @@ instruct shaddP_reg_reg_ext_b(iRegPNoSp dst, iRegP src1, iRegI src2, immIScale i %} // Shift Add Long -instruct shaddL_reg_reg_b(iRegLNoSp dst, iRegL src1, iRegL src2, immIScale imm) %{ +instruct shaddL_reg_reg_b(iRegLNoSp dst, iRegL src1, iRegL src2, immI_scale imm) %{ predicate(UseZba); match(Set dst (AddL src1 (LShiftL src2 imm))); @@ -381,7 +381,7 @@ instruct shaddL_reg_reg_b(iRegLNoSp dst, iRegL src1, iRegL src2, immIScale imm) ins_pipe(ialu_reg_reg); %} -instruct shaddL_reg_reg_ext_b(iRegLNoSp dst, iRegL src1, iRegI src2, immIScale imm) %{ +instruct shaddL_reg_reg_ext_b(iRegLNoSp dst, iRegL src1, iRegI src2, immI_scale imm) %{ predicate(UseZba); match(Set dst (AddL src1 (LShiftL (ConvI2L src2) imm))); @@ -518,7 +518,7 @@ instruct maxI_reg_reg_b(iRegINoSp dst, iRegI src1, iRegI src2) %{ // n.b. this is selected in preference to the rule above because it // avoids loading constant 0 into a source register -instruct minI_reg_zero_b(iRegINoSp dst, iRegI src1, immI0 zero) %{ +instruct minI_reg_zero_b(iRegINoSp dst, iRegI src1, immI_0 zero) %{ predicate(UseZbb); match(Set dst (MinI src1 zero)); match(Set dst (MinI zero src1)); @@ -533,7 +533,7 @@ instruct minI_reg_zero_b(iRegINoSp dst, iRegI src1, immI0 zero) %{ ins_pipe(ialu_reg_reg); %} -instruct maxI_reg_zero_b(iRegINoSp dst, iRegI src1, immI0 zero) %{ +instruct maxI_reg_zero_b(iRegINoSp dst, iRegI src1, immI_0 zero) %{ predicate(UseZbb); match(Set dst (MaxI src1 zero)); match(Set dst (MaxI zero src1)); @@ -652,7 +652,7 @@ instruct ornL_reg_reg_b(iRegLNoSp dst, iRegL src1, iRegL src2, immL_M1 m1) %{ %} // AndI 0b0..010..0 + ConvI2B -instruct convI2Bool_andI_reg_immIpowerOf2(iRegINoSp dst, iRegIorL2I src, immIpowerOf2 mask) %{ +instruct convI2Bool_andI_reg_immIpowerOf2(iRegINoSp dst, iRegIorL2I src, immI_single_set_bit mask) %{ predicate(UseZbs); match(Set dst (Conv2B (AndI src mask))); ins_cost(ALU_COST); @@ -664,3 +664,102 @@ instruct convI2Bool_andI_reg_immIpowerOf2(iRegINoSp dst, iRegIorL2I src, immIpow ins_pipe(ialu_reg_reg); %} + +instruct andL_urshiftL_reg_1(iRegLNoSp dst, iRegL src1, iRegIorL2I src2, immL_1 one) %{ + predicate(UseZbs); + match(Set dst (AndL (URShiftL src1 src2) one)); + ins_cost(ALU_COST); + + format %{ "bext $dst, $src1, $src2\t#@andL_urshiftL_reg_1" %} + ins_encode %{ + __ bext(as_Register($dst$$reg), as_Register($src1$$reg), + as_Register($src2$$reg)); + %} + + ins_pipe(ialu_reg_reg); +%} + +instruct orL_reg_lshiftL_1(iRegLNoSp dst, iRegL src, immL_1 one, iRegIorL2I shift) %{ + predicate(UseZbs); + match(Set dst (OrL src (LShiftL one shift))); + ins_cost(ALU_COST); + + format %{ "bset $dst, $src, $shift\t#@orL_reg_lshiftL_1" %} + ins_encode %{ + __ bset(as_Register($dst$$reg), as_Register($src$$reg), + as_Register($shift$$reg)); + %} + + ins_pipe(ialu_reg_reg); +%} + +instruct xorL_reg_lshiftL_1(iRegLNoSp dst, iRegL src, immL_1 one, iRegIorL2I shift) %{ + predicate(UseZbs); + match(Set dst (XorL src (LShiftL one shift))); + ins_cost(ALU_COST); + + format %{ "binv $dst, $src, $shift\t#@xorL_reg_lshiftL_1" %} + ins_encode %{ + __ binv(as_Register($dst$$reg), as_Register($src$$reg), + as_Register($shift$$reg)); + %} + + ins_pipe(ialu_reg_reg); +%} + +instruct andL_reg_lshiftL_1_not(iRegLNoSp dst, iRegL src, immL_1 one, iRegIorL2I shift, immL_M1 m1) %{ + predicate(UseZbs); + match(Set dst (AndL src (XorL (LShiftL one shift) m1))); + ins_cost(ALU_COST); + + format %{ "bclr $dst, $src, $shift\t#@andL_reg_lshiftL_1_not" %} + ins_encode %{ + __ bclr(as_Register($dst$$reg), as_Register($src$$reg), + as_Register($shift$$reg)); + %} + + ins_pipe(ialu_reg_reg); +%} + +// Prefer ori/xori/andi when the mask fits in a signed 12-bit immediate. +instruct orL_reg_immL_SingleBit(iRegLNoSp dst, iRegL src, immL_single_set_bit mask) %{ + predicate(UseZbs && !Assembler::is_simm12(n->in(2)->get_long())); + match(Set dst (OrL src mask)); + ins_cost(ALU_COST); + + format %{ "bseti $dst, $src, exact_log2($mask)\t#@orL_reg_immL_SingleBit" %} + ins_encode %{ + __ bseti(as_Register($dst$$reg), as_Register($src$$reg), + exact_log2_long($mask$$constant)); + %} + + ins_pipe(ialu_reg_shift); +%} + +instruct xorL_reg_immL_SingleBit(iRegLNoSp dst, iRegL src, immL_single_set_bit mask) %{ + predicate(UseZbs && !Assembler::is_simm12(n->in(2)->get_long())); + match(Set dst (XorL src mask)); + ins_cost(ALU_COST); + + format %{ "binvi $dst, $src, exact_log2($mask)\t#@xorL_reg_immL_SingleBit" %} + ins_encode %{ + __ binvi(as_Register($dst$$reg), as_Register($src$$reg), + exact_log2_long($mask$$constant)); + %} + + ins_pipe(ialu_reg_shift); +%} + +instruct andL_reg_immL_SingleZero(iRegLNoSp dst, iRegL src, immL_single_clear_bit mask) %{ + predicate(UseZbs && !Assembler::is_simm12(n->in(2)->get_long())); + match(Set dst (AndL src mask)); + ins_cost(ALU_COST); + + format %{ "bclri $dst, $src, exact_log2(~$mask)\t#@andL_reg_immL_SingleZero" %} + ins_encode %{ + __ bclri(as_Register($dst$$reg), as_Register($src$$reg), + exact_log2_long(~$mask$$constant)); + %} + + ins_pipe(ialu_reg_shift); +%} diff --git a/src/hotspot/cpu/riscv/riscv_v.ad b/src/hotspot/cpu/riscv/riscv_v.ad index 09c790f6dd10..03a0989daacf 100644 --- a/src/hotspot/cpu/riscv/riscv_v.ad +++ b/src/hotspot/cpu/riscv/riscv_v.ad @@ -500,7 +500,7 @@ instruct vadd_fp_masked(vReg dst_src1, vReg src2, vRegMask_V0 v0) %{ // vector-immediate add (unpredicated) -instruct vadd_vi(vReg dst, vReg src1, immI5 con) %{ +instruct vadd_vi(vReg dst, vReg src1, immI_simm5 con) %{ match(Set dst (AddVB src1 (Replicate con))); match(Set dst (AddVS src1 (Replicate con))); match(Set dst (AddVI src1 (Replicate con))); @@ -515,7 +515,7 @@ instruct vadd_vi(vReg dst, vReg src1, immI5 con) %{ ins_pipe(pipe_slow); %} -instruct vaddL_vi(vReg dst, vReg src1, immL5 con) %{ +instruct vaddL_vi(vReg dst, vReg src1, immL_simm5 con) %{ match(Set dst (AddVL src1 (Replicate con))); format %{ "vaddL_vi $dst, $src1, $con" %} ins_encode %{ @@ -558,7 +558,7 @@ instruct vaddL_vx(vReg dst, vReg src1, iRegL src2) %{ // vector-immediate add (predicated) -instruct vadd_vi_masked(vReg dst_src, immI5 con, vRegMask_V0 v0) %{ +instruct vadd_vi_masked(vReg dst_src, immI_simm5 con, vRegMask_V0 v0) %{ match(Set dst_src (AddVB (Binary dst_src (Replicate con)) v0)); match(Set dst_src (AddVS (Binary dst_src (Replicate con)) v0)); match(Set dst_src (AddVI (Binary dst_src (Replicate con)) v0)); @@ -573,7 +573,7 @@ instruct vadd_vi_masked(vReg dst_src, immI5 con, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vaddL_vi_masked(vReg dst_src, immL5 con, vRegMask_V0 v0) %{ +instruct vaddL_vi_masked(vReg dst_src, immL_simm5 con, vRegMask_V0 v0) %{ match(Set dst_src (AddVL (Binary dst_src (Replicate con)) v0)); format %{ "vaddL_vi_masked $dst_src, $dst_src, $con, $v0" %} ins_encode %{ @@ -907,7 +907,7 @@ instruct vand_masked(vReg dst_src1, vReg src2, vRegMask_V0 v0) %{ // vector-immediate and (unpredicated) -instruct vand_vi(vReg dst, vReg src1, immI5 con) %{ +instruct vand_vi(vReg dst, vReg src1, immI_simm5 con) %{ predicate(Matcher::vector_element_basic_type(n) == T_BYTE || Matcher::vector_element_basic_type(n) == T_SHORT || Matcher::vector_element_basic_type(n) == T_INT); @@ -923,7 +923,7 @@ instruct vand_vi(vReg dst, vReg src1, immI5 con) %{ ins_pipe(pipe_slow); %} -instruct vandL_vi(vReg dst, vReg src1, immL5 con) %{ +instruct vandL_vi(vReg dst, vReg src1, immL_simm5 con) %{ predicate(Matcher::vector_element_basic_type(n) == T_LONG); match(Set dst (AndV src1 (Replicate con))); format %{ "vandL_vi $dst, $src1, $con" %} @@ -969,7 +969,7 @@ instruct vandL_vx(vReg dst, vReg src1, iRegL src2) %{ // vector-immediate and (predicated) -instruct vand_vi_masked(vReg dst_src, immI5 con, vRegMask_V0 v0) %{ +instruct vand_vi_masked(vReg dst_src, immI_simm5 con, vRegMask_V0 v0) %{ predicate(Matcher::vector_element_basic_type(n) == T_BYTE || Matcher::vector_element_basic_type(n) == T_SHORT || Matcher::vector_element_basic_type(n) == T_INT); @@ -985,7 +985,7 @@ instruct vand_vi_masked(vReg dst_src, immI5 con, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vandL_vi_masked(vReg dst_src, immL5 con, vRegMask_V0 v0) %{ +instruct vandL_vi_masked(vReg dst_src, immL_simm5 con, vRegMask_V0 v0) %{ predicate(Matcher::vector_element_basic_type(n) == T_LONG); match(Set dst_src (AndV (Binary dst_src (Replicate con)) v0)); format %{ "vandL_vi_masked $dst_src, $dst_src, $con, $v0" %} @@ -1061,7 +1061,7 @@ instruct vor_masked(vReg dst_src1, vReg src2, vRegMask_V0 v0) %{ // vector-immediate or (unpredicated) -instruct vor_vi(vReg dst, vReg src1, immI5 con) %{ +instruct vor_vi(vReg dst, vReg src1, immI_simm5 con) %{ predicate(Matcher::vector_element_basic_type(n) == T_BYTE || Matcher::vector_element_basic_type(n) == T_SHORT || Matcher::vector_element_basic_type(n) == T_INT); @@ -1077,7 +1077,7 @@ instruct vor_vi(vReg dst, vReg src1, immI5 con) %{ ins_pipe(pipe_slow); %} -instruct vorL_vi(vReg dst, vReg src1, immL5 con) %{ +instruct vorL_vi(vReg dst, vReg src1, immL_simm5 con) %{ predicate(Matcher::vector_element_basic_type(n) == T_LONG); match(Set dst (OrV src1 (Replicate con))); format %{ "vorL_vi $dst, $src1, $con" %} @@ -1123,7 +1123,7 @@ instruct vorL_vx(vReg dst, vReg src1, iRegL src2) %{ // vector-immediate or (predicated) -instruct vor_vi_masked(vReg dst_src, immI5 con, vRegMask_V0 v0) %{ +instruct vor_vi_masked(vReg dst_src, immI_simm5 con, vRegMask_V0 v0) %{ predicate(Matcher::vector_element_basic_type(n) == T_BYTE || Matcher::vector_element_basic_type(n) == T_SHORT || Matcher::vector_element_basic_type(n) == T_INT); @@ -1139,7 +1139,7 @@ instruct vor_vi_masked(vReg dst_src, immI5 con, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vorL_vi_masked(vReg dst_src, immL5 con, vRegMask_V0 v0) %{ +instruct vorL_vi_masked(vReg dst_src, immL_simm5 con, vRegMask_V0 v0) %{ predicate(Matcher::vector_element_basic_type(n) == T_LONG); match(Set dst_src (OrV (Binary dst_src (Replicate con)) v0)); format %{ "vorL_vi_masked $dst_src, $dst_src, $con, $v0" %} @@ -1215,7 +1215,7 @@ instruct vxor_masked(vReg dst_src1, vReg src2, vRegMask_V0 v0) %{ // vector-immediate xor (unpredicated) -instruct vxor_vi(vReg dst, vReg src1, immI5 con) %{ +instruct vxor_vi(vReg dst, vReg src1, immI_simm5 con) %{ predicate(Matcher::vector_element_basic_type(n) == T_BYTE || Matcher::vector_element_basic_type(n) == T_SHORT || Matcher::vector_element_basic_type(n) == T_INT); @@ -1231,7 +1231,7 @@ instruct vxor_vi(vReg dst, vReg src1, immI5 con) %{ ins_pipe(pipe_slow); %} -instruct vxorL_vi(vReg dst, vReg src1, immL5 con) %{ +instruct vxorL_vi(vReg dst, vReg src1, immL_simm5 con) %{ predicate(Matcher::vector_element_basic_type(n) == T_LONG); match(Set dst (XorV src1 (Replicate con))); format %{ "vxorL_vi $dst, $src1, $con" %} @@ -1277,7 +1277,7 @@ instruct vxorL_vx(vReg dst, vReg src1, iRegL src2) %{ // vector-immediate xor (predicated) -instruct vxor_vi_masked(vReg dst_src, immI5 con, vRegMask_V0 v0) %{ +instruct vxor_vi_masked(vReg dst_src, immI_simm5 con, vRegMask_V0 v0) %{ predicate(Matcher::vector_element_basic_type(n) == T_BYTE || Matcher::vector_element_basic_type(n) == T_SHORT || Matcher::vector_element_basic_type(n) == T_INT); @@ -1293,7 +1293,7 @@ instruct vxor_vi_masked(vReg dst_src, immI5 con, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vxorL_vi_masked(vReg dst_src, immL5 con, vRegMask_V0 v0) %{ +instruct vxorL_vi_masked(vReg dst_src, immL_simm5 con, vRegMask_V0 v0) %{ predicate(Matcher::vector_element_basic_type(n) == T_LONG); match(Set dst_src (XorV (Binary dst_src (Replicate con)) v0)); format %{ "vxorL_vi_masked $dst_src, $dst_src, $con, $v0" %} @@ -3211,7 +3211,7 @@ instruct replicateL(vReg dst, iRegL src) %{ ins_pipe(pipe_slow); %} -instruct replicate_imm5(vReg dst, immI5 con) %{ +instruct replicate_imm5(vReg dst, immI_simm5 con) %{ predicate(Matcher::is_non_long_integral_vector(n)); match(Set dst (Replicate con)); format %{ "replicate_imm5 $dst, $con" %} @@ -3223,7 +3223,7 @@ instruct replicate_imm5(vReg dst, immI5 con) %{ ins_pipe(pipe_slow); %} -instruct replicateL_imm5(vReg dst, immL5 con) %{ +instruct replicateL_imm5(vReg dst, immL_simm5 con) %{ predicate(Matcher::vector_element_basic_type(n) == T_LONG); match(Set dst (Replicate con)); format %{ "replicateL_imm5 $dst, $con" %} @@ -4569,11 +4569,11 @@ instruct vstringL_indexof_char(iRegP_R11 str1, iRegI_R12 cnt1, iRegI_R13 ch, %} // clearing of an array -instruct vclearArray_reg_reg(iRegL_R29 cnt, iRegP_R28 base, immL0 zero, +instruct vclearArray_reg_reg(iRegL_R29 cnt, iRegP_R28 base, immL_0 zero, vReg_V4 v4, vReg_V5 v5, vReg_V6 v6, vReg_V7 v7, Universe dummy) %{ - predicate(!UseBlockZeroing && UseRVV); + predicate(UseRVV); match(Set dummy (ClearArray (Binary cnt base) zero)); effect(USE_KILL cnt, USE_KILL base, TEMP v4, TEMP v5, TEMP v6, TEMP v7); @@ -4587,7 +4587,7 @@ instruct vclearArray_reg_reg(iRegL_R29 cnt, iRegP_R28 base, immL0 zero, %} // Vector Load Const -instruct vloadcon(vReg dst, immI0 src) %{ +instruct vloadcon(vReg dst, immI_0 src) %{ match(Set dst (VectorLoadConst src)); format %{ "vloadcon $dst\t# generate iota indices" %} ins_encode %{ @@ -6110,7 +6110,7 @@ instruct vmask_fromlong(vRegMask dst, iRegL src) %{ // Not matched. Condition is negated and value zero is moved to the right side in CMoveINode::Ideal. -// instruct cmovI_vtest_anytrue(iRegINoSp dst, cmpOp cop, vRegMask op1, vRegMask op2, immI0 zero, immI_1 one) %{ +// instruct cmovI_vtest_anytrue(iRegINoSp dst, cmpOp cop, vRegMask op1, vRegMask op2, immI_0 zero, immI_1 one) %{ // predicate(n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::ne && // static_cast(n->in(1)->in(2))->get_predicate() == BoolTest::ne); // match(Set dst (CMoveI (Binary cop (VectorTest op1 op2)) (Binary zero one))); @@ -6125,7 +6125,7 @@ instruct vmask_fromlong(vRegMask dst, iRegL src) %{ // ins_pipe(pipe_slow); // %} -instruct cmovI_vtest_anytrue_negate(iRegINoSp dst, cmpOp cop, vRegMask op1, vRegMask op2, immI0 zero, immI_1 one) %{ +instruct cmovI_vtest_anytrue_negate(iRegINoSp dst, cmpOp cop, vRegMask op1, vRegMask op2, immI_0 zero, immI_1 one) %{ predicate(n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::eq && static_cast(n->in(1)->in(2))->get_predicate() == BoolTest::ne); match(Set dst (CMoveI (Binary cop (VectorTest op1 op2)) (Binary one zero))); @@ -6144,7 +6144,7 @@ instruct cmovI_vtest_anytrue_negate(iRegINoSp dst, cmpOp cop, vRegMask op1, vReg // Not matched. Condition is negated and value zero is moved to the right side in CMoveINode::Ideal. -// instruct cmovI_vtest_alltrue(iRegINoSp dst, cmpOp cop, vRegMask op1, vRegMask op2, immI0 zero, immI_1 one) %{ +// instruct cmovI_vtest_alltrue(iRegINoSp dst, cmpOp cop, vRegMask op1, vRegMask op2, immI_0 zero, immI_1 one) %{ // predicate(n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::eq && // static_cast(n->in(1)->in(2))->get_predicate() == BoolTest::overflow); // match(Set dst (CMoveI (Binary cop (VectorTest op1 op2)) (Binary zero one))); @@ -6160,7 +6160,7 @@ instruct cmovI_vtest_anytrue_negate(iRegINoSp dst, cmpOp cop, vRegMask op1, vReg // ins_pipe(pipe_slow); // %} -instruct cmovI_vtest_alltrue_negate(iRegINoSp dst, cmpOp cop, vRegMask op1, vRegMask op2, immI0 zero, immI_1 one) %{ +instruct cmovI_vtest_alltrue_negate(iRegINoSp dst, cmpOp cop, vRegMask op1, vRegMask op2, immI_0 zero, immI_1 one) %{ predicate(n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::ne && static_cast(n->in(1)->in(2))->get_predicate() == BoolTest::overflow); match(Set dst (CMoveI (Binary cop (VectorTest op1 op2)) (Binary one zero))); @@ -6285,6 +6285,54 @@ instruct cmovD_vtest_alltrue(fRegD dst, fRegD src, cmpOpEqNe cop, vRegMask op1, ins_pipe(pipe_slow); %} +instruct cmovP_vtest_anytrue(iRegPNoSp dst, iRegP src, cmpOpEqNe cop, vRegMask op1, vRegMask op2) %{ + predicate(static_cast(n->in(1)->in(2))->get_predicate() == BoolTest::ne); + match(Set dst (CMoveP (Binary cop (VectorTest op1 op2)) (Binary dst src))); + format %{ "CMoveP $dst, (vectortest $cop $op1 $op2), $dst, $src\t#@cmovP_vtest_anytrue" %} + ins_encode %{ + vectorTestCMove(masm, $cop$$cmpcode, $dst$$Register, $src$$Register, + as_VectorRegister($op1$$reg), Matcher::vector_element_basic_type(this, $op1), + Matcher::vector_length(this, $op1), BoolTest::ne); + %} + ins_pipe(pipe_slow); +%} + +instruct cmovP_vtest_alltrue(iRegPNoSp dst, iRegP src, cmpOpEqNe cop, vRegMask op1, vRegMask op2) %{ + predicate(static_cast(n->in(1)->in(2))->get_predicate() == BoolTest::overflow); + match(Set dst (CMoveP (Binary cop (VectorTest op1 op2)) (Binary dst src))); + format %{ "CMoveP $dst, (vectortest $cop $op1 $op2), $dst, $src\t#@cmovP_vtest_alltrue" %} + ins_encode %{ + vectorTestCMove(masm, $cop$$cmpcode, $dst$$Register, $src$$Register, + as_VectorRegister($op1$$reg), Matcher::vector_element_basic_type(this, $op1), + Matcher::vector_length(this, $op1), BoolTest::overflow); + %} + ins_pipe(pipe_slow); +%} + +instruct cmovN_vtest_anytrue(iRegNNoSp dst, iRegN src, cmpOpEqNe cop, vRegMask op1, vRegMask op2) %{ + predicate(static_cast(n->in(1)->in(2))->get_predicate() == BoolTest::ne); + match(Set dst (CMoveN (Binary cop (VectorTest op1 op2)) (Binary dst src))); + format %{ "CMoveN $dst, (vectortest $cop $op1 $op2), $dst, $src\t#@cmovN_vtest_anytrue" %} + ins_encode %{ + vectorTestCMove(masm, $cop$$cmpcode, $dst$$Register, $src$$Register, + as_VectorRegister($op1$$reg), Matcher::vector_element_basic_type(this, $op1), + Matcher::vector_length(this, $op1), BoolTest::ne); + %} + ins_pipe(pipe_slow); +%} + +instruct cmovN_vtest_alltrue(iRegNNoSp dst, iRegN src, cmpOpEqNe cop, vRegMask op1, vRegMask op2) %{ + predicate(static_cast(n->in(1)->in(2))->get_predicate() == BoolTest::overflow); + match(Set dst (CMoveN (Binary cop (VectorTest op1 op2)) (Binary dst src))); + format %{ "CMoveN $dst, (vectortest $cop $op1 $op2), $dst, $src\t#@cmovN_vtest_alltrue" %} + ins_encode %{ + vectorTestCMove(masm, $cop$$cmpcode, $dst$$Register, $src$$Register, + as_VectorRegister($op1$$reg), Matcher::vector_element_basic_type(this, $op1), + Matcher::vector_length(this, $op1), BoolTest::overflow); + %} + ins_pipe(pipe_slow); +%} + // anytrue instruct vtest_anytrue_branch(cmpOpEqNe cop, vRegMask op1, vRegMask op2, label lbl) %{ diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp index 9793dddb68be..1148a9568d8e 100644 --- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp @@ -829,22 +829,10 @@ class StubGenerator: public StubCodeGenerator { StubCodeMark mark(this, stub_id); Label done; - const Register base = x28, cnt = x29, tmp1 = x30, tmp2 = x31; + const Register base = x28, cnt = x29, tmp1 = x30; start = __ pc(); - if (UseBlockZeroing) { - int zicboz_block_size = VM_Version::zicboz_block_size.value(); - // Ensure count >= 2 * zicboz_block_size so that it still deserves - // a cbo.zero after alignment. - Label small; - int low_limit = MAX2(2 * zicboz_block_size, (int)BlockZeroingLowLimit) / wordSize; - __ mv(tmp1, low_limit); - __ blt(cnt, tmp1, small); - __ zero_dcache_blocks(base, cnt, tmp1, tmp2); - __ bind(small); - } - { // Clear the remaining blocks. Label loop; @@ -3767,8 +3755,7 @@ class StubGenerator: public StubCodeGenerator { if (bs_asm->nmethod_patching_type() == NMethodPatchingType::conc_instruction_and_data_patch) { BarrierSetNMethod* bs_nm = BarrierSet::barrier_set()->barrier_set_nmethod(); Address thread_epoch_addr(xthread, in_bytes(bs_nm->thread_disarmed_guard_value_offset()) + 4); - __ la(t1, ExternalAddress(bs_asm->patching_epoch_addr())); - __ lwu(t1, t1); + __ lwu(t1, ExternalAddress(bs_asm->patching_epoch_addr())); __ sw(t1, thread_epoch_addr); // There are two ways this can work: // - The writer did system icache shootdown after the instruction stream update. @@ -5427,8 +5414,7 @@ class StubGenerator: public StubCodeGenerator { __ bind(preemption_cancelled); __ sb(zr, Address(xthread, JavaThread::preemption_cancelled_offset())); __ la(fp, Address(sp, checked_cast(ContinuationEntry::size() + 2 * wordSize))); - __ la(t1, ExternalAddress(ContinuationEntry::thaw_call_pc_address())); - __ ld(t1, Address(t1)); + __ ld(t1, ExternalAddress(ContinuationEntry::thaw_call_pc_address())); __ jr(t1); // record the stub start and end diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.cpp b/src/hotspot/cpu/riscv/vm_version_riscv.cpp index fbf61492d4cd..33fd60e3db72 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.cpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.cpp @@ -224,19 +224,6 @@ void VM_Version::common_initialize() { FLAG_SET_DEFAULT(UsePopCountInstruction, false); } - if (UseZicboz && zicboz_block_size.value() > 0) { - assert(is_power_of_2(zicboz_block_size.value()), "Sanity"); - if (FLAG_IS_DEFAULT(UseBlockZeroing)) { - FLAG_SET_DEFAULT(UseBlockZeroing, true); - } - if (FLAG_IS_DEFAULT(BlockZeroingLowLimit)) { - FLAG_SET_DEFAULT(BlockZeroingLowLimit, 4 * zicboz_block_size.value()); - } - } else if (UseBlockZeroing) { - warning("Block zeroing is not available"); - FLAG_SET_DEFAULT(UseBlockZeroing, false); - } - if (UseRVV) { // read vector length from vector CSR vlenb _initial_vector_length = cpu_vector_length(); diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.hpp b/src/hotspot/cpu/riscv/vm_version_riscv.hpp index 68e870fee2d0..acdab9d8ea27 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.hpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.hpp @@ -275,12 +275,8 @@ class VM_Version : public Abstract_VM_Version { decl(Zfh , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfh)) \ /* Zfhmin Minimal Half-Precision Floating-Point instructions */ \ decl(Zfhmin , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfhmin)) \ - /* Zicbom Cache Block Management Operations */ \ - decl(Zicbom , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicbom)) \ /* Zicbop Cache Block Prefetch Operations */ \ decl(Zicbop , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicbop)) \ - /* Zicboz Cache Block Zero Operations */ \ - decl(Zicboz , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicboz)) \ /* Ziccid Instruction/Data Coherence and Consistency */ \ decl(Ziccid , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZiccid)) \ /* Base Counters and Timers */ \ @@ -336,7 +332,6 @@ class VM_Version : public Abstract_VM_Version { decl(unaligned_scalar , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ /* Performance of misaligned vector accesses (unknown, unspported, slow, fast) */ \ decl(unaligned_vector , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ - decl(zicboz_block_size , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ #define DECLARE_RV_NON_EXT_FEATURE(PRETTY, LINUX_BIT, FSTRING, FLAGF) \ struct PRETTY##RVNonExtFeatureValue : public RVNonExtFeatureValue { \ @@ -446,9 +441,7 @@ class VM_Version : public Abstract_VM_Version { RV_ENABLE_EXTENSION(UseZbb) \ RV_ENABLE_EXTENSION(UseZbs) \ RV_ENABLE_EXTENSION(UseZic64b) \ - RV_ENABLE_EXTENSION(UseZicbom) \ RV_ENABLE_EXTENSION(UseZicbop) \ - RV_ENABLE_EXTENSION(UseZicboz) \ RV_ENABLE_EXTENSION(UseZihintpause) \ static void useRVA22U64Profile(); @@ -465,9 +458,7 @@ class VM_Version : public Abstract_VM_Version { RV_ENABLE_EXTENSION(UseZfa) \ RV_ENABLE_EXTENSION(UseZfhmin) \ RV_ENABLE_EXTENSION(UseZic64b) \ - RV_ENABLE_EXTENSION(UseZicbom) \ RV_ENABLE_EXTENSION(UseZicbop) \ - RV_ENABLE_EXTENSION(UseZicboz) \ RV_ENABLE_EXTENSION(UseZicond) \ RV_ENABLE_EXTENSION(UseZihintpause) \ RV_ENABLE_EXTENSION(UseZvfhmin) \ diff --git a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp index d410668306a0..e7919a203eae 100644 --- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp @@ -1994,7 +1994,7 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { } // If we don't know anything, just go through the generic arraycopy. - if (default_type == nullptr) { + if (default_type == nullptr || !default_type->is_loaded()) { address copyfunc_addr = StubRoutines::generic_arraycopy(); if (copyfunc_addr == nullptr) { @@ -2381,7 +2381,7 @@ void LIR_Assembler::shift_op(LIR_Code code, LIR_Opr left, jint count, LIR_Opr de } if (dest->is_single_cpu()) { assert(code == lir_shl || left == dest, "left and dest must be equal for 2 operand form right shifts"); - count = count & 0x1F; // Java spec + count &= right_n_bits(LogBitsPerInt); // Java spec switch (code) { case lir_shl: __ z_sllg (dest->as_register(), left->as_register(), count); break; case lir_shr: __ z_sra (dest->as_register(), count); break; @@ -2389,7 +2389,7 @@ void LIR_Assembler::shift_op(LIR_Code code, LIR_Opr left, jint count, LIR_Opr de default: ShouldNotReachHere(); } } else if (dest->is_double_cpu()) { - count = count & 63; // Java spec + count &= right_n_bits(LogBitsPerLong); // Java spec Register l = left->as_pointer_register(); Register d = dest->as_pointer_register(); switch (code) { diff --git a/src/hotspot/cpu/s390/c1_LIRGenerator_s390.cpp b/src/hotspot/cpu/s390/c1_LIRGenerator_s390.cpp index 07f5953fc9da..5e0d0ba2bbd5 100644 --- a/src/hotspot/cpu/s390/c1_LIRGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/c1_LIRGenerator_s390.cpp @@ -820,7 +820,8 @@ void LIRGenerator::do_NewObjectArray(NewObjectArray* x) { // In case of patching (i.e., object class is not yet loaded), we need to reexecute the instruction // and therefore provide the state before the parameters have been consumed. CodeEmitInfo* patching_info = nullptr; - if (!x->klass()->is_loaded() || PatchALot) { + ciKlass* obj = ciObjArrayKlass::make(x->klass()); + if (!x->klass()->is_loaded() || !obj->is_loaded() || PatchALot) { patching_info = state_for (x, x->state_before()); } @@ -835,13 +836,12 @@ void LIRGenerator::do_NewObjectArray(NewObjectArray* x) { LIR_Opr klass_reg = FrameMap::Z_R11_metadata_opr; LIR_Opr len = length.result(); - ciKlass* obj = ciObjArrayKlass::make(x->klass()); bool is_flat = obj->is_loaded() && obj->is_flat_array_klass(); bool is_null_free = obj->is_loaded() && obj->as_array_klass()->is_elem_null_free(); CodeStub* slow_path = new NewObjectArrayStub(klass_reg, len, reg, info, is_null_free); - if (obj == ciEnv::unloaded_ciobjarrayklass()) { - BAILOUT("encountered unloaded_ciobjarrayklass due to out of memory error"); + if (x->klass()->is_loaded() && !obj->is_loaded()) { + BAILOUT("encountered unloaded ciobjarrayklass due to out of memory error"); } klass2reg_with_patching(klass_reg, obj, patching_info); __ allocate_array(reg, len, tmp1, tmp2, tmp3, tmp4, T_OBJECT, klass_reg, slow_path, true, is_null_free || is_flat); diff --git a/src/hotspot/cpu/s390/matcher_s390.hpp b/src/hotspot/cpu/s390/matcher_s390.hpp index 7e006bdc6c7b..1c33ebea215b 100644 --- a/src/hotspot/cpu/s390/matcher_s390.hpp +++ b/src/hotspot/cpu/s390/matcher_s390.hpp @@ -107,6 +107,10 @@ // Java calling convention forces doubles to be aligned. static const bool misaligned_doubles_ok = true; + // Use MachUncommonTrapNode for representing uncommon trap sequences. + // This is an alternative to representing uncommon traps as runtime calls. + static const bool use_mach_uncommon_trap_node = false; + // Do floats take an entire double register or just half? // // A float in resides in a zarch double register. When storing it by diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index 99457c6e0684..c1d7d161c00e 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -9938,38 +9938,54 @@ instruct cmpb_RegN_immN_Far(cmpOpT boolnode, iRegN src1, immN8 src2, label labl, // both forms beat the trinary form of long-compare and both are very useful // on platforms which have few registers. +// Convert condition code into -1,0,1, where +// -1 means less +// 0 means equal +// 1 means greater. + // Manifest a CmpL3 result in an integer register. Very painful. // This is the test to avoid. instruct cmpL3_reg_reg(iRegI dst, iRegL src1, iRegL src2, flagsReg cr) %{ match(Set dst (CmpL3 src1 src2)); effect(KILL cr); - ins_cost(DEFAULT_COST * 5 + BRANCH_COST); - size(24); + ins_cost(DEFAULT_COST * 4); + size(20); format %{ "CmpL3 $dst,$src1,$src2" %} ins_encode %{ - Label done; - // compare registers __ z_cgr($src1$$Register, $src2$$Register); - // Convert condition code into -1,0,1, where - // -1 means less - // 0 means equal - // 1 means greater. - if (VM_Version::has_LoadStoreConditional()) { - Register one = Z_R0_scratch; - Register minus_one = Z_R1_scratch; - __ z_lghi(minus_one, -1); - __ z_lghi(one, 1); - __ z_lghi( $dst$$Register, 0); - __ z_locgr($dst$$Register, one, Assembler::bcondHigh); - __ z_locgr($dst$$Register, minus_one, Assembler::bcondLow); - } else { - __ clear_reg($dst$$Register, true, false); - __ z_bre(done); - __ z_lhi($dst$$Register, 1); - __ z_brh(done); - __ z_lhi($dst$$Register, -1); - } - __ bind(done); + __ load_const_optimized($dst$$Register, 1); + __ load_on_condition_imm_32($dst$$Register, 0, Assembler::bcondEqual); + __ load_on_condition_imm_32($dst$$Register, -1, Assembler::bcondLow); + %} + ins_pipe(pipe_class_dummy); +%} + +instruct cmpU3_reg_reg(iRegI dst, iRegI src1, iRegI src2, flagsReg cr)%{ + match(Set dst (CmpU3 src1 src2)); + effect(KILL cr); + ins_cost(DEFAULT_COST * 4); + size(18); + format %{ "cmpU3 $dst,$src1,$src2" %} + ins_encode %{ + __ z_clr($src1$$Register, $src2$$Register); + __ load_const_optimized($dst$$Register, 1); + __ load_on_condition_imm_32($dst$$Register, 0, Assembler::bcondEqual); + __ load_on_condition_imm_32($dst$$Register, -1, Assembler::bcondLow); + %} + ins_pipe(pipe_class_dummy); +%} + +instruct cmpUL3_reg_reg(iRegI dst, iRegL src1, iRegL src2, flagsReg cr)%{ + match(Set dst (CmpUL3 src1 src2)); + effect(KILL cr); + ins_cost(DEFAULT_COST * 4); + size(20); + format %{ "cmpUL3 $dst,$src1,$src2" %} + ins_encode %{ + __ z_clgr($src1$$Register, $src2$$Register); + __ load_const_optimized($dst$$Register, 1); + __ load_on_condition_imm_32($dst$$Register, 0, Assembler::bcondEqual); + __ load_on_condition_imm_32($dst$$Register, -1, Assembler::bcondLow); %} ins_pipe(pipe_class_dummy); %} diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp index 2f470ab44558..c736e6d5b637 100644 --- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp @@ -490,6 +490,7 @@ void LIR_Assembler::return_op(LIR_Opr result, C1SafepointPollStub* code_stub) { __ testptr(rax, rax); __ jcc(Assembler::notZero, not_null); // Returned value is null, zero all return registers because they may belong to oop fields + __ xorq(j_rarg0, j_rarg0); __ xorq(j_rarg1, j_rarg1); __ xorq(j_rarg2, j_rarg2); __ xorq(j_rarg3, j_rarg3); @@ -2433,7 +2434,7 @@ void LIR_Assembler::shift_op(LIR_Code code, LIR_Opr left, jint count, LIR_Opr de if (dest->is_single_cpu()) { // first move left into dest so that left is not destroyed by the shift Register value = dest->as_register(); - count = count & 0x1F; // Java spec + count &= right_n_bits(LogBitsPerInt); // Java spec move_regs(left->as_register(), value); switch (code) { @@ -2445,7 +2446,7 @@ void LIR_Assembler::shift_op(LIR_Code code, LIR_Opr left, jint count, LIR_Opr de } else if (dest->is_double_cpu()) { // first move left into dest so that left is not destroyed by the shift Register value = dest->as_register_lo(); - count = count & 0x1F; // Java spec + count &= right_n_bits(LogBitsPerLong); // Java spec move_regs(left->as_register_lo(), value); switch (code) { @@ -2533,7 +2534,7 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { } // if we don't know anything, just go through the generic arraycopy - if (default_type == nullptr) { + if (default_type == nullptr || !default_type->is_loaded()) { // save outgoing arguments on stack in case call to System.arraycopy is needed // HACK ALERT. This code used to push the parameters in a hardwired fashion // for interpreter calling conventions. Now we have to do it in new style conventions. diff --git a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp index 94009af28b84..5723e03d7ee0 100644 --- a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp @@ -1176,7 +1176,8 @@ void LIRGenerator::do_NewObjectArray(NewObjectArray* x) { // in case of patching (i.e., object class is not yet loaded), we need to reexecute the instruction // and therefore provide the state before the parameters have been consumed CodeEmitInfo* patching_info = nullptr; - if (!x->klass()->is_loaded() || PatchALot) { + ciKlass* obj = ciObjArrayKlass::make(x->klass()); + if (!x->klass()->is_loaded() || !obj->is_loaded() || PatchALot) { patching_info = state_for(x, x->state_before()); } @@ -1192,15 +1193,14 @@ void LIRGenerator::do_NewObjectArray(NewObjectArray* x) { length.load_item_force(FrameMap::rbx_opr); LIR_Opr len = length.result(); - ciKlass* obj = ciObjArrayKlass::make(x->klass()); // TODO 8265122 Implement a fast path for this bool is_flat = obj->is_loaded() && obj->is_flat_array_klass(); bool is_null_free = obj->is_loaded() && obj->as_array_klass()->is_elem_null_free(); CodeStub* slow_path = new NewObjectArrayStub(klass_reg, len, reg, info, is_null_free); - if (obj == ciEnv::unloaded_ciobjarrayklass()) { - BAILOUT("encountered unloaded_ciobjarrayklass due to out of memory error"); + if (x->klass()->is_loaded() && !obj->is_loaded()) { + BAILOUT("encountered unloaded ciobjarrayklass due to out of memory error"); } klass2reg_with_patching(klass_reg, obj, patching_info); bool always_slow_path = is_null_free || is_flat; diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index b4ca8648b326..4e164a9dab47 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -460,7 +460,7 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register reg_rax, Register t, C2FastUnlockStub* stub = nullptr; if (!Compile::current()->output()->in_scratch_emit_size()) { - stub = new (Compile::current()->comp_arena()) C2FastUnlockStub(obj, mark, reg_rax, thread); + stub = new (Compile::current()->comp_arena()) C2FastUnlockStub(obj, reg_rax, thread); Compile::current()->output()->add_stub(stub); } diff --git a/src/hotspot/cpu/x86/interp_masm_x86.cpp b/src/hotspot/cpu/x86/interp_masm_x86.cpp index efafe816e155..0550a1fbb571 100644 --- a/src/hotspot/cpu/x86/interp_masm_x86.cpp +++ b/src/hotspot/cpu/x86/interp_masm_x86.cpp @@ -1060,6 +1060,7 @@ void InterpreterMacroAssembler::remove_activation(TosState state, testptr(rax, rax); jcc(Assembler::notZero, not_null); // Returned value is null, zero all return registers because they may belong to oop fields + xorq(j_rarg0, j_rarg0); xorq(j_rarg1, j_rarg1); xorq(j_rarg2, j_rarg2); xorq(j_rarg3, j_rarg3); diff --git a/src/hotspot/cpu/x86/matcher_x86.hpp b/src/hotspot/cpu/x86/matcher_x86.hpp index 5b012768cb9d..f351cf4b1ab2 100644 --- a/src/hotspot/cpu/x86/matcher_x86.hpp +++ b/src/hotspot/cpu/x86/matcher_x86.hpp @@ -101,6 +101,10 @@ // Java calling convention forces doubles to be aligned. static const bool misaligned_doubles_ok = true; + // Use MachUncommonTrapNode for representing uncommon trap sequences. + // This is an alternative to representing uncommon traps as runtime calls. + static const bool use_mach_uncommon_trap_node = false; + // Are floats converted to double when stored to stack during deoptimization? // On x64 it is stored without conversion so we can use normal access. static constexpr bool float_in_double() { diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_poly_mont.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_poly_mont.cpp index 76b6fa97fa58..fe6494fa82ec 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_poly_mont.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_poly_mont.cpp @@ -1,4 +1,5 @@ /* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2024, 2026, Intel Corporation. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -677,11 +678,11 @@ address StubGenerator::generate_intpoly_assign() { // MontgomeryIntPolynP256: 5 = 4 + 1 // IntegerPolynomial1305: 5 = 4 + 1 // IntegerPolynomial25519: 5 = 4 + 1 + // IntegerPolynomialP384: 7 = 4 + 2 + 1 // IntegerPolynomialP256: 10 = 8 + 2 // Curve25519OrderField: 10 = 8 + 2 // Curve25519OrderField: 10 = 8 + 2 // P256OrderField: 10 = 8 + 2 - // IntegerPolynomialP384: 14 = 8 + 4 + 2 // P384OrderField: 14 = 8 + 4 + 2 // IntegerPolynomial448: 16 = 8 + 8 // Curve448OrderField: 16 = 8 + 8 @@ -712,7 +713,7 @@ address StubGenerator::generate_intpoly_assign() { XMMRegister select = xmm2; Register tmp = r9; - Label L_Length5, L_Length10, L_Length14, L_Length16, L_Length19, L_DefaultLoop, L_Done; + Label L_Length5, L_Length7, L_Length10, L_Length14, L_Length16, L_Length19, L_DefaultLoop, L_Done; __ negq(set); if (UseAVX > 2) { @@ -727,6 +728,8 @@ address StubGenerator::generate_intpoly_assign() { // number of limbs which is not a secret) __ cmpl(length, 5); __ jcc(Assembler::equal, L_Length5); + __ cmpl(length, 7); + __ jcc(Assembler::equal, L_Length7); __ cmpl(length, 10); __ jcc(Assembler::equal, L_Length10); __ cmpl(length, 14); @@ -753,6 +756,12 @@ address StubGenerator::generate_intpoly_assign() { assign_avx (aLimbs, bLimbs, 8, select, A, B, Assembler::AVX_256bit, _masm); __ jmp(L_Done); + __ bind(L_Length7); // 1 + 2 + 4 + assign_scalar(aLimbs, bLimbs, 0, set, tmp, _masm); + assign_avx(aLimbs, bLimbs, 8, select, A, B, Assembler::AVX_128bit, _masm); + assign_avx(aLimbs, bLimbs, 24, select, A, B, Assembler::AVX_256bit, _masm); + __ jmp(L_Done); + __ bind(L_Length10); // 2 + 8 assign_avx(aLimbs, bLimbs, 0, select, A, B, Assembler::AVX_128bit, _masm); assign_avx(aLimbs, bLimbs, 16, select, A, B, Assembler::AVX_512bit, _masm); diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index df62cf56c7c9..21d096eeb19b 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -11060,20 +11060,6 @@ instruct subI_rReg_ndd(rRegI dst, rRegI src1, rRegI src2, rFlagsReg cr) ins_pipe(ialu_reg_reg); %} -instruct subI_rReg_rReg_imm_ndd(rRegI dst, rRegI src1, immI src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (SubI src1 src2)); - effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_opr1); - - format %{ "esubl $dst, $src1, $src2\t# int ndd" %} - ins_encode %{ - __ esubl($dst$$Register, $src1$$Register, $src2$$constant, false); - %} - ins_pipe(ialu_reg_reg); -%} - instruct subI_rReg_mem(rRegI dst, memory src, rFlagsReg cr) %{ match(Set dst (SubI dst (LoadI src))); @@ -11130,20 +11116,6 @@ instruct subL_rReg_ndd(rRegL dst, rRegL src1, rRegL src2, rFlagsReg cr) ins_pipe(ialu_reg_reg); %} -instruct subL_rReg_rReg_imm_ndd(rRegL dst, rRegL src1, immL32 src2, rFlagsReg cr) -%{ - predicate(UseAPX); - match(Set dst (SubL src1 src2)); - effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_opr1); - - format %{ "esubq $dst, $src1, $src2\t# long ndd" %} - ins_encode %{ - __ esubq($dst$$Register, $src1$$Register, $src2$$constant, false); - %} - ins_pipe(ialu_reg_reg); -%} - instruct subL_rReg_mem(rRegL dst, memory src, rFlagsReg cr) %{ match(Set dst (SubL dst (LoadL src))); diff --git a/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp b/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp index 90a61b7e8b63..04257dd0ed7f 100644 --- a/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp +++ b/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp @@ -39,6 +39,7 @@ #include "prims/jniFastGetField.hpp" #include "prims/jvm_misc.hpp" #include "runtime/arguments.hpp" +#include "runtime/atomicAccess.hpp" #include "runtime/frame.inline.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/java.hpp" @@ -547,10 +548,6 @@ void os::thread_wx_enable_write_impl() { #endif // MACOS_AARCH64 -static inline void atomic_copy64(const volatile void *src, volatile void *dst) { - *(jlong *) dst = *(const jlong *) src; -} - extern "C" { int SpinPause() { // We don't use StubRoutines::aarch64::spin_wait stub in order to @@ -616,14 +613,14 @@ extern "C" { if (from > to) { const jlong *end = from + count; while (from < end) - atomic_copy64(from++, to++); + AtomicAccess::store(to++, AtomicAccess::load(from++)); } else if (from < to) { const jlong *end = from; from += count - 1; to += count - 1; while (from >= end) - atomic_copy64(from--, to--); + AtomicAccess::store(to--, AtomicAccess::load(from--)); } } diff --git a/src/hotspot/os_cpu/bsd_zero/atomicAccess_bsd_zero.hpp b/src/hotspot/os_cpu/bsd_zero/atomicAccess_bsd_zero.hpp index 5399f39bf2fa..ef2a12f143d5 100644 --- a/src/hotspot/os_cpu/bsd_zero/atomicAccess_bsd_zero.hpp +++ b/src/hotspot/os_cpu/bsd_zero/atomicAccess_bsd_zero.hpp @@ -127,13 +127,6 @@ inline T AtomicAccess::PlatformCmpxchg<8>::operator()(T volatile* dest, return value; } -// Atomically copy 64 bits of data -inline void atomic_copy64(const volatile void *src, volatile void *dst) { - int64_t tmp; - __atomic_load(reinterpret_cast(src), &tmp, __ATOMIC_RELAXED); - __atomic_store(reinterpret_cast(dst), &tmp, __ATOMIC_RELAXED); -} - template<> template inline T AtomicAccess::PlatformLoad<8>::operator()(T const volatile* src) const { diff --git a/src/hotspot/os_cpu/bsd_zero/os_bsd_zero.cpp b/src/hotspot/os_cpu/bsd_zero/os_bsd_zero.cpp index c9a537feacb5..91a82afb13ec 100644 --- a/src/hotspot/os_cpu/bsd_zero/os_bsd_zero.cpp +++ b/src/hotspot/os_cpu/bsd_zero/os_bsd_zero.cpp @@ -222,14 +222,14 @@ extern "C" { if (from > to) { const jlong *end = from + count; while (from < end) - atomic_copy64(from++, to++); + AtomicAccess::store(to++, AtomicAccess::load(from++)); } else if (from < to) { const jlong *end = from; from += count - 1; to += count - 1; while (from >= end) - atomic_copy64(from--, to--); + AtomicAccess::store(to--, AtomicAccess::load(from--)); } } diff --git a/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp index 216e6b729909..796f27744c11 100644 --- a/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp +++ b/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp @@ -37,6 +37,7 @@ #include "prims/jniFastGetField.hpp" #include "prims/jvm_misc.hpp" #include "runtime/arguments.hpp" +#include "runtime/atomicAccess.hpp" #include "runtime/frame.inline.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/java.hpp" @@ -394,10 +395,6 @@ int os::extra_bang_size_in_bytes() { return 0; } -static inline void atomic_copy64(const volatile void *src, volatile void *dst) { - *(jlong *) dst = *(const jlong *) src; -} - extern "C" { int SpinPause() { using spin_wait_func_ptr_t = void (*)(); @@ -449,14 +446,14 @@ extern "C" { if (from > to) { const jlong *end = from + count; while (from < end) - atomic_copy64(from++, to++); + AtomicAccess::store(to++, AtomicAccess::load(from++)); } else if (from < to) { const jlong *end = from; from += count - 1; to += count - 1; while (from >= end) - atomic_copy64(from--, to--); + AtomicAccess::store(to--, AtomicAccess::load(from--)); } } diff --git a/src/hotspot/os_cpu/linux_riscv/os_linux_riscv.cpp b/src/hotspot/os_cpu/linux_riscv/os_linux_riscv.cpp index d059046c57fb..57a57d546c05 100644 --- a/src/hotspot/os_cpu/linux_riscv/os_linux_riscv.cpp +++ b/src/hotspot/os_cpu/linux_riscv/os_linux_riscv.cpp @@ -37,6 +37,7 @@ #include "prims/jniFastGetField.hpp" #include "prims/jvm_misc.hpp" #include "runtime/arguments.hpp" +#include "runtime/atomicAccess.hpp" #include "runtime/frame.inline.hpp" #include "runtime/globals.hpp" #include "runtime/interfaceSupport.inline.hpp" @@ -452,10 +453,6 @@ int os::extra_bang_size_in_bytes() { return 0; } -static inline void atomic_copy64(const volatile void *src, volatile void *dst) { - *(jlong *) dst = *(const jlong *) src; -} - extern "C" { int SpinPause() { if (UseZihintpause) { @@ -504,14 +501,14 @@ extern "C" { if (from > to) { const jlong *end = from + count; while (from < end) { - atomic_copy64(from++, to++); + AtomicAccess::store(to++, AtomicAccess::load(from++)); } } else if (from < to) { const jlong *end = from; from += count - 1; to += count - 1; while (from >= end) { - atomic_copy64(from--, to--); + AtomicAccess::store(to--, AtomicAccess::load(from--)); } } } diff --git a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp index 36b029d45769..5fd71d6cd4b7 100644 --- a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp +++ b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp @@ -50,7 +50,6 @@ #define RISCV_HWPROBE_EXT_ZBA (1 << 3) #define RISCV_HWPROBE_EXT_ZBB (1 << 4) #define RISCV_HWPROBE_EXT_ZBS (1 << 5) -#define RISCV_HWPROBE_EXT_ZICBOZ (1 << 6) #define RISCV_HWPROBE_EXT_ZBC (1 << 7) #define RISCV_HWPROBE_EXT_ZBKB (1 << 8) #define RISCV_HWPROBE_EXT_ZBKC (1 << 9) @@ -84,6 +83,7 @@ #define RISCV_HWPROBE_EXT_ZAWRS (1ULL << 48) #define RISCV_HWPROBE_EXT_ZABHA (1ULL << 58) #define RISCV_HWPROBE_EXT_ZALASR (1ULL << 59) +#define RISCV_HWPROBE_EXT_ZICBOP (1ULL << 60) #define RISCV_HWPROBE_KEY_CPUPERF_0 5 #define RISCV_HWPROBE_MISALIGNED_UNKNOWN (0 << 0) @@ -93,8 +93,6 @@ #define RISCV_HWPROBE_MISALIGNED_UNSUPPORTED (4 << 0) #define RISCV_HWPROBE_MISALIGNED_MASK (7 << 0) -#define RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE 6 - #define RISCV_HWPROBE_KEY_HIGHEST_VIRT_ADDRESS 7 #define RISCV_HWPROBE_KEY_TIME_CSR_FREQ 8 @@ -138,7 +136,7 @@ static struct riscv_hwprobe query[] = {{RISCV_HWPROBE_KEY_MVENDORID, 0}, {RISCV_HWPROBE_KEY_BASE_BEHAVIOR, 0}, {RISCV_HWPROBE_KEY_IMA_EXT_0, 0}, {RISCV_HWPROBE_KEY_CPUPERF_0, 0}, - {RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE, 0}, + {-1, 0}, // Unused key 6 {RISCV_HWPROBE_KEY_HIGHEST_VIRT_ADDRESS, 0}, {RISCV_HWPROBE_KEY_TIME_CSR_FREQ, 0}, {RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF, 0}, @@ -240,10 +238,10 @@ void RiscvHwprobe::add_features_from_query_result() { if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZCB)) { VM_Version::ext_Zcb.enable_feature(); } -#ifndef PRODUCT - if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZICBOZ)) { - VM_Version::ext_Zicboz.enable_feature(); + if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZICBOP)) { + VM_Version::ext_Zicbop.enable_feature(); } +#ifndef PRODUCT // Currently tests shows that cmove using Zicond instructions will bring // performance regression, but to get a test coverage all the time, will // still prefer to enabling it in debug version. @@ -309,7 +307,4 @@ void RiscvHwprobe::add_features_from_query_result() { VM_Version::unaligned_vector.enable_feature( query[RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF].value); } - if (is_valid(RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE)) { - VM_Version::zicboz_block_size.enable_feature(query[RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE].value); - } } diff --git a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp index bfe77da19199..aae91dfa904f 100644 --- a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp +++ b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp @@ -320,8 +320,6 @@ void VM_Version::vendor_features() { void VM_Version::rivos_features() { // Enable common features not dependent on marchid/mimpid. - ext_Zicbom.enable_feature(); - ext_Zicboz.enable_feature(); ext_Zicbop.enable_feature(); // If we running on a pre-6.5 kernel @@ -377,9 +375,7 @@ void VM_Version::xuantie_features() { ext_Zfa.enable_feature(); ext_Zfh.enable_feature(); ext_Zfhmin.enable_feature(); - ext_Zicbom.enable_feature(); ext_Zicbop.enable_feature(); - ext_Zicboz.enable_feature(); ext_Ziccid.enable_feature(); ext_Zicond.enable_feature(); ext_Zicntr.enable_feature(); diff --git a/src/hotspot/os_cpu/linux_zero/atomicAccess_linux_zero.hpp b/src/hotspot/os_cpu/linux_zero/atomicAccess_linux_zero.hpp index 85ab04cb6cb8..3abffc4cd9ae 100644 --- a/src/hotspot/os_cpu/linux_zero/atomicAccess_linux_zero.hpp +++ b/src/hotspot/os_cpu/linux_zero/atomicAccess_linux_zero.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright 2007, 2008, 2011, 2015, Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -128,13 +128,6 @@ inline T AtomicAccess::PlatformCmpxchg<8>::operator()(T volatile* dest, return value; } -// Atomically copy 64 bits of data -inline void atomic_copy64(const volatile void *src, volatile void *dst) { - int64_t tmp; - __atomic_load(reinterpret_cast(src), &tmp, __ATOMIC_RELAXED); - __atomic_store(reinterpret_cast(dst), &tmp, __ATOMIC_RELAXED); -} - template<> template inline T AtomicAccess::PlatformLoad<8>::operator()(T const volatile* src) const { diff --git a/src/hotspot/os_cpu/linux_zero/os_linux_zero.cpp b/src/hotspot/os_cpu/linux_zero/os_linux_zero.cpp index 6cad3e9fff95..0a07665f7a9f 100644 --- a/src/hotspot/os_cpu/linux_zero/os_linux_zero.cpp +++ b/src/hotspot/os_cpu/linux_zero/os_linux_zero.cpp @@ -412,14 +412,14 @@ extern "C" { if (from > to) { const jlong *end = from + count; while (from < end) - atomic_copy64(from++, to++); + AtomicAccess::store(to++, AtomicAccess::load(from++)); } else if (from < to) { const jlong *end = from; from += count - 1; to += count - 1; while (from >= end) - atomic_copy64(from--, to--); + AtomicAccess::store(to--, AtomicAccess::load(from--)); } } diff --git a/src/hotspot/share/adlc/output_c.cpp b/src/hotspot/share/adlc/output_c.cpp index a21c6472ce9d..6adff64b456f 100644 --- a/src/hotspot/share/adlc/output_c.cpp +++ b/src/hotspot/share/adlc/output_c.cpp @@ -1484,7 +1484,7 @@ void ArchDesc::defineExpand(FILE *fp, InstructForm *node) { unsigned i; // Generate Expand function header - fprintf(fp, "MachNode* %sNode::Expand(State* state, Node_List& proj_list, Node* mem) {\n", node->_ident); + fprintf(fp, "MachNode* %sNode::Expand(State* state, Node_List& proj_list, Node* mem) {\n", node->_ident); fprintf(fp, " Compile* C = Compile::current();\n"); // Generate expand code if( node->expands() ) { diff --git a/src/hotspot/share/adlc/output_h.cpp b/src/hotspot/share/adlc/output_h.cpp index cd25acea664e..82dbd218fe53 100644 --- a/src/hotspot/share/adlc/output_h.cpp +++ b/src/hotspot/share/adlc/output_h.cpp @@ -713,15 +713,10 @@ void gen_inst_format(FILE *fp, FormDict &globals, InstructForm &inst, bool for_c assert(0,"ShouldNotReachHere"); } fprintf(fp, " st->cr();\n" ); - fprintf(fp, " if (_jvms) _jvms->format(ra, this, st); else st->print_cr(\" No JVM State Info\");\n" ); - fprintf(fp, " st->print(\" # \");\n" ); - fprintf(fp, " if( _jvms && _oop_map ) _oop_map->print_on(st);\n"); + fprintf(fp, " format_jvms_and_oopmap(ra, st);\n" ); } else if(inst.is_ideal_safepoint()) { - fprintf(fp, " st->print_raw(\"\");\n" ); - fprintf(fp, " if (_jvms) _jvms->format(ra, this, st); else st->print_cr(\" No JVM State Info\");\n" ); - fprintf(fp, " st->print(\" # \");\n" ); - fprintf(fp, " if( _jvms && _oop_map ) _oop_map->print_on(st);\n"); + fprintf(fp, " format_jvms_and_oopmap(ra, st);\n" ); } else if( inst.is_ideal_if() ) { fprintf(fp, " st->print(\" P=%%f C=%%f\",_prob,_fcnt);\n" ); @@ -1809,7 +1804,7 @@ void ArchDesc::declareClasses(FILE *fp) { instr->needs_constant_base() || (instr->_matrule != nullptr && instr->num_opnds() != instr->num_unique_opnds()) ) { - fprintf(fp," virtual MachNode *Expand(State *state, Node_List &proj_list, Node* mem);\n"); + fprintf(fp," virtual MachNode *Expand(State* state, Node_List& proj_list, Node* mem);\n"); } if (instr->is_pinned(_globalNames)) { diff --git a/src/hotspot/share/c1/c1_GraphBuilder.cpp b/src/hotspot/share/c1/c1_GraphBuilder.cpp index c260b118ed91..873fb0d8f726 100644 --- a/src/hotspot/share/c1/c1_GraphBuilder.cpp +++ b/src/hotspot/share/c1/c1_GraphBuilder.cpp @@ -1079,16 +1079,16 @@ void GraphBuilder::load_indexed(BasicType type) { bool need_membar = false; LoadIndexed* load_indexed = nullptr; Instruction* result = nullptr; - if (array->is_loaded_flat_array()) { - ciType* array_type = array->declared_type(); - ciFlatArrayKlass* array_klass = array_type->as_flat_array_klass(); + ciFlatArrayKlass* array_klass = array->as_loaded_flat_array_klass_or_null(); + if (array_klass != nullptr) { ciValueKlass* elem_klass = array_klass->element_klass()->as_value_klass(); bool can_delay_access = false; ciBytecodeStream s(method()); s.force_bci(bci()); s.next(); - if (s.cur_bc() == Bytecodes::_getfield) { + // Only delay the access if the following getfield is in the same basic block + if (s.cur_bc() == Bytecodes::_getfield && block_at(s.cur_bci()) == nullptr) { bool is_null_free = array_klass->is_elem_null_free(); bool will_link; ciField* next_field = s.get_field(will_link); @@ -2015,7 +2015,8 @@ void GraphBuilder::access_field(Bytecodes::Code code) { ciBytecodeStream s(method()); s.force_bci(bci()); s.next(); - if (s.cur_bc() == Bytecodes::_getfield && !needs_patching) { + // Only delay the access if the following getfield is in the same basic block + if (s.cur_bc() == Bytecodes::_getfield && block_at(s.cur_bci()) == nullptr && !needs_patching) { ciField* next_field = s.get_field(will_link); ciInstanceKlass* next_holder = next_field->holder(); bool next_needs_patching = !next_holder->is_loaded() || diff --git a/src/hotspot/share/c1/c1_Instruction.cpp b/src/hotspot/share/c1/c1_Instruction.cpp index 80a7bdcd742a..ef0d4329f497 100644 --- a/src/hotspot/share/c1/c1_Instruction.cpp +++ b/src/hotspot/share/c1/c1_Instruction.cpp @@ -125,12 +125,14 @@ ciKlass* Instruction::as_loaded_klass_or_null() const { return nullptr; } -bool Instruction::is_loaded_flat_array() const { +ciFlatArrayKlass* Instruction::as_loaded_flat_array_klass_or_null() const { if (UseArrayFlattening) { - ciType* type = declared_type(); - return type != nullptr && type->is_flat_array_klass(); + ciType* type = exact_type(); + if (type != nullptr && type->is_flat_array_klass()) { + return type->as_flat_array_klass(); + } } - return false; + return nullptr; } bool Instruction::maybe_flat_array() const { @@ -270,8 +272,9 @@ ciType* LoadIndexed::declared_type() const { } bool StoreIndexed::is_exact_flat_array_store() const { - if (array()->is_loaded_flat_array() && value()->as_Constant() == nullptr && value()->declared_type() != nullptr) { - ciKlass* element_klass = array()->declared_type()->as_flat_array_klass()->element_klass(); + ciFlatArrayKlass* array_klass = array()->as_loaded_flat_array_klass_or_null(); + if (array_klass != nullptr && value()->as_Constant() == nullptr && value()->declared_type() != nullptr) { + ciKlass* element_klass = array_klass->element_klass(); ciKlass* actual_klass = value()->declared_type()->as_klass(); // Inlining can expose more specific types than the callee's signature. In diff --git a/src/hotspot/share/c1/c1_Instruction.hpp b/src/hotspot/share/c1/c1_Instruction.hpp index 59ae95f23521..ffe5dcaba899 100644 --- a/src/hotspot/share/c1/c1_Instruction.hpp +++ b/src/hotspot/share/c1/c1_Instruction.hpp @@ -32,6 +32,7 @@ // Predefined classes class ciField; +class ciFlatArrayKlass; class ValueStack; class InstructionPrinter; class IRScope; @@ -510,7 +511,8 @@ class Instruction: public CompilationResourceObj { return _next; } - bool is_loaded_flat_array() const; + ciFlatArrayKlass* as_loaded_flat_array_klass_or_null() const; + bool is_loaded_flat_array() const { return as_loaded_flat_array_klass_or_null() != nullptr; } bool maybe_flat_array() const; bool maybe_null_free_array() const; diff --git a/src/hotspot/share/c1/c1_LIRGenerator.cpp b/src/hotspot/share/c1/c1_LIRGenerator.cpp index 045e8cdbbc7e..31ba5452c64d 100644 --- a/src/hotspot/share/c1/c1_LIRGenerator.cpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.cpp @@ -1732,9 +1732,8 @@ LIR_Opr LIRGenerator::get_and_load_element_address(LIRItem& array, LIRItem& inde // We need to be careful with overflows in 32-bit arithmetic Unimplemented(); #endif - ciType* array_type = array.value()->declared_type(); - ciFlatArrayKlass* flat_array_klass = array_type->as_flat_array_klass(); - assert(flat_array_klass->is_loaded(), "must be"); + ciFlatArrayKlass* flat_array_klass = array.value()->as_loaded_flat_array_klass_or_null(); + assert(flat_array_klass != nullptr && flat_array_klass->is_loaded(), "must be"); int array_header_size = flat_array_klass->array_header_in_bytes(); int shift = flat_array_klass->log2_element_size(); @@ -1778,7 +1777,8 @@ LIR_Opr LIRGenerator::access_flat_array(bool is_load, LIRItem& array, LIRItem& i // Find the starting address of the source (inside the array) LIR_Opr elm_op = get_and_load_element_address(array, index); - ciFlatArrayKlass* array_klass = array.value()->declared_type()->as_flat_array_klass(); + ciFlatArrayKlass* array_klass = array.value()->as_loaded_flat_array_klass_or_null(); + assert(array_klass != nullptr, "must be"); ciValueKlass* elem_klass = nullptr; if (field != nullptr) { elem_klass = field->type()->as_value_klass(); @@ -1921,7 +1921,8 @@ bool LIRGenerator::needs_null_free_array_store_check(StoreIndexed* x) { void LIRGenerator::do_StoreIndexed(StoreIndexed* x) { assert(x->is_pinned(),""); assert(x->elt_type() != T_ARRAY, "never used"); - bool is_loaded_flat_array = x->array()->is_loaded_flat_array(); + ciFlatArrayKlass* array_klass = x->array()->as_loaded_flat_array_klass_or_null(); + bool is_loaded_flat_array = array_klass != nullptr; bool needs_range_check = x->compute_needs_range_check(); bool use_length = x->length() != nullptr; bool obj_store = is_reference_type(x->elt_type()); @@ -1997,7 +1998,6 @@ void LIRGenerator::do_StoreIndexed(StoreIndexed* x) { } if (is_loaded_flat_array) { - ciFlatArrayKlass* array_klass = x->array()->declared_type()->as_flat_array_klass(); ciValueKlass* elem_klass = array_klass->element_klass()->as_value_klass(); bool null_free = array_klass->is_elem_null_free(); if (null_free && !x->value()->is_null_free()) { @@ -2391,8 +2391,7 @@ void LIRGenerator::do_LoadIndexed(LoadIndexed* x) { } } - ciFlatArrayKlass* flat_array_klass = x->array()->is_loaded_flat_array() ? - x->array()->declared_type()->as_flat_array_klass() : nullptr; + ciFlatArrayKlass* flat_array_klass = x->array()->as_loaded_flat_array_klass_or_null(); bool assert_null = flat_array_klass != nullptr && !flat_array_klass->is_elem_null_free() && !flat_array_klass->element_klass()->as_value_klass()->is_initialized(); if (assert_null) { @@ -2423,12 +2422,11 @@ void LIRGenerator::do_LoadIndexed(LoadIndexed* x) { LIR_Opr payload = access_flat_array(true, array, index, buffer, x->delayed() == nullptr ? nullptr : x->delayed()->field(), x->delayed() == nullptr ? 0 : x->delayed()->offset()); - ciFlatArrayKlass* array_klass = x->array()->declared_type()->as_flat_array_klass(); - if (array_klass->is_elem_null_free()) { + if (flat_array_klass->is_elem_null_free()) { set_result(x, x->buffer()->operand()); } else { // Check the null marker and set result to null if it's not set - ciValueKlass* elem_klass = array_klass->element_klass()->as_value_klass(); + ciValueKlass* elem_klass = flat_array_klass->element_klass()->as_value_klass(); BasicType bt = elem_klass->atomic_size_to_basic_type(false); assert(payload->is_valid(), "nullable flat array load must return the atomic payload"); __ logical_and(payload, null_marker_mask(bt, elem_klass->null_marker_offset_in_payload()), payload); diff --git a/src/hotspot/share/cds/aotArtifactFinder.cpp b/src/hotspot/share/cds/aotArtifactFinder.cpp index 90009271fb45..141c4b591dcf 100644 --- a/src/hotspot/share/cds/aotArtifactFinder.cpp +++ b/src/hotspot/share/cds/aotArtifactFinder.cpp @@ -28,10 +28,10 @@ #include "cds/aotHeap.hpp" #include "cds/aotLogging.hpp" #include "cds/aotReferenceObjSupport.hpp" +#include "cds/aotSystemDictionary.hpp" #include "cds/dumpTimeClassInfo.inline.hpp" #include "cds/lambdaProxyClassDictionary.hpp" #include "cds/regeneratedClasses.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" #include "logging/log.hpp" #include "memory/metaspaceClosure.hpp" @@ -77,7 +77,7 @@ void AOTArtifactFinder::find_artifacts() { // // Note, if a class is not excluded, it does NOT mean it will be automatically included // into the AOT cache -- that will be decided by the code below. - SystemDictionaryShared::finish_exclusion_checks(); + AOTSystemDictionary::finish_exclusion_checks(); AOTReferenceObjSupport::init_keep_alive_objs_table(); start_scanning_for_oops(); @@ -114,7 +114,7 @@ void AOTArtifactFinder::find_artifacts() { #endif // Add all the InstanceKlasses (and their array classes) that are always included. - SystemDictionaryShared::dumptime_table()->iterate_all_live_classes([&] (InstanceKlass* ik, DumpTimeClassInfo& info) { + AOTSystemDictionary::dumptime_table()->iterate_all_live_classes([&] (InstanceKlass* ik, DumpTimeClassInfo& info) { bool skip = info.is_excluded(); if (!(ik->is_initialized() && ik->has_aot_safe_initializer())) { if (info.is_aot_tooling_class()) { @@ -163,7 +163,7 @@ void AOTArtifactFinder::find_artifacts() { #endif // Exclude all the (hidden) classes that have not been discovered by the code above. - SystemDictionaryShared::dumptime_table()->iterate_all_live_classes([&] (InstanceKlass* k, DumpTimeClassInfo& info) { + AOTSystemDictionary::dumptime_table()->iterate_all_live_classes([&] (InstanceKlass* k, DumpTimeClassInfo& info) { if (!info.is_excluded() && _seen_classes->get(k) == nullptr) { info.set_excluded(); info.set_has_checked_exclusion(); @@ -231,7 +231,7 @@ void AOTArtifactFinder::add_aot_inited_class(InstanceKlass* ik) { } void AOTArtifactFinder::append_to_all_cached_classes(Klass* k) { - precond(!SystemDictionaryShared::should_be_excluded(k)); + precond(!AOTSystemDictionary::should_be_excluded(k)); _all_cached_classes->append(k); } @@ -343,7 +343,7 @@ void AOTArtifactFinder::check_critical_classes() { } void AOTArtifactFinder::check_critical_class(InstanceKlass* ik) { - if (SystemDictionaryShared::is_excluded_class(ik)) { + if (AOTSystemDictionary::is_excluded_class(ik)) { ResourceMark rm; const char* msg = err_msg("Critical class %s has been excluded. %s cannot be written.", ik->external_name(), diff --git a/src/hotspot/share/cds/aotClassFilter.cpp b/src/hotspot/share/cds/aotClassFilter.cpp index 959bea6a6230..1e755f3abc41 100644 --- a/src/hotspot/share/cds/aotClassFilter.cpp +++ b/src/hotspot/share/cds/aotClassFilter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,7 +44,7 @@ AOTClassFilter::FilterMark::~FilterMark() { _filtering_thread = nullptr; } -// Is called only from SystemDictionaryShared::init_dumptime_info(), which holds DumpTimeTable_lock +// Is called only from AOTSystemDictionary::init_dumptime_info(), which holds DumpTimeTable_lock bool AOTClassFilter::is_aot_tooling_class(InstanceKlass* ik) { assert_lock_strong(DumpTimeTable_lock); if (_current_mark == nullptr || _filtering_thread != Thread::current()) { diff --git a/src/hotspot/share/cds/aotClassFilter.hpp b/src/hotspot/share/cds/aotClassFilter.hpp index b85b83c3a324..f3fbd728900e 100644 --- a/src/hotspot/share/cds/aotClassFilter.hpp +++ b/src/hotspot/share/cds/aotClassFilter.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,7 +31,7 @@ class InstanceKlass; class Thread; -// Used by SystemDictionaryShared/AOTArtifactFinder to filter out classes that +// Used by AOTSystemDictionary/AOTArtifactFinder to filter out classes that // shouldn't be included into the AOT cache -- for example, classes that are used only // in the training/assembly phases for building contents in the AOT cache. // diff --git a/src/hotspot/share/cds/aotClassInitializer.cpp b/src/hotspot/share/cds/aotClassInitializer.cpp index 62076931e69f..10990fb84460 100644 --- a/src/hotspot/share/cds/aotClassInitializer.cpp +++ b/src/hotspot/share/cds/aotClassInitializer.cpp @@ -24,11 +24,12 @@ #include "cds/aotClassInitializer.hpp" #include "cds/aotLinkedClassBulkLoader.hpp" +#include "cds/aotMetaspace.hpp" #include "cds/archiveBuilder.hpp" #include "cds/cdsConfig.hpp" #include "cds/regeneratedClasses.hpp" #include "classfile/symbolTable.hpp" -#include "classfile/systemDictionaryShared.hpp" +#include "classfile/systemDictionary.hpp" #include "classfile/vmSymbols.hpp" #include "oops/instanceKlass.inline.hpp" #include "oops/symbol.hpp" diff --git a/src/hotspot/share/cds/aotClassLinker.cpp b/src/hotspot/share/cds/aotClassLinker.cpp index 94e97820e58c..d7683d32cf78 100644 --- a/src/hotspot/share/cds/aotClassLinker.cpp +++ b/src/hotspot/share/cds/aotClassLinker.cpp @@ -26,6 +26,7 @@ #include "cds/aotConstantPoolResolver.hpp" #include "cds/aotHeap.hpp" #include "cds/aotLinkedClassTable.hpp" +#include "cds/aotSystemDictionary.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveUtils.inline.hpp" #include "cds/cdsConfig.hpp" @@ -33,7 +34,6 @@ #include "classfile/classLoader.hpp" #include "classfile/dictionary.hpp" #include "classfile/systemDictionary.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" #include "memory/resourceArea.hpp" #include "oops/constantPool.inline.hpp" @@ -127,7 +127,7 @@ bool AOTClassLinker::try_add_candidate(InstanceKlass* ik) { assert(is_initialized(), "sanity"); assert(CDSConfig::is_dumping_aot_linked_classes(), "sanity"); - if (!SystemDictionaryShared::is_builtin(ik)) { + if (!AOTSystemDictionary::is_builtin(ik)) { // not loaded by a class loader which we know about return false; } @@ -136,7 +136,7 @@ bool AOTClassLinker::try_add_candidate(InstanceKlass* ik) { return true; } - if (!ik->is_linked() && SystemDictionaryShared::has_class_failed_verification(ik)) { + if (!ik->is_linked() && AOTSystemDictionary::has_class_failed_verification(ik)) { return false; } diff --git a/src/hotspot/share/cds/aotConstantPoolResolver.cpp b/src/hotspot/share/cds/aotConstantPoolResolver.cpp index a13785057a4e..3863bb0d383e 100644 --- a/src/hotspot/share/cds/aotConstantPoolResolver.cpp +++ b/src/hotspot/share/cds/aotConstantPoolResolver.cpp @@ -25,11 +25,11 @@ #include "cds/aotClassLinker.hpp" #include "cds/aotConstantPoolResolver.hpp" #include "cds/aotHeap.hpp" +#include "cds/aotSystemDictionary.hpp" #include "cds/archiveBuilder.hpp" #include "cds/cdsConfig.hpp" #include "cds/lambdaFormInvokers.inline.hpp" #include "classfile/systemDictionary.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" #include "interpreter/bytecodeStream.hpp" #include "interpreter/interpreterRuntime.hpp" @@ -88,7 +88,7 @@ bool AOTConstantPoolResolver::is_class_resolution_deterministic(InstanceKlass* c if (resolved_class->is_instance_klass()) { InstanceKlass* ik = InstanceKlass::cast(resolved_class); - if (!ik->in_aot_cache() && SystemDictionaryShared::is_excluded_class(ik)) { + if (!ik->in_aot_cache() && AOTSystemDictionary::is_excluded_class(ik)) { return false; } @@ -159,7 +159,7 @@ void AOTConstantPoolResolver::preresolve_string_cp_entries(InstanceKlass* ik, TR // However, we want to aggressively resolve all klass/field/method constants for // LambdaForm Invoker Holder classes, Lambda Proxy classes, and LambdaForm classes, // so that the compiler can inline through them. - if (SystemDictionaryShared::is_builtin_loader(ik->class_loader_data())) { + if (AOTSystemDictionary::is_builtin_loader(ik->class_loader_data())) { bool eager_resolve = false; if (LambdaFormInvokers::may_be_regenerated_class(ik->name())) { @@ -213,7 +213,7 @@ void AOTConstantPoolResolver::resolve_string(constantPoolHandle cp, int cp_index #endif void AOTConstantPoolResolver::preresolve_class_cp_entries(JavaThread* current, InstanceKlass* ik, GrowableArray* preresolve_list) { - if (!SystemDictionaryShared::is_builtin_loader(ik->class_loader_data())) { + if (!AOTSystemDictionary::is_builtin_loader(ik->class_loader_data())) { return; } @@ -444,7 +444,7 @@ bool AOTConstantPoolResolver::check_methodtype_signature(ConstantPool* cp, Symbo return false; } - if (SystemDictionaryShared::should_be_excluded(k)) { + if (AOTSystemDictionary::should_be_excluded(k)) { if (log_is_enabled(Warning, aot, resolve)) { ResourceMark rm; log_warning(aot, resolve)("Cannot aot-resolve %s because %s is excluded", @@ -456,8 +456,8 @@ bool AOTConstantPoolResolver::check_methodtype_signature(ConstantPool* cp, Symbo // cp->pool_holder() must be able to resolve k in production run precond(CDSConfig::is_dumping_aot_linked_classes()); - precond(SystemDictionaryShared::is_builtin_loader(cp->pool_holder()->class_loader_data())); - precond(SystemDictionaryShared::is_builtin_loader(k->class_loader_data())); + precond(AOTSystemDictionary::is_builtin_loader(cp->pool_holder()->class_loader_data())); + precond(AOTSystemDictionary::is_builtin_loader(k->class_loader_data())); if (ss.at_return_type() && return_type_ret != nullptr) { *return_type_ret = k; @@ -540,7 +540,7 @@ bool AOTConstantPoolResolver::check_lambda_metafactory_methodhandle_arg(Constant } return false; } - if (SystemDictionaryShared::should_be_excluded(k)) { + if (AOTSystemDictionary::should_be_excluded(k)) { if (log_is_enabled(Warning, aot, resolve)) { ResourceMark rm; log_warning(aot, resolve)("Cannot aot-resolve Lambda proxy because %s is excluded", k->external_name()); @@ -550,8 +550,8 @@ bool AOTConstantPoolResolver::check_lambda_metafactory_methodhandle_arg(Constant // cp->pool_holder() must be able to resolve k in production run precond(CDSConfig::is_dumping_aot_linked_classes()); - precond(SystemDictionaryShared::is_builtin_loader(cp->pool_holder()->class_loader_data())); - precond(SystemDictionaryShared::is_builtin_loader(k->class_loader_data())); + precond(AOTSystemDictionary::is_builtin_loader(cp->pool_holder()->class_loader_data())); + precond(AOTSystemDictionary::is_builtin_loader(k->class_loader_data())); } return check_methodtype_signature(cp, sig); @@ -564,7 +564,7 @@ bool AOTConstantPoolResolver::is_indy_resolution_deterministic(ConstantPool* cp, } InstanceKlass* pool_holder = cp->pool_holder(); - if (!SystemDictionaryShared::is_builtin(pool_holder)) { + if (!AOTSystemDictionary::is_builtin(pool_holder)) { return false; } diff --git a/src/hotspot/share/cds/aotHeap.cpp b/src/hotspot/share/cds/aotHeap.cpp index 2737e0f2165a..1b092414c588 100644 --- a/src/hotspot/share/cds/aotHeap.cpp +++ b/src/hotspot/share/cds/aotHeap.cpp @@ -35,6 +35,7 @@ #include "cds/aotReferenceObjSupport.hpp" #include "cds/aotStreamedHeapLoader.hpp" #include "cds/aotStreamedHeapWriter.hpp" +#include "cds/aotSystemDictionary.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveUtils.inline.hpp" #include "cds/cds_globals.hpp" @@ -48,7 +49,6 @@ #include "classfile/stringTable.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" #include "classfile/vmSymbols.hpp" #include "code/aotCodeCache.hpp" @@ -728,7 +728,7 @@ class MetaspaceObjToOopHandleTable: public HashTablepool_holder()->class_loader_data())) { + if (AOTSystemDictionary::is_builtin_loader(src->pool_holder()->class_loader_data())) { _scratch_objects_table->set_oop(src, dest); } } @@ -961,7 +961,7 @@ void AOTHeap::copy_java_mirror(oop orig_mirror, oop scratch_m) { } static objArrayOop get_archived_resolved_references(InstanceKlass* src_ik) { - if (SystemDictionaryShared::is_builtin_loader(src_ik->class_loader_data())) { + if (AOTSystemDictionary::is_builtin_loader(src_ik->class_loader_data())) { objArrayOop rr = src_ik->constants()->resolved_references_or_null(); if (rr != nullptr && !AOTHeap::is_too_large_to_archive(rr)) { return AOTHeap::scratch_resolved_references(src_ik->constants()); @@ -1302,7 +1302,7 @@ class AOTHeap::CopyKlassSubGraphInfoToArchive : StackObj { if (info.subgraph_object_klasses() != nullptr || info.subgraph_entry_fields() != nullptr) { ArchivedKlassSubGraphInfoRecord* record = AOTHeap::archive_subgraph_info(&info); Klass* buffered_k = ArchiveBuilder::get_buffered_klass(klass); - unsigned int hash = SystemDictionaryShared::hash_for_shared_dictionary((address)buffered_k); + unsigned int hash = AOTSystemDictionary::hash_for_shared_dictionary((address)buffered_k); _writer->add(hash, AOTCompressedPointers::encode_not_null(record)); } return true; // keep on iterating @@ -1407,7 +1407,7 @@ void AOTHeap::resolve_classes_for_subgraphs(JavaThread* current, ArchivableStati for (int i = 0; fields[i].valid(); i++) { ArchivableStaticFieldInfo* info = &fields[i]; TempNewSymbol klass_name = SymbolTable::new_symbol(info->klass_name); - InstanceKlass* k = SystemDictionaryShared::find_builtin_class(klass_name); + InstanceKlass* k = AOTSystemDictionary::find_builtin_class(klass_name); assert(k != nullptr && k->defined_by_boot_loader(), "sanity"); resolve_classes_for_subgraph_of(current, k); } @@ -1546,7 +1546,7 @@ AOTHeap::resolve_or_init_classes_for_subgraph_of(Klass* k, bool do_init, TRAPS) if (!k->in_aot_cache()) { return nullptr; } - unsigned int hash = SystemDictionaryShared::hash_for_shared_dictionary_quick(k); + unsigned int hash = AOTSystemDictionary::hash_for_shared_dictionary_quick(k); const ArchivedKlassSubGraphInfoRecord* record = _run_time_subgraph_info_table.lookup(k, hash, 0); // Initialize from archived data. Currently this is done only @@ -1598,7 +1598,7 @@ AOTHeap::resolve_or_init_classes_for_subgraph_of(Klass* k, bool do_init, TRAPS) void AOTHeap::resolve_or_init(const char* klass_name, bool do_init, TRAPS) { TempNewSymbol klass_name_sym = SymbolTable::new_symbol(klass_name); - InstanceKlass* k = SystemDictionaryShared::find_builtin_class(klass_name_sym); + InstanceKlass* k = AOTSystemDictionary::find_builtin_class(klass_name_sym); if (k == nullptr) { return; } @@ -1672,7 +1672,7 @@ void AOTHeap::init_archived_fields_for(Klass* k, const ArchivedKlassSubGraphInfo } void AOTHeap::clear_archived_roots_of(Klass* k) { - unsigned int hash = SystemDictionaryShared::hash_for_shared_dictionary_quick(k); + unsigned int hash = AOTSystemDictionary::hash_for_shared_dictionary_quick(k); const ArchivedKlassSubGraphInfoRecord* record = _run_time_subgraph_info_table.lookup(k, hash, 0); if (record != nullptr) { Array* entry_field_records = record->entry_field_records(); diff --git a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp index 438587281815..3f79a1520638 100644 --- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp +++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp @@ -32,8 +32,9 @@ #include "classfile/classLoaderData.hpp" #include "classfile/javaClasses.hpp" #include "classfile/javaStackTraceClasses.hpp" +#include "classfile/moduleEntry.hpp" +#include "classfile/packageEntry.hpp" #include "classfile/systemDictionary.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" #include "compiler/compilationPolicy.hpp" #include "gc/shared/gcVMOperations.hpp" diff --git a/src/hotspot/share/cds/aotMapLogger.cpp b/src/hotspot/share/cds/aotMapLogger.cpp index aadb91f838a2..5ece1eae2958 100644 --- a/src/hotspot/share/cds/aotMapLogger.cpp +++ b/src/hotspot/share/cds/aotMapLogger.cpp @@ -27,13 +27,14 @@ #include "cds/aotMappedHeapWriter.hpp" #include "cds/aotStreamedHeapLoader.hpp" #include "cds/aotStreamedHeapWriter.hpp" +#include "cds/aotSystemDictionary.hpp" #include "cds/archiveUtils.hpp" #include "cds/cdsConfig.hpp" #include "cds/filemap.hpp" #include "classfile/moduleEntry.hpp" #include "classfile/packageEntry.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" +#include "code/aotCodeCache.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" #include "memory/metaspaceClosure.hpp" @@ -116,9 +117,64 @@ void AOTMapLogger::dumptime_log(ArchiveBuilder* builder, FileMapInfo* mapinfo, } #endif + log_ac_region(); + log_info(aot, map)("[End of AOT cache map]"); } + +static void log_embedded_stub(const intptr_t pointer, const uint size, const uint stub_id, const char* stub_name, const uint entry_id) { + log_debug(aot, map)(PTR_FORMAT ": @@ %-17s %d id=%d blob=%d %s", + pointer, "EmbeddedStub", size, stub_id, entry_id, stub_name); +} + +void AOTMapLogger::log_ac_region() { + ResourceMark rm; + + AOTCodeCache* cache = AOTCodeCache::open_for_dump(); + if (cache != nullptr) { + AOTCodeEntry* entries = cache->_store_entries; // Pointer to latest entry + uint code_count = cache->_store_entries_cnt; + + for (int i = code_count - 1; i >= 0; i--) { + AOTCodeEntry* entry = &entries[i]; + if (entry == nullptr || entry->load_fail()) { + continue; + } + + uint name_offset = entry->offset() + entry->name_offset(); + const char* name = cache-> _store_buffer + name_offset; + + switch (entry->kind()) { + case AOTCodeEntry::Kind::Nmethod: + log_debug(aot, map)(PTR_FORMAT ": @@ %-17s %d level=%d id=%d %s", + p2i(entry), AOTCodeCache::get_kind_name(entry->kind()), + entry->size(), entry->comp_level(), entry->comp_id(), name); + break; + case AOTCodeEntry::Kind::StubGenBlob: + { + BlobId blob_id = (BlobId) entry->id(); + + //First we log the stub blob + log_debug(aot, map)(PTR_FORMAT ": @@ %-17s %d id=%d %s", + p2i(entry), AOTCodeCache::get_kind_name(entry->kind()), + entry->size(), entry->id(), StubInfo::name(blob_id)); + + //Now we log each stub embedded inside the blob + cache->iterate_embedded_stubs(entry, log_embedded_stub); + } + break; + default: + log_debug(aot, map)(PTR_FORMAT ": @@ %-17s %d id=%d %s", + p2i(entry), AOTCodeCache::get_kind_name(entry->kind()), + entry->size(), entry->id(), name); + break; + } + } + } +} + + // This class is used to find the location and type of all the // archived metaspace objects. class AOTMapLogger::RuntimeGatherArchivedMetaspaceObjs : public UniqueMetaspaceClosure { @@ -160,11 +216,11 @@ void AOTMapLogger::runtime_log(FileMapInfo* static_mapinfo, FileMapInfo* dynamic // another object starts. There's also no type information. // // However, we can rebuild our index by iterating over all the objects using - // MetaspaceClosure, starting from the dictionary of Klasses in SystemDictionaryShared. + // MetaspaceClosure, starting from the dictionary of Klasses in AOTSystemDictionary. GrowableArray klasses; - SystemDictionaryShared::get_all_archived_classes(/*is_static*/true, &klasses); + AOTSystemDictionary::get_all_archived_classes(/*is_static*/true, &klasses); if (dynamic_mapinfo != nullptr) { - SystemDictionaryShared::get_all_archived_classes(/*is_static*/false, &klasses); + AOTSystemDictionary::get_all_archived_classes(/*is_static*/false, &klasses); } for (int i = 0; i < klasses.length(); i++) { diff --git a/src/hotspot/share/cds/aotMapLogger.hpp b/src/hotspot/share/cds/aotMapLogger.hpp index 1a8a2549809e..4aea9c74c0ac 100644 --- a/src/hotspot/share/cds/aotMapLogger.hpp +++ b/src/hotspot/share/cds/aotMapLogger.hpp @@ -160,7 +160,7 @@ class AOTMapLogger : AllStatic { static void log_klass_training_data(KlassTrainingData* ktd, address requested_addr, const char* type_name, int bytes, Thread* current); static void log_method_training_data(MethodTrainingData* mtd, address requested_addr, const char* type_name, int bytes, Thread* current); static void log_compile_training_data(CompileTrainingData* ctd, address requested_addr, const char* type_name, int bytes, Thread* current); - + static void log_ac_region(); #if INCLUDE_CDS_JAVA_HEAP static void dumptime_log_mapped_heap_region(AOTMappedHeapInfo* mapped_heap_info); diff --git a/src/hotspot/share/cds/aotMappedHeapLoader.cpp b/src/hotspot/share/cds/aotMappedHeapLoader.cpp index ab2fc7dc474d..c25a37fa06b4 100644 --- a/src/hotspot/share/cds/aotMappedHeapLoader.cpp +++ b/src/hotspot/share/cds/aotMappedHeapLoader.cpp @@ -29,8 +29,8 @@ #include "cds/aotMetaspace.hpp" #include "cds/aotModules.hpp" #include "cds/cdsConfig.hpp" +#include "cds/filemap.hpp" #include "classfile/stringTable.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "gc/shared/collectedHeap.hpp" #include "logging/log.hpp" #include "logging/logMessage.hpp" diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index d43c9c297467..d72657e94d4f 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -36,6 +36,7 @@ #include "cds/aotMetaspace.hpp" #include "cds/aotModules.hpp" #include "cds/aotReferenceObjSupport.hpp" +#include "cds/aotSystemDictionary.hpp" #include "cds/archiveBuilder.hpp" #include "cds/cds_globals.hpp" #include "cds/cdsConfig.hpp" @@ -58,7 +59,6 @@ #include "classfile/stringTable.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" #include "classfile/vmSymbols.hpp" #include "code/aotCodeCache.hpp" @@ -526,14 +526,14 @@ void AOTMetaspace::serialize(SerializeClosure* soc) { SymbolTable::serialize_shared_table_header(soc); StringTable::serialize_shared_table_header(soc); AOTHeap::serialize_tables(soc); - SystemDictionaryShared::serialize_dictionary_headers(soc); + AOTSystemDictionary::serialize_dictionary_headers(soc); AOTLinkedClassBulkLoader::serialize(soc); FinalImageRecipes::serialize(soc); TrainingData::serialize(soc); InstanceMirrorKlass::serialize_offsets(soc); // Dump/restore well known classes (pointers) - SystemDictionaryShared::serialize_vm_classes(soc); + AOTSystemDictionary::serialize_vm_classes(soc); soc->do_tag(--tag); CDS_JAVA_HEAP_ONLY(AOTModules::serialize(soc);) @@ -709,7 +709,7 @@ class StaticArchiveBuilder : public ArchiveBuilder { virtual void iterate_roots(MetaspaceClosure* it) { AOTArtifactFinder::all_cached_classes_do(it); - SystemDictionaryShared::dumptime_classes_do(it); + AOTSystemDictionary::dumptime_classes_do(it); Universe::metaspace_pointers_do(it); vmSymbols::metaspace_pointers_do(it); TrainingData::iterate_roots(it); @@ -749,7 +749,7 @@ char* VM_PopulateDumpSharedSpace::dump_early_read_only_tables() { char* VM_PopulateDumpSharedSpace::dump_read_only_tables(AOTClassLocationConfig*& cl_config) { ArchiveBuilder::OtherROAllocMark mark; - SystemDictionaryShared::write_to_archive(); + AOTSystemDictionary::write_to_archive(); cl_config = AOTClassLocationConfig::dumptime()->write_to_archive(); AOTClassLinker::write_to_archive(); if (CDSConfig::is_dumping_preimage_static_archive()) { @@ -783,7 +783,7 @@ void VM_PopulateDumpSharedSpace::doit() { guarantee(!CDSConfig::is_using_archive(), "We should not be using an archive when we dump"); } - DEBUG_ONLY(SystemDictionaryShared::NoClassLoadingMark nclm); + DEBUG_ONLY(AOTSystemDictionary::NoClassLoadingMark nclm); _pending_method_handle_intrinsics = new (mtClassShared) GrowableArray(256, mtClassShared); if (CDSConfig::is_dumping_method_handles()) { @@ -1399,7 +1399,7 @@ bool AOTMetaspace::try_link_class(JavaThread* current, InstanceKlass* ik) { } if (ik->is_loaded() && !ik->is_linked() && ik->can_be_verified_at_dumptime() && - !SystemDictionaryShared::has_class_failed_verification(ik)) { + !AOTSystemDictionary::has_class_failed_verification(ik)) { bool saved = BytecodeVerificationLocal; if (ik->defined_by_other_loaders() && ik->class_loader() == nullptr) { // The verification decision is based on BytecodeVerificationRemote @@ -1420,9 +1420,9 @@ bool AOTMetaspace::try_link_class(JavaThread* current, InstanceKlass* ik) { current->pending_exception()->klass()->external_name(), message == nullptr ? "(no message)" : java_lang_String::as_utf8_string(message)); CLEAR_PENDING_EXCEPTION; - SystemDictionaryShared::set_class_has_failed_verification(ik); + AOTSystemDictionary::set_class_has_failed_verification(ik); } else { - assert(!SystemDictionaryShared::has_class_failed_verification(ik), "sanity"); + assert(!AOTSystemDictionary::has_class_failed_verification(ik), "sanity"); ik->compute_has_loops_flag_for_methods(); } BytecodeVerificationLocal = saved; @@ -2254,11 +2254,11 @@ void AOTMetaspace::initialize_shared_spaces() { tty->print_cr("Static archive version %d", static_mapinfo->version()); } - SystemDictionaryShared::print_shared_archive(tty); + AOTSystemDictionary::print_shared_archive(tty); if (dynamic_mapinfo != nullptr) { tty->print_cr("\n\nDynamic archive name: %s", dynamic_mapinfo->full_path()); tty->print_cr("Dynamic archive version %d", dynamic_mapinfo->version()); - SystemDictionaryShared::print_shared_archive(tty, false/*dynamic*/); + AOTSystemDictionary::print_shared_archive(tty, false/*dynamic*/); } TrainingData::print_archived_training_data_on(tty); diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/cds/aotSystemDictionary.cpp similarity index 88% rename from src/hotspot/share/classfile/systemDictionaryShared.cpp rename to src/hotspot/share/cds/aotSystemDictionary.cpp index 0d6c345762ce..d5e07cc90796 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/cds/aotSystemDictionary.cpp @@ -29,6 +29,7 @@ #include "cds/aotHeap.hpp" #include "cds/aotLogging.hpp" #include "cds/aotMetaspace.hpp" +#include "cds/aotSystemDictionary.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveUtils.hpp" #include "cds/cdsConfig.hpp" @@ -50,7 +51,6 @@ #include "classfile/javaClasses.inline.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/verificationType.hpp" #include "classfile/vmClasses.hpp" #include "classfile/vmSymbols.hpp" @@ -80,14 +80,14 @@ #include "utilities/hashTable.hpp" #include "utilities/stringUtils.hpp" -SystemDictionaryShared::ArchiveInfo SystemDictionaryShared::_info_for_static_archive; -SystemDictionaryShared::ArchiveInfo SystemDictionaryShared::_info_for_dynamic_archive; -SystemDictionaryShared::ArchiveInfo SystemDictionaryShared::_info_for_dumping; +AOTSystemDictionary::ArchiveInfo AOTSystemDictionary::_info_for_static_archive; +AOTSystemDictionary::ArchiveInfo AOTSystemDictionary::_info_for_dynamic_archive; +AOTSystemDictionary::ArchiveInfo AOTSystemDictionary::_info_for_dumping; -DumpTimeSharedClassTable* SystemDictionaryShared::_dumptime_table = nullptr; +DumpTimeSharedClassTable* AOTSystemDictionary::_dumptime_table = nullptr; // Used by NoClassLoadingMark -DEBUG_ONLY(bool SystemDictionaryShared::_class_loading_may_happen = true;) +DEBUG_ONLY(bool AOTSystemDictionary::_class_loading_may_happen = true;) #ifdef ASSERT static void check_klass_after_loading(const Klass* k) { @@ -97,7 +97,7 @@ static void check_klass_after_loading(const Klass* k) { } #endif -InstanceKlass* SystemDictionaryShared::load_shared_class_for_builtin_loader( +InstanceKlass* AOTSystemDictionary::load_shared_class_for_builtin_loader( Symbol* class_name, Handle class_loader, TRAPS) { assert(CDSConfig::is_using_archive(), "must be"); InstanceKlass* ik = find_builtin_class(class_name); @@ -116,7 +116,7 @@ InstanceKlass* SystemDictionaryShared::load_shared_class_for_builtin_loader( } // This function is called for loading only UNREGISTERED classes -InstanceKlass* SystemDictionaryShared::lookup_from_stream(Symbol* class_name, +InstanceKlass* AOTSystemDictionary::lookup_from_stream(Symbol* class_name, Handle class_loader, Handle protection_domain, const ClassFileStream* cfs, @@ -153,7 +153,7 @@ InstanceKlass* SystemDictionaryShared::lookup_from_stream(Symbol* class_name, THREAD); } -InstanceKlass* SystemDictionaryShared::acquire_class_for_current_thread( +InstanceKlass* AOTSystemDictionary::acquire_class_for_current_thread( InstanceKlass *ik, Handle class_loader, Handle protection_domain, @@ -192,19 +192,19 @@ InstanceKlass* SystemDictionaryShared::acquire_class_for_current_thread( // Guaranteed to return non-null value for non-shared classes. // k must not be a shared class. -DumpTimeClassInfo* SystemDictionaryShared::get_info(InstanceKlass* k) { +DumpTimeClassInfo* AOTSystemDictionary::get_info(InstanceKlass* k) { MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); return get_info_locked(k); } -DumpTimeClassInfo* SystemDictionaryShared::get_info_locked(InstanceKlass* k) { +DumpTimeClassInfo* AOTSystemDictionary::get_info_locked(InstanceKlass* k) { assert_lock_strong(DumpTimeTable_lock); DumpTimeClassInfo* info = _dumptime_table->get_info(k); assert(info != nullptr, "must be"); return info; } -void SystemDictionaryShared::check_code_source(InstanceKlass* ik, const ClassFileStream* cfs) { +void AOTSystemDictionary::check_code_source(InstanceKlass* ik, const ClassFileStream* cfs) { if (CDSConfig::is_dumping_preimage_static_archive() && !is_builtin_loader(ik->class_loader_data())) { if (cfs == nullptr || cfs->source() == nullptr || strncmp(cfs->source(), "file:", 5) != 0) { // AOT cache filtering: @@ -212,13 +212,13 @@ void SystemDictionaryShared::check_code_source(InstanceKlass* ik, const ClassFil // we can avoid caching dynamically generated classes that are likely to change from // run to run. This is similar to the filtering in ClassListWriter::write_to_stream() // for the classic CDS static archive. - SystemDictionaryShared::log_exclusion(ik, "Not loaded from \"file:\" code source"); - SystemDictionaryShared::set_excluded(ik); + AOTSystemDictionary::log_exclusion(ik, "Not loaded from \"file:\" code source"); + AOTSystemDictionary::set_excluded(ik); } } } -bool SystemDictionaryShared::should_be_excluded_impl(InstanceKlass* k, DumpTimeClassInfo* info) { +bool AOTSystemDictionary::should_be_excluded_impl(InstanceKlass* k, DumpTimeClassInfo* info) { assert_lock_strong(DumpTimeTable_lock); if (!info->has_checked_exclusion()) { @@ -232,7 +232,7 @@ bool SystemDictionaryShared::should_be_excluded_impl(InstanceKlass* k, DumpTimeC // returns bool and takes a single parameter of Symbol* // The return value indicates whether we want to keep on iterating or not. template -void SystemDictionaryShared::iterate_verification_constraint_names(InstanceKlass* k, DumpTimeClassInfo* info, Function func) { +void AOTSystemDictionary::iterate_verification_constraint_names(InstanceKlass* k, DumpTimeClassInfo* info, Function func) { int n = info->num_verifier_constraints(); bool cont; // continue iterating? for (int i = 0; i < n; i++) { @@ -251,7 +251,7 @@ void SystemDictionaryShared::iterate_verification_constraint_names(InstanceKlass } // This is a table of classes that need to be checked for exclusion. -class SystemDictionaryShared::ExclusionCheckCandidates +class AOTSystemDictionary::ExclusionCheckCandidates : public HashTable { void add_candidate(InstanceKlass* k) { if (contains(k)) { @@ -261,7 +261,7 @@ class SystemDictionaryShared::ExclusionCheckCandidates return; } - DumpTimeClassInfo* info = SystemDictionaryShared::get_info_locked(k); + DumpTimeClassInfo* info = AOTSystemDictionary::get_info_locked(k); if (info->has_checked_exclusion()) { // We have check exclusion of k and all of its dependencies, so there's no need to check again. return; @@ -291,7 +291,7 @@ class SystemDictionaryShared::ExclusionCheckCandidates } if (CDSConfig::is_preserving_verification_constraints()) { - SystemDictionaryShared::iterate_verification_constraint_names(k, info, [&] (Symbol* constraint_class_name) { + AOTSystemDictionary::iterate_verification_constraint_names(k, info, [&] (Symbol* constraint_class_name) { Klass* constraint_bottom_class = find_verification_constraint_bottom_class(k, constraint_class_name); if (constraint_bottom_class != nullptr && constraint_bottom_class->is_instance_klass()) { add_candidate(InstanceKlass::cast(constraint_bottom_class)); @@ -329,7 +329,7 @@ class SystemDictionaryShared::ExclusionCheckCandidates // when verifying ik's bytecodes. // // This method ensure that exclusion check is performed on X and all of its exclusion dependencies. -void SystemDictionaryShared::check_exclusion_for_self_and_dependencies(InstanceKlass* ik) { +void AOTSystemDictionary::check_exclusion_for_self_and_dependencies(InstanceKlass* ik) { assert_lock_strong(DumpTimeTable_lock); ResourceMark rm; @@ -380,7 +380,7 @@ void SystemDictionaryShared::check_exclusion_for_self_and_dependencies(InstanceK }); } -void SystemDictionaryShared::log_exclusion(InstanceKlass* k, const char* reason, bool is_warning) { +void AOTSystemDictionary::log_exclusion(InstanceKlass* k, const char* reason, bool is_warning) { ResourceMark rm; if (is_warning) { aot_log_warning(aot)("Skipping %s: %s", k->name()->as_C_string(), reason); @@ -389,7 +389,7 @@ void SystemDictionaryShared::log_exclusion(InstanceKlass* k, const char* reason, } } -bool SystemDictionaryShared::is_jfr_event_class(InstanceKlass *k) { +bool AOTSystemDictionary::is_jfr_event_class(InstanceKlass *k) { while (k) { if (k->name()->equals("jdk/internal/event/Event")) { return true; @@ -399,7 +399,7 @@ bool SystemDictionaryShared::is_jfr_event_class(InstanceKlass *k) { return false; } -bool SystemDictionaryShared::check_self_exclusion(InstanceKlass* k) { +bool AOTSystemDictionary::check_self_exclusion(InstanceKlass* k) { bool log_warning = false; const char* error = check_self_exclusion_helper(k, log_warning); if (error != nullptr) { @@ -410,7 +410,7 @@ bool SystemDictionaryShared::check_self_exclusion(InstanceKlass* k) { } } -const char* SystemDictionaryShared::check_self_exclusion_helper(InstanceKlass* k, bool& log_warning) { +const char* AOTSystemDictionary::check_self_exclusion_helper(InstanceKlass* k, bool& log_warning) { assert_lock_strong(DumpTimeTable_lock); if (CDSConfig::is_dumping_final_static_archive() && k->defined_by_other_loaders() && k->in_aot_cache()) { @@ -493,7 +493,7 @@ const char* SystemDictionaryShared::check_self_exclusion_helper(InstanceKlass* k } // Returns true if DumpTimeClassInfo::is_excluded() is true for at least one of k's exclusion dependencies. -bool SystemDictionaryShared::check_dependencies_exclusion(InstanceKlass* k, DumpTimeClassInfo* info) { +bool AOTSystemDictionary::check_dependencies_exclusion(InstanceKlass* k, DumpTimeClassInfo* info) { InstanceKlass* super = k->java_super(); if (super != nullptr && is_dependency_excluded(k, super, "super")) { return true; @@ -550,7 +550,7 @@ bool SystemDictionaryShared::check_dependencies_exclusion(InstanceKlass* k, Dump return false; } -bool SystemDictionaryShared::is_dependency_excluded(InstanceKlass* k, InstanceKlass* dependency, const char* type) { +bool AOTSystemDictionary::is_dependency_excluded(InstanceKlass* k, InstanceKlass* dependency, const char* type) { if (CDSConfig::is_dumping_dynamic_archive() && AOTMetaspace::in_aot_cache(dependency)) { return false; } @@ -563,7 +563,7 @@ bool SystemDictionaryShared::is_dependency_excluded(InstanceKlass* k, InstanceKl return false; } -bool SystemDictionaryShared::check_verification_constraint_exclusion(InstanceKlass* k, Symbol* constraint_class_name) { +bool AOTSystemDictionary::check_verification_constraint_exclusion(InstanceKlass* k, Symbol* constraint_class_name) { Klass* constraint_bottom_class = find_verification_constraint_bottom_class(k, constraint_class_name); if (constraint_bottom_class == nullptr) { // We don't have a bottom class (constraint_class_name is a type array), or constraint_class_name @@ -584,7 +584,7 @@ bool SystemDictionaryShared::check_verification_constraint_exclusion(InstanceKla return false; } -Klass* SystemDictionaryShared::find_verification_constraint_bottom_class(InstanceKlass* k, Symbol* constraint_class_name) { +Klass* AOTSystemDictionary::find_verification_constraint_bottom_class(InstanceKlass* k, Symbol* constraint_class_name) { Thread* current = Thread::current(); Handle loader(current, k->class_loader()); Klass* constraint_class = SystemDictionary::find_instance_or_array_klass(current, constraint_class_name, loader); @@ -600,14 +600,14 @@ Klass* SystemDictionaryShared::find_verification_constraint_bottom_class(Instanc return constraint_class; } -bool SystemDictionaryShared::is_builtin_loader(ClassLoaderData* loader_data) { +bool AOTSystemDictionary::is_builtin_loader(ClassLoaderData* loader_data) { oop class_loader = loader_data->class_loader(); return (class_loader == nullptr || SystemDictionary::is_system_class_loader(class_loader) || SystemDictionary::is_platform_class_loader(class_loader)); } -bool SystemDictionaryShared::has_platform_or_app_classes() { +bool AOTSystemDictionary::has_platform_or_app_classes() { if (FileMapInfo::current_info()->has_platform_or_app_classes()) { return true; } @@ -620,7 +620,7 @@ bool SystemDictionaryShared::has_platform_or_app_classes() { // The following stack shows how this code is reached: // -// [0] SystemDictionaryShared::find_or_load_shared_class() +// [0] AOTSystemDictionary::find_or_load_shared_class() // [1] JVM_FindLoadedClass // [2] java.lang.ClassLoader.findLoadedClass0() // [3] java.lang.ClassLoader.findLoadedClass() @@ -643,7 +643,7 @@ bool SystemDictionaryShared::has_platform_or_app_classes() { // was loaded by the same class loader during dump time. // // Given these assumptions, we intercept the findLoadedClass() call to invoke -// SystemDictionaryShared::find_or_load_shared_class() to load the shared class from +// AOTSystemDictionary::find_or_load_shared_class() to load the shared class from // the archive for the 2 built-in class loaders. This way, // we can improve start-up because we avoid decoding the classfile, // and avoid delegating to the parent loader. @@ -651,7 +651,7 @@ bool SystemDictionaryShared::has_platform_or_app_classes() { // NOTE: there's a lot of assumption about the Java code. If any of that change, this // needs to be redesigned. -InstanceKlass* SystemDictionaryShared::find_or_load_shared_class( +InstanceKlass* AOTSystemDictionary::find_or_load_shared_class( Symbol* name, Handle class_loader, TRAPS) { InstanceKlass* k = nullptr; if (CDSConfig::is_using_archive()) { @@ -697,7 +697,7 @@ class UnregisteredClassesTable : public HashTable< static UnregisteredClassesTable* _unregistered_classes_table = nullptr; // true == class was successfully added; false == a duplicated class (with the same name) already exists. -bool SystemDictionaryShared::add_unregistered_class(Thread* current, InstanceKlass* klass) { +bool AOTSystemDictionary::add_unregistered_class(Thread* current, InstanceKlass* klass) { // We don't allow duplicated unregistered classes with the same name. // We only archive the first class with that name that succeeds putting // itself into the table. @@ -715,7 +715,7 @@ bool SystemDictionaryShared::add_unregistered_class(Thread* current, InstanceKla return (klass == *v); } -InstanceKlass* SystemDictionaryShared::get_unregistered_class(Symbol* name) { +InstanceKlass* AOTSystemDictionary::get_unregistered_class(Symbol* name) { assert(CDSConfig::is_dumping_archive() || ClassListWriter::is_enabled(), "sanity"); if (_unregistered_classes_table == nullptr) { return nullptr; @@ -724,7 +724,7 @@ InstanceKlass* SystemDictionaryShared::get_unregistered_class(Symbol* name) { return k != nullptr ? *k : nullptr; } -void SystemDictionaryShared::copy_unregistered_class_size_and_crc32(InstanceKlass* klass) { +void AOTSystemDictionary::copy_unregistered_class_size_and_crc32(InstanceKlass* klass) { precond(CDSConfig::is_dumping_final_static_archive()); precond(klass->in_aot_cache()); @@ -739,7 +739,7 @@ void SystemDictionaryShared::copy_unregistered_class_size_and_crc32(InstanceKlas info->_clsfile_crc32 = record->crc()->_clsfile_crc32; } -void SystemDictionaryShared::set_shared_class_misc_info(InstanceKlass* k, ClassFileStream* cfs) { +void AOTSystemDictionary::set_shared_class_misc_info(InstanceKlass* k, ClassFileStream* cfs) { assert(CDSConfig::is_dumping_archive(), "sanity"); assert(!is_builtin(k), "must be unregistered class"); DumpTimeClassInfo* info = get_info(k); @@ -747,7 +747,7 @@ void SystemDictionaryShared::set_shared_class_misc_info(InstanceKlass* k, ClassF info->_clsfile_crc32 = ClassLoader::crc32(0, (const char*)cfs->buffer(), cfs->length()); } -void SystemDictionaryShared::initialize() { +void AOTSystemDictionary::initialize() { if (CDSConfig::is_dumping_archive()) { _dumptime_table = new (mtClass) DumpTimeSharedClassTable; LambdaProxyClassDictionary::dumptime_init(); @@ -757,21 +757,21 @@ void SystemDictionaryShared::initialize() { } } -void SystemDictionaryShared::init_dumptime_info(InstanceKlass* k) { +void AOTSystemDictionary::init_dumptime_info(InstanceKlass* k) { MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); - assert(SystemDictionaryShared::class_loading_may_happen(), "sanity"); + assert(AOTSystemDictionary::class_loading_may_happen(), "sanity"); DumpTimeClassInfo* info = _dumptime_table->allocate_info(k); if (AOTClassFilter::is_aot_tooling_class(k)) { info->set_is_aot_tooling_class(); } } -void SystemDictionaryShared::remove_dumptime_info(InstanceKlass* k) { +void AOTSystemDictionary::remove_dumptime_info(InstanceKlass* k) { MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); _dumptime_table->remove(k); } -void SystemDictionaryShared::handle_class_unloading(InstanceKlass* klass) { +void AOTSystemDictionary::handle_class_unloading(InstanceKlass* klass) { if (CDSConfig::is_dumping_archive()) { remove_dumptime_info(klass); } @@ -797,7 +797,7 @@ void SystemDictionaryShared::handle_class_unloading(InstanceKlass* klass) { } } -void SystemDictionaryShared::init_dumptime_info_from_preimage(InstanceKlass* k) { +void AOTSystemDictionary::init_dumptime_info_from_preimage(InstanceKlass* k) { init_dumptime_info(k); copy_verification_info_from_preimage(k); copy_linking_constraints_from_preimage(k); @@ -810,7 +810,7 @@ void SystemDictionaryShared::init_dumptime_info_from_preimage(InstanceKlass* k) } // Check if a class or any of its supertypes has been redefined. -bool SystemDictionaryShared::has_been_redefined(InstanceKlass* k) { +bool AOTSystemDictionary::has_been_redefined(InstanceKlass* k) { if (k->has_been_redefined()) { return true; } @@ -828,7 +828,7 @@ bool SystemDictionaryShared::has_been_redefined(InstanceKlass* k) { } // k is a class before relocating by ArchiveBuilder -void SystemDictionaryShared::validate_before_archiving(InstanceKlass* k) { +void AOTSystemDictionary::validate_before_archiving(InstanceKlass* k) { ResourceMark rm; const char* name = k->name()->as_C_string(); DumpTimeClassInfo* info = _dumptime_table->get(k); @@ -857,7 +857,7 @@ class UnregisteredClassesDuplicationChecker : StackObj { UnregisteredClassesDuplicationChecker() : _thread(Thread::current()) {} void do_entry(InstanceKlass* k, DumpTimeClassInfo& info) { - if (!SystemDictionaryShared::is_builtin(k)) { + if (!AOTSystemDictionary::is_builtin(k)) { _list.append(k); } } @@ -882,16 +882,16 @@ class UnregisteredClassesDuplicationChecker : StackObj { _list.sort(compare_by_loader); for (int i = 0; i < _list.length(); i++) { InstanceKlass* k = _list.at(i); - bool i_am_first = SystemDictionaryShared::add_unregistered_class(_thread, k); + bool i_am_first = AOTSystemDictionary::add_unregistered_class(_thread, k); if (!i_am_first) { - SystemDictionaryShared::log_exclusion(k, "Duplicated unregistered class"); - SystemDictionaryShared::set_excluded_locked(k); + AOTSystemDictionary::log_exclusion(k, "Duplicated unregistered class"); + AOTSystemDictionary::set_excluded_locked(k); } } } }; -void SystemDictionaryShared::link_all_exclusion_check_candidates(InstanceKlass* ik) { +void AOTSystemDictionary::link_all_exclusion_check_candidates(InstanceKlass* ik) { bool need_to_link = false; { MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); @@ -917,7 +917,7 @@ void SystemDictionaryShared::link_all_exclusion_check_candidates(InstanceKlass* // AOTConstantPoolResolver before or after we enter the CDS safepoint. // When called before the safepoint, we need to link the class so that // it can be checked by should_be_excluded_impl(). -bool SystemDictionaryShared::should_be_excluded(Klass* k) { +bool AOTSystemDictionary::should_be_excluded(Klass* k) { assert(CDSConfig::is_dumping_archive(), "sanity"); // This method could be called during AOT compilation from compiler thread. @@ -972,7 +972,7 @@ bool SystemDictionaryShared::should_be_excluded(Klass* k) { } } -void SystemDictionaryShared::finish_exclusion_checks() { +void AOTSystemDictionary::finish_exclusion_checks() { assert_at_safepoint(); if (CDSConfig::is_dumping_dynamic_archive() || CDSConfig::is_dumping_preimage_static_archive()) { // Do this first -- if a base class is excluded due to duplication, @@ -984,7 +984,7 @@ void SystemDictionaryShared::finish_exclusion_checks() { } _dumptime_table->iterate_all_live_classes([&] (InstanceKlass* k, DumpTimeClassInfo& info) { - SystemDictionaryShared::should_be_excluded_impl(k, &info); + AOTSystemDictionary::should_be_excluded_impl(k, &info); }); _dumptime_table->update_counts(); @@ -993,7 +993,7 @@ void SystemDictionaryShared::finish_exclusion_checks() { } } -bool SystemDictionaryShared::is_excluded_class(InstanceKlass* k) { +bool AOTSystemDictionary::is_excluded_class(InstanceKlass* k) { assert(!class_loading_may_happen(), "class loading must be disabled"); assert_lock_strong(DumpTimeTable_lock); assert(CDSConfig::is_dumping_archive(), "sanity"); @@ -1001,37 +1001,37 @@ bool SystemDictionaryShared::is_excluded_class(InstanceKlass* k) { return p->is_excluded(); } -void SystemDictionaryShared::set_excluded_locked(InstanceKlass* k) { +void AOTSystemDictionary::set_excluded_locked(InstanceKlass* k) { assert_lock_strong(DumpTimeTable_lock); assert(CDSConfig::is_dumping_archive(), "sanity"); DumpTimeClassInfo* info = get_info_locked(k); info->set_excluded(); } -void SystemDictionaryShared::set_excluded(InstanceKlass* k) { +void AOTSystemDictionary::set_excluded(InstanceKlass* k) { assert(CDSConfig::is_dumping_archive(), "sanity"); DumpTimeClassInfo* info = get_info(k); info->set_excluded(); } -void SystemDictionaryShared::set_class_has_failed_verification(InstanceKlass* ik) { +void AOTSystemDictionary::set_class_has_failed_verification(InstanceKlass* ik) { assert(CDSConfig::is_dumping_archive(), "sanity"); DumpTimeClassInfo* p = get_info(ik); p->set_failed_verification(); } -bool SystemDictionaryShared::has_class_failed_verification(InstanceKlass* ik) { +bool AOTSystemDictionary::has_class_failed_verification(InstanceKlass* ik) { assert(CDSConfig::is_dumping_archive(), "sanity"); DumpTimeClassInfo* p = _dumptime_table->get(ik); return (p == nullptr) ? false : p->failed_verification(); } -void SystemDictionaryShared::set_from_class_file_load_hook(InstanceKlass* ik) { +void AOTSystemDictionary::set_from_class_file_load_hook(InstanceKlass* ik) { log_exclusion(ik, "From ClassFileLoadHook"); set_excluded(ik); } -void SystemDictionaryShared::dumptime_classes_do(MetaspaceClosure* it) { +void AOTSystemDictionary::dumptime_classes_do(MetaspaceClosure* it) { assert_lock_strong(DumpTimeTable_lock); auto do_klass = [&] (InstanceKlass* k, DumpTimeClassInfo& info) { @@ -1059,7 +1059,7 @@ void SystemDictionaryShared::dumptime_classes_do(MetaspaceClosure* it) { // The meaning of (*skip_assignability_check): // true: is_reference_assignable_from() should SKIP the assignability check // false: is_reference_assignable_from() should COMPLETE the assignability check -void SystemDictionaryShared::add_verification_constraint(InstanceKlass* k, Symbol* name, +void AOTSystemDictionary::add_verification_constraint(InstanceKlass* k, Symbol* name, Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object, bool* skip_assignability_check) { assert(CDSConfig::is_dumping_archive(), "sanity"); @@ -1092,19 +1092,19 @@ void SystemDictionaryShared::add_verification_constraint(InstanceKlass* k, Symbo // When the old verifier is verifying the class at dump time, it tries to resolve a // class with the given . For the verification result to be valid at run time, we must // ensure that resolves to the exact same Klass as in dump time. -void SystemDictionaryShared::add_old_verification_constraint(Thread* current, InstanceKlass* ik, Symbol* name) { +void AOTSystemDictionary::add_old_verification_constraint(Thread* current, InstanceKlass* ik, Symbol* name) { precond(CDSConfig::is_preserving_verification_constraints()); DumpTimeClassInfo* info = get_info(ik); info->add_verification_constraint(name); } -void SystemDictionaryShared::add_enum_klass_static_field(InstanceKlass* ik, int root_index) { +void AOTSystemDictionary::add_enum_klass_static_field(InstanceKlass* ik, int root_index) { assert(CDSConfig::is_dumping_heap(), "sanity"); DumpTimeClassInfo* info = get_info_locked(ik); info->add_enum_klass_static_field(root_index); } -void SystemDictionaryShared::check_verification_constraints(InstanceKlass* klass, +void AOTSystemDictionary::check_verification_constraints(InstanceKlass* klass, TRAPS) { assert(CDSConfig::is_using_archive(), "called at run time with CDS enabled only"); RunTimeClassInfo* record = RunTimeClassInfo::get_for(klass); @@ -1147,7 +1147,7 @@ void SystemDictionaryShared::check_verification_constraints(InstanceKlass* klass } } -void SystemDictionaryShared::copy_verification_info_from_preimage(InstanceKlass* klass) { +void AOTSystemDictionary::copy_verification_info_from_preimage(InstanceKlass* klass) { assert(CDSConfig::is_using_archive(), "called at run time with CDS enabled only"); DumpTimeClassInfo* dt_info = get_info(klass); RunTimeClassInfo* rt_info = RunTimeClassInfo::get_for(klass); // from preimage @@ -1179,7 +1179,7 @@ static oop get_class_loader_by(char type) { // Record class loader constraints that are checked inside // InstanceKlass::link_class(), so that these can be checked quickly // at runtime without laying out the vtable/itables. -void SystemDictionaryShared::record_linking_constraint(Symbol* name, InstanceKlass* klass, +void AOTSystemDictionary::record_linking_constraint(Symbol* name, InstanceKlass* klass, Handle loader1, Handle loader2) { // A linking constraint check is executed when: // - klass extends or implements type S @@ -1235,7 +1235,7 @@ void SystemDictionaryShared::record_linking_constraint(Symbol* name, InstanceKla // returns true IFF there's no need to re-initialize the i/v-tables for klass for // the purpose of checking class loader constraints. -bool SystemDictionaryShared::check_linking_constraints(Thread* current, InstanceKlass* klass) { +bool AOTSystemDictionary::check_linking_constraints(Thread* current, InstanceKlass* klass) { assert(CDSConfig::is_using_archive(), "called at run time with CDS enabled only"); LogTarget(Info, class, loader, constraints) log; if (klass->defined_by_boot_loader()) { @@ -1282,7 +1282,7 @@ bool SystemDictionaryShared::check_linking_constraints(Thread* current, Instance return false; } -void SystemDictionaryShared::copy_linking_constraints_from_preimage(InstanceKlass* klass) { +void AOTSystemDictionary::copy_linking_constraints_from_preimage(InstanceKlass* klass) { assert(CDSConfig::is_using_archive(), "called at run time with CDS enabled only"); JavaThread* current = JavaThread::current(); if (klass->defined_by_platform_loader() || klass->defined_by_app_loader()) { @@ -1300,18 +1300,18 @@ void SystemDictionaryShared::copy_linking_constraints_from_preimage(InstanceKlas } } -unsigned int SystemDictionaryShared::hash_for_shared_dictionary(address ptr) { +unsigned int AOTSystemDictionary::hash_for_shared_dictionary(address ptr) { if (ArchiveBuilder::is_active() && ArchiveBuilder::current()->is_in_buffer_space(ptr)) { uintx offset = ArchiveBuilder::current()->any_to_offset(ptr); unsigned int hash = primitive_hash(offset); DEBUG_ONLY({ if (AOTMetaspace::in_aot_cache(ptr)) { - assert(hash == SystemDictionaryShared::hash_for_shared_dictionary_quick(ptr), "must be"); + assert(hash == AOTSystemDictionary::hash_for_shared_dictionary_quick(ptr), "must be"); } }); return hash; } else { - return SystemDictionaryShared::hash_for_shared_dictionary_quick(ptr); + return AOTSystemDictionary::hash_for_shared_dictionary_quick(ptr); } } @@ -1333,7 +1333,7 @@ class CopySharedClassInfoToArchive : StackObj { unsigned int hash; Symbol* name = info._klass->name(); name = ArchiveBuilder::current()->get_buffered_addr(name); - hash = SystemDictionaryShared::hash_for_shared_dictionary((address)name); + hash = AOTSystemDictionary::hash_for_shared_dictionary((address)name); if (_is_builtin && info._klass->is_hidden()) { // skip } else { @@ -1351,7 +1351,7 @@ class CopySharedClassInfoToArchive : StackObj { } }; -void SystemDictionaryShared::write_dictionary(RunTimeSharedDictionary* dictionary, +void AOTSystemDictionary::write_dictionary(RunTimeSharedDictionary* dictionary, bool is_builtin) { CompactHashtableStats stats; dictionary->reset(); @@ -1362,7 +1362,7 @@ void SystemDictionaryShared::write_dictionary(RunTimeSharedDictionary* dictionar writer.dump(dictionary, is_builtin ? "builtin dictionary" : "unregistered dictionary"); } -void SystemDictionaryShared::write_to_archive(bool is_static_archive) { +void AOTSystemDictionary::write_to_archive(bool is_static_archive) { ArchiveInfo* archive = get_archive(is_static_archive, /*is_dumping=*/true); write_dictionary(&archive->_builtin_dictionary, true); @@ -1374,7 +1374,7 @@ void SystemDictionaryShared::write_to_archive(bool is_static_archive) { } } -void SystemDictionaryShared::serialize_dictionary_headers(SerializeClosure* soc, +void AOTSystemDictionary::serialize_dictionary_headers(SerializeClosure* soc, bool is_static_archive) { ArchiveInfo* archive = get_archive(is_static_archive, soc->writing()); @@ -1383,20 +1383,20 @@ void SystemDictionaryShared::serialize_dictionary_headers(SerializeClosure* soc, LambdaProxyClassDictionary::serialize(soc, is_static_archive); } -void SystemDictionaryShared::serialize_vm_classes(SerializeClosure* soc) { +void AOTSystemDictionary::serialize_vm_classes(SerializeClosure* soc) { for (auto id : EnumRange{}) { soc->do_ptr(vmClasses::klass_addr_at(id)); } } const RunTimeClassInfo* -SystemDictionaryShared::find_record(RunTimeSharedDictionary* static_dict, RunTimeSharedDictionary* dynamic_dict, Symbol* name) { +AOTSystemDictionary::find_record(RunTimeSharedDictionary* static_dict, RunTimeSharedDictionary* dynamic_dict, Symbol* name) { if (!CDSConfig::is_using_archive() || !name->in_aot_cache()) { // The names of all shared classes must also be a shared Symbol. return nullptr; } - unsigned int hash = SystemDictionaryShared::hash_for_shared_dictionary_quick(name); + unsigned int hash = AOTSystemDictionary::hash_for_shared_dictionary_quick(name); const RunTimeClassInfo* record = nullptr; if (DynamicArchive::is_mapped()) { // Use the regenerated holder classes in the dynamic archive as they @@ -1422,7 +1422,7 @@ SystemDictionaryShared::find_record(RunTimeSharedDictionary* static_dict, RunTim return record; } -InstanceKlass* SystemDictionaryShared::find_builtin_class(Symbol* name) { +InstanceKlass* AOTSystemDictionary::find_builtin_class(Symbol* name) { const RunTimeClassInfo* record = find_record(&_info_for_static_archive._builtin_dictionary, &_info_for_dynamic_archive._builtin_dictionary, name); @@ -1440,13 +1440,13 @@ InstanceKlass* SystemDictionaryShared::find_builtin_class(Symbol* name) { } } -void SystemDictionaryShared::update_shared_entry(InstanceKlass* k, int id) { +void AOTSystemDictionary::update_shared_entry(InstanceKlass* k, int id) { assert(CDSConfig::is_dumping_static_archive(), "class ID is used only for static dump (from classlist)"); DumpTimeClassInfo* info = get_info(k); info->_id = id; } -const char* SystemDictionaryShared::loader_type_for_shared_class(Klass* k) { +const char* AOTSystemDictionary::loader_type_for_shared_class(Klass* k) { assert(k != nullptr, "Sanity"); assert(k->in_aot_cache(), "Must be"); assert(k->is_instance_klass(), "Must be"); @@ -1464,7 +1464,7 @@ const char* SystemDictionaryShared::loader_type_for_shared_class(Klass* k) { } } -void SystemDictionaryShared::get_all_archived_classes(bool is_static_archive, GrowableArray* classes) { +void AOTSystemDictionary::get_all_archived_classes(bool is_static_archive, GrowableArray* classes) { ArchiveInfo* archive = get_archive(is_static_archive, /*is_dumping=*/false); archive->_builtin_dictionary.iterate_all([&] (const RunTimeClassInfo* record) { classes->append(record->klass()); @@ -1484,7 +1484,7 @@ class SharedDictionaryPrinter : StackObj { void do_value(const RunTimeClassInfo* record) { ResourceMark rm; _st->print_cr("%4d: %s %s", _index++, record->klass()->external_name(), - SystemDictionaryShared::loader_type_for_shared_class(record->klass())); + AOTSystemDictionary::loader_type_for_shared_class(record->klass())); if (record->klass()->array_klasses() != nullptr) { record->klass()->array_klasses()->cds_print_value_on(_st); _st->cr(); @@ -1493,7 +1493,7 @@ class SharedDictionaryPrinter : StackObj { int index() const { return _index; } }; -void SystemDictionaryShared::ArchiveInfo::print_on(const char* prefix, +void AOTSystemDictionary::ArchiveInfo::print_on(const char* prefix, outputStream* st, bool is_static_archive) { st->print_cr("%sShared Dictionary", prefix); @@ -1505,7 +1505,7 @@ void SystemDictionaryShared::ArchiveInfo::print_on(const char* prefix, LambdaProxyClassDictionary::print_on(prefix, st, p.index(), is_static_archive); } -void SystemDictionaryShared::ArchiveInfo::print_table_statistics(const char* prefix, +void AOTSystemDictionary::ArchiveInfo::print_table_statistics(const char* prefix, outputStream* st, bool is_static_archive) { st->print_cr("%sArchve Statistics", prefix); @@ -1514,7 +1514,7 @@ void SystemDictionaryShared::ArchiveInfo::print_table_statistics(const char* pre LambdaProxyClassDictionary::print_statistics(st, is_static_archive); } -void SystemDictionaryShared::print_shared_archive(outputStream* st, bool is_static) { +void AOTSystemDictionary::print_shared_archive(outputStream* st, bool is_static) { if (CDSConfig::is_using_archive()) { if (is_static) { _info_for_static_archive.print_on("", st, true); @@ -1526,12 +1526,12 @@ void SystemDictionaryShared::print_shared_archive(outputStream* st, bool is_stat } } -void SystemDictionaryShared::print_on(outputStream* st) { +void AOTSystemDictionary::print_on(outputStream* st) { print_shared_archive(st, true); print_shared_archive(st, false); } -void SystemDictionaryShared::print_table_statistics(outputStream* st) { +void AOTSystemDictionary::print_table_statistics(outputStream* st) { if (CDSConfig::is_using_archive()) { _info_for_static_archive.print_table_statistics("Static ", st, true); if (DynamicArchive::is_mapped()) { @@ -1540,7 +1540,7 @@ void SystemDictionaryShared::print_table_statistics(outputStream* st) { } } -bool SystemDictionaryShared::is_dumptime_table_empty() { +bool AOTSystemDictionary::is_dumptime_table_empty() { assert_lock_strong(DumpTimeTable_lock); _dumptime_table->update_counts(); if (_dumptime_table->count_of(true) == 0 && _dumptime_table->count_of(false) == 0){ diff --git a/src/hotspot/share/classfile/systemDictionaryShared.hpp b/src/hotspot/share/cds/aotSystemDictionary.hpp similarity index 98% rename from src/hotspot/share/classfile/systemDictionaryShared.hpp rename to src/hotspot/share/cds/aotSystemDictionary.hpp index f83f5c21fb81..5eabb6e1c113 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.hpp +++ b/src/hotspot/share/cds/aotSystemDictionary.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP -#define SHARE_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP +#ifndef SHARE_CDS_AOTSYSTEMDICTIONARY_HPP +#define SHARE_CDS_AOTSYSTEMDICTIONARY_HPP #include "cds/aotMetaspace.hpp" #include "cds/cds_globals.hpp" @@ -135,7 +135,7 @@ class SharedClassLoadingMark { } }; -class SystemDictionaryShared: public SystemDictionary { +class AOTSystemDictionary: public SystemDictionary { friend class LambdaProxyClassDictionary; struct ArchiveInfo { @@ -303,7 +303,7 @@ class SystemDictionaryShared: public SystemDictionary { #ifdef ASSERT // This object marks a critical period when writing the CDS archive. During this // period, the JVM must not load any new classes, so as to avoid adding new - // items in the SystemDictionaryShared::_dumptime_table. + // items in the AOTSystemDictionary::_dumptime_table. class NoClassLoadingMark: public StackObj { public: NoClassLoadingMark() { @@ -327,4 +327,4 @@ class SystemDictionaryShared: public SystemDictionary { static unsigned int hash_for_shared_dictionary(address ptr); }; -#endif // SHARE_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP +#endif // SHARE_CDS_AOTSYSTEMDICTIONARY_HPP diff --git a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp index 0cbc08319312..e4bb9c0ed325 100644 --- a/src/hotspot/share/cds/archiveBuilder.cpp +++ b/src/hotspot/share/cds/archiveBuilder.cpp @@ -30,6 +30,7 @@ #include "cds/aotMapLogger.hpp" #include "cds/aotMetaspace.hpp" #include "cds/aotModules.hpp" +#include "cds/aotSystemDictionary.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveUtils.hpp" #include "cds/cdsConfig.hpp" @@ -40,7 +41,6 @@ #include "classfile/classLoader.hpp" #include "classfile/javaClasses.hpp" #include "classfile/symbolTable.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" #include "interpreter/abstractInterpreter.hpp" #include "jvm.h" @@ -528,14 +528,14 @@ void ArchiveBuilder::gather_source_objs() { bool ArchiveBuilder::is_excluded(Klass* klass) { if (klass->is_instance_klass()) { InstanceKlass* ik = InstanceKlass::cast(klass); - return SystemDictionaryShared::is_excluded_class(ik); + return AOTSystemDictionary::is_excluded_class(ik); } else if (klass->is_objArray_klass()) { Klass* bottom = ObjArrayKlass::cast(klass)->bottom_klass(); if (CDSConfig::is_dumping_dynamic_archive() && AOTMetaspace::in_aot_cache_static_region(bottom)) { // The bottom class is in the static archive so it's clearly not excluded. return false; } else if (bottom->is_instance_klass()) { - return SystemDictionaryShared::is_excluded_class(InstanceKlass::cast(bottom)); + return AOTSystemDictionary::is_excluded_class(InstanceKlass::cast(bottom)); } } diff --git a/src/hotspot/share/cds/archiveUtils.cpp b/src/hotspot/share/cds/archiveUtils.cpp index 9dbd8ce1cfa7..686c6b63fdff 100644 --- a/src/hotspot/share/cds/archiveUtils.cpp +++ b/src/hotspot/share/cds/archiveUtils.cpp @@ -25,6 +25,7 @@ #include "cds/aotCompressedPointers.hpp" #include "cds/aotLogging.hpp" #include "cds/aotMetaspace.hpp" +#include "cds/aotSystemDictionary.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveUtils.hpp" #include "cds/cdsConfig.hpp" @@ -34,7 +35,6 @@ #include "cds/dynamicArchive.hpp" #include "cds/filemap.hpp" #include "cds/lambdaProxyClassDictionary.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" #include "interpreter/bootstrapInfo.hpp" #include "memory/metaspaceUtils.hpp" @@ -410,7 +410,7 @@ char* DumpRegion::allocate_metaspace_obj(size_t num_bytes, address src, Metaspac char* oldtop = top(); if (is_instance_class) { - SystemDictionaryShared::validate_before_archiving((InstanceKlass*)src); + AOTSystemDictionary::validate_before_archiving((InstanceKlass*)src); allocate(RuntimeClassInfoPtrSize); } @@ -537,7 +537,7 @@ void ArchiveUtils::log_to_classlist(BootstrapInfo* bootstrap_specifier, TRAPS) { if (ClassListWriter::is_enabled()) { if (LambdaProxyClassDictionary::is_supported_invokedynamic(bootstrap_specifier)) { const constantPoolHandle& pool = bootstrap_specifier->pool(); - if (SystemDictionaryShared::is_builtin_loader(pool->pool_holder()->class_loader_data())) { + if (AOTSystemDictionary::is_builtin_loader(pool->pool_holder()->class_loader_data())) { // Currently lambda proxy classes are supported only for the built-in loaders. ResourceMark rm(THREAD); int pool_index = bootstrap_specifier->bss_index(); diff --git a/src/hotspot/share/cds/cdsEnumKlass.cpp b/src/hotspot/share/cds/cdsEnumKlass.cpp index 4fb82b67bc40..fc5b1f206dac 100644 --- a/src/hotspot/share/cds/cdsEnumKlass.cpp +++ b/src/hotspot/share/cds/cdsEnumKlass.cpp @@ -23,8 +23,8 @@ */ #include "cds/aotHeap.hpp" +#include "cds/aotSystemDictionary.hpp" #include "cds/cdsEnumKlass.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" #include "memory/resourceArea.hpp" #include "oops/fieldStreams.inline.hpp" @@ -107,7 +107,7 @@ void CDSEnumKlass::archive_static_field(int level, KlassSubGraphInfo* subgraph_i log_info(aot, heap)("Archived enum obj @%d %s::%s (" INTPTR_FORMAT ")", root_index, ik->external_name(), fd.name()->as_C_string(), p2i((oopDesc*)oop_field)); - SystemDictionaryShared::add_enum_klass_static_field(ik, root_index); + AOTSystemDictionary::add_enum_klass_static_field(ik, root_index); } bool CDSEnumKlass::initialize_enum_klass(InstanceKlass* k, TRAPS) { diff --git a/src/hotspot/share/cds/cdsHeapVerifier.cpp b/src/hotspot/share/cds/cdsHeapVerifier.cpp index ee4ccea84a9c..8e05fcfad608 100644 --- a/src/hotspot/share/cds/cdsHeapVerifier.cpp +++ b/src/hotspot/share/cds/cdsHeapVerifier.cpp @@ -31,7 +31,6 @@ #include "classfile/stringTable.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmSymbols.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" diff --git a/src/hotspot/share/cds/cdsProtectionDomain.cpp b/src/hotspot/share/cds/cdsProtectionDomain.cpp index a4b449a0b033..4e2d55565b9b 100644 --- a/src/hotspot/share/cds/cdsProtectionDomain.cpp +++ b/src/hotspot/share/cds/cdsProtectionDomain.cpp @@ -23,13 +23,15 @@ */ #include "cds/aotClassLocation.hpp" +#include "cds/aotMetaspace.hpp" #include "cds/cdsConfig.hpp" #include "cds/cdsProtectionDomain.hpp" #include "classfile/classLoader.hpp" #include "classfile/classLoaderData.inline.hpp" #include "classfile/javaClasses.hpp" #include "classfile/moduleEntry.hpp" -#include "classfile/systemDictionaryShared.hpp" +#include "classfile/packageEntry.hpp" +#include "classfile/systemDictionary.hpp" #include "classfile/vmClasses.hpp" #include "classfile/vmSymbols.hpp" #include "memory/oopFactory.hpp" diff --git a/src/hotspot/share/cds/classListParser.cpp b/src/hotspot/share/cds/classListParser.cpp index 24cd9454e088..22abc62dfccc 100644 --- a/src/hotspot/share/cds/classListParser.cpp +++ b/src/hotspot/share/cds/classListParser.cpp @@ -25,6 +25,7 @@ #include "cds/aotConstantPoolResolver.hpp" #include "cds/aotLogging.hpp" #include "cds/aotMetaspace.hpp" +#include "cds/aotSystemDictionary.hpp" #include "cds/archiveUtils.hpp" #include "cds/classListParser.hpp" #include "cds/lambdaFormInvokers.hpp" @@ -35,7 +36,6 @@ #include "classfile/javaStackTraceClasses.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" #include "classfile/vmSymbols.hpp" #include "interpreter/bytecode.hpp" @@ -515,7 +515,7 @@ void ClassListParser::check_supertype_obstruction(int specified_supertype_id, co if (specified_supertype->defined_by_other_loaders()) { return; // Only registered supertypes can be obstructed } - const InstanceKlass* obstructor = SystemDictionaryShared::get_unregistered_class(specified_supertype->name()); + const InstanceKlass* obstructor = AOTSystemDictionary::get_unregistered_class(specified_supertype->name()); if (obstructor == nullptr) { return; // No unregistered types with the same name have been loaded, i.e. no obstruction } @@ -585,7 +585,7 @@ InstanceKlass* ClassListParser::load_class_from_source(Symbol* class_name, TRAPS assert(k->defined_by_other_loaders(), "must be"); - bool added = SystemDictionaryShared::add_unregistered_class(THREAD, k); + bool added = AOTSystemDictionary::add_unregistered_class(THREAD, k); if (!added) { // We allow only a single unregistered class for each unique name. error("Duplicated class %s", _class_name); @@ -768,7 +768,7 @@ Klass* ClassListParser::load_current_class(Symbol* class_name_symbol, TRAPS) { if (is_id_specified()) { InstanceKlass* ik = InstanceKlass::cast(klass); int id = this->id(); - SystemDictionaryShared::update_shared_entry(ik, id); + AOTSystemDictionary::update_shared_entry(ik, id); bool created; id2klass_table()->put_if_absent(id, ik, &created); if (!created) { @@ -875,7 +875,7 @@ void ClassListParser::parse_constant_pool_tag() { } } - if (SystemDictionaryShared::should_be_excluded(ik)) { + if (AOTSystemDictionary::should_be_excluded(ik)) { if (log_is_enabled(Warning, aot, resolve)) { ResourceMark rm; log_warning(aot, resolve)("Cannot aot-resolve constants for %s because it is excluded", ik->external_name()); diff --git a/src/hotspot/share/cds/classListWriter.cpp b/src/hotspot/share/cds/classListWriter.cpp index c90e233df737..519ea99d66f5 100644 --- a/src/hotspot/share/cds/classListWriter.cpp +++ b/src/hotspot/share/cds/classListWriter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,6 +22,7 @@ * */ +#include "cds/aotSystemDictionary.hpp" #include "cds/cds_globals.hpp" #include "cds/classListWriter.hpp" #include "cds/lambdaFormInvokers.inline.hpp" @@ -30,7 +31,6 @@ #include "classfile/classLoaderData.hpp" #include "classfile/classLoaderDataGraph.hpp" #include "classfile/moduleEntry.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "memory/resourceArea.hpp" #include "oops/constantPool.inline.hpp" #include "oops/instanceKlass.hpp" @@ -107,7 +107,7 @@ void ClassListWriter::write_to_stream(const InstanceKlass* k, outputStream* stre assert_locked(); ClassLoaderData* loader_data = k->class_loader_data(); - bool is_builtin_loader = SystemDictionaryShared::is_builtin_loader(loader_data); + bool is_builtin_loader = AOTSystemDictionary::is_builtin_loader(loader_data); if (!is_builtin_loader) { // class may be loaded from shared archive if (!k->in_aot_cache()) { @@ -122,7 +122,7 @@ void ClassListWriter::write_to_stream(const InstanceKlass* k, outputStream* stre // Shared unregistered classes are skipped since their real source are not recorded in shared space. return; } - if (!SystemDictionaryShared::add_unregistered_class(Thread::current(), (InstanceKlass*)k)) { + if (!AOTSystemDictionary::add_unregistered_class(Thread::current(), (InstanceKlass*)k)) { return; } } @@ -224,7 +224,7 @@ void ClassListWriter::write_resolved_constants() { } void ClassListWriter::write_resolved_constants_for(InstanceKlass* ik) { - if (!SystemDictionaryShared::is_builtin_loader(ik->class_loader_data()) || + if (!AOTSystemDictionary::is_builtin_loader(ik->class_loader_data()) || ik->is_hidden()) { return; } diff --git a/src/hotspot/share/cds/dumpTimeClassInfo.cpp b/src/hotspot/share/cds/dumpTimeClassInfo.cpp index 0f5773a2729e..06fa69d31a02 100644 --- a/src/hotspot/share/cds/dumpTimeClassInfo.cpp +++ b/src/hotspot/share/cds/dumpTimeClassInfo.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,12 +22,12 @@ * */ +#include "cds/aotSystemDictionary.hpp" #include "cds/archiveBuilder.hpp" #include "cds/dumpTimeClassInfo.inline.hpp" #include "cds/runTimeClassInfo.hpp" #include "classfile/classLoader.hpp" #include "classfile/classLoaderData.inline.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "memory/resourceArea.hpp" DumpTimeClassInfo::~DumpTimeClassInfo() { @@ -143,7 +143,7 @@ int DumpTimeClassInfo::enum_klass_static_field(int which_field) { } bool DumpTimeClassInfo::is_builtin() { - return SystemDictionaryShared::is_builtin(_klass); + return AOTSystemDictionary::is_builtin(_klass); } DumpTimeClassInfo* DumpTimeSharedClassTable::allocate_info(InstanceKlass* k) { @@ -159,7 +159,7 @@ DumpTimeClassInfo* DumpTimeSharedClassTable::get_info(InstanceKlass* k) { assert(CDSConfig::is_dumping_final_static_archive() || !k->in_aot_cache(), "Do not call with shared classes"); DumpTimeClassInfo* p = get(k); assert(p != nullptr, "we must not see any non-shared InstanceKlass* that's " - "not stored with SystemDictionaryShared::init_dumptime_info"); + "not stored with AOTSystemDictionary::init_dumptime_info"); assert(p->_klass == k, "Sanity"); return p; } diff --git a/src/hotspot/share/cds/dumpTimeClassInfo.inline.hpp b/src/hotspot/share/cds/dumpTimeClassInfo.inline.hpp index 2d0953288b05..3bc55100dee7 100644 --- a/src/hotspot/share/cds/dumpTimeClassInfo.inline.hpp +++ b/src/hotspot/share/cds/dumpTimeClassInfo.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,9 +27,9 @@ #include "cds/dumpTimeClassInfo.hpp" +#include "cds/aotSystemDictionary.hpp" #include "cds/cdsConfig.hpp" #include "classfile/classLoaderData.inline.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "oops/instanceKlass.hpp" #include "oops/klass.inline.hpp" #include "runtime/safepoint.hpp" @@ -51,9 +51,9 @@ void DumpTimeSharedClassTable::iterate_all_live_classes(Function function) const function(k, info); assert(k->is_loader_alive(), "must not change"); } else { - if (!SystemDictionaryShared::is_excluded_class(k)) { - SystemDictionaryShared::log_exclusion(k, "Class loader not alive"); - SystemDictionaryShared::set_excluded_locked(k); + if (!AOTSystemDictionary::is_excluded_class(k)) { + AOTSystemDictionary::log_exclusion(k, "Class loader not alive"); + AOTSystemDictionary::set_excluded_locked(k); } } }; diff --git a/src/hotspot/share/cds/dynamicArchive.cpp b/src/hotspot/share/cds/dynamicArchive.cpp index 30afc2c2a5a1..3a27c1098bdc 100644 --- a/src/hotspot/share/cds/dynamicArchive.cpp +++ b/src/hotspot/share/cds/dynamicArchive.cpp @@ -28,6 +28,7 @@ #include "cds/aotCompressedPointers.hpp" #include "cds/aotLogging.hpp" #include "cds/aotMetaspace.hpp" +#include "cds/aotSystemDictionary.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveUtils.inline.hpp" #include "cds/cds_globals.hpp" @@ -40,7 +41,6 @@ #include "classfile/classLoaderData.inline.hpp" #include "classfile/javaStackTraceClasses.hpp" #include "classfile/symbolTable.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmSymbols.hpp" #include "gc/shared/collectedHeap.hpp" #include "gc/shared/gc_globals.hpp" @@ -118,12 +118,12 @@ class DynamicArchiveBuilder : public ArchiveBuilder { void doit_inner() { verify_universe("Before CDS dynamic dump"); - DEBUG_ONLY(SystemDictionaryShared::NoClassLoadingMark nclm); + DEBUG_ONLY(AOTSystemDictionary::NoClassLoadingMark nclm); // Block concurrent class unloading from changing the _dumptime_table MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); - if (SystemDictionaryShared::is_dumptime_table_empty()) { + if (AOTSystemDictionary::is_dumptime_table_empty()) { log_warning(cds, dynamic)("There is no class to be included in the dynamic archive."); return; } @@ -157,7 +157,7 @@ class DynamicArchiveBuilder : public ArchiveBuilder { SymbolTable::write_to_archive(symbols()); ArchiveBuilder::OtherROAllocMark mark; - SystemDictionaryShared::write_to_archive(false); + AOTSystemDictionary::write_to_archive(false); cl_config = AOTClassLocationConfig::dumptime()->write_to_archive(); serialized_data = ro_region()->top(); @@ -181,7 +181,7 @@ class DynamicArchiveBuilder : public ArchiveBuilder { virtual void iterate_roots(MetaspaceClosure* it) { AOTArtifactFinder::all_cached_classes_do(it); - SystemDictionaryShared::dumptime_classes_do(it); + AOTSystemDictionary::dumptime_classes_do(it); } }; @@ -358,7 +358,7 @@ class VM_PopulateDynamicDumpSharedSpace: public VM_Heap_Sync_Operation { void DynamicArchive::serialize(SerializeClosure* soc) { SymbolTable::serialize_shared_table_header(soc, false); - SystemDictionaryShared::serialize_dictionary_headers(soc, false); + AOTSystemDictionary::serialize_dictionary_headers(soc, false); } void DynamicArchive::dump_impl(bool jcmd_request, const char* archive_name, TRAPS) { diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index d0feb197ef28..13f089bddcef 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -28,6 +28,7 @@ #include "cds/aotMappedHeapLoader.hpp" #include "cds/aotMappedHeapWriter.hpp" #include "cds/aotMetaspace.hpp" +#include "cds/aotSystemDictionary.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveUtils.inline.hpp" #include "cds/cds_globals.hpp" @@ -40,7 +41,6 @@ #include "classfile/classLoader.inline.hpp" #include "classfile/classLoaderData.inline.hpp" #include "classfile/symbolTable.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" #include "classfile/vmSymbols.hpp" #include "compiler/compilerDefinitions.inline.hpp" @@ -2076,7 +2076,7 @@ bool FileMapHeader::validate() { aot_log_info(aot)("The %s was created with less restrictive " "verification setting than the current setting.", file_type); // Pretend that we didn't have any archived platform/app classes, so they won't be loaded - // by SystemDictionaryShared. + // by AOTSystemDictionary. _has_platform_or_app_classes = false; } diff --git a/src/hotspot/share/cds/finalImageRecipes.cpp b/src/hotspot/share/cds/finalImageRecipes.cpp index 993b368a07b2..306a99b1a419 100644 --- a/src/hotspot/share/cds/finalImageRecipes.cpp +++ b/src/hotspot/share/cds/finalImageRecipes.cpp @@ -23,6 +23,7 @@ */ #include "cds/aotConstantPoolResolver.hpp" +#include "cds/aotSystemDictionary.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveUtils.inline.hpp" #include "cds/cdsConfig.hpp" @@ -31,7 +32,6 @@ #include "classfile/javaClasses.hpp" #include "classfile/javaStackTraceClasses.hpp" #include "classfile/systemDictionary.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" #include "memory/oopFactory.hpp" #include "memory/resourceArea.hpp" @@ -190,9 +190,9 @@ void FinalImageRecipes::load_all_classes(TRAPS) { if (k->is_instance_klass()) { InstanceKlass* ik = InstanceKlass::cast(k); if (ik->defined_by_other_loaders()) { - SystemDictionaryShared::init_dumptime_info(ik); - SystemDictionaryShared::add_unregistered_class(THREAD, ik); - SystemDictionaryShared::copy_unregistered_class_size_and_crc32(ik); + AOTSystemDictionary::init_dumptime_info(ik); + AOTSystemDictionary::add_unregistered_class(THREAD, ik); + AOTSystemDictionary::copy_unregistered_class_size_and_crc32(ik); } else if (!ik->is_hidden()) { Klass* actual = SystemDictionary::resolve_or_fail(ik->name(), class_loader, true, CHECK); if (actual != ik) { diff --git a/src/hotspot/share/cds/lambdaFormInvokers.cpp b/src/hotspot/share/cds/lambdaFormInvokers.cpp index 9d52dff147cb..8bb39da2e9e3 100644 --- a/src/hotspot/share/cds/lambdaFormInvokers.cpp +++ b/src/hotspot/share/cds/lambdaFormInvokers.cpp @@ -25,6 +25,7 @@ #include "cds/aotClassFilter.hpp" #include "cds/aotCompressedPointers.hpp" #include "cds/aotMetaspace.hpp" +#include "cds/aotSystemDictionary.hpp" #include "cds/archiveBuilder.hpp" #include "cds/cdsConfig.hpp" #include "cds/lambdaFormInvokers.inline.hpp" @@ -36,7 +37,6 @@ #include "classfile/klassFactory.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" #include "classfile/vmSymbols.hpp" #include "logging/log.hpp" @@ -194,7 +194,7 @@ void LambdaFormInvokers::regenerate_holder_classes(TRAPS) { // Fake it, so that it will be included into the archive. klass->set_shared_classpath_index(0); // Set the "generated" bit, so it won't interfere with JVMTI. - // See SystemDictionaryShared::find_builtin_class(). + // See AOTSystemDictionary::find_builtin_class(). klass->set_is_aot_generated_class(); } } else { @@ -239,7 +239,7 @@ void LambdaFormInvokers::regenerate_class(char* class_name, ClassFileStream& st, result->set_is_aot_generated_class(); if (!klass->in_aot_cache()) { log_info(aot, lambda)("regenerate_class excluding klass %s %s", class_name, klass->name()->as_C_string()); - SystemDictionaryShared::set_excluded(InstanceKlass::cast(klass)); // exclude the existing class from dump + AOTSystemDictionary::set_excluded(InstanceKlass::cast(klass)); // exclude the existing class from dump } log_info(aot, lambda)("Regenerated class %s, old: " INTPTR_FORMAT " new: " INTPTR_FORMAT, class_name, p2i(klass), p2i(result)); diff --git a/src/hotspot/share/cds/lambdaProxyClassDictionary.cpp b/src/hotspot/share/cds/lambdaProxyClassDictionary.cpp index 07b804c8ded4..c1c1c6b0658a 100644 --- a/src/hotspot/share/cds/lambdaProxyClassDictionary.cpp +++ b/src/hotspot/share/cds/lambdaProxyClassDictionary.cpp @@ -24,11 +24,11 @@ #include "cds/aotClassFilter.hpp" #include "cds/aotCompressedPointers.hpp" +#include "cds/aotSystemDictionary.hpp" #include "cds/archiveBuilder.hpp" #include "cds/cdsConfig.hpp" #include "cds/cdsProtectionDomain.hpp" #include "cds/lambdaProxyClassDictionary.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "interpreter/bootstrapInfo.hpp" #include "jfr/jfrEvents.hpp" #include "memory/metaspaceClosure.hpp" @@ -45,11 +45,11 @@ DumpTimeLambdaProxyClassInfo::~DumpTimeLambdaProxyClassInfo() { } unsigned int LambdaProxyClassKey::hash() const { - return SystemDictionaryShared::hash_for_shared_dictionary((address)_caller_ik) + - SystemDictionaryShared::hash_for_shared_dictionary((address)_invoked_name) + - SystemDictionaryShared::hash_for_shared_dictionary((address)_invoked_type) + - SystemDictionaryShared::hash_for_shared_dictionary((address)_method_type) + - SystemDictionaryShared::hash_for_shared_dictionary((address)_instantiated_method_type); + return AOTSystemDictionary::hash_for_shared_dictionary((address)_caller_ik) + + AOTSystemDictionary::hash_for_shared_dictionary((address)_invoked_name) + + AOTSystemDictionary::hash_for_shared_dictionary((address)_invoked_type) + + AOTSystemDictionary::hash_for_shared_dictionary((address)_method_type) + + AOTSystemDictionary::hash_for_shared_dictionary((address)_instantiated_method_type); } unsigned int RunTimeLambdaProxyClassKey::hash() const { @@ -179,10 +179,10 @@ void LambdaProxyClassDictionary::add_lambda_proxy_class(InstanceKlass* caller_ik InstanceKlass* nest_host = caller_ik->nest_host(CHECK); assert(nest_host != nullptr, "unexpected nullptr nest_host"); - DumpTimeClassInfo* info = SystemDictionaryShared::get_info_locked(lambda_ik); + DumpTimeClassInfo* info = AOTSystemDictionary::get_info_locked(lambda_ik); if (info != nullptr && !lambda_ik->is_non_strong_hidden() && - SystemDictionaryShared::is_builtin(lambda_ik) && - SystemDictionaryShared::is_builtin(caller_ik) + AOTSystemDictionary::is_builtin(lambda_ik) && + AOTSystemDictionary::is_builtin(caller_ik) // Don't include the lambda proxy if its nest host is not in the "linked" state. && nest_host->is_linked()) { // Set _is_registered_lambda_proxy in DumpTimeClassInfo so that the lambda_ik @@ -201,7 +201,7 @@ void LambdaProxyClassDictionary::add_lambda_proxy_class(InstanceKlass* caller_ik } bool LambdaProxyClassDictionary::is_registered_lambda_proxy_class(InstanceKlass* ik) { - DumpTimeClassInfo* info = SystemDictionaryShared::get_info_locked(ik); + DumpTimeClassInfo* info = AOTSystemDictionary::get_info_locked(ik); bool result = (info != nullptr) ? info->_is_registered_lambda_proxy : false; if (result) { assert(CDSConfig::is_dumping_lambdas_in_legacy_mode(), "only used in legacy lambda proxy support"); @@ -210,7 +210,7 @@ bool LambdaProxyClassDictionary::is_registered_lambda_proxy_class(InstanceKlass* } void LambdaProxyClassDictionary::reset_registered_lambda_proxy_class(InstanceKlass* ik) { - DumpTimeClassInfo* info = SystemDictionaryShared::get_info_locked(ik); + DumpTimeClassInfo* info = AOTSystemDictionary::get_info_locked(ik); if (info != nullptr) { info->_is_registered_lambda_proxy = false; info->set_excluded(); @@ -480,12 +480,12 @@ class LambdaProxyClassDictionary::CleanupDumpTimeLambdaProxyClassTable: StackObj // If the caller class and/or nest_host are excluded, the associated lambda proxy // must also be excluded. - bool always_exclude = SystemDictionaryShared::should_be_excluded(caller_ik) || - SystemDictionaryShared::should_be_excluded(nest_host); + bool always_exclude = AOTSystemDictionary::should_be_excluded(caller_ik) || + AOTSystemDictionary::should_be_excluded(nest_host); for (int i = info._proxy_klasses->length() - 1; i >= 0; i--) { InstanceKlass* ik = info._proxy_klasses->at(i); - if (always_exclude || SystemDictionaryShared::should_be_excluded(ik)) { + if (always_exclude || AOTSystemDictionary::should_be_excluded(ik)) { LambdaProxyClassDictionary::reset_registered_lambda_proxy_class(ik); info._proxy_klasses->remove_at(i); } @@ -512,7 +512,7 @@ class SharedLambdaDictionaryPrinter : StackObj { Klass* k = record->proxy_klass_head(); while (k != nullptr) { _st->print_cr("%4d: %s %s", _index++, k->external_name(), - SystemDictionaryShared::loader_type_for_shared_class(k)); + AOTSystemDictionary::loader_type_for_shared_class(k)); k = k->next_link(); } } diff --git a/src/hotspot/share/cds/runTimeClassInfo.cpp b/src/hotspot/share/cds/runTimeClassInfo.cpp index a1f50ab4ffaf..3d149f2e27bd 100644 --- a/src/hotspot/share/cds/runTimeClassInfo.cpp +++ b/src/hotspot/share/cds/runTimeClassInfo.cpp @@ -23,16 +23,16 @@ */ #include "cds/aotCompressedPointers.hpp" +#include "cds/aotSystemDictionary.hpp" #include "cds/archiveBuilder.hpp" #include "cds/dumpTimeClassInfo.hpp" #include "cds/runTimeClassInfo.hpp" -#include "classfile/systemDictionaryShared.hpp" void RunTimeClassInfo::init(DumpTimeClassInfo& info) { InstanceKlass* k = info._klass; _klass = AOTCompressedPointers::encode_not_null(k); - if (!SystemDictionaryShared::is_builtin(k)) { + if (!AOTSystemDictionary::is_builtin(k)) { CrcInfo* c = crc(); c->_clsfile_size = info._clsfile_size; c->_clsfile_crc32 = info._clsfile_crc32; @@ -93,7 +93,7 @@ InstanceKlass* RunTimeClassInfo::klass() const { } size_t RunTimeClassInfo::crc_size(InstanceKlass* klass) { - if (!SystemDictionaryShared::is_builtin(klass)) { + if (!AOTSystemDictionary::is_builtin(klass)) { return sizeof(CrcInfo); } else { return 0; diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index e94f1161c4d9..ea36d68d3970 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -22,6 +22,7 @@ * */ +#include "cds/aotSystemDictionary.hpp" #include "cds/cdsConfig.hpp" #include "classfile/classFileParser.hpp" #include "classfile/classFileStream.hpp" @@ -35,7 +36,6 @@ #include "classfile/packageEntry.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/verificationType.hpp" #include "classfile/verifier.hpp" #include "classfile/vmClasses.hpp" @@ -5771,7 +5771,7 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, if (!is_internal()) { ik->print_class_load_logging(_loader_data, module_entry, _stream); if (CDSConfig::is_dumping_archive()) { - SystemDictionaryShared::check_code_source(ik, _stream); + AOTSystemDictionary::check_code_source(ik, _stream); } if (ik->minor_version() == JAVA_PREVIEW_MINOR_VERSION && diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp index fea2a8f9a26f..7f4619ab991f 100644 --- a/src/hotspot/share/classfile/classLoader.cpp +++ b/src/hotspot/share/classfile/classLoader.cpp @@ -24,6 +24,7 @@ #include "cds/aotClassLocation.hpp" #include "cds/aotHeap.hpp" +#include "cds/aotSystemDictionary.hpp" #include "cds/cds_globals.hpp" #include "cds/cdsConfig.hpp" #include "cds/cdsProtectionDomain.hpp" @@ -39,7 +40,6 @@ #include "classfile/packageEntry.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" #include "classfile/vmSymbols.hpp" #include "compiler/compileBroker.hpp" @@ -389,7 +389,7 @@ bool ClassPathZipEntry::has_entry(const char* name, Handle class_loader, bool is // loader but CDS.getResource() will ensure the resource is retrieved from the correct // JAR file anyway. if (class_loader != nullptr && is_multi_release_jar) { - assert(SystemDictionaryShared::is_builtin_loader(ClassLoaderData::class_loader_data(class_loader())), "must be"); + assert(AOTSystemDictionary::is_builtin_loader(ClassLoaderData::class_loader_data(class_loader())), "must be"); JavaValue result(T_OBJECT); oop class_name_oop = java_lang_String::create_oop_from_str(name, CHECK_false); Handle h_class_name(THREAD, class_name_oop); @@ -1323,11 +1323,11 @@ void ClassLoader::record_result(JavaThread* current, InstanceKlass* ik, return; } - if (!SystemDictionaryShared::is_builtin_loader(ik->class_loader_data())) { + if (!AOTSystemDictionary::is_builtin_loader(ik->class_loader_data())) { // A class loaded by a user-defined classloader. assert(ik->shared_classpath_index() < 0, "not assigned yet"); ik->set_shared_classpath_index(UNREGISTERED_INDEX); - SystemDictionaryShared::set_shared_class_misc_info(ik, (ClassFileStream*)stream); + AOTSystemDictionary::set_shared_class_misc_info(ik, (ClassFileStream*)stream); return; } diff --git a/src/hotspot/share/classfile/classLoaderData.cpp b/src/hotspot/share/classfile/classLoaderData.cpp index 51f02de3e755..cd39216037e9 100644 --- a/src/hotspot/share/classfile/classLoaderData.cpp +++ b/src/hotspot/share/classfile/classLoaderData.cpp @@ -47,6 +47,7 @@ // the singleton class the_null_class_loader_data(). #include "cds/aotHeap.hpp" +#include "cds/aotSystemDictionary.hpp" #include "classfile/classLoaderData.inline.hpp" #include "classfile/classLoaderDataGraph.inline.hpp" #include "classfile/dictionary.hpp" @@ -55,7 +56,6 @@ #include "classfile/packageEntry.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" @@ -953,7 +953,7 @@ void ClassLoaderData::free_deallocate_list_C_heap_structures() { // this class (scratch or error class) in do_unloading(). remove_class(ik); // But still have to remove it from the dumptime_table. - SystemDictionaryShared::handle_class_unloading(ik); + AOTSystemDictionary::handle_class_unloading(ik); } } } diff --git a/src/hotspot/share/classfile/klassFactory.cpp b/src/hotspot/share/classfile/klassFactory.cpp index 4e599feb3fff..3ad908c38a05 100644 --- a/src/hotspot/share/classfile/klassFactory.cpp +++ b/src/hotspot/share/classfile/klassFactory.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,6 +22,7 @@ * */ +#include "cds/aotSystemDictionary.hpp" #include "cds/cdsConfig.hpp" #include "cds/filemap.hpp" #include "classfile/classFileParser.hpp" @@ -30,7 +31,6 @@ #include "classfile/classLoaderData.inline.hpp" #include "classfile/classLoadInfo.hpp" #include "classfile/klassFactory.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "memory/resourceArea.hpp" #include "prims/jvmtiEnvBase.hpp" #include "prims/jvmtiRedefineClasses.hpp" @@ -208,7 +208,7 @@ InstanceKlass* KlassFactory::create_from_stream(ClassFileStream* stream, InstanceKlass* result = parser.create_instance_klass(old_stream != stream, *cl_inst_info, CHECK_NULL); assert(result != nullptr, "result cannot be null with no pending exception"); if (CDSConfig::is_dumping_archive() && stream->from_class_file_load_hook()) { - SystemDictionaryShared::set_from_class_file_load_hook(result); + AOTSystemDictionary::set_from_class_file_load_hook(result); } if (cached_class_file != nullptr) { diff --git a/src/hotspot/share/classfile/moduleEntry.cpp b/src/hotspot/share/classfile/moduleEntry.cpp index 8a644f027aee..e0e1a0faa4b5 100644 --- a/src/hotspot/share/classfile/moduleEntry.cpp +++ b/src/hotspot/share/classfile/moduleEntry.cpp @@ -25,6 +25,7 @@ #include "cds/aotClassLocation.hpp" #include "cds/aotHeap.hpp" #include "cds/aotModules.hpp" +#include "cds/aotSystemDictionary.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveUtils.hpp" #include "cds/cdsConfig.hpp" @@ -33,7 +34,6 @@ #include "classfile/javaClasses.inline.hpp" #include "classfile/moduleEntry.hpp" #include "classfile/systemDictionary.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "jni.h" #include "logging/log.hpp" #include "logging/logStream.hpp" @@ -409,7 +409,7 @@ void ModuleEntry::metaspace_pointers_do(MetaspaceClosure* it) { #if INCLUDE_CDS_JAVA_HEAP bool ModuleEntry::should_be_archived() const { - return SystemDictionaryShared::is_builtin_loader(loader_data()); + return AOTSystemDictionary::is_builtin_loader(loader_data()); } void ModuleEntry::remove_unshareable_info() { diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index 65ff8ec57bf1..1b19fcfacf07 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -86,7 +86,7 @@ #include "utilities/macros.hpp" #include "utilities/utf8.hpp" #if INCLUDE_CDS -#include "classfile/systemDictionaryShared.hpp" +#include "cds/aotSystemDictionary.hpp" #endif #if INCLUDE_JFR #include "jfr/jfr.hpp" @@ -896,7 +896,7 @@ InstanceKlass* SystemDictionary::resolve_class_from_stream( #if INCLUDE_CDS if (!CDSConfig::is_dumping_static_archive()) { - k = SystemDictionaryShared::lookup_from_stream(class_name, + k = AOTSystemDictionary::lookup_from_stream(class_name, class_loader, cl_info.protection_domain(), st, @@ -1288,7 +1288,7 @@ void SystemDictionary::load_shared_class_misc(InstanceKlass* ik, ClassLoaderData ClassLoadingService::notify_class_loaded(ik, true /* shared class */); if (CDSConfig::is_dumping_final_static_archive()) { - SystemDictionaryShared::init_dumptime_info_from_preimage(ik); + AOTSystemDictionary::init_dumptime_info_from_preimage(ik); } } @@ -1436,7 +1436,7 @@ InstanceKlass* SystemDictionary::load_instance_class_impl(Symbol* class_name, Ha if (CDSConfig::is_using_archive()) { PerfTraceTime vmtimer(ClassLoader::perf_shared_classload_time()); - InstanceKlass* ik = SystemDictionaryShared::find_builtin_class(class_name); + InstanceKlass* ik = AOTSystemDictionary::find_builtin_class(class_name); if (ik != nullptr && ik->defined_by_boot_loader() && !ik->shared_loading_failed()) { SharedClassLoadingMark slm(THREAD, ik); k = load_shared_class(ik, class_loader, Handle(), nullptr, pkg_entry, CHECK_NULL); @@ -1751,7 +1751,7 @@ void SystemDictionary::initialize(TRAPS) { LoaderConstraintTable::initialize(); PlaceholderTable::initialize(); #if INCLUDE_CDS - SystemDictionaryShared::initialize(); + AOTSystemDictionary::initialize(); if (CDSConfig::is_dumping_archive()) { AOTClassLocationConfig::dumptime_init(THREAD); } @@ -1940,7 +1940,7 @@ bool SystemDictionary::add_loader_constraint(Symbol* class_name, #if INCLUDE_CDS if (CDSConfig::is_dumping_archive() && klass_being_linked != nullptr && !klass_being_linked->in_aot_cache()) { - SystemDictionaryShared::record_linking_constraint(constraint_name, + AOTSystemDictionary::record_linking_constraint(constraint_name, InstanceKlass::cast(klass_being_linked), class_loader1, class_loader2); } @@ -2585,7 +2585,7 @@ bool SystemDictionary::is_nonpublic_Object_method(Method* m) { // ---------------------------------------------------------------------------- void SystemDictionary::print_on(outputStream *st) { - CDS_ONLY(SystemDictionaryShared::print_on(st)); + CDS_ONLY(AOTSystemDictionary::print_on(st)); GCMutexLocker mu(SystemDictionary_lock); ClassLoaderDataGraph::print_dictionary(st); @@ -2617,7 +2617,7 @@ void SystemDictionary::dump(outputStream *st, bool verbose) { if (verbose) { print_on(st); } else { - CDS_ONLY(SystemDictionaryShared::print_table_statistics(st)); + CDS_ONLY(AOTSystemDictionary::print_table_statistics(st)); ClassLoaderDataGraph::print_table_statistics(st); LoaderConstraintTable::print_table_statistics(st); } diff --git a/src/hotspot/share/classfile/systemDictionary.hpp b/src/hotspot/share/classfile/systemDictionary.hpp index 0ac9c98af539..c696e3628bb8 100644 --- a/src/hotspot/share/classfile/systemDictionary.hpp +++ b/src/hotspot/share/classfile/systemDictionary.hpp @@ -327,7 +327,7 @@ class SystemDictionary : AllStatic { static void restore_archived_method_handle_intrinsics_impl(TRAPS) NOT_CDS_RETURN; protected: - // Used by AOTLinkedClassBulkLoader, LambdaProxyClassDictionary, VMClasses and SystemDictionaryShared + // Used by AOTLinkedClassBulkLoader, LambdaProxyClassDictionary, VMClasses and AOTSystemDictionary static bool add_loader_constraint(Symbol* name, Klass* klass_being_linked, Handle loader1, Handle loader2); diff --git a/src/hotspot/share/classfile/verificationType.cpp b/src/hotspot/share/classfile/verificationType.cpp index aedb620aabe1..4698e506dfdb 100644 --- a/src/hotspot/share/classfile/verificationType.cpp +++ b/src/hotspot/share/classfile/verificationType.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,10 +22,10 @@ * */ +#include "cds/aotSystemDictionary.hpp" #include "cds/cdsConfig.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/verificationType.hpp" #include "classfile/verifier.hpp" #include "classfile/vmClasses.hpp" @@ -118,7 +118,7 @@ bool VerificationType::is_reference_assignable_from( #if INCLUDE_CDS if (CDSConfig::is_dumping_archive()) { bool skip_assignability_check = false; - SystemDictionaryShared::add_verification_constraint(context->current_class(), + AOTSystemDictionary::add_verification_constraint(context->current_class(), name(), from.name(), from_field_is_protected, from.is_array(), from.is_object(), &skip_assignability_check); if (skip_assignability_check) { diff --git a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp index f16fbc041e2c..97c50b8db51f 100644 --- a/src/hotspot/share/classfile/verifier.cpp +++ b/src/hotspot/share/classfile/verifier.cpp @@ -62,7 +62,7 @@ #include "utilities/align.hpp" #include "utilities/bytes.hpp" #if INCLUDE_CDS -#include "classfile/systemDictionaryShared.hpp" +#include "cds/aotSystemDictionary.hpp" #endif #define NOFAILOVER_MAJOR_VERSION 51 @@ -239,8 +239,8 @@ bool Verifier::verify(InstanceKlass* klass, bool should_verify_class, TRAPS) { // Exclude any classes that are verified with the old verifier when the verification constraints // cannot be preserved. if (CDSConfig::is_dumping_archive() && !CDSConfig::is_preserving_verification_constraints()) { - SystemDictionaryShared::log_exclusion(klass, "Verified with old verifier"); - SystemDictionaryShared::set_excluded(klass); + AOTSystemDictionary::log_exclusion(klass, "Verified with old verifier"); + AOTSystemDictionary::set_excluded(klass); } #endif message_buffer = NEW_RESOURCE_ARRAY(char, message_buffer_len); diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index bec24305db2a..fd6735365c9a 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -117,6 +117,11 @@ const char* aot_code_entry_kind_name[] = { #undef DECL_KIND_STRING }; +// Used when logging, we need the human-readable name +const char* AOTCodeCache::get_kind_name(const AOTCodeEntry::Kind kind) { + return aot_code_entry_kind_name[kind]; +} + // Stream to printing AOTCodeCache loading failure. // Print to error channel when -XX:AOTMode is set to "on" static LogStream& load_failure_log() { @@ -2056,8 +2061,9 @@ bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind // In the case of a multi-stub blob we need to write start, end, // secondary entries and extras. For any other blob entry addresses // beyond the blob start will be stored in the blob as offsets. + uint embedded_stub_offset = 0; if (stub_data != nullptr) { - if (!cache->write_stub_data(blob, stub_data)) { + if (!cache->write_stub_data(blob, stub_data, &embedded_stub_offset)) { return false; } } @@ -2107,6 +2113,9 @@ bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind AOTCodeEntry* entry = new(cache) AOTCodeEntry(entry_kind, encode_id(entry_kind, id), entry_position, entry_size, name_offset, name_size, blob_offset, has_oop_maps); + if (embedded_stub_offset != 0) { + entry->set_embedded_stub_offset(embedded_stub_offset - entry_position); + } log_debug(aot, codecache, stubs)("Wrote code blob '%s' (id=%u, kind=%s) to AOT Code Cache", name, id, aot_code_entry_kind_name[entry_kind]); return true; } @@ -2125,10 +2134,15 @@ bool AOTCodeCache::store_multi_stub_blob(CodeBlob& blob, BlobId id, AOTStubData* return store_code_blob(blob, AOTCodeEntry::StubGenBlob, (uint)id, StubInfo::name(id), stub_data, code_buffer); } -bool AOTCodeCache::write_stub_data(CodeBlob &blob, AOTStubData *stub_data) { +bool AOTCodeCache::write_stub_data(CodeBlob& blob, AOTStubData* stub_data, uint* stubs_offset) { if (!align_write_int()) { return false; } + + if (stubs_offset != nullptr) { + *stubs_offset = _write_position; + } + uint* count_ptr = (uint *)reserve_bytes(sizeof(uint)); if (count_ptr == nullptr) { return false; @@ -4499,10 +4513,7 @@ void AOTCodeAddressTable::init_extrs() { #if INCLUDE_JFR ADD_EXTERNAL_ADDRESS(JfrIntrinsicSupport::write_checkpoint); ADD_EXTERNAL_ADDRESS(JfrIntrinsicSupport::return_lease); - ADD_EXTERNAL_ADDRESS(JfrIntrinsicSupport::load_barrier); - ADD_EXTERNAL_ADDRESS(JfrIntrinsicSupport::epoch_address()); ADD_EXTERNAL_ADDRESS(JfrIntrinsicSupport::epoch_generation_address()); - ADD_EXTERNAL_ADDRESS(JfrIntrinsicSupport::signal_address()); ADD_EXTERNAL_ADDRESS(&VMContinuations) #endif // For JFR @@ -5360,6 +5371,48 @@ void AOTStubData::check_stored(StubId stub_id) { } #endif +//This is a helper function that executes a function over each embedded stub available +// Embedded StubGenBlobs are assumed to have the following structure +// [ count | StubId | offset | size | N | offset_1 | offset_2 | ... | offset_N ] +void AOTCodeCache::iterate_embedded_stubs(const AOTCodeEntry* entry, const embedded_stub_func func) { + assert(entry->kind() == AOTCodeEntry::Kind::StubGenBlob, "invalid call"); + // jump to the embedded stubs section + uint pos = entry->offset() + entry->embedded_stub_offset(); + // skip stub count + pos += sizeof(uint); + + // Now we can get the information we want to log (embedded stubs) + // get the first embedded stub id + StubId stub_id = *(StubId*)(_store_buffer + pos); + pos += sizeof(StubId); + + // loop until we run out of stubs (no stubid) + while (stub_id != StubId::NO_STUBID) { + assert(stub_id > StubId::NO_STUBID && stub_id < StubId::NUM_STUBIDS, "Embedded Stub ID is not valid."); + assert(StubInfo::blob(stub_id) == (BlobId) entry->id(), "We found an embedded stub that doesn't belong here."); + uint offset = *(uint*)(_store_buffer + pos); + pos += sizeof(uint); + uint size = *(uint*)(_store_buffer + pos); + pos += sizeof(uint); + + //Call the function on the embedded stub + (*func)(p2i(_store_buffer + entry->offset() + entry->code_offset() + offset), + size, (uint) stub_id, StubInfo::name(stub_id), (uint) entry->id()); + + //Get the number of secondary/extra entry offsets + int n = *(int*) (_store_buffer + pos); + pos += sizeof(int); + //to skip them and prepare for the following stub (if exists) + pos += n * sizeof(uint); + // the entry+extras count for the stub read from the file should exceed the declared entry count + assert(n >= StubInfo::entry_count(stub_id) - 1, "We are missing entries on this stub."); + + // position ourselves in the potential following stub + stub_id = *(StubId*)(_store_buffer + pos); + pos += sizeof(StubId); + } +} + //============ Statistic ============ void AOTCodeCache::print_timers_on(outputStream* st) { diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp index ac536eb1cfc7..9f4bf9be6ff3 100644 --- a/src/hotspot/share/code/aotCodeCache.hpp +++ b/src/hotspot/share/code/aotCodeCache.hpp @@ -125,7 +125,10 @@ class AOTCodeEntry { uint _name_size; uint _code_offset; // Start of code in cache (zero for blobs) - uint _comp_level; // compilation level, nmethod only + union { + uint _embedded_stub_offset; // Start of embedded stubs in StubGenBlobs + uint _comp_level; // compilation level, nmethod only + }; uint _comp_id; // compilation id, nmethod only uint _num_inlined_bytecodes; // nmethod only uint _inline_instructions_size; // size from training run, nmethod only @@ -156,7 +159,12 @@ class AOTCodeEntry { _name_size = name_size; _code_offset = code_offset; - _comp_level = comp_level; + if (_kind == StubGenBlob) { + _embedded_stub_offset = 0; + } else { + _comp_level = comp_level; + } + _comp_id = comp_id; _num_inlined_bytecodes = 0; _inline_instructions_size = 0; @@ -179,6 +187,15 @@ class AOTCodeEntry { uint name_size() const { return _name_size; } uint code_offset() const { return _code_offset; } + void set_embedded_stub_offset(const uint off) { + precond(_kind == StubGenBlob); + _embedded_stub_offset = off; + } + uint embedded_stub_offset() const { + precond(_kind == StubGenBlob); + return _embedded_stub_offset; + } + bool has_oop_maps() const { return _has_oop_maps; } bool has_vectors() const { return _has_vectors; } @@ -190,7 +207,10 @@ class AOTCodeEntry { uint inline_instructions_size() const { return _inline_instructions_size; } void set_inline_instructions_size(int size) { _inline_instructions_size = size; } - uint comp_level() const { return _comp_level; } + uint comp_level() const { + precond(_kind != StubGenBlob); + return _comp_level; + } uint comp_id() const { return _comp_id; } bool has_clinit_barriers() const { return _has_clinit_barriers; } @@ -532,15 +552,13 @@ class AOTStubData : public StackObj { #if defined(RISCV64) && !defined(ZERO) #define AOTCODECACHE_CONFIGS_RISCV_DO(do_var, do_fun) \ - do_var(intx, BlockZeroingLowLimit) /* zero blocks stub */ \ - do_var(bool, UseBlockZeroing) /* zero blocks stub and nmethods */ \ + do_var(intx, BlockZeroingLowLimit) /* array clear nmethods */ \ do_var(bool, UseConservativeFence) /* fence encoding in stubs and nmethods */ \ do_var(bool, UseCtxFencei) /* method entry barrier stub */ \ do_var(bool, UseSecondarySupersCache) /* secondary supers cache in nmethods */ \ do_var(bool, UseZabha) /* narrow cmpxchg selection in nmethods */ \ do_var(bool, UsePopCountInstruction) /* other platforms record it in CPU features */ \ do_fun(int, SatpMode, (int)VM_Version::satp_mode.value()) \ - do_fun(int, RVZicbozBlockSize, (int)VM_Version::zicboz_block_size.value()) \ // END #else #define AOTCODECACHE_CONFIGS_RISCV_DO(do_var, do_fun) @@ -968,7 +986,7 @@ class AOTCodeCache : public CHeapObj { bool write_metadata(Metadata* m); bool write_oops(nmethod* nm); bool write_metadata(nmethod* nm); - bool write_stub_data(CodeBlob& blob, AOTStubData *stub_data); + bool write_stub_data(CodeBlob& blob, AOTStubData* stub_data, uint* stubs_offset = nullptr); #ifndef PRODUCT bool write_asm_remarks(AsmRemarks& asm_remarks, GrowableArray& remarks, GrowableArray& remarks_len, uint* size); @@ -1065,6 +1083,9 @@ class AOTCodeCache : public CHeapObj { static bool is_caching_enabled() NOT_CDS_RETURN_(false); static bool verify_jvmti_state(ciEnv* env) NOT_CDS_RETURN_(true); + //Helper for logging + static const char* get_kind_name(AOTCodeEntry::Kind kind); + // It is used before AOTCodeCache is initialized. static bool maybe_dumping_code() NOT_CDS_RETURN_(false); @@ -1078,6 +1099,9 @@ class AOTCodeCache : public CHeapObj { static void print_on(outputStream* st) NOT_CDS_RETURN; static void print_statistics_on(outputStream* st) NOT_CDS_RETURN; static void print_timers_on(outputStream* st) NOT_CDS_RETURN; + + typedef void (*embedded_stub_func)(intptr_t pointer, uint size, uint stub_id, const char* stub_name, uint entry_id) ; + void iterate_embedded_stubs(const AOTCodeEntry* entry, embedded_stub_func func); }; // Control block for loading a single AOTCodeEntry asset from the "ac" region diff --git a/src/hotspot/share/compiler/compilationMemStatInternals.hpp b/src/hotspot/share/compiler/compilationMemStatInternals.hpp index 06e008278e90..e177dca56180 100644 --- a/src/hotspot/share/compiler/compilationMemStatInternals.hpp +++ b/src/hotspot/share/compiler/compilationMemStatInternals.hpp @@ -257,7 +257,6 @@ class ArenaStatCounter : public CHeapObj { void on_arena_chunk_deallocation(size_t size, uint64_t stamp); void print_peak_state_on(outputStream* st) const; - void print_error_state_on(outputStream* st) const; size_t limit() const { return _limit; } bool hit_limit() const { return _hit_limit; } diff --git a/src/hotspot/share/compiler/compilationPolicy.hpp b/src/hotspot/share/compiler/compilationPolicy.hpp index a9fdc3cb81ef..35f3034a6ec9 100644 --- a/src/hotspot/share/compiler/compilationPolicy.hpp +++ b/src/hotspot/share/compiler/compilationPolicy.hpp @@ -377,7 +377,6 @@ class CompilationPolicy : AllStatic { static CompLevel initial_compile_level(const methodHandle& method); // Return highest level possible static CompLevel highest_compile_level(); - static void dump(); }; #endif // SHARE_COMPILER_COMPILATIONPOLICY_HPP diff --git a/src/hotspot/share/compiler/compileBroker.hpp b/src/hotspot/share/compiler/compileBroker.hpp index daecc48e642a..0c27422eee87 100644 --- a/src/hotspot/share/compiler/compileBroker.hpp +++ b/src/hotspot/share/compiler/compileBroker.hpp @@ -240,7 +240,6 @@ class CompileBroker: AllStatic { static uint _total_bailout_count; static uint _total_invalidated_count; static uint _total_not_entrant_count; - static uint _total_native_compile_count; static uint _total_osr_compile_count; static uint _total_standard_compile_count; static uint _total_compiler_stopped_count; @@ -446,7 +445,6 @@ class CompileBroker: AllStatic { static int get_total_compile_count() { return _total_compile_count; } static int get_total_bailout_count() { return _total_bailout_count; } static int get_total_invalidated_count() { return _total_invalidated_count; } - static int get_total_native_compile_count() { return _total_native_compile_count; } static int get_total_osr_compile_count() { return _total_osr_compile_count; } static int get_total_standard_compile_count() { return _total_standard_compile_count; } static int get_total_compiler_stopped_count() { return _total_compiler_stopped_count; } diff --git a/src/hotspot/share/compiler/compileLog.hpp b/src/hotspot/share/compiler/compileLog.hpp index a92d03862f67..f0b9efdcc990 100644 --- a/src/hotspot/share/compiler/compileLog.hpp +++ b/src/hotspot/share/compiler/compileLog.hpp @@ -55,8 +55,6 @@ class CompileLog : public xmlStream { static CompileLog* volatile _list_head; // head of static chain - void va_tag(bool push, const char* format, va_list ap) ATTRIBUTE_PRINTF(3, 0); - public: CompileLog(const char* file_name, FILE* fp, intx thread_id); ~CompileLog(); diff --git a/src/hotspot/share/compiler/compilerOracle.hpp b/src/hotspot/share/compiler/compilerOracle.hpp index b5b571e58eaa..65efec1cd561 100644 --- a/src/hotspot/share/compiler/compilerOracle.hpp +++ b/src/hotspot/share/compiler/compilerOracle.hpp @@ -134,7 +134,6 @@ class CompilerOracle : AllStatic { private: static bool _quiet; static void print_parse_error(char* error_msg, char* original_line); - static void print_command(CompileCommandEnum option, const char* name, enum OptionType type); // The core parser. static bool parse_from_input(inputStream::Input* input, diff --git a/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp b/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp index cf4bacbb98b2..5fa2edf4f424 100644 --- a/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp +++ b/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp @@ -268,7 +268,7 @@ static void refine_barrier_by_new_val_type(const Node* n) { bool G1BarrierSetC2::expand_barriers(Compile* C, PhaseIterGVN& igvn) const { ResourceMark rm; VectorSet visited; - Node_List worklist; + Node_List worklist; worklist.push(C->root()); while (worklist.size() > 0) { Node* n = worklist.pop(); @@ -519,8 +519,8 @@ void G1BarrierSetC2::analyze_dominating_barriers() const { // Find allocations and memory accesses (stores and atomic operations), and // track them in lists. - Node_List accesses; - Node_List allocations; + Node_List accesses; + Node_List allocations; for (uint i = 0; i < cfg->number_of_blocks(); ++i) { const Block* const block = cfg->get_block(i); for (uint j = 0; j < block->number_of_nodes(); ++j) { diff --git a/src/hotspot/share/gc/g1/g1CardTable.hpp b/src/hotspot/share/gc/g1/g1CardTable.hpp index 2c3f52fa0602..81861f570274 100644 --- a/src/hotspot/share/gc/g1/g1CardTable.hpp +++ b/src/hotspot/share/gc/g1/g1CardTable.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -118,7 +118,7 @@ class G1CardTable : public CardTable { // perform the dirtying atomically. inline bool mark_clean_as_from_remset(CardValue* card); - // Change Clean cards in a (large) area on the card table as From_Remset, preserving + // Mark Clean cards in a (large) area on the card table as From_Remset, preserving // cards already marked otherwise. Assumes that most cards in that area are Clean. // Not atomic. inline size_t mark_clean_range_as_from_remset(size_t start_card_index, size_t num_cards); diff --git a/src/hotspot/share/gc/g1/g1CardTable.inline.hpp b/src/hotspot/share/gc/g1/g1CardTable.inline.hpp index 370dc22ded05..747093dc68ba 100644 --- a/src/hotspot/share/gc/g1/g1CardTable.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CardTable.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,17 +44,6 @@ inline bool G1CardTable::mark_clean_as_from_remset(CardValue* card) { return false; } -// Returns bits from a where mask is 0, and bits from b where mask is 1. -// -// Example: -// a = 0xAAAAAAAA -// b = 0xBBBBBBBB -// mask = 0xFF00FF00 -// result = 0xBBAABBAA -inline size_t blend(size_t a, size_t b, size_t mask) { - return (a & ~mask) | (b & mask); -} - inline size_t G1CardTable::mark_clean_range_as_from_remset(size_t start_card_index, size_t num_cards) { assert(is_aligned(start_card_index, sizeof(size_t)), "Start card index must be aligned."); assert(is_aligned(num_cards, sizeof(size_t)), "Number of cards to change must be evenly divisible."); @@ -73,10 +62,13 @@ inline size_t G1CardTable::mark_clean_range_as_from_remset(size_t start_card_ind } else if ((value & WordAlreadyScanned) == 0) { // Do nothing if there is no "Clean" card in it. } else { - // There is a mix of cards in there. Tread "slowly". - size_t clean_card_mask = (value & WordAlreadyScanned) * 0xff; // All "Clean" cards have 0xff, all other places 0x00 now. - result += population_count(clean_card_mask) / BitsPerByte; - *cur_word = blend(value, WordAllFromRemset, clean_card_mask); + // There is a mix of cards in there. Tread slowly. + CardValue* cur = (CardValue*)cur_word; + for (size_t i = 0; i < sizeof(size_t); i++) { + if (mark_clean_as_from_remset(&cur[i])) { + result++; + } + } } cur_word++; } diff --git a/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp b/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp index 93d8da0d842c..ac9a2c6ac3c6 100644 --- a/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp @@ -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 @@ -129,15 +129,23 @@ void G1FullGCCompactTask::compact_humongous_obj(G1HeapRegion* src_hr) { copy_object_to_new_location(obj); uint dest_start_idx = _g1h->addr_to_region(destination); + + cleanup_humongous_regions(src_hr, dest_start_idx, num_regions); + // Update the metadata for the destination regions. _g1h->set_humongous_metadata(_g1h->region_at(dest_start_idx), num_regions, word_size, false); +} - // Free the source regions that do not overlap with the destination regions. +void G1FullGCCompactTask::cleanup_humongous_regions(G1HeapRegion* src_hr, uint dest_start_idx, uint num_regions) { uint src_start_idx = src_hr->hrm_index(); - free_non_overlapping_regions(src_start_idx, dest_start_idx, num_regions); -} + // If the source start region overlaps the destination, release its card set + // group explicitly, since it will not be freed by the loop below. + if (src_hr->rem_set()->has_card_set_group() && + (src_start_idx >= dest_start_idx && src_start_idx < (dest_start_idx + num_regions))) { + src_hr->clear_humongous(); + } -void G1FullGCCompactTask::free_non_overlapping_regions(uint src_start_idx, uint dest_start_idx, uint num_regions) { + // Free the source regions that do not overlap with the destination regions. uint dest_end_idx = dest_start_idx + num_regions -1; uint src_end_idx = src_start_idx + num_regions - 1; diff --git a/src/hotspot/share/gc/g1/g1FullGCCompactTask.hpp b/src/hotspot/share/gc/g1/g1FullGCCompactTask.hpp index 5c3929f1eed7..a7aa1a7b0bff 100644 --- a/src/hotspot/share/gc/g1/g1FullGCCompactTask.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCCompactTask.hpp @@ -42,7 +42,8 @@ class G1FullGCCompactTask : public G1FullGCTask { void compact_region(G1HeapRegion* hr); void compact_humongous_obj(G1HeapRegion* hr); - void free_non_overlapping_regions(uint src_start_idx, uint dest_start_idx, uint num_regions); + + void cleanup_humongous_regions(G1HeapRegion* src_hr, uint dest_start_idx, uint num_regions); static void copy_object_to_new_location(oop obj); diff --git a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp index 511ef579b543..a31bfe5bff0f 100644 --- a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp +++ b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp @@ -1063,12 +1063,12 @@ bool BarrierSetC2::is_allocation(const Node* node) { return offset == in_bytes(Thread::tlab_top_offset()); } -void BarrierSetC2::elide_dominated_barriers(Node_List& accesses, Node_List& access_dominators) const { +void BarrierSetC2::elide_dominated_barriers(const Node_List& accesses, const Node_List& access_dominators) const { Compile* const C = Compile::current(); PhaseCFG* const cfg = C->cfg(); for (uint i = 0; i < accesses.size(); i++) { - MachNode* const access = accesses.at(i)->as_Mach(); + MachNode* const access = accesses.at(i); intptr_t access_offset; const Node* const access_obj = get_base_and_offset(access, access_offset); Block* const access_block = cfg->get_block_for_node(access); diff --git a/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp b/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp index e116b4c94fc6..7fe9dc477673 100644 --- a/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp +++ b/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp @@ -68,7 +68,6 @@ class PhaseIdealLoop; class PhaseMacroExpand; class Type; class TypePtr; -class Unique_Node_List; // This class wraps a node and a type. class C2AccessValue: public StackObj { @@ -214,7 +213,7 @@ class C2OptAccess: public C2Access { class BarrierSetC2State : public ArenaObj { protected: - Node_Array _live; + Node_Array _live; public: BarrierSetC2State(Arena* arena) : _live(arena) {} @@ -360,7 +359,7 @@ class BarrierSetC2: public CHeapObj { // Elide GC barriers from instructions in 'accesses' if they are dominated by // instructions in 'access_dominators' (according to elide_mach_barrier()) and // there is no safepoint poll in between. - void elide_dominated_barriers(Node_List& accesses, Node_List& access_dominators) const; + void elide_dominated_barriers(const Node_List& accesses, const Node_List& access_dominators) const; virtual void late_barrier_analysis() const { } virtual void compute_liveness_at_stubs() const; virtual int estimate_stub_size() const { return 0; } diff --git a/src/hotspot/share/gc/shared/partialArraySplitter.hpp b/src/hotspot/share/gc/shared/partialArraySplitter.hpp index 5235d2cd11da..017e5696004d 100644 --- a/src/hotspot/share/gc/shared/partialArraySplitter.hpp +++ b/src/hotspot/share/gc/shared/partialArraySplitter.hpp @@ -34,13 +34,13 @@ // Each worker uses its own splitter to scan arrays in parallel. // Queued tasks share an atomic progress record; each claim returns a -// disjoint, self-contained chunk and decrements the record's ref-count. +// disjoint, self-contained chunk and decrements the record's remaining chunk count. class PartialArraySplitter { PartialArrayStateAllocator _allocator; PartialArrayTaskStepper _stepper; TASKQUEUE_STATS_ONLY(PartialArrayTaskStats _stats;) - // The state's ref-count must already include these tasks before publication. + // References for all chunks are reserved when the state is allocated. template void enqueue(Queue* queue, PartialArrayState* state, uint count); @@ -73,7 +73,7 @@ class PartialArraySplitter { // the queue if needed, permitting other workers to steal and process them // even while the caller is processing this claim. // - // Decrements the state's ref-count for the current task. Callers must not + // Decrements the state's remaining chunk count. Callers must not // use state after this call; it may have been recycled and reused. // // stolen indicates whether the state task was obtained from this queue or diff --git a/src/hotspot/share/gc/shared/partialArraySplitter.inline.hpp b/src/hotspot/share/gc/shared/partialArraySplitter.inline.hpp index dc702072f3f9..1746d665b6b7 100644 --- a/src/hotspot/share/gc/shared/partialArraySplitter.inline.hpp +++ b/src/hotspot/share/gc/shared/partialArraySplitter.inline.hpp @@ -72,14 +72,11 @@ PartialArraySplitter::claim(PartialArrayState* state, Queue* queue, bool stolen) size_t chunk_size = state->_chunk_size; uint count = _stepper.continuation_tasks(start, state->_length, chunk_size); if (count > 0) { - // Increment the ref-count for all new tasks before publication. A thief - // may immediately claim and release any task we push. - state->add_references(count); enqueue(queue, state, count); } Claim result{state->array(), start, start + chunk_size}; // Capture everything needed for scanning before decrementing the state's - // ref-count. + // remaining chunk count. _allocator.release(state); return result; } diff --git a/src/hotspot/share/gc/shared/partialArrayState.cpp b/src/hotspot/share/gc/shared/partialArrayState.cpp index 81d80b71ceb0..a437a81ac7da 100644 --- a/src/hotspot/share/gc/shared/partialArrayState.cpp +++ b/src/hotspot/share/gc/shared/partialArrayState.cpp @@ -40,18 +40,13 @@ PartialArrayState::PartialArrayState(objArrayOop array, _length(length), _chunk_size(chunk_size), _index(index), - _refcount(1) + _num_remaining_chunks((length - index) / chunk_size) { - assert(index <= length, "precondition"); + assert(index < length, "precondition"); assert(chunk_size > 0, "precondition"); assert((length - index) % chunk_size == 0, "whole chunks remain"); } -void PartialArrayState::add_references(size_t count) { - size_t new_count = _refcount.add_then_fetch(count, memory_order_relaxed); - assert(new_count >= count, "reference count overflow"); -} - class PartialArrayStateAllocator::FreeListEntry { public: FreeListEntry* _next; @@ -93,9 +88,9 @@ PartialArrayState* PartialArrayStateAllocator::allocate(objArrayOop array, } void PartialArrayStateAllocator::release(PartialArrayState* state) { - size_t refcount = state->_refcount.sub_then_fetch(1u, memory_order_release); - if (refcount != 0) { - assert(refcount + 1 != 0, "refcount underflow"); + size_t num_remaining_chunks = state->_num_remaining_chunks.sub_then_fetch(1u, memory_order_release); + if (num_remaining_chunks != 0) { + assert(num_remaining_chunks + 1 != 0, "remaining chunk count underflow"); } else { OrderAccess::acquire(); // Don't need to call destructor; can't if not destructible. diff --git a/src/hotspot/share/gc/shared/partialArrayState.hpp b/src/hotspot/share/gc/shared/partialArrayState.hpp index 302d73ae879c..207c6ce0dec1 100644 --- a/src/hotspot/share/gc/shared/partialArrayState.hpp +++ b/src/hotspot/share/gc/shared/partialArrayState.hpp @@ -49,17 +49,17 @@ class PartialArrayStateManager; // claiming a chunk, not while scanning it. // // States are allocated and released using a PartialArrayStateAllocator. -// Each queued or currently claiming task contributes one to the state's -// ref-count. Increment the ref-count before publishing continuation tasks. -// After a task has claimed its chunk and published any continuations, decrement -// the ref-count by one. The worker that decrements it to zero recycles the -// state on its own allocator for later reuse. +// Each full-sized chunk reserves a reference to the state at allocation, +// including chunks whose tasks have not yet been published. After a task has +// claimed its chunk, published any continuations, and captured everything needed +// for scanning, decrement the remaining chunk count by one. The worker that +// decrements it to zero recycles the state on its own allocator for later reuse. class PartialArrayState { objArrayOop _array; size_t _length; size_t _chunk_size; Atomic _index; - Atomic _refcount; + Atomic _num_remaining_chunks; friend class PartialArrayStateAllocator; friend class PartialArraySplitter; @@ -68,9 +68,6 @@ class PartialArrayState { size_t index, size_t length, size_t chunk_size); - // Increment the ref-count by count before publishing that many tasks to a queue. - void add_references(size_t count); - size_t claim_next() { size_t start = _index.fetch_then_add(_chunk_size, memory_order_relaxed); assert(start < _length, "every task must have a chunk to claim"); @@ -99,7 +96,7 @@ class PartialArrayState { // states will eventually be reclaimed when the arena is reset. // // The allocators each provide a free-list of states. When a state is -// released and its reference count has reached zero, it is added to the +// released and its remaining chunk count has reached zero, it is added to the // allocator's free-list, for use by future allocation requests. This causes // the maximum number of allocated states to be based on the number of // in-progress arrays, rather than the total number of arrays that need to be @@ -123,8 +120,9 @@ class PartialArrayStateAllocator : public CHeapObj { size_t index, size_t length, size_t chunk_size); - // Decrement the state's ref-count by one. If it reaches zero, recycle on - // this allocator's free-list. The state must belong to the same manager. + // Decrement the state's remaining chunk count by one. If it reaches zero, + // recycle on this allocator's free-list. The caller must have finished + // accessing the state. The state must belong to the same manager. void release(PartialArrayState* state); }; diff --git a/src/hotspot/share/gc/shared/partialArrayTaskStats.hpp b/src/hotspot/share/gc/shared/partialArrayTaskStats.hpp index e02a9f460b09..2eea99dec61a 100644 --- a/src/hotspot/share/gc/shared/partialArrayTaskStats.hpp +++ b/src/hotspot/share/gc/shared/partialArrayTaskStats.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -57,18 +57,6 @@ class PartialArrayTaskStats { // Trivially copied and destroyed. - // Number of arrays split into partial array tasks. - size_t split() const { return _split; } - - // Number of partial array tasks pushed onto a queue. - size_t pushed() const { return _pushed; } - - // Number of partial array tasks stolen from some other queue. - size_t stolen() const { return _stolen; } - - // Number of partial array tasks processed. - size_t processed() const { return _processed; } - void inc_split() { _split += 1; } void inc_pushed(size_t n) { _pushed += n; } void inc_stolen() { _stolen += 1; } diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp index d876a5517755..f05811cf5540 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp @@ -197,11 +197,11 @@ bool ShenandoahBarrierSetC2::can_remove_load_barrier(Node* root) { ResourceMark rm; VectorSet visited; - Node_List worklist; + Node_List worklist; worklist.push(root); while (worklist.size() > 0) { - Node* n = worklist.pop(); + const Node* n = worklist.pop(); if (visited.test_set(n->_idx)) { continue; } @@ -339,7 +339,7 @@ uint8_t ShenandoahBarrierSetC2::refine_store(Node* n, uint8_t bd) { void ShenandoahBarrierSetC2::final_refinement(Compile* compile) const { ResourceMark rm; - Unique_Node_List wq; + Unique_Node_List wq; RootNode* root = compile->root(); wq.push(root); @@ -482,7 +482,8 @@ void ShenandoahBarrierSetC2::analyze_dominating_barriers() const { } ResourceMark rm; - Node_List accesses, dominators; + Node_List accesses; + Node_List dominators; PhaseCFG* const cfg = Compile::current()->cfg(); for (uint i = 0; i < cfg->number_of_blocks(); ++i) { @@ -754,7 +755,7 @@ void ShenandoahBarrierSetC2::verify_gc_barriers(Compile* compile, CompilePhase p bool expect_load_store_barriers = expect_load_barriers || expect_store_barriers; bool expect_some_real = final_phase; - Unique_Node_List wq; + Unique_Node_List wq; RootNode* root = compile->root(); wq.push(root); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp b/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp index a9d3e5a3d9bf..b44477853a53 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp @@ -85,7 +85,10 @@ class ShenandoahMarkRefsSuperClosure : public ShenandoahSuperClosure { _weak = weak; } - virtual void do_nmethod(nmethod* nm) { + inline void do_cld(ClassLoaderData* cld) override; + inline void do_klass(Klass* k) override; + + void do_nmethod(nmethod* nm) override { assert(!is_weak(), "Can't handle weak marking of nmethods"); ShenandoahSuperClosure::do_nmethod(nm); } @@ -116,8 +119,8 @@ class ShenandoahRedirtyCardsMarkClosure : public ShenandoahMarkRefsSuperClosure void do_oop_work(T* p) { work(p); } public: - ShenandoahRedirtyCardsMarkClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp, ShenandoahObjToScanQueue* old_q) - : ShenandoahMarkRefsSuperClosure(q, rp, old_q) {} + ShenandoahRedirtyCardsMarkClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp) + : ShenandoahMarkRefsSuperClosure(q, rp, nullptr) {} ALWAYSINLINE void do_oop(narrowOop* p) override { do_oop_work(p); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp index 0ff970ae9362..05b1c3b7b42b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp @@ -27,6 +27,7 @@ #include "gc/shenandoah/shenandoahClosures.hpp" +#include "classfile/classLoaderData.inline.hpp" #include "gc/shared/barrierSetNMethod.hpp" #include "gc/shenandoah/shenandoahAsserts.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" @@ -76,6 +77,15 @@ ShenandoahMarkRefsSuperClosure::ShenandoahMarkRefsSuperClosure(ShenandoahObjToSc _mark_context(ShenandoahHeap::heap()->marking_context()), _weak(false) {} +void ShenandoahMarkRefsSuperClosure::do_cld(ClassLoaderData* cld) { + cld->oops_do(this, _weak ? ClassLoaderData::_claim_finalizable + : ClassLoaderData::_claim_strong); +} + +void ShenandoahMarkRefsSuperClosure::do_klass(Klass* k) { + do_cld(k->class_loader_data()); +} + template ALWAYSINLINE void ShenandoahMarkRefsSuperClosure::work(T* p) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index 0b875fa1c2ca..366e0148e03b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -93,7 +93,7 @@ class ShenandoahBreakpointMarkScope : public StackObj { ShenandoahConcurrentGC::ShenandoahConcurrentGC(ShenandoahController* controller, ShenandoahGeneration* generation, bool do_old_gc_bootstrap) : ShenandoahGC(generation), - _mark(generation), + _mark(generation, do_old_gc_bootstrap), _controller(controller), _abbreviated(false), _do_old_gc_bootstrap(do_old_gc_bootstrap) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp index 0297c4c2ff04..a07ef3794007 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp @@ -80,8 +80,8 @@ class ShenandoahFinalMarkingTask : public WorkerTask { } }; -ShenandoahConcurrentMark::ShenandoahConcurrentMark(ShenandoahGeneration* generation) : - ShenandoahMark(generation) {} +ShenandoahConcurrentMark::ShenandoahConcurrentMark(ShenandoahGeneration* generation, bool bootstrapping) : + ShenandoahMark(generation, bootstrapping) {} // Mark concurrent roots during concurrent phases template @@ -120,8 +120,7 @@ template void ShenandoahMarkConcurrentRootsTask::work(uint worker_id) { ShenandoahConcurrentWorkerSession worker_session(worker_id); ShenandoahObjToScanQueue* q = _queue_set->queue(worker_id); - ShenandoahObjToScanQueue* old_q = (_old_queue_set == nullptr) ? - nullptr : _old_queue_set->queue(worker_id); + ShenandoahObjToScanQueue* old_q = (_old_queue_set == nullptr) ? nullptr : _old_queue_set->queue(worker_id); ShenandoahMarkRefsClosure cl(q, _rp, old_q); _root_scanner.roots_do(&cl, worker_id); } @@ -135,7 +134,8 @@ void ShenandoahConcurrentMark::mark_concurrent_roots() { switch (_generation->type()) { case YOUNG: { - ShenandoahMarkConcurrentRootsTask task(task_queues(), old_task_queues(), rp, + auto old_queues = _bootstrapping ? old_task_queues() : nullptr; + ShenandoahMarkConcurrentRootsTask task(task_queues(), old_queues, rp, ShenandoahPhaseTimings::conc_mark_roots, workers->active_workers()); workers->run_task(&task); break; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.hpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.hpp index 17b27037658d..c935c2e02fcd 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.hpp @@ -39,7 +39,7 @@ class ShenandoahConcurrentMark: public ShenandoahMark { template friend class ShenandoahFinalMarkingTask; public: - ShenandoahConcurrentMark(ShenandoahGeneration* generation); + ShenandoahConcurrentMark(ShenandoahGeneration* generation, bool bootstrapping); // Concurrent mark roots void mark_concurrent_roots(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp index a7379170ad52..471d8c218907 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp @@ -172,8 +172,6 @@ void ShenandoahFullGC::do_it() { Universe::verify(); } - heap->set_full_gc_in_progress(true); - assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "must be at a safepoint"); assert(Thread::current()->is_VM_thread(), "Do full GC only while world is stopped"); @@ -182,6 +180,9 @@ void ShenandoahFullGC::do_it() { heap->pre_full_gc_dump(_gc_timer); } + // Set it after pre_full_gc_dump() to allow heap dumps to run. + heap->set_full_gc_in_progress(true); + { ShenandoahGCPhase prepare_phase(ShenandoahPhaseTimings::full_gc_prepare); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp index 718b32ee7fc9..00971ce74c9f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp @@ -367,7 +367,7 @@ void ShenandoahGeneration::scan_remembered_set(bool is_concurrent) { ShenandoahReferenceProcessor* rp = ref_processor(); ShenandoahRegionChunkIterator work_list(heap); - ShenandoahScanRememberedTask task(task_queues(), old_gen_task_queues(), rp, &work_list, is_concurrent); + ShenandoahScanRememberedTask task(task_queues(), rp, &work_list, is_concurrent); heap->assert_gc_workers(nworkers); heap->workers()->run_task(&task); if (ShenandoahEnableCardStats) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp index 5db519df1efa..8bd743806505 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -499,8 +499,8 @@ void ShenandoahGenerationalControlThread::service_concurrent_cycle(ShenandoahGen } } } else { - assert(generation->is_global(), "If not young, must be GLOBAL"); - assert(!do_old_gc_bootstrap, "Do not bootstrap with GLOBAL GC"); + assert(generation->is_global(), "If not young, must be Global"); + assert(!do_old_gc_bootstrap, "Do not bootstrap with Global GC"); if (_heap->cancelled_gc()) { msg = "At end of Interrupted Concurrent Global GC"; } else { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp index 80ed946a4811..7bfe1e0aa115 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp @@ -48,18 +48,18 @@ void ShenandoahMark::end_mark() { } } -ShenandoahMark::ShenandoahMark(ShenandoahGeneration* generation) : +ShenandoahMark::ShenandoahMark(ShenandoahGeneration* generation, bool bootstrapping) : _generation(generation), _task_queues(generation->task_queues()), _old_gen_task_queues(generation->old_gen_task_queues()), - _string_dedup(StringDedup::is_enabled()) { + _string_dedup(StringDedup::is_enabled()), + _bootstrapping(bootstrapping) { } template void ShenandoahMark::mark_loop_prework(uint w, TaskTerminator *t, StringDedup::Requests* const req) { - ShenandoahObjToScanQueueSet* queues = task_queues(); ShenandoahObjToScanQueue* q = get_queue(w); - ShenandoahObjToScanQueue* old_q = get_old_queue(w); + ShenandoahObjToScanQueue* old_q = _bootstrapping ? get_old_queue(w) : nullptr; ShenandoahReferenceProcessor *rp = _generation->ref_processor(); ShenandoahHeap* const heap = ShenandoahHeap::heap(); ShenandoahLiveData* ld = heap->get_liveness_cache(w); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp index b2d860e144de..b3e42305b55b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp @@ -45,9 +45,10 @@ class ShenandoahMark: public StackObj { ShenandoahObjToScanQueueSet* const _task_queues; ShenandoahObjToScanQueueSet* const _old_gen_task_queues; bool const _string_dedup; + bool const _bootstrapping; protected: - ShenandoahMark(ShenandoahGeneration* generation); + ShenandoahMark(ShenandoahGeneration* generation, bool bootstrapping); public: template diff --git a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp index 30636b3d448e..446eed736987 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp @@ -295,7 +295,6 @@ bool ShenandoahReferenceProcessor::should_discover(oop reference, ReferenceType T* referent_addr = (T*) java_lang_ref_Reference::referent_addr_raw(reference); T heap_oop = RawAccess<>::oop_load(referent_addr); oop referent = CompressedOops::decode(heap_oop); - ShenandoahHeap* heap = ShenandoahHeap::heap(); if (is_inactive(reference, referent, type)) { log_trace(gc,ref)("Reference inactive: " PTR_FORMAT, p2i(reference)); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp index de8bd8674687..a3b25fb6d3c7 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp @@ -63,7 +63,7 @@ void ShenandoahSTWMarkTask::work(uint worker_id) { } ShenandoahSTWMark::ShenandoahSTWMark(ShenandoahGeneration* generation) : - ShenandoahMark(generation), + ShenandoahMark(generation, false), _root_scanner(ShenandoahPhaseTimings::full_gc_mark), _terminator(ShenandoahHeap::heap()->workers()->active_workers(), task_queues()) { assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Must be at a Shenandoah safepoint"); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp index a594ea70410b..f6fb74bcf2cc 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp @@ -780,11 +780,10 @@ void ShenandoahDirectCardMarkRememberedSet::swap_card_tables() { } ShenandoahScanRememberedTask::ShenandoahScanRememberedTask(ShenandoahObjToScanQueueSet* queue_set, - ShenandoahObjToScanQueueSet* old_queue_set, ShenandoahReferenceProcessor* rp, ShenandoahRegionChunkIterator* work_list, bool is_concurrent) : WorkerTask("Scan Remembered Set"), - _queue_set(queue_set), _old_queue_set(old_queue_set), _rp(rp), _work_list(work_list), _is_concurrent(is_concurrent) { + _queue_set(queue_set), _rp(rp), _work_list(work_list), _is_concurrent(is_concurrent) { bool old_bitmap_stable = ShenandoahHeap::heap()->old_generation()->is_mark_complete(); log_debug(gc, remset)("Scan remembered set using bitmap: %s", BOOL_TO_STR(old_bitmap_stable)); } @@ -806,8 +805,7 @@ void ShenandoahScanRememberedTask::do_work(uint worker_id) { ShenandoahWorkerTimingsTracker x(ShenandoahPhaseTimings::init_scan_rset, ShenandoahPhaseTimings::Work, worker_id); ShenandoahObjToScanQueue* q = _queue_set->queue(worker_id); - ShenandoahObjToScanQueue* old = _old_queue_set == nullptr ? nullptr : _old_queue_set->queue(worker_id); - ShenandoahRedirtyCardsMarkClosure cl(q, _rp, old); + ShenandoahRedirtyCardsMarkClosure cl(q, _rp); ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); ShenandoahScanRemembered* scanner = heap->old_generation()->card_scan(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp index 207656532184..d034904d7671 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp @@ -940,14 +940,12 @@ class ShenandoahRegionChunkIterator : public StackObj { class ShenandoahScanRememberedTask : public WorkerTask { private: ShenandoahObjToScanQueueSet* _queue_set; - ShenandoahObjToScanQueueSet* _old_queue_set; ShenandoahReferenceProcessor* _rp; ShenandoahRegionChunkIterator* _work_list; bool _is_concurrent; public: ShenandoahScanRememberedTask(ShenandoahObjToScanQueueSet* queue_set, - ShenandoahObjToScanQueueSet* old_queue_set, ShenandoahReferenceProcessor* rp, ShenandoahRegionChunkIterator* work_list, bool is_concurrent); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp index f44641de968e..63f9e75f5b1d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp @@ -98,6 +98,10 @@ VM_ShenandoahFullGC::VM_ShenandoahFullGC(GCCause::Cause gc_cause, ShenandoahFull : VM_ShenandoahReferenceOperation(full_gc->generation()), _gc_cause(gc_cause), _full_gc(full_gc) { } +bool VM_ShenandoahFullGC::allow_nested_vm_operations() const { + return !ShenandoahHeap::heap()->is_full_gc_in_progress(); +} + void VM_ShenandoahFullGC::doit() { ShenandoahGCPauseMark mark(_gc_id, "Full GC", SvcGCMarker::FULL); set_active_generation(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp index 4530175c0815..057fad381418 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp @@ -97,6 +97,7 @@ class VM_ShenandoahFullGC : public VM_ShenandoahReferenceOperation { VM_Operation::VMOp_Type type() const override { return VMOp_ShenandoahFullGC; } const char* name() const override { return "Shenandoah Full GC"; } void doit() override; + bool allow_nested_vm_operations() const override; }; class VM_ShenandoahInitUpdateRefs: public VM_ShenandoahOperation { diff --git a/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp b/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp index 7d732347c296..01c4f0a3f238 100644 --- a/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp +++ b/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp @@ -482,14 +482,14 @@ void ZBarrierSetC2::analyze_dominating_barriers() const { Compile* const C = Compile::current(); PhaseCFG* const cfg = C->cfg(); - Node_List loads; - Node_List load_dominators; + Node_List loads; + Node_List load_dominators; - Node_List stores; - Node_List store_dominators; + Node_List stores; + Node_List store_dominators; - Node_List atomics; - Node_List atomic_dominators; + Node_List atomics; + Node_List atomic_dominators; // Step 1 - Find accesses and allocations, and track them in lists for (uint i = 0; i < cfg->number_of_blocks(); ++i) { diff --git a/src/hotspot/share/gc/z/zForwarding.cpp b/src/hotspot/share/gc/z/zForwarding.cpp index 92fc1bc89df9..32a6fc60b389 100644 --- a/src/hotspot/share/gc/z/zForwarding.cpp +++ b/src/hotspot/share/gc/z/zForwarding.cpp @@ -356,7 +356,33 @@ void ZForwarding::relocated_remembered_fields_notify_concurrent_scan_of() { assert(res == ZPublishState::reject, "Unexpected value"); } -bool ZForwarding::relocated_remembered_fields_published_contains(volatile zpointer* p) { +inline static int compare_zpointer(volatile zpointer* const& p1, volatile zpointer* const& p2) { + if (p1 < p2) { + return -1; + } else if (p1 > p2) { + return 1; + } else { + return 0; + } +} + +inline static int compare_zpointer_elem(volatile zpointer** p1, volatile zpointer** p2) { + return compare_zpointer(*p1, *p2); +} + +void ZForwarding::relocated_remembered_fields_published_sort() { + _relocated_remembered_fields_array.sort(compare_zpointer_elem); +} + +bool ZForwarding::relocated_remembered_fields_published_contains(volatile zpointer* p, bool is_sorted) { + if (is_sorted) { + // Binary search + bool found = false; + _relocated_remembered_fields_array.find_sorted(p, found); + return found; + } + + // Linear search for (volatile zpointer* const elem : _relocated_remembered_fields_array) { if (elem == p) { return true; diff --git a/src/hotspot/share/gc/z/zForwarding.hpp b/src/hotspot/share/gc/z/zForwarding.hpp index 72319bd2ebb5..c5744e798df9 100644 --- a/src/hotspot/share/gc/z/zForwarding.hpp +++ b/src/hotspot/share/gc/z/zForwarding.hpp @@ -166,7 +166,8 @@ class ZForwarding { bool relocated_remembered_fields_is_concurrently_scanned() const; template void relocated_remembered_fields_apply_to_published(Function function); - bool relocated_remembered_fields_published_contains(volatile zpointer* p); + void relocated_remembered_fields_published_sort(); + bool relocated_remembered_fields_published_contains(volatile zpointer* p, bool is_sorted); void verify() const; }; diff --git a/src/hotspot/share/gc/z/zVerify.cpp b/src/hotspot/share/gc/z/zVerify.cpp index db3db14afa2f..89320941f972 100644 --- a/src/hotspot/share/gc/z/zVerify.cpp +++ b/src/hotspot/share/gc/z/zVerify.cpp @@ -607,7 +607,7 @@ void ZVerify::on_color_flip() { } } -void ZVerify::before_relocation(ZForwarding* forwarding) { +void ZVerify::remset_before(ZForwarding* forwarding) { if (!ZVerifyRemembered) { return; } @@ -633,17 +633,23 @@ void ZVerify::before_relocation(ZForwarding* forwarding) { }); } +void ZVerify::before_relocation(ZForwarding* forwarding) { + remset_before(forwarding); +} + class ZVerifyRemsetAfterOopClosure : public BasicOopIterateClosure { private: ZForwarding* const _forwarding; zaddress_unsafe _from_addr; zaddress _to_addr; + const bool _after_relocation; public: - ZVerifyRemsetAfterOopClosure(ZForwarding* forwarding) + ZVerifyRemsetAfterOopClosure(ZForwarding* forwarding, bool after_relocation) : _forwarding(forwarding), _from_addr(zaddress_unsafe::null), - _to_addr(zaddress::null) {} + _to_addr(zaddress::null), + _after_relocation(after_relocation) {} void set_from_addr(zaddress_unsafe addr) { _from_addr = addr; @@ -709,7 +715,8 @@ class ZVerifyRemsetAfterOopClosure : public BasicOopIterateClosure { } guarantee(ZGeneration::young()->is_phase_mark(), "Should be in the mark phase " BAD_REMSET_ARG(p, ptr, _to_addr)); - guarantee(_forwarding->relocated_remembered_fields_published_contains(p), BAD_REMSET_ARG(p, ptr, _to_addr)); + guarantee(_after_relocation, "The published array should only be needed to be verified after relocation" BAD_REMSET_ARG(p, ptr, _to_addr)); + guarantee(_forwarding->relocated_remembered_fields_published_contains(p, _after_relocation), BAD_REMSET_ARG(p, ptr, _to_addr)); } virtual void do_oop(narrowOop* p) { @@ -721,8 +728,13 @@ class ZVerifyRemsetAfterOopClosure : public BasicOopIterateClosure { } }; -void ZVerify::after_relocation_internal(ZForwarding* forwarding) { - ZVerifyRemsetAfterOopClosure cl(forwarding); +void ZVerify::remset_after_inner(ZForwarding* forwarding, bool after_relocation) { + if (after_relocation) { + // Used to optimize otherwise linear scanning + forwarding->relocated_remembered_fields_published_sort(); + } + + ZVerifyRemsetAfterOopClosure cl(forwarding, after_relocation); forwarding->address_unsafe_iterate_via_table([&](zaddress_unsafe from_addr) { // If no field in this object was in the store barrier buffer @@ -738,7 +750,7 @@ void ZVerify::after_relocation_internal(ZForwarding* forwarding) { }); } -void ZVerify::after_relocation(ZForwarding* forwarding) { +void ZVerify::remset_after_relocation(ZForwarding* forwarding) { if (!ZVerifyRemembered) { return; } @@ -757,10 +769,14 @@ void ZVerify::after_relocation(ZForwarding* forwarding) { return; } - after_relocation_internal(forwarding); + remset_after_inner(forwarding, true /* after_relocation */); } -void ZVerify::after_scan(ZForwarding* forwarding) { +void ZVerify::after_relocation(ZForwarding* forwarding) { + remset_after_relocation(forwarding); +} + +void ZVerify::remset_after_scan(ZForwarding* forwarding) { if (!ZVerifyRemembered) { return; } @@ -778,5 +794,9 @@ void ZVerify::after_scan(ZForwarding* forwarding) { return; } - after_relocation_internal(forwarding); + remset_after_inner(forwarding, false /* after_relocation */); +} + +void ZVerify::after_scan(ZForwarding* forwarding) { + remset_after_scan(forwarding); } diff --git a/src/hotspot/share/gc/z/zVerify.hpp b/src/hotspot/share/gc/z/zVerify.hpp index c035caf430f1..bf41e3bd4837 100644 --- a/src/hotspot/share/gc/z/zVerify.hpp +++ b/src/hotspot/share/gc/z/zVerify.hpp @@ -40,7 +40,10 @@ class ZVerify : public AllStatic { static void objects(bool verify_weaks); static void threads_start_processing(); - static void after_relocation_internal(ZForwarding* forwarding); + static void remset_before(ZForwarding* forwarding); + static void remset_after_relocation(ZForwarding* forwarding); + static void remset_after_scan(ZForwarding* forwarding); + static void remset_after_inner(ZForwarding* forwarding, bool after_relocation); public: static void before_zoperation(); diff --git a/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp b/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp index 5eee5aeda965..c28e1c840a65 100644 --- a/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp +++ b/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp @@ -45,6 +45,7 @@ #include "services/diagnosticArgument.hpp" #include "services/diagnosticFramework.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/ostream.hpp" bool register_jfr_dcmds() { @@ -190,7 +191,7 @@ static oop construct_dcmd_instance(JfrJavaArguments* args, TRAPS) { return args->result()->get_oop(); } -JfrDCmd::JfrDCmd(outputStream* output, bool heap, int num_arguments) : DCmd(output, heap), _args(nullptr), _num_arguments(num_arguments), _delimiter('\0') {} +JfrDCmd::JfrDCmd(outputStream* output, bool heap, int num_arguments) : DCmd(output, heap), _args(nullptr), _args_length(0), _num_arguments(num_arguments), _delimiter('\0') {} void JfrDCmd::invoke(JfrJavaArguments& method, TRAPS) const { JavaValue constructor_result(T_OBJECT); @@ -211,6 +212,7 @@ void JfrDCmd::invoke(JfrJavaArguments& method, TRAPS) const { void JfrDCmd::parse(CmdLine* line, char delim, TRAPS) { _args = line->args_addr(); + _args_length = line->args_len(); _delimiter = delim; // Error checking done in execute. // Will not matter from DCmdFactory perspective @@ -228,8 +230,10 @@ void JfrDCmd::execute(DCmdSource source, TRAPS) { static const char signature[] = "(Ljava/lang/String;Ljava/lang/String;C)[Ljava/lang/String;"; JavaValue result(T_OBJECT); + stringStream arguments; + arguments.write(_args, _args_length); JfrJavaArguments execute(&result, javaClass(), "execute", signature, CHECK); - jstring argument = JfrJavaSupport::new_string(_args, CHECK); + jstring argument = JfrJavaSupport::new_string(arguments.base(), CHECK); jstring s = nullptr; if (source == DCmd_Source_Internal) { s = JfrJavaSupport::new_string("internal", CHECK); diff --git a/src/hotspot/share/jfr/dcmd/jfrDcmds.hpp b/src/hotspot/share/jfr/dcmd/jfrDcmds.hpp index aecd187a2e03..08aeb2ab7a60 100644 --- a/src/hotspot/share/jfr/dcmd/jfrDcmds.hpp +++ b/src/hotspot/share/jfr/dcmd/jfrDcmds.hpp @@ -31,6 +31,7 @@ class JfrJavaArguments; class JfrDCmd : public DCmd { private: const char* _args; + size_t _args_length; const int _num_arguments; char _delimiter; protected: diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp index 235ff51bd34f..4d2a324b4e6c 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2025 SAP SE. All rights reserved. * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -42,6 +43,7 @@ #include "runtime/vmOperation.hpp" #include "runtime/vmThread.hpp" #include "signals_posix.hpp" +#include "utilities/spinYield.hpp" #include "utilities/ticks.hpp" static const int64_t RECOMPUTE_INTERVAL_MS = 100; @@ -67,7 +69,7 @@ static JavaThread* get_java_thread_if_valid() { } JfrCPUTimeTraceQueue::JfrCPUTimeTraceQueue(u4 capacity) : - _data(nullptr), _capacity(capacity), _head(0), _lost_samples(0), _lost_samples_due_to_queue_full(0) { + _lock(UNLOCKED), _data(nullptr), _capacity(capacity), _head(0), _lost_samples(0), _lost_samples_due_to_queue_full(0) { if (capacity != 0) { _data = JfrCHeapObj::new_array(capacity); } @@ -80,41 +82,67 @@ JfrCPUTimeTraceQueue::~JfrCPUTimeTraceQueue() { } } +JfrCPUTimeTraceQueue::LockState JfrCPUTimeTraceQueue::lock_state() const { + return AtomicAccess::load_acquire(&_lock); +} + +bool JfrCPUTimeTraceQueue::try_acquire_enqueue_lock() { + return AtomicAccess::cmpxchg(&_lock, UNLOCKED, ENQUEUE, memory_order_seq_cst) == UNLOCKED; +} + +bool JfrCPUTimeTraceQueue::try_acquire_dequeue_lock() { + while (true) { + LockState state = lock_state(); + if (state == UNLOCKED && + AtomicAccess::cmpxchg(&_lock, UNLOCKED, DEQUEUE, memory_order_seq_cst) == UNLOCKED) { + return true; // successfully locked for dequeuing + } + if (state == DEQUEUE) { + return false; // already locked for dequeuing + } + // else wait for the lock to be released by a signal handler + } +} + +void JfrCPUTimeTraceQueue::acquire_dequeue_lock() { + SpinYield s; + while (lock_state() != UNLOCKED || + AtomicAccess::cmpxchg(&_lock, UNLOCKED, DEQUEUE, memory_order_seq_cst) != UNLOCKED) { + s.wait(); + } +} + +void JfrCPUTimeTraceQueue::release_lock() { + assert(lock_state() != UNLOCKED, "invariant"); + AtomicAccess::release_store(&_lock, UNLOCKED); +} + bool JfrCPUTimeTraceQueue::enqueue(JfrCPUTimeSampleRequest& request) { - assert(JavaThread::current()->jfr_thread_local()->is_cpu_time_jfr_enqueue_locked(), "invariant"); + assert(lock_state() == ENQUEUE, "invariant"); assert(&JavaThread::current()->jfr_thread_local()->cpu_time_jfr_queue() == this, "invariant"); - u4 elementIndex; - do { - elementIndex = AtomicAccess::load_acquire(&_head); - if (elementIndex >= _capacity) { - return false; - } - } while (AtomicAccess::cmpxchg(&_head, elementIndex, elementIndex + 1) != elementIndex); - _data[elementIndex] = request; + if (_head >= _capacity) { + return false; + } + _data[_head++] = request; return true; } JfrCPUTimeSampleRequest& JfrCPUTimeTraceQueue::at(u4 index) { + assert(lock_state() == DEQUEUE, "invariant"); assert(index < _head, "invariant"); return _data[index]; } -static volatile u4 _lost_samples_sum = 0; - u4 JfrCPUTimeTraceQueue::size() const { - return AtomicAccess::load_acquire(&_head); -} - -void JfrCPUTimeTraceQueue::set_size(u4 size) { - AtomicAccess::release_store(&_head, size); + assert(lock_state() != UNLOCKED, "invariant"); + return _head; } -u4 JfrCPUTimeTraceQueue::capacity() const { - return AtomicAccess::load_acquire(&_capacity); -} +static volatile u4 _lost_samples_sum = 0; void JfrCPUTimeTraceQueue::set_capacity(u4 capacity) { - if (capacity == AtomicAccess::load(&_capacity)) { + assert(lock_state() == DEQUEUE, "invariant"); + if (capacity == _capacity) { return; } _head = 0; @@ -127,11 +155,7 @@ void JfrCPUTimeTraceQueue::set_capacity(u4 capacity) { } else { _data = nullptr; } - AtomicAccess::release_store(&_capacity, capacity); -} - -bool JfrCPUTimeTraceQueue::is_empty() const { - return AtomicAccess::load_acquire(&_head) == 0; + _capacity = capacity; } u4 JfrCPUTimeTraceQueue::lost_samples() const { @@ -156,11 +180,16 @@ u4 JfrCPUTimeTraceQueue::get_and_reset_lost_samples_due_to_queue_full() { } void JfrCPUTimeTraceQueue::init() { - set_capacity(JfrCPUTimeTraceQueue::CPU_TIME_QUEUE_INITIAL_CAPACITY); + set_capacity(CPU_TIME_QUEUE_INITIAL_CAPACITY); +} + +void JfrCPUTimeTraceQueue::deallocate() { + set_capacity(0); } void JfrCPUTimeTraceQueue::clear() { - AtomicAccess::release_store(&_head, (u4)0); + assert(lock_state() == DEQUEUE, "invariant"); + _head = 0; } void JfrCPUTimeTraceQueue::resize_if_needed() { @@ -168,7 +197,7 @@ void JfrCPUTimeTraceQueue::resize_if_needed() { if (lost_samples_due_to_queue_full == 0) { return; } - u4 capacity = AtomicAccess::load(&_capacity); + u4 capacity = _capacity; if (capacity < CPU_TIME_QUEUE_MAX_CAPACITY) { float ratio = (float)lost_samples_due_to_queue_full / (float)capacity; int factor = 1; @@ -254,15 +283,14 @@ class JfrCPUSamplerThread : public NonJavaThread { void stackwalk_threads_in_native(); bool create_timer_for_thread(JavaThread* thread, timer_t &timerid); + void allow_signal_handlers(); void stop_signal_handlers(); + void assert_no_active_signal_handlers(); // returns false if the stop signal bit was set, true otherwise bool increment_signal_handler_count(); - void decrement_signal_handler_count(); - void initialize_active_signal_handler_counter(); - protected: virtual void post_run(); public: @@ -274,7 +302,7 @@ class JfrCPUSamplerThread : public NonJavaThread { void handle_timer_signal(siginfo_t* info, void* context); bool init_timers(); - void stop_timer(); + void stop_timers(); virtual void print_on(outputStream* st) const; void trigger_async_processing_of_cpu_time_jfr_requests(); @@ -309,7 +337,10 @@ void JfrCPUSamplerThread::on_javathread_create(JavaThread* thread) { } JfrThreadLocal* tl = thread->jfr_thread_local(); assert(tl != nullptr, "invariant"); - tl->cpu_time_jfr_queue().init(); + + JfrCPUTimeTraceQueue& queue = tl->cpu_time_jfr_queue(); + queue.acquire_dequeue_lock(); + queue.init(); timer_t timerid; if (create_timer_for_thread(thread, timerid)) { tl->set_cpu_timer(&timerid); @@ -317,8 +348,9 @@ void JfrCPUSamplerThread::on_javathread_create(JavaThread* thread) { if (!AtomicAccess::or_then_fetch(&_warned_about_timer_creation_failure, true)) { log_warning(jfr)("Failed to create timer for a thread"); } - tl->deallocate_cpu_time_jfr_queue(); + queue.deallocate(); } + queue.release_lock(); } void JfrCPUSamplerThread::on_javathread_terminate(JavaThread* thread) { @@ -328,14 +360,16 @@ void JfrCPUSamplerThread::on_javathread_terminate(JavaThread* thread) { if (timer == nullptr) { return; // no timer was created for this thread } - tl->acquire_cpu_time_jfr_dequeue_lock(); + + JfrCPUTimeTraceQueue& queue = tl->cpu_time_jfr_queue(); + queue.acquire_dequeue_lock(); tl->unset_cpu_timer(); - tl->deallocate_cpu_time_jfr_queue(); - s4 lost_samples = tl->cpu_time_jfr_queue().lost_samples(); + queue.deallocate(); + s4 lost_samples = queue.lost_samples(); if (lost_samples > 0) { JfrCPUTimeThreadSampling::send_lost_event(JfrTicks::now(), lost_samples); } - tl->release_cpu_time_jfr_queue_lock(); + queue.release_lock(); } void JfrCPUSamplerThread::start_thread() { @@ -349,7 +383,6 @@ void JfrCPUSamplerThread::start_thread() { void JfrCPUSamplerThread::enroll() { if (AtomicAccess::cmpxchg(&_disenrolled, true, false)) { AtomicAccess::store(&_warned_about_timer_creation_failure, false); - initialize_active_signal_handler_counter(); log_trace(jfr)("Enrolling CPU thread sampler"); _sample.signal(); if (!init_timers()) { @@ -357,6 +390,7 @@ void JfrCPUSamplerThread::enroll() { disenroll(); return; } + allow_signal_handlers(); log_trace(jfr)("Enrolled CPU thread sampler"); } } @@ -365,8 +399,9 @@ void JfrCPUSamplerThread::disenroll() { if (!AtomicAccess::cmpxchg(&_disenrolled, false, true)) { log_trace(jfr)("Disenrolling CPU thread sampler"); if (AtomicAccess::load_acquire(&_signal_handler_installed)) { - stop_timer(); + // Ensure no signal handlers are running before deleting timers and queues stop_signal_handlers(); + stop_timers(); } _sample.wait(); log_trace(jfr)("Disenrolled CPU thread sampler"); @@ -407,7 +442,7 @@ void JfrCPUSamplerThread::stackwalk_threads_in_native() { JavaThread* jt = tlh.list()->thread_at(i); JfrThreadLocal* tl = jt->jfr_thread_local(); if (tl->wants_async_processing_of_cpu_time_jfr_requests()) { - if (jt->thread_state() != _thread_in_native || !tl->try_acquire_cpu_time_jfr_dequeue_lock()) { + if (jt->thread_state() != _thread_in_native || !tl->cpu_time_jfr_queue().try_acquire_dequeue_lock()) { tl->set_do_async_processing_of_cpu_time_jfr_requests(false); continue; } @@ -416,7 +451,7 @@ void JfrCPUSamplerThread::stackwalk_threads_in_native() { } else { tl->set_do_async_processing_of_cpu_time_jfr_requests(false); } - tl->release_cpu_time_jfr_queue_lock(); + tl->cpu_time_jfr_queue().release_lock(); } } } @@ -616,7 +651,7 @@ void JfrCPUSamplerThread::handle_timer_signal(siginfo_t* info, void* context) { queue.increment_lost_samples(); return; } - if (!tl->try_acquire_cpu_time_jfr_enqueue_lock()) { + if (!queue.try_acquire_enqueue_lock()) { queue.increment_lost_samples(); return; } @@ -647,7 +682,7 @@ void JfrCPUSamplerThread::handle_timer_signal(siginfo_t* info, void* context) { tl->set_do_async_processing_of_cpu_time_jfr_requests(false); } - tl->release_cpu_time_jfr_queue_lock(); + queue.release_lock(); } static const int SIG = SIGPROF; @@ -690,6 +725,12 @@ bool JfrCPUSamplerThread::create_timer_for_thread(JavaThread* thread, timer_t& t } +void JfrCPUSamplerThread::allow_signal_handlers() { + assert_no_active_signal_handlers(); + // reset STOP_SIGNAL_BIT to allow SIGPROF handlers to run + AtomicAccess::release_store(&_active_signal_handlers, (u4)0); +} + void JfrCPUSamplerThread::stop_signal_handlers() { // set the stop signal bit AtomicAccess::or_then_fetch(&_active_signal_handlers, STOP_SIGNAL_BIT, memory_order_acq_rel); @@ -699,26 +740,31 @@ void JfrCPUSamplerThread::stop_signal_handlers() { } } +void JfrCPUSamplerThread::assert_no_active_signal_handlers() { + assert(AtomicAccess::load(&_active_signal_handlers) == STOP_SIGNAL_BIT, "invariant"); +} + // returns false if the stop signal bit was set, true otherwise bool JfrCPUSamplerThread::increment_signal_handler_count() { - // increment the count of active signal handlers - u4 old_value = AtomicAccess::fetch_then_add(&_active_signal_handlers, (u4)1, memory_order_acq_rel); - if ((old_value & STOP_SIGNAL_BIT) != 0) { - // if the stop signal bit was set, we are not allowed to increment - AtomicAccess::dec(&_active_signal_handlers, memory_order_acq_rel); - return false; + u4 count = AtomicAccess::load_acquire(&_active_signal_handlers); + while (true) { + if ((count & STOP_SIGNAL_BIT) != 0) { + // if the stop signal bit was set, we are not allowed to increment + return false; + } + // atomically increment the count of active signal handlers + u4 expected = count; + count = AtomicAccess::cmpxchg(&_active_signal_handlers, expected, expected + 1, memory_order_acq_rel); + if (count == expected) { + return true; + } } - return true; } void JfrCPUSamplerThread::decrement_signal_handler_count() { AtomicAccess::dec(&_active_signal_handlers, memory_order_acq_rel); } -void JfrCPUSamplerThread::initialize_active_signal_handler_counter() { - AtomicAccess::release_store(&_active_signal_handlers, (u4)0); -} - class VM_JFRInitializeCPUTimeSampler : public VM_Operation { private: JfrCPUSamplerThread* const _sampler; @@ -744,6 +790,8 @@ bool JfrCPUSamplerThread::init_timers() { return false; } AtomicAccess::release_store(&_signal_handler_installed, true); + + assert_no_active_signal_handlers(); VM_JFRInitializeCPUTimeSampler op(this); VMThread::execute(&op); return true; @@ -766,13 +814,17 @@ class VM_JFRTerminateCPUTimeSampler : public VM_Operation { if (timer == nullptr) { continue; } - tl->deallocate_cpu_time_jfr_queue(); + JfrCPUTimeTraceQueue& queue = tl->cpu_time_jfr_queue(); + queue.acquire_dequeue_lock(); + queue.deallocate(); tl->unset_cpu_timer(); + queue.release_lock(); } }; }; -void JfrCPUSamplerThread::stop_timer() { +void JfrCPUSamplerThread::stop_timers() { + assert_no_active_signal_handlers(); VM_JFRTerminateCPUTimeSampler op(this); VMThread::execute(&op); } diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp index 2585446b2ba2..8d98d8a73115 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2025 SAP SE. All rights reserved. + * 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 @@ -42,24 +43,34 @@ struct JfrCPUTimeSampleRequest { }; // Fixed size async-signal-safe SPSC linear queue backed by an array. -// Designed to be only used under lock and read linearly +// All accesses to the queue, with the exception of the atomic +// lost-samples counters, are guarded by a tri-state lock: +// - ENQUEUE: locked for enqueuing by a signal handler +// - DEQUEUE: locked for dequeuing, initialization, or deallocation +// - UNLOCKED: no lock held class JfrCPUTimeTraceQueue { + private: + enum LockState { + UNLOCKED, + ENQUEUE, + DEQUEUE + }; - // the default queue capacity, scaled if the sampling period is smaller than 10ms - // when the thread is started - static const u4 CPU_TIME_QUEUE_CAPACITY = 500; - + volatile LockState _lock; JfrCPUTimeSampleRequest* _data; - volatile u4 _capacity; + u4 _capacity; // next unfilled index - volatile u4 _head; + u4 _head; volatile u4 _lost_samples; volatile u4 _lost_samples_due_to_queue_full; static const u4 CPU_TIME_QUEUE_INITIAL_CAPACITY = 20; static const u4 CPU_TIME_QUEUE_MAX_CAPACITY = 2000; -public: + + LockState lock_state() const; + + public: JfrCPUTimeTraceQueue(u4 capacity); ~JfrCPUTimeTraceQueue(); @@ -71,15 +82,9 @@ class JfrCPUTimeTraceQueue { u4 size() const; - void set_size(u4 size); - - u4 capacity() const; - // deletes all samples in the queue void set_capacity(u4 capacity); - bool is_empty() const; - u4 lost_samples() const; void increment_lost_samples(); @@ -96,8 +101,16 @@ class JfrCPUTimeTraceQueue { // init the queue capacity void init(); + void deallocate(); + void clear(); + // signal safe; fails if the lock is already held + bool try_acquire_enqueue_lock(); + // waits while the lock is held for enqueue; fails if it is held for dequeue + bool try_acquire_dequeue_lock(); + void acquire_dequeue_lock(); + void release_lock(); }; @@ -123,8 +136,6 @@ class JfrCPUTimeThreadSampling : public JfrCHeapObj { void update_run_state(JfrCPUSamplerThrottle& throttle); - static void set_rate(JfrCPUSamplerThrottle& throttle); - public: static void set_rate(double rate); static void set_period(u8 nanos); diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.cpp b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.cpp index 362950af20a4..688b1762636a 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.cpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.cpp @@ -383,15 +383,14 @@ static void drain_enqueued_cpu_time_requests(const JfrTicks& now, JfrThreadLocal assert(current != nullptr, "invariant"); #ifdef LINUX tl->set_do_async_processing_of_cpu_time_jfr_requests(false); + JfrCPUTimeTraceQueue& queue = tl->cpu_time_jfr_queue(); if (lock) { - tl->acquire_cpu_time_jfr_dequeue_lock(); + queue.acquire_dequeue_lock(); } - JfrCPUTimeTraceQueue& queue = tl->cpu_time_jfr_queue(); for (u4 i = 0; i < queue.size(); i++) { record_cpu_time_thread(queue.at(i), now, tl, jt, current); } queue.clear(); - assert(queue.is_empty(), "invariant"); tl->set_has_cpu_time_jfr_requests(false); if (queue.lost_samples() > 0) { // Lost samples belong to the platform thread that owns the queue. @@ -402,7 +401,7 @@ static void drain_enqueued_cpu_time_requests(const JfrTicks& now, JfrThreadLocal queue.resize_if_needed(); } if (lock) { - tl->release_cpu_time_jfr_queue_lock(); + queue.release_lock(); } #endif } diff --git a/src/hotspot/share/jfr/support/jfrIntrinsics.cpp b/src/hotspot/share/jfr/support/jfrIntrinsics.cpp index 54208b4aebfd..eb3e1c9eb264 100644 --- a/src/hotspot/share/jfr/support/jfrIntrinsics.cpp +++ b/src/hotspot/share/jfr/support/jfrIntrinsics.cpp @@ -61,19 +61,6 @@ void* JfrIntrinsicSupport::return_lease(JavaThread* jt) { return nullptr; } -void JfrIntrinsicSupport::load_barrier(const Klass* klass) { - assert(klass != nullptr, "sanity"); - JfrTraceIdLoadBarrier::load_barrier(klass); -} - -address JfrIntrinsicSupport::epoch_address() { - return JfrTraceIdEpoch::epoch_address(); -} - address JfrIntrinsicSupport::epoch_generation_address() { return JfrTraceIdEpoch::epoch_generation_address(); } - -address JfrIntrinsicSupport::signal_address() { - return JfrTraceIdEpoch::signal_address(); -} diff --git a/src/hotspot/share/jfr/support/jfrIntrinsics.hpp b/src/hotspot/share/jfr/support/jfrIntrinsics.hpp index f56420ac813b..7b11ceabb34b 100644 --- a/src/hotspot/share/jfr/support/jfrIntrinsics.hpp +++ b/src/hotspot/share/jfr/support/jfrIntrinsics.hpp @@ -38,9 +38,6 @@ class JfrIntrinsicSupport : AllStatic { public: static void* write_checkpoint(JavaThread* jt); static void* return_lease(JavaThread* jt); - static void load_barrier(const Klass* klass); - static address epoch_address(); - static address signal_address(); static address epoch_generation_address(); }; @@ -59,8 +56,6 @@ class JfrIntrinsicSupport : AllStatic { #define JFR_INTRINSICS(do_intrinsic, do_class, do_name, do_signature, do_alias) \ do_intrinsic(_counterTime, jdk_jfr_internal_JVM, counterTime_name, void_long_signature, F_SN) \ do_name( counterTime_name, "counterTime") \ - do_intrinsic(_getClassId, jdk_jfr_internal_JVM, getClassId_name, class_long_signature, F_SN) \ - do_name( getClassId_name, "getClassId") \ do_intrinsic(_getEventWriter, jdk_jfr_internal_JVM, getEventWriter_name, getEventWriter_signature, F_SN) \ do_name( getEventWriter_name, "getEventWriter") \ do_intrinsic(_jvm_commit, jdk_jfr_internal_JVM, commit_name, long_long_signature, F_SN) \ diff --git a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp index b1a5d5e203cb..042d9cfc52be 100644 --- a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp +++ b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp @@ -45,7 +45,6 @@ #include "runtime/os.hpp" #include "runtime/threadIdentifier.hpp" #include "utilities/sizes.hpp" -#include "utilities/spinYield.hpp" JfrThreadLocal::JfrThreadLocal() : _sample_request(), @@ -85,7 +84,6 @@ JfrThreadLocal::JfrThreadLocal() : _sampling_critical_section(false) #ifdef LINUX ,_cpu_timer(nullptr), - _cpu_time_jfr_locked(UNLOCKED), _has_cpu_time_jfr_requests(false), _cpu_time_jfr_queue(0), _do_async_processing_of_cpu_time_jfr_requests(false) @@ -603,43 +601,6 @@ timer_t* JfrThreadLocal::cpu_timer() const { return _cpu_timer; } -bool JfrThreadLocal::is_cpu_time_jfr_enqueue_locked() { - return AtomicAccess::load_acquire(&_cpu_time_jfr_locked) == ENQUEUE; -} - -bool JfrThreadLocal::is_cpu_time_jfr_dequeue_locked() { - return AtomicAccess::load_acquire(&_cpu_time_jfr_locked) == DEQUEUE; -} - -bool JfrThreadLocal::try_acquire_cpu_time_jfr_enqueue_lock() { - return AtomicAccess::cmpxchg(&_cpu_time_jfr_locked, UNLOCKED, ENQUEUE) == UNLOCKED; -} - -bool JfrThreadLocal::try_acquire_cpu_time_jfr_dequeue_lock() { - CPUTimeLockState got; - while (true) { - CPUTimeLockState got = AtomicAccess::cmpxchg(&_cpu_time_jfr_locked, UNLOCKED, DEQUEUE); - if (got == UNLOCKED) { - return true; // successfully locked for dequeue - } - if (got == DEQUEUE) { - return false; // already locked for dequeue - } - // else wait for the lock to be released from a signal handler - } -} - -void JfrThreadLocal::acquire_cpu_time_jfr_dequeue_lock() { - SpinYield s; - while (AtomicAccess::cmpxchg(&_cpu_time_jfr_locked, UNLOCKED, DEQUEUE) != UNLOCKED) { - s.wait(); - } -} - -void JfrThreadLocal::release_cpu_time_jfr_queue_lock() { - AtomicAccess::release_store(&_cpu_time_jfr_locked, UNLOCKED); -} - void JfrThreadLocal::set_has_cpu_time_jfr_requests(bool has_requests) { AtomicAccess::release_store(&_has_cpu_time_jfr_requests, has_requests); } @@ -652,10 +613,6 @@ JfrCPUTimeTraceQueue& JfrThreadLocal::cpu_time_jfr_queue() { return _cpu_time_jfr_queue; } -void JfrThreadLocal::deallocate_cpu_time_jfr_queue() { - cpu_time_jfr_queue().set_capacity(0); -} - void JfrThreadLocal::set_do_async_processing_of_cpu_time_jfr_requests(bool wants) { AtomicAccess::release_store(&_do_async_processing_of_cpu_time_jfr_requests, wants); } diff --git a/src/hotspot/share/jfr/support/jfrThreadLocal.hpp b/src/hotspot/share/jfr/support/jfrThreadLocal.hpp index f6b85cae6ac1..0acb78d771f6 100644 --- a/src/hotspot/share/jfr/support/jfrThreadLocal.hpp +++ b/src/hotspot/share/jfr/support/jfrThreadLocal.hpp @@ -85,15 +85,6 @@ class JfrThreadLocal { #ifdef LINUX timer_t* _cpu_timer; - - enum CPUTimeLockState { - UNLOCKED, - // locked for enqueuing - ENQUEUE, - // locked for dequeuing - DEQUEUE - }; - volatile CPUTimeLockState _cpu_time_jfr_locked; volatile bool _has_cpu_time_jfr_requests; JfrCPUTimeTraceQueue _cpu_time_jfr_queue; volatile bool _do_async_processing_of_cpu_time_jfr_requests; @@ -371,26 +362,10 @@ class JfrThreadLocal { void unset_cpu_timer(); timer_t* cpu_timer() const; - // The CPU time JFR lock has three different states: - // - ENQUEUE: lock for enqueuing CPU time requests - // - DEQUEUE: lock for dequeuing CPU time requests - // - UNLOCKED: no lock held - // This ensures that we can safely enqueue and dequeue CPU time requests, - // without interleaving - - bool is_cpu_time_jfr_enqueue_locked(); - bool is_cpu_time_jfr_dequeue_locked(); - - bool try_acquire_cpu_time_jfr_enqueue_lock(); - bool try_acquire_cpu_time_jfr_dequeue_lock(); - void acquire_cpu_time_jfr_dequeue_lock(); - void release_cpu_time_jfr_queue_lock(); - void set_has_cpu_time_jfr_requests(bool has_events); bool has_cpu_time_jfr_requests(); JfrCPUTimeTraceQueue& cpu_time_jfr_queue(); - void deallocate_cpu_time_jfr_queue(); void set_do_async_processing_of_cpu_time_jfr_requests(bool wants); bool wants_async_processing_of_cpu_time_jfr_requests(); diff --git a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp index cc71d390d9c8..05d2877619a0 100644 --- a/src/hotspot/share/oops/constantPool.cpp +++ b/src/hotspot/share/oops/constantPool.cpp @@ -24,6 +24,7 @@ #include "cds/aotConstantPoolResolver.hpp" #include "cds/aotHeap.hpp" +#include "cds/aotSystemDictionary.hpp" #include "cds/archiveBuilder.hpp" #include "cds/cdsConfig.hpp" #include "classfile/classLoader.hpp" @@ -33,7 +34,6 @@ #include "classfile/metadataOnStackMark.hpp" #include "classfile/stringTable.hpp" #include "classfile/systemDictionary.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" #include "classfile/vmSymbols.hpp" #include "code/codeCache.hpp" @@ -333,7 +333,7 @@ refArrayOop ConstantPool::prepare_resolved_references_for_archiving() { } InstanceKlass *ik = pool_holder(); - if (!SystemDictionaryShared::is_builtin_loader(ik->class_loader_data())) { + if (!AOTSystemDictionary::is_builtin_loader(ik->class_loader_data())) { // Archiving resolved references for classes from non-builtin loaders // is not yet supported. return nullptr; @@ -550,7 +550,7 @@ void ConstantPool::remove_resolved_klass_if_non_archivable(int cp_index) { k = resolved_klass_at(cp_index); if (k == nullptr) { // We'd come here if the referenced class has been excluded via - // SystemDictionaryShared::is_excluded_class(). As a result, ArchiveBuilder + // AOTSystemDictionary::is_excluded_class(). As a result, ArchiveBuilder // has cleared the resolved_klasses()->at(...) pointer to null. Thus, we // need to revert the tag to JVM_CONSTANT_UnresolvedClass. can_archive = false; diff --git a/src/hotspot/share/oops/cpCache.cpp b/src/hotspot/share/oops/cpCache.cpp index 2fa899b129af..2d17e79f8bf9 100644 --- a/src/hotspot/share/oops/cpCache.cpp +++ b/src/hotspot/share/oops/cpCache.cpp @@ -29,7 +29,6 @@ #include "classfile/javaStackTraceClasses.hpp" #include "classfile/resolutionErrors.hpp" #include "classfile/systemDictionary.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" #include "code/codeCache.hpp" #include "interpreter/bytecodes.hpp" diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index 911f249f327d..6930d93d074a 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -26,6 +26,7 @@ #include "cds/aotHeap.hpp" #include "cds/aotLinkedClassBulkLoader.hpp" #include "cds/aotMetaspace.hpp" +#include "cds/aotSystemDictionary.hpp" #include "cds/archiveUtils.hpp" #include "cds/cdsConfig.hpp" #include "cds/cdsEnumKlass.hpp" @@ -38,7 +39,6 @@ #include "classfile/javaStackTraceClasses.hpp" #include "classfile/moduleEntry.hpp" #include "classfile/systemDictionary.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/verifier.hpp" #include "classfile/vmClasses.hpp" #include "classfile/vmSymbols.hpp" @@ -840,7 +840,7 @@ void InstanceKlass::deallocate_contents(ClassLoaderData* loader_data) { } set_annotations(nullptr); - SystemDictionaryShared::handle_class_unloading(this); + AOTSystemDictionary::handle_class_unloading(this); #if INCLUDE_CDS_JAVA_HEAP if (CDSConfig::is_dumping_heap()) { @@ -1142,7 +1142,7 @@ bool InstanceKlass::link_class_or_fail(TRAPS) { } bool InstanceKlass::link_class_impl(TRAPS) { - if (CDSConfig::is_dumping_static_archive() && SystemDictionaryShared::has_class_failed_verification(this)) { + if (CDSConfig::is_dumping_static_archive() && AOTSystemDictionary::has_class_failed_verification(this)) { // This is for CDS static dump only -- we use the in_error_state to indicate that // the class has failed verification. Throwing the NoClassDefFoundError here is just // a convenient way to stop repeat attempts to verify the same (bad) class. @@ -1252,7 +1252,7 @@ bool InstanceKlass::link_class_impl(TRAPS) { // also sets rewritten rewrite_class(CHECK_false); } else if (in_aot_cache()) { - SystemDictionaryShared::check_verification_constraints(this, CHECK_false); + AOTSystemDictionary::check_verification_constraints(this, CHECK_false); } // relocate jsrs and link methods after they are all rewritten @@ -1270,7 +1270,7 @@ bool InstanceKlass::link_class_impl(TRAPS) { // 3) the class was not verified during dump time bool need_init_table = true; if (in_aot_cache() && verified_at_dump_time() && - SystemDictionaryShared::check_linking_constraints(THREAD, this)) { + AOTSystemDictionary::check_linking_constraints(THREAD, this)) { need_init_table = false; } if (need_init_table) { @@ -3111,7 +3111,7 @@ void InstanceKlass::remove_unshareable_info() { Klass::remove_unshareable_info(); - if (SystemDictionaryShared::has_class_failed_verification(this)) { + if (AOTSystemDictionary::has_class_failed_verification(this)) { // Classes are attempted to link during dumping and may fail, // but these classes are still in the dictionary and class list in CLD. // If the class has failed verification, there is nothing else to remove. @@ -3314,14 +3314,14 @@ bool InstanceKlass::can_be_verified_at_dumptime() const { if (CDSConfig::is_old_class_for_verifier(this)) { // The old verifier does not save verification constraints, so at run time - // SystemDictionaryShared::check_verification_constraints() will not work for this class. + // AOTSystemDictionary::check_verification_constraints() will not work for this class. return false; } if (CDSConfig::is_dumping_final_static_archive() && fail_over_verified()) { // This is a class with version >50 but was verified with the old verifier in the training run, // which had -XX:+AOTClassLinking. However, we are now in the assembly run with -XX:-AOTClassLinking. - // As SystemDictionaryShared::check_verification_constraints() does not support this case, + // As AOTSystemDictionary::check_verification_constraints() does not support this case, // we must exclude this class. assert(!CDSConfig::is_dumping_aot_linked_classes(), "must be"); return false; @@ -3367,7 +3367,7 @@ void InstanceKlass::unload_class(InstanceKlass* ik) { // notify ClassLoadingService of class unload ClassLoadingService::notify_class_unloaded(ik); - SystemDictionaryShared::handle_class_unloading(ik); + AOTSystemDictionary::handle_class_unloading(ik); if (log_is_enabled(Info, class, unload)) { ResourceMark rm; diff --git a/src/hotspot/share/oops/klass.cpp b/src/hotspot/share/oops/klass.cpp index 3962b5552ad1..ace59f8f7014 100644 --- a/src/hotspot/share/oops/klass.cpp +++ b/src/hotspot/share/oops/klass.cpp @@ -23,6 +23,7 @@ */ #include "cds/aotHeap.hpp" +#include "cds/aotSystemDictionary.hpp" #include "cds/cdsConfig.hpp" #include "cds/cppVtables.hpp" #include "classfile/classLoader.hpp" @@ -31,7 +32,6 @@ #include "classfile/javaClasses.inline.hpp" #include "classfile/moduleEntry.hpp" #include "classfile/systemDictionary.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" #include "classfile/vmSymbols.hpp" #include "gc/shared/collectedHeap.inline.hpp" @@ -135,7 +135,7 @@ void Klass::set_name(Symbol* n) { } if (CDSConfig::is_dumping_archive() && is_instance_klass()) { - SystemDictionaryShared::init_dumptime_info(InstanceKlass::cast(this)); + AOTSystemDictionary::init_dumptime_info(InstanceKlass::cast(this)); } } diff --git a/src/hotspot/share/oops/methodData.cpp b/src/hotspot/share/oops/methodData.cpp index a7b2cab16f09..31c0a82a1820 100644 --- a/src/hotspot/share/oops/methodData.cpp +++ b/src/hotspot/share/oops/methodData.cpp @@ -22,9 +22,9 @@ * */ +#include "cds/aotSystemDictionary.hpp" #include "cds/cdsConfig.hpp" #include "ci/ciMethodData.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmSymbols.hpp" #include "compiler/compilationPolicy.hpp" #include "compiler/compilerDefinitions.inline.hpp" @@ -324,7 +324,7 @@ static bool is_excluded(Klass* k) { log_debug(aot, training)("Purged %s from MDO: unloaded class", k->name()->as_C_string()); return true; } else { - bool excluded = SystemDictionaryShared::should_be_excluded(k) || !SystemDictionaryShared::is_builtin_loader(k->class_loader_data()); + bool excluded = AOTSystemDictionary::should_be_excluded(k) || !AOTSystemDictionary::is_builtin_loader(k->class_loader_data()); if (excluded) { log_debug(aot, training)("Purged %s from MDO: excluded class", k->name()->as_C_string()); } diff --git a/src/hotspot/share/oops/trainingData.cpp b/src/hotspot/share/oops/trainingData.cpp index 2b87c899fe16..a73d27977a68 100644 --- a/src/hotspot/share/oops/trainingData.cpp +++ b/src/hotspot/share/oops/trainingData.cpp @@ -23,13 +23,13 @@ */ #include "cds/aotCompressedPointers.hpp" +#include "cds/aotSystemDictionary.hpp" #include "cds/cdsConfig.hpp" #include "ci/ciEnv.hpp" #include "ci/ciMetadata.hpp" #include "classfile/compactHashtable.hpp" #include "classfile/javaClasses.hpp" #include "classfile/symbolTable.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "compiler/compileBroker.hpp" #include "compiler/compileTask.hpp" #include "memory/metadataFactory.hpp" @@ -125,7 +125,7 @@ static bool is_excluded(InstanceKlass* k) { } if (CDSConfig::is_at_aot_safepoint()) { // Check for AOT exclusion only at AOT safe point. - return SystemDictionaryShared::should_be_excluded(k) || !SystemDictionaryShared::is_builtin_loader(k->class_loader_data()); + return AOTSystemDictionary::should_be_excluded(k) || !AOTSystemDictionary::is_builtin_loader(k->class_loader_data()); } return false; } @@ -733,7 +733,7 @@ bool TrainingData::Key::can_compute_cds_hash(const Key* const& k) { } uint TrainingData::Key::cds_hash(const Key* const& k) { - return SystemDictionaryShared::hash_for_shared_dictionary((address)k->meta()); + return AOTSystemDictionary::hash_for_shared_dictionary((address)k->meta()); } TrainingData* TrainingData::lookup_archived_training_data(const Key* k) { diff --git a/src/hotspot/share/opto/addnode.hpp b/src/hotspot/share/opto/addnode.hpp index 1d78a3e5a672..e87e23898633 100644 --- a/src/hotspot/share/opto/addnode.hpp +++ b/src/hotspot/share/opto/addnode.hpp @@ -433,7 +433,6 @@ class MinINode : public MinMaxNode { class MaxLNode : public MinMaxNode { public: MaxLNode(Compile* C, Node* in1, Node* in2) : MinMaxNode(in1, in2) { - init_flags(Flag_is_macro); C->add_macro_node(this); } virtual int Opcode() const; @@ -452,7 +451,6 @@ class MaxLNode : public MinMaxNode { class MinLNode : public MinMaxNode { public: MinLNode(Compile* C, Node* in1, Node* in2) : MinMaxNode(in1, in2) { - init_flags(Flag_is_macro); C->add_macro_node(this); } virtual int Opcode() const; diff --git a/src/hotspot/share/opto/arraycopynode.cpp b/src/hotspot/share/opto/arraycopynode.cpp index d0fa123fda35..289ae5011f26 100644 --- a/src/hotspot/share/opto/arraycopynode.cpp +++ b/src/hotspot/share/opto/arraycopynode.cpp @@ -43,7 +43,6 @@ ArrayCopyNode::ArrayCopyNode(Compile* C, bool alloc_tightly_coupled, bool has_ne _src_type(TypeOopPtr::BOTTOM), _dest_type(TypeOopPtr::BOTTOM) { init_class_id(Class_ArrayCopy); - init_flags(Flag_is_macro); C->add_macro_node(this); } @@ -426,10 +425,6 @@ const TypeAryPtr* ArrayCopyNode::get_address_type(PhaseGVN* phase, const TypePtr return atp->add_offset(Type::OffsetBot)->is_aryptr(); } -const TypePtr* ArrayCopyNode::get_src_adr_type(PhaseGVN* phase) const { - return get_address_type(phase, _src_type, in(Src)); -} - void ArrayCopyNode::array_copy_test_overlap(GraphKit& kit, bool disjoint_bases, int count, Node*& backward_ctl) { Node* ctl = kit.control(); if (!disjoint_bases && count > 1) { diff --git a/src/hotspot/share/opto/arraycopynode.hpp b/src/hotspot/share/opto/arraycopynode.hpp index d02c860919ff..1b8d4a210c5d 100644 --- a/src/hotspot/share/opto/arraycopynode.hpp +++ b/src/hotspot/share/opto/arraycopynode.hpp @@ -95,7 +95,6 @@ class ArrayCopyNode : public CallNode { _arraycopy_type_Type = TypeFunc::make(domain, range); } - const TypePtr* get_src_adr_type(PhaseGVN* phase) const; private: ArrayCopyNode(Compile* C, bool alloc_tightly_coupled, bool has_negative_length_guard); diff --git a/src/hotspot/share/opto/block.cpp b/src/hotspot/share/opto/block.cpp index 1da63ce81808..9138965c8c74 100644 --- a/src/hotspot/share/opto/block.cpp +++ b/src/hotspot/share/opto/block.cpp @@ -205,13 +205,17 @@ bool Block::has_uncommon_code() const { en = en->in(0); if (en->is_Catch()) en = en->in(0); - if (en->is_MachProj() && en->in(0)->is_MachCall()) { - MachCallNode* call = en->in(0)->as_MachCall(); - if (call->cnt() != COUNT_UNKNOWN && call->cnt() <= PROB_UNLIKELY_MAG(4)) { - // This is true for slow-path stubs like new_{instance,array}, - // slow_arraycopy, complete_monitor_locking, uncommon_trap. - // The magic number corresponds to the probability of an uncommon_trap, - // even though it is a count not a probability. + if (en->is_MachProj()) { + if (en->in(0)->is_MachCall()) { + MachCallNode* call = en->in(0)->as_MachCall(); + if (call->cnt() != COUNT_UNKNOWN && call->cnt() <= PROB_UNLIKELY_MAG(4)) { + // This is true for slow-path stubs like new_{instance,array}, + // slow_arraycopy, complete_monitor_locking, uncommon_trap. + // The magic number corresponds to the probability of an uncommon_trap, + // even though it is a count not a probability. + return true; + } + } else if (en->in(0)->is_MachUncommonTrap()) { return true; } } @@ -416,7 +420,7 @@ uint PhaseCFG::build_cfg() { VectorSet visited; // Allocate stack with enough space to avoid frequent realloc - Node_Stack nstack(C->live_nodes() >> 1); + Node_Stack nstack(C->live_nodes() >> 1); nstack.push(_root, 0); uint sum = 0; // Counter for blocks diff --git a/src/hotspot/share/opto/block.hpp b/src/hotspot/share/opto/block.hpp index d66611467529..ded1a24d2d4d 100644 --- a/src/hotspot/share/opto/block.hpp +++ b/src/hotspot/share/opto/block.hpp @@ -115,7 +115,7 @@ class Block : public CFGElement { private: // Nodes in this block, in order - Node_List _nodes; + Node_List _nodes; public: @@ -189,7 +189,6 @@ class Block : public CFGElement { int num_fall_throughs(); // How many fall-through candidate this block has void update_uncommon_branch(Block* un); // Lower branch prob to uncommon code bool succ_fall_through(uint i); // Is successor "i" is a fall-through candidate - Block* lone_fall_through(); // Return lone fall-through Block or null Block* dom_lca(Block* that); // Compute LCA in dominator tree. @@ -424,12 +423,12 @@ class PhaseCFG : public Phase { void global_code_motion(); // Schedule Nodes early in their basic blocks. - bool schedule_early(VectorSet &visited, Node_Stack &roots); + bool schedule_early(VectorSet& visited, Node_Stack& roots); // For each node, find the latest block it can be scheduled into // and then select the cheapest block between the latest and earliest // block to place the node. - void schedule_late(VectorSet &visited, Node_Stack &stack); + void schedule_late(VectorSet& visited, Node_Stack& stack); // Compute the (backwards) latency of a node from a single use int latency_from_use(Node *n, const Node *def, Node *use); @@ -438,7 +437,7 @@ class PhaseCFG : public Phase { void partial_latency_of_defs(Node *n); // Compute the instruction global latency with a backwards walk - void compute_latencies_backwards(VectorSet &visited, Node_Stack &stack); + void compute_latencies_backwards(VectorSet& visited, Node_Stack& stack); // Check if a block between early and LCA block of uses is cheaper by // frequency-based policy, latency-based policy and random-based policy @@ -455,12 +454,12 @@ class PhaseCFG : public Phase { void needed_for_next_call(Block* block, Node* this_call, VectorSet& next_call); // Perform basic-block local scheduling - Node* select(Block* block, Node_List& worklist, GrowableArray& ready_cnt, VectorSet& next_call, uint sched_slot, + Node* select(Block* block, Node_List& worklist, GrowableArray& ready_cnt, VectorSet& next_call, uint sched_slot, intptr_t* recacl_pressure_nodes); void adjust_register_pressure(Node* n, Block* block, intptr_t *recalc_pressure_nodes, bool finalize_mode); // Schedule a call next in the block - uint sched_call(Block* block, uint node_cnt, Node_List& worklist, GrowableArray& ready_cnt, MachCallNode* mcall, VectorSet& next_call); + uint sched_call(Block* block, uint node_cnt, Node_List& worklist, GrowableArray& ready_cnt, MachCallNode* mcall, VectorSet& next_call); // Cleanup if any code lands between a Call and his Catch void call_catch_cleanup(Block* block); @@ -606,7 +605,7 @@ class PhaseCFG : public Phase { bool is_uncommon(const Block* block); #ifdef ASSERT - Unique_Node_List _raw_oops; + Unique_Node_List _raw_oops; #endif // Do global code motion by first building dominator tree and estimate block frequency @@ -740,7 +739,6 @@ class CFGLoop : public CFGElement { assert(hd->head()->is_Loop(), "must begin with loop head node"); return hd; } - Block* backedge_block(); // Return the block on the backedge of the loop (else null) void compute_loop_depth(int depth); void compute_freq(); // compute frequency with loop assuming head freq 1.0f void scale_freq(); // scale frequency by loop trip count (including outer loops) diff --git a/src/hotspot/share/opto/buildOopMap.cpp b/src/hotspot/share/opto/buildOopMap.cpp index 3f165e98ed23..ccf0756eab72 100644 --- a/src/hotspot/share/opto/buildOopMap.cpp +++ b/src/hotspot/share/opto/buildOopMap.cpp @@ -360,10 +360,10 @@ OopMap *OopFlow::build_oop_map( Node *n, int max_reg, PhaseRegAlloc *regalloc, i #ifdef ASSERT if (t->isa_rawptr()) { ResourceMark rm; - Unique_Node_List worklist; + Unique_Node_List worklist; worklist.push(def); for (uint i = 0; i < worklist.size(); i++) { - Node* m = worklist.at(i); + const Node* m = worklist.at(i); if (C->cfg()->_raw_oops.member(m)) { def->dump(); m->dump(); diff --git a/src/hotspot/share/opto/c2_CodeStubs.hpp b/src/hotspot/share/opto/c2_CodeStubs.hpp index 5664ee03e5c9..abb96e84d970 100644 --- a/src/hotspot/share/opto/c2_CodeStubs.hpp +++ b/src/hotspot/share/opto/c2_CodeStubs.hpp @@ -100,20 +100,15 @@ class C2EntryBarrierStub: public C2CodeStub { class C2FastUnlockStub : public C2CodeStub { private: Register _obj; - Register _mark; Register _t; Register _thread; - Label _slow_path; Label _push_and_slow_path; - Label _unlocked_continuation; public: - C2FastUnlockStub(Register obj, Register mark, Register t, Register thread) : C2CodeStub(), - _obj(obj), _mark(mark), _t(t), _thread(thread) {} + C2FastUnlockStub(Register obj, Register t, Register thread) : C2CodeStub(), + _obj(obj), _t(t), _thread(thread) {} int max_size() const; void emit(C2_MacroAssembler& masm); - Label& slow_path() { return _slow_path; } Label& push_and_slow_path() { return _push_and_slow_path; } - Label& unlocked_continuation() { return _unlocked_continuation; } Label& slow_path_continuation() { return continuation(); } }; diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 3379da006347..651ac5e008d9 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -943,8 +943,8 @@ product(bool, PreloadReduceTraps, true, DIAGNOSTIC, \ "Preload code should avoid traps as much as possible.") \ \ - develop(bool, KillPathsReachableByDeadTypeNode, true, \ - "When a Type node becomes top, make paths where the node is " \ + develop(bool, KillPathsReachableByDeadDataNode, true, \ + "When a data node becomes top, make paths where the node is " \ "used dead by replacing them with a Halt node. Turning this off " \ "could corrupt the graph in rare cases and should be used with " \ "care.") \ @@ -1015,6 +1015,9 @@ \ product(bool, UseAOTCodeCounters, true, DIAGNOSTIC, \ "Use AOT code counter to trigger JIT compilation") \ + \ + product(bool, PreferCallBasedUncommonTraps, false, EXPERIMENTAL, \ + "Prefer MachCall-based uncommon trap sequences.") \ // end of C2_FLAGS diff --git a/src/hotspot/share/opto/callGenerator.hpp b/src/hotspot/share/opto/callGenerator.hpp index c373490f3646..7af8db63638b 100644 --- a/src/hotspot/share/opto/callGenerator.hpp +++ b/src/hotspot/share/opto/callGenerator.hpp @@ -161,20 +161,12 @@ class CallGenerator : public ArenaObj { CallGenerator* if_missed, CallGenerator* if_hit); - // How to make a call that optimistically assumes a MethodHandle target: - static CallGenerator* for_predicted_dynamic_call(ciMethodHandle* predicted_method_handle, - CallGenerator* if_missed, - CallGenerator* if_hit, - float hit_prob); - // How to make a call that gives up and goes back to the interpreter: static CallGenerator* for_uncommon_trap(ciMethod* m, Deoptimization::DeoptReason reason, Deoptimization::DeoptAction action); // Registry for intrinsics: - static CallGenerator* for_intrinsic(ciMethod* m); - static void register_intrinsic(ciMethod* m, CallGenerator* cg); static CallGenerator* for_predicated_intrinsic(CallGenerator* intrinsic, CallGenerator* cg); virtual Node* generate_predicate(JVMState* jvms, int predicate) { return nullptr; }; diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index 7e942e2df026..cd722763eead 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -2093,7 +2093,6 @@ AllocateNode::AllocateNode(Compile* C, const TypeFunc *atype, : CallNode(atype, nullptr, TypeRawPtr::BOTTOM, TypePtr::BOTTOM) { init_class_id(Class_Allocate); - init_flags(Flag_is_macro); _is_scalar_replaceable = false; _is_non_escaping = false; _is_allocation_MemBar_redundant = false; @@ -2353,50 +2352,6 @@ bool AbstractLockNode::find_matching_unlock(const Node* ctrl, LockNode* lock, return false; } -// -// Find the lock matching an unlock. Returns null if a safepoint -// or complicated control is encountered first. -LockNode *AbstractLockNode::find_matching_lock(UnlockNode* unlock) { - LockNode *lock_result = nullptr; - // find the matching lock, or an intervening safepoint - Node *ctrl = next_control(unlock->in(0)); - while (1) { - assert(ctrl != nullptr, "invalid control graph"); - assert(!ctrl->is_Start(), "missing lock for unlock"); - if (ctrl->is_top()) break; // dead control path - if (ctrl->is_Proj()) ctrl = ctrl->in(0); - if (ctrl->is_SafePoint()) { - break; // found a safepoint (may be the lock we are searching for) - } else if (ctrl->is_Region()) { - // Check for a simple diamond pattern. Punt on anything more complicated - if (ctrl->req() == 3 && ctrl->in(1) != nullptr && ctrl->in(2) != nullptr) { - Node *in1 = next_control(ctrl->in(1)); - Node *in2 = next_control(ctrl->in(2)); - if (((in1->is_IfTrue() && in2->is_IfFalse()) || - (in2->is_IfTrue() && in1->is_IfFalse())) && (in1->in(0) == in2->in(0))) { - ctrl = next_control(in1->in(0)->in(0)); - } else { - break; - } - } else { - break; - } - } else { - ctrl = next_control(ctrl->in(0)); // keep searching - } - } - if (ctrl->is_Lock()) { - LockNode *lock = ctrl->as_Lock(); - Node* lock_obj = lock->obj_node(); - Node* unlock_obj = unlock->obj_node(); - if (lock_obj->eqv_uncast(unlock_obj) && - BoxLockNode::same_slot(lock->box_node(), unlock->box_node())) { - lock_result = lock; - } - } - return lock_result; -} - // This code corresponds to case 3 above. bool AbstractLockNode::find_lock_and_unlock_through_if(Node* node, LockNode* lock, @@ -2819,7 +2774,6 @@ PowDNode::PowDNode(Compile* C, Node* base, Node* exp) OptoRuntime::Math_DD_D_Type(), StubRoutines::dpow() != nullptr ? StubRoutines::dpow() : CAST_FROM_FN_PTR(address, SharedRuntime::dpow), "pow") { - add_flag(Flag_is_macro); C->add_macro_node(this); init_req(TypeFunc::Parms + 0, base); diff --git a/src/hotspot/share/opto/callnode.hpp b/src/hotspot/share/opto/callnode.hpp index c70e812fa5ed..7fbc4a5431e9 100644 --- a/src/hotspot/share/opto/callnode.hpp +++ b/src/hotspot/share/opto/callnode.hpp @@ -929,7 +929,6 @@ class CallStaticJavaNode : public CallJavaNode { : CallJavaNode(tf, addr, method, TypePtr::BOTTOM, TypePtr::BOTTOM), _safe_for_fold_compare(true) { init_class_id(Class_CallStaticJava); if (C->eliminate_boxing() && (method != nullptr) && (method->is_boxing_method() || method->is_unboxing_method())) { - init_flags(Flag_is_macro); C->add_macro_node(this); } const TypeTuple *r = tf->range_sig(); @@ -940,7 +939,6 @@ class CallStaticJavaNode : public CallJavaNode { r->field_at(TypeFunc::Parms)->isa_oopptr() && r->field_at(TypeFunc::Parms)->is_oopptr()->can_be_value_type()) { // Make sure this call is processed by PhaseMacroExpand::expand_mh_intrinsic_return - init_flags(Flag_is_macro); C->add_macro_node(this); } } @@ -956,11 +954,11 @@ class CallStaticJavaNode : public CallJavaNode { static int extract_uncommon_trap_request(const Node* call); bool is_boxing_method() const { - return is_macro() && (method() != nullptr) && method()->is_boxing_method(); + return Compile::current()->eliminate_boxing() && (method() != nullptr) && method()->is_boxing_method(); } bool is_unboxing_method() const { - return is_macro() && (method() != nullptr) && method()->is_unboxing_method(); + return Compile::current()->eliminate_boxing() && (method() != nullptr) && method()->is_unboxing_method(); } // Late inlining modifies the JVMState, so we need to deep clone it @@ -1310,7 +1308,6 @@ class AbstractLockNode: public CallNode { GrowableArray &lock_ops); bool find_unlocks_for_region(const RegionNode* region, LockNode* lock, GrowableArray &lock_ops); - LockNode *find_matching_lock(UnlockNode* unlock); // Update the counter to indicate that this lock was eliminated. void set_eliminated_lock_counter() PRODUCT_RETURN; @@ -1398,7 +1395,6 @@ class LockNode : public AbstractLockNode { virtual uint size_of() const; // Size is bigger LockNode(Compile* C, const TypeFunc* tf) : AbstractLockNode(tf, TypeRawPtr::BOTTOM, TypePtr::BOTTOM) { init_class_id(Class_Lock); - init_flags(Flag_is_macro); C->add_macro_node(this); } virtual bool guaranteed_safepoint() { return false; } @@ -1427,7 +1423,6 @@ class UnlockNode : public AbstractLockNode { #endif { init_class_id(Class_Unlock); - init_flags(Flag_is_macro); C->add_macro_node(this); } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); diff --git a/src/hotspot/share/opto/castnode.cpp b/src/hotspot/share/opto/castnode.cpp index f2d22f8a2f6b..63861d5f5374 100644 --- a/src/hotspot/share/opto/castnode.cpp +++ b/src/hotspot/share/opto/castnode.cpp @@ -357,7 +357,7 @@ void CastIINode::remove_range_check_cast(Compile* C) { // range check or a null divisor check. assert(in(0) != nullptr, "All RangeCheck CastII must have a control dependency"); ResourceMark rm; - Unique_Node_List wq; + Unique_Node_List wq; wq.push(this); for (uint next = 0; next < wq.size(); ++next) { Node* m = wq.at(next); diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index 1e5e3ccc3434..f53614b5358a 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -43,6 +43,7 @@ #include "opto/phaseX.hpp" #include "opto/regalloc.hpp" #include "opto/regmask.hpp" +#include "opto/rootnode.hpp" #include "opto/runtime.hpp" #include "opto/subnode.hpp" #include "opto/valuetypenode.hpp" @@ -374,11 +375,11 @@ bool RegionNode::is_possible_unsafe_loop() const { bool RegionNode::is_unreachable_from_root(const PhaseGVN* phase) const { ResourceMark rm; - Node_List nstack; + Node_List nstack; VectorSet visited; // Mark all control nodes reachable from root outputs - Node* n = (Node*)phase->C->root(); + const Node* n = phase->C->root(); nstack.push(n); visited.set(n->_idx); while (nstack.size() != 0) { @@ -403,7 +404,7 @@ bool RegionNode::is_unreachable_from_root(const PhaseGVN* phase) const { // (no path to root except through false NeverBranch exit) bool RegionNode::is_in_infinite_subgraph() { ResourceMark rm; - Unique_Node_List worklist; + Unique_Node_List worklist; worklist.push(this); return RegionNode::are_all_nodes_in_infinite_subgraph(worklist); } @@ -411,10 +412,10 @@ bool RegionNode::is_in_infinite_subgraph() { // Are all nodes in worklist in infinite subgraph? // (no path to root except through false NeverBranch exit) // worklist is directly used for the traversal -bool RegionNode::are_all_nodes_in_infinite_subgraph(Unique_Node_List& worklist) { +bool RegionNode::are_all_nodes_in_infinite_subgraph(Unique_Node_List& worklist) { // BFS traversal down the CFG, except through NeverBranch exits for (uint i = 0; i < worklist.size(); ++i) { - Node* n = worklist.at(i); + const Node* n = worklist.at(i); assert(n->is_CFG(), "only traverse CFG"); if (n->is_Root()) { // Found root -> there was an exit! @@ -669,6 +670,8 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) { if (outer_sfpt != nullptr && outer_out != nullptr) { Node* in = outer_sfpt->in(0); igvn->replace_node(outer_out, in); + // Now, in has 2 CFG outputs, disconnect outer_sfpt from it + igvn->replace_input_of(outer_sfpt, 0, igvn->C->top()); LoopNode* outer = as_CountedLoop()->outer_loop(); igvn->replace_input_of(outer, LoopNode::LoopBackControl, igvn->C->top()); } @@ -699,14 +702,13 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) { if (add_to_worklist) { igvn->add_users_to_worklist(this); // Check for further allowed opts } - for (DUIterator_Last imin, i = last_outs(imin); i >= imin; --i) { + uint edges_removed = 0; + for (DUIterator_Last imin, i = last_outs(imin); i >= imin; i -= edges_removed) { + edges_removed = 1; Node* n = last_out(i); igvn->hash_delete(n); // Remove from worklist before modifying edges if (n->outcnt() == 0) { - int uses_found = n->replace_edge(this, phase->C->top(), igvn); - if (uses_found > 1) { // (--i) done at the end of the loop. - i -= (uses_found - 1); - } + edges_removed = n->replace_edge(this, phase->C->top(), igvn); continue; } if( n->is_Phi() ) { // Collapse all Phis @@ -725,10 +727,7 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) { } else if( n->is_Region() ) { // Update all incoming edges assert(n != this, "Must be removed from DefUse edges"); - int uses_found = n->replace_edge(this, parent_ctrl, igvn); - if (uses_found > 1) { // (--i) done at the end of the loop. - i -= (uses_found - 1); - } + edges_removed = n->replace_edge(this, parent_ctrl, igvn); } else { assert(n->in(0) == this, "Expect RegionNode to be control parent"); @@ -834,7 +833,7 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) { void RegionNode::remove_unreachable_subgraph(PhaseIterGVN* igvn) { Node* top = igvn->C->top(); ResourceMark rm; - Unique_Node_List unreachable; // visit each only once + Unique_Node_List unreachable; // visit each only once unreachable.push(this); // Recursively find all control inputs. for (uint i = 0; i < unreachable.size(); i++) { @@ -1169,18 +1168,18 @@ PhiNode* PhiNode::split_out_instance(const TypePtr* at, PhaseIterGVN *igvn) cons } Compile *C = igvn->C; ResourceMark rm; - Node_Array node_map; - Node_Stack stack(C->live_nodes() >> 4); + Node_Array node_map; + Node_Stack stack(C->live_nodes() >> 4); PhiNode *nphi = slice_memory(at); igvn->register_new_node_with_optimizer( nphi ); node_map.map(_idx, nphi); - stack.push((Node *)this, 1); + stack.push(this, 1); while(!stack.is_empty()) { - PhiNode *ophi = stack.node()->as_Phi(); + const PhiNode* ophi = stack.node(); uint i = stack.index(); assert(i >= 1, "not control edge"); stack.pop(); - nphi = node_map[ophi->_idx]->as_Phi(); + nphi = node_map[ophi->_idx]; for (; i < ophi->req(); i++) { Node *in = ophi->in(i); if (in == nullptr || igvn->type(in) == Type::TOP) @@ -1691,11 +1690,11 @@ Node* PhiNode::unique_constant_input_recursive(PhaseGVN* phase) { ResourceMark rm; Node* unique = nullptr; - Unique_Node_List visited; + Unique_Node_List visited; visited.push(this); for (uint visited_idx = 0; visited_idx < visited.size(); visited_idx++) { - Node* current = visited.at(visited_idx); + const PhiNode* current = visited.at(visited_idx); for (uint i = 1; i < current->req(); i++) { Node* phi_in = current->in(i); if (phi_in == nullptr) { @@ -1703,7 +1702,7 @@ Node* PhiNode::unique_constant_input_recursive(PhaseGVN* phase) { } if (phi_in->is_Phi()) { - visited.push(phi_in); + visited.push(phi_in->as_Phi()); } else { if (unique == nullptr) { if (!phi_in->is_Con()) { @@ -2121,13 +2120,13 @@ bool PhiNode::is_unsafe_data_reference(Node *in) const { ResourceMark rm; - Node_List nstack; + Node_List nstack; VectorSet visited; nstack.push(in); // Start with unique input. visited.set(in->_idx); while (nstack.size() != 0) { - Node* n = nstack.pop(); + const Node* n = nstack.pop(); uint cnt = n->req(); uint i = (n->is_Proj() && !n->is_CFG()) ? 0 : 1; for (; i < cnt; i++) { @@ -2155,10 +2154,10 @@ bool PhiNode::is_dead_phi() { } ResourceMark rm; - Unique_Node_List worklist; + Unique_Node_List worklist; worklist.push(this); for (uint wl_idx = 0; wl_idx < worklist.size(); wl_idx++) { - Node* n = worklist.at(wl_idx); + const Node* n = worklist.at(wl_idx); for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { Node* out = n->fast_out(i); if (out->is_Phi()) { @@ -2176,7 +2175,7 @@ bool PhiNode::is_dead_phi() { // and so could cause the region to be optimized out? bool PhiNode::wait_for_region_igvn(PhaseGVN* phase) { PhaseIterGVN* igvn = phase->is_IterGVN(); - Unique_Node_List& worklist = igvn->_worklist; + Unique_Node_List& worklist = igvn->_worklist; bool delay = false; Node* r = in(0); for (uint j = 1; j < req(); j++) { @@ -3066,7 +3065,7 @@ class PushValueTypeDown { } } - Unique_Node_List after; + Unique_Node_List after; after.push(_root_phi); for (uint i = 0; i < after.size(); ++i) { Node* n = after.at(i); @@ -3148,16 +3147,16 @@ class PushValueTypeDown { // Record that vt was created to replace phi to be able to use the value type node when reaching the phi again // through data loops. _clones.map(phi->_idx, vt); - Node_List casts; + Node_List casts; for (uint i = 1; i < phi->req(); ++i) { assert(casts.size() == 0, "must be cleared"); Node* n = phi->in(i); - if (n == nullptr) { + if (n == nullptr || n->is_top()) { continue; } while (should_skip_over(n)) { if (n->is_ConstraintCast()) { - casts.push(n); + casts.push(n->as_ConstraintCast()); } n = n->in(1); } @@ -3175,11 +3174,13 @@ class PushValueTypeDown { assert(n->is_top() || n->is_ValueType(), "Only InlineType or top at this point."); while (casts.size() != 0) { // Push the cast(s) through the ValueTypeNode - Node *cast = casts.pop()->clone(); - cast->set_req_X(1, n->as_ValueType()->get_oop(), _phase); - n = n->clone(); - n->as_ValueType()->set_oop(*_phase, _phase->transform(cast)); - n = _phase->transform(n); + if (!n->is_top()) { + Node* cast = casts.pop()->clone(); + cast->set_req_X(1, n->as_ValueType()->get_oop(), _phase); + n = n->clone(); + n->as_ValueType()->set_oop(*_phase, _phase->transform(cast)); + n = _phase->transform(n); + } if (n->is_top()) { if (casts.size() > 0) { // We could be skipping some unprocessed casts that are also dead. Clear the list for the next phi input. @@ -3204,9 +3205,9 @@ class PushValueTypeDown { PhaseGVN* _phase; bool _can_reshape; ciValueKlass* _value_klass; - Unique_Node_List _nodes_from_phi; - Unique_Node_List _subgraph_to_clone; - Node_List _clones; + Unique_Node_List _nodes_from_phi; + Unique_Node_List _subgraph_to_clone; + Node_List _clones; DEBUG_ONLY(uint _init_nodes); public: @@ -3224,6 +3225,9 @@ class PushValueTypeDown { for (uint next = 0; next < _nodes_from_phi.size(); next++) { Node* n = _nodes_from_phi.at(next); + if (n->is_top()) { + continue; + } if (n->is_Phi()) { assert(n->bottom_type()->make_ptr() != nullptr, "broken graph"); if (n != _root_phi && !_can_reshape) { @@ -3370,15 +3374,15 @@ bool PhiNode::can_be_replaced_by(PhaseGVN* phase, const PhiNode* other) const { (other->_excluded_idx == nullptr || !other->_excluded_idx->contains(phase->C->get_alias_index(at))); } -Node* PhiNode::clone_through_phi(Node* root_phi, const Type* t, uint c, PhaseIterGVN* igvn) { - Node_Stack stack(1); - VectorSet visited; - Node_List node_map; +Node* PhiNode::clone_through_phi(PhiNode* root_phi, const Type* t, uint c, PhaseIterGVN* igvn) { + Node_Stack stack(1); + VectorSet visited; + Node_List node_map; stack.push(root_phi, 1); // ignore control visited.set(root_phi->_idx); - Node* new_phi = new PhiNode(root_phi->in(0), t); + PhiNode* new_phi = new PhiNode(root_phi->in(0), t); node_map.map(root_phi->_idx, new_phi); while (stack.is_nonempty()) { @@ -3394,7 +3398,7 @@ Node* PhiNode::clone_through_phi(Node* root_phi, const Type* t, uint c, PhaseIte Node* new_phi = node_map[n->_idx]; if (!visited.test_set(def->_idx)) { // not visited yet node_map.map(def->_idx, new PhiNode(def->in(0), t)); - stack.push(def, 1); // ignore control + stack.push(def->as_Phi(), 1); // ignore control } Node* new_in = node_map[def->_idx]; new_phi->set_req(idx, new_in); @@ -3415,16 +3419,16 @@ Node* PhiNode::clone_through_phi(Node* root_phi, const Type* t, uint c, PhaseIte return new_phi; } -Node* PhiNode::merge_through_phi(Node* root_phi, PhaseIterGVN* igvn) { - Node_Stack stack(1); - VectorSet visited; +Node* PhiNode::merge_through_phi(PhiNode* root_phi, PhaseIterGVN* igvn) { + Node_Stack stack(1); + VectorSet visited; stack.push(root_phi, 1); // ignore control visited.set(root_phi->_idx); VectorBoxNode* cached_vbox = nullptr; while (stack.is_nonempty()) { - Node* n = stack.node(); + const Node* n = stack.node(); uint idx = stack.index(); if (idx < n->req()) { stack.set_index(idx + 1); @@ -3433,7 +3437,7 @@ Node* PhiNode::merge_through_phi(Node* root_phi, PhaseIterGVN* igvn) { continue; // ignore dead path } else if (in->isa_Phi()) { if (!visited.test_set(in->_idx)) { - stack.push(in, 1); // ignore control + stack.push(in->as_Phi(), 1); // ignore control } } else if (in->Opcode() == Op_VectorBox) { VectorBoxNode* vbox = static_cast(in); @@ -3608,7 +3612,7 @@ void JumpProjNode::dump_spec(outputStream *st) const { void JumpProjNode::dump_compact_spec(outputStream *st) const { ProjNode::dump_compact_spec(st); - st->print("(%d)%d@%d", _switch_val, _proj_no, _dest_bci); + st->print("%d@%d", _con, _dest_bci); } #endif diff --git a/src/hotspot/share/opto/cfgnode.hpp b/src/hotspot/share/opto/cfgnode.hpp index 9ee37d7466d9..0160fbb6a20c 100644 --- a/src/hotspot/share/opto/cfgnode.hpp +++ b/src/hotspot/share/opto/cfgnode.hpp @@ -115,7 +115,7 @@ class RegionNode : public Node { bool is_unreachable_region(const PhaseGVN* phase); #ifdef ASSERT bool is_in_infinite_subgraph(); - static bool are_all_nodes_in_infinite_subgraph(Unique_Node_List& worklist); + static bool are_all_nodes_in_infinite_subgraph(Unique_Node_List& worklist); #endif //ASSERT LoopStatus loop_status() const { return _loop_status; }; void set_loop_status(LoopStatus status); @@ -321,8 +321,8 @@ class PhiNode : public TypeNode { bool wait_for_region_igvn(PhaseGVN* phase); bool is_data_loop(RegionNode* r, Node* uin, const PhaseGVN* phase); - static Node* clone_through_phi(Node* root_phi, const Type* t, uint c, PhaseIterGVN* igvn); - static Node* merge_through_phi(Node* root_phi, PhaseIterGVN* igvn); + static Node* clone_through_phi(PhiNode* root_phi, const Type* t, uint c, PhaseIterGVN* igvn); + static Node* merge_through_phi(PhiNode* root_phi, PhaseIterGVN* igvn); bool must_wait_for_region_in_irreducible_loop(PhaseGVN* phase) const; @@ -810,19 +810,16 @@ class JumpProjNode : public JProjNode { private: const int _dest_bci; - const uint _proj_no; - const int _switch_val; public: - JumpProjNode(Node* jumpnode, uint proj_no, int dest_bci, int switch_val) - : JProjNode(jumpnode, proj_no), _dest_bci(dest_bci), _proj_no(proj_no), _switch_val(switch_val) { + JumpProjNode(Node* jumpnode, uint proj_no, int dest_bci) + : JProjNode(jumpnode, proj_no), _dest_bci(dest_bci) { init_class_id(Class_JumpProj); } virtual int Opcode() const; virtual const Type* bottom_type() const { return Type::CONTROL; } int dest_bci() const { return _dest_bci; } - int switch_val() const { return _switch_val; } - uint proj_no() const { return _proj_no; } + uint proj_no() const { return _con; } #ifndef PRODUCT virtual void dump_spec(outputStream *st) const; virtual void dump_compact_spec(outputStream *st) const; @@ -884,6 +881,7 @@ class CreateExNode : public TypeNode { CreateExNode(const Type* t, Node* control, Node* i_o) : TypeNode(t, 2) { init_req(0, control); init_req(1, i_o); + init_class_id(Class_CreateEx); } virtual int Opcode() const; virtual Node* Identity(PhaseGVN* phase); diff --git a/src/hotspot/share/opto/chaitin.cpp b/src/hotspot/share/opto/chaitin.cpp index c0cb85010f85..e86dc478620c 100644 --- a/src/hotspot/share/opto/chaitin.cpp +++ b/src/hotspot/share/opto/chaitin.cpp @@ -2602,7 +2602,7 @@ void PhaseChaitin::dump_lrg(uint lidx, bool defs_only) const { #ifdef ASSERT // Verify that base pointers and derived pointers are still sane. void PhaseChaitin::verify_base_ptrs(ResourceArea* a) const { - Unique_Node_List worklist(a); + Unique_Node_List worklist(a); for (uint i = 0; i < _cfg.number_of_blocks(); i++) { Block* block = _cfg.get_block(i); for (uint j = block->end_idx() + 1; j > 1; j--) { @@ -2619,7 +2619,7 @@ void PhaseChaitin::verify_base_ptrs(ResourceArea* a) const { if (jvms->oopoff() < sfpt->req()) { // Check each derived/base pair for (uint idx = jvms->oopoff(); idx < sfpt->req(); idx++) { - Node* check = sfpt->in(idx); + const Node* check = sfpt->in(idx); bool is_derived = ((idx - jvms->oopoff()) & 1) == 0; // search upwards through spills and spill phis for AddP worklist.clear(); diff --git a/src/hotspot/share/opto/chaitin.hpp b/src/hotspot/share/opto/chaitin.hpp index 296b4699a8fb..2f09bdedc0e6 100644 --- a/src/hotspot/share/opto/chaitin.hpp +++ b/src/hotspot/share/opto/chaitin.hpp @@ -722,27 +722,27 @@ class PhaseChaitin : public PhaseRegAlloc { Node *skip_copies( Node *c ); // Replace the old node with the current live version of that value // and yank the old value if it's dead. - int replace_and_yank_if_dead( Node *old, OptoReg::Name nreg, - Block *current_block, Node_List& value, Node_List& regnd ) { + int replace_and_yank_if_dead(Node* old, OptoReg::Name nreg, + Block* current_block, Node_List& value, Node_List& regnd) { Node* v = regnd[nreg]; assert(v->outcnt() != 0, "no dead values"); old->replace_by(v); return yank_if_dead(old, current_block, &value, ®nd); } - int yank_if_dead( Node *old, Block *current_block, Node_List *value, Node_List *regnd ) { + int yank_if_dead(Node* old, Block* current_block, Node_List* value, Node_List* regnd) { return yank_if_dead_recurse(old, old, current_block, value, regnd); } - int yank_if_dead_recurse(Node *old, Node *orig_old, Block *current_block, - Node_List *value, Node_List *regnd); - int yank( Node *old, Block *current_block, Node_List *value, Node_List *regnd ); - int elide_copy( Node *n, int k, Block *current_block, Node_List *value, Node_List *regnd, bool can_change_regs ); - int use_prior_register( Node *copy, uint idx, Node *def, Block *current_block, Node_List *value, Node_List *regnd ); + int yank_if_dead_recurse(Node* old, Node* orig_old, Block* current_block, + Node_List* value, Node_List* regnd); + int yank(Node* old, Block* current_block, Node_List* value, Node_List* regnd); + int elide_copy(Node* n, int k, Block* current_block, Node_List* value, Node_List* regnd, bool can_change_regs); + int use_prior_register(Node* copy, uint idx, Node* def, Block* current_block, Node_List* value, Node_List* regnd); bool may_be_copy_of_callee( Node *def ) const; // If nreg already contains the same constant as val then eliminate it bool eliminate_copy_of_constant(Node* val, Node* n, - Block *current_block, Node_List& value, Node_List ®nd, + Block* current_block, Node_List& value, Node_List& regnd, OptoReg::Name nreg, OptoReg::Name nreg2); // Extend the node to LRG mapping void add_reference( const Node *node, const Node *old_node); diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index cae76794fc38..089c9bab680a 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -123,6 +123,7 @@ macro(CompareAndExchangeI) macro(CompareAndExchangeL) macro(CompareAndExchangeP) macro(CompareAndExchangeN) +macro(DeadPath) macro(GetAndAddB) macro(GetAndAddS) macro(GetAndAddI) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 63edde39f72b..03de90df0fd9 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -311,7 +311,7 @@ void Compile::gvn_replace_by(Node* n, Node* nn) { // Identify all nodes that are reachable from below, useful. // Use breadth-first pass that records state in a Unique_Node_List, // recursive traversal is slower. -void Compile::identify_useful_nodes(Unique_Node_List &useful) { +void Compile::identify_useful_nodes(Unique_Node_List& useful) { int estimated_worklist_size = live_nodes(); useful.map( estimated_worklist_size, nullptr ); // preallocate space @@ -320,6 +320,8 @@ void Compile::identify_useful_nodes(Unique_Node_List &useful) { // If 'top' is cached, declare it useful to preserve cached node if (cached_top_node()) { useful.push(cached_top_node()); } + if (dead_path()) { useful.push(dead_path()); } + // Push all useful nodes onto the list, breadthfirst for( uint next = 0; next < useful.size(); ++next ) { assert( next < unique(), "Unique useful nodes < total nodes"); @@ -335,9 +337,9 @@ void Compile::identify_useful_nodes(Unique_Node_List &useful) { // Update dead_node_list with any missing dead nodes using useful // list. Consider all non-useful nodes to be useless i.e., dead nodes. -void Compile::update_dead_node_list(Unique_Node_List &useful) { +void Compile::update_dead_node_list(Unique_Node_List& useful) { uint max_idx = unique(); - VectorSet& useful_node_set = useful.member_set(); + const VectorSet& useful_node_set = useful.member_set(); for (uint node_idx = 0; node_idx < max_idx; node_idx++) { // If node with index node_idx is not in useful set, @@ -348,7 +350,7 @@ void Compile::update_dead_node_list(Unique_Node_List &useful) { } } -void Compile::remove_useless_late_inlines(GrowableArray* inlines, Unique_Node_List &useful) { +void Compile::remove_useless_late_inlines(GrowableArray* inlines, const Unique_Node_List& useful) { int shift = 0; for (int i = 0; i < inlines->length(); i++) { CallGenerator* cg = inlines->at(i); @@ -379,7 +381,7 @@ void Compile::remove_useless_late_inlines(GrowableArray* inlines } template::value)> -void Compile::remove_useless_nodes(GrowableArray& node_list, Unique_Node_List& useful) { +void Compile::remove_useless_nodes(GrowableArray& node_list, const Unique_Node_List& useful) { for (int i = node_list.length() - 1; i >= 0; i--) { N* node = node_list.at(i); if (!useful.member(node)) { @@ -396,7 +398,7 @@ void Compile::remove_useless_node(Node* dead) { // it reachable by adding use edges. So, we will NOT count Con nodes // as dead to be conservative about the dead node count at any // given time. - if (!dead->is_Con()) { + if (!dead->is_Con() && dead != dead_path()) { record_dead_node(dead->_idx); } if (dead->is_macro()) { @@ -440,7 +442,7 @@ void Compile::remove_useless_node(Node* dead) { } // Disconnect all useless nodes by disconnecting those at the boundary. -void Compile::disconnect_useless_nodes(Unique_Node_List& useful, Unique_Node_List& worklist, const Unique_Node_List* root_and_safepoints) { +void Compile::disconnect_useless_nodes(Unique_Node_List& useful, Unique_Node_List& worklist, const Unique_Node_List* root_and_safepoints) { uint next = 0; while (next < useful.size()) { Node *n = useful.at(next++); @@ -695,7 +697,6 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, _strength_reduction(false), _do_cleanup(false), _has_reserved_stack_access(target->has_reserved_stack_access()), - _has_circular_value_type(false), #ifndef PRODUCT _igv_idx(0), _trace_opto_output(directive->TraceOptoOutputOption), @@ -729,6 +730,7 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, _node_arena_one(mtCompiler, Arena::Tag::tag_node), _node_arena_two(mtCompiler, Arena::Tag::tag_node), _node_arena(&_node_arena_one), + _dead_path(nullptr), _mach_constant_base_node(nullptr), _Compile_types(mtCompiler, Arena::Tag::tag_type), _initial_gvn(nullptr), @@ -774,10 +776,6 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, set_print_assembly(false); // must initialize. #endif -#ifndef PRODUCT - set_parsed_irreducible_loop(false); -#endif - if (directive->ReplayInlineOption) { _replay_inline_data = ciReplay::load_inline_data(method(), entry_bci(), ci_env->comp_level()); } @@ -797,6 +795,7 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, } Init(/*do_aliasing=*/ true); + set_dead_path(new DeadPathNode()); print_compile_messages(); @@ -810,7 +809,7 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, // GVN that will be run immediately on new nodes uint estimated_size = method()->code_size()*4+64; estimated_size = (estimated_size < MINIMUM_NODE_HASH ? MINIMUM_NODE_HASH : estimated_size); - _igvn_worklist = new (comp_arena()) Unique_Node_List(comp_arena()); + _igvn_worklist = new (comp_arena()) Unique_Node_List(comp_arena()); _types = new (comp_arena()) Type_Array(comp_arena()); _node_hash = new (comp_arena()) NodeHash(comp_arena(), estimated_size); PhaseGVN gvn; @@ -1011,7 +1010,6 @@ Compile::Compile(ciEnv* ci_env, _inlining_progress(false), _inlining_incrementally(false), _has_reserved_stack_access(false), - _has_circular_value_type(false), #ifndef PRODUCT _igv_idx(0), _trace_opto_output(directive->TraceOptoOutputOption), @@ -1036,6 +1034,7 @@ Compile::Compile(ciEnv* ci_env, _node_arena_one(mtCompiler, Arena::Tag::tag_node), _node_arena_two(mtCompiler, Arena::Tag::tag_node), _node_arena(&_node_arena_one), + _dead_path(nullptr), _mach_constant_base_node(nullptr), _Compile_types(mtCompiler, Arena::Tag::tag_type), _initial_gvn(nullptr), @@ -1074,7 +1073,6 @@ Compile::Compile(ciEnv* ci_env, #ifndef PRODUCT set_print_assembly(PrintFrameConverterAssembly); - set_parsed_irreducible_loop(false); #else set_print_assembly(false); // Must initialize. #endif @@ -1084,7 +1082,7 @@ Compile::Compile(ciEnv* ci_env, Init(/*do_aliasing=*/ false); init_tf((*generator)()); - _igvn_worklist = new (comp_arena()) Unique_Node_List(comp_arena()); + _igvn_worklist = new (comp_arena()) Unique_Node_List(comp_arena()); _types = new (comp_arena()) Type_Array(comp_arena()); _node_hash = new (comp_arena()) NodeHash(comp_arena(), 255); @@ -1285,7 +1283,7 @@ void Compile::set_cached_top_node(Node* tn) { #ifdef ASSERT uint Compile::count_live_nodes_by_graph_walk() { - Unique_Node_List useful(comp_arena()); + Unique_Node_List useful(comp_arena()); // Get useful node list by walking the graph. identify_useful_nodes(useful); return useful.size(); @@ -1304,7 +1302,7 @@ void Compile::print_missing_nodes() { // by walking the flow graph and find the missing ones using // _dead_node_list. - Unique_Node_List useful(comp_arena()); + Unique_Node_List useful(comp_arena()); // Get useful node list by walking the graph. identify_useful_nodes(useful); @@ -1317,7 +1315,7 @@ void Compile::print_missing_nodes() { _log->stamp(); _log->end_head(); } - VectorSet& useful_member_set = useful.member_set(); + const VectorSet& useful_member_set = useful.member_set(); int last_idx = l_nodes_by_walk; for (int i = 0; i < last_idx; i++) { if (useful_member_set.test(i)) { @@ -2027,11 +2025,11 @@ void Compile::remove_value_type(Node* n) { // Does the return value keep otherwise useless value type allocations alive? static bool return_val_keeps_allocations_alive(Node* ret_val) { ResourceMark rm; - Unique_Node_List wq; + Unique_Node_List wq; wq.push(ret_val); bool some_allocations = false; for (uint i = 0; i < wq.size(); i++) { - Node* n = wq.at(i); + const Node* n = wq.at(i); if (n->outcnt() > 1) { // Some other use for the allocation return false; @@ -2052,15 +2050,15 @@ static bool return_val_keeps_allocations_alive(Node* ret_val) { return some_allocations; } -bool Compile::clear_argument_if_only_used_as_buffer_at_calls(Node* result_cast, PhaseIterGVN& igvn) { +bool Compile::clear_argument_if_only_used_as_buffer_at_calls(const Node* result_cast, PhaseIterGVN& igvn) { ResourceMark rm; - Unique_Node_List wq; + Unique_Node_List wq; wq.push(result_cast); - Node_List calls; + Node_List calls; for (uint i = 0; i < wq.size(); ++i) { - Node* n = wq.at(i); + const Node* n = wq.at(i); for (DUIterator_Fast jmax, j = n->fast_outs(jmax); j < jmax; j++) { - Node* u = n->fast_out(j); + const Node* u = n->fast_out(j); if (u->is_Phi()) { wq.push(u); } else if (u->is_ValueType() && u->as_ValueType()->get_oop() == n) { @@ -2099,7 +2097,7 @@ bool Compile::clear_argument_if_only_used_as_buffer_at_calls(Node* result_cast, } } for (uint i = 0; i < calls.size(); ++i) { - CallJavaNode* call = calls.at(i)->as_CallJava(); + CallJavaNode* call = calls.at(i); uint nargs = call->tf()->domain_cc()->cnt(); for (uint k = TypeFunc::Parms; k < nargs; k++) { Node* in = call->in(k); @@ -2116,7 +2114,7 @@ void Compile::process_value_types(PhaseIterGVN &igvn, bool remove) { #ifdef ASSERT { ResourceMark rm; - Unique_Node_List wq; + Unique_Node_List wq; wq.push(C->root()); for (uint i = 0; i < wq.size(); ++i) { Node* n = wq.at(i); @@ -2321,11 +2319,11 @@ void Compile::adjust_flat_array_access_aliases(PhaseIterGVN& igvn) { // one per element field. This should help memory access // optimizations. ResourceMark rm; - Unique_Node_List wq; + Unique_Node_List wq; wq.push(root()); - Node_List mergememnodes; - Node_List memnodes; + Node_List mergememnodes; + Node_List memnodes; // Alias index currently shared by all flat memory accesses int index = get_alias_index(TypeAryPtr::INLINES); @@ -2337,12 +2335,12 @@ void Compile::adjust_flat_array_access_aliases(PhaseIterGVN& igvn) { const TypePtr* adr_type = nullptr; adr_type = get_adr_type(get_alias_index(n->in_adr_type())); if (adr_type == TypeAryPtr::INLINES) { - memnodes.push(n); + memnodes.push(n->as_Mem()); } } else if (n->is_MergeMem()) { MergeMemNode* mm = n->as_MergeMem(); if (mm->memory_at(index) != mm->base_memory()) { - mergememnodes.push(n); + mergememnodes.push(n->as_MergeMem()); } } for (uint j = 0; j < n->req(); j++) { @@ -2369,16 +2367,16 @@ void Compile::adjust_flat_array_access_aliases(PhaseIterGVN& igvn) { int start_alias = num_alias_types(); // Start of new aliases for (uint i = 0; i < memnodes.size(); i++) { - Node* m = memnodes.at(i); + MemNode* m = memnodes.at(i); const TypePtr* adr_type = m->in_adr_type(); #ifdef ASSERT - m->as_Mem()->set_adr_type(adr_type); + m->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); } - Node_Stack stack(0); + Node_Stack stack(0); #ifdef ASSERT VectorSet seen(Thread::current()->resource_area()); #endif @@ -2386,7 +2384,7 @@ void Compile::adjust_flat_array_access_aliases(PhaseIterGVN& igvn) { // is moved to the right slice. Start from the MergeMem nodes. uint last = unique(); for (uint i = 0; i < mergememnodes.size(); i++) { - MergeMemNode* current = mergememnodes.at(i)->as_MergeMem(); + MergeMemNode* current = mergememnodes.at(i); if (current->outcnt() == 0) { // This node is killed by a previous iteration continue; @@ -2678,7 +2676,7 @@ void Compile::record_unstable_if_trap(UnstableIfTrap* trap) { } } -void Compile::remove_useless_unstable_if_traps(Unique_Node_List& useful) { +void Compile::remove_useless_unstable_if_traps(const Unique_Node_List& useful) { for (int i = _unstable_if_traps.length() - 1; i >= 0; i--) { UnstableIfTrap* trap = _unstable_if_traps.at(i); Node* n = trap->uncommon_trap(); @@ -3028,7 +3026,7 @@ void Compile::process_late_inline_calls_no_inline(PhaseIterGVN& igvn) { assert(inlining_incrementally() == false, "not allowed"); set_strength_reduction(true); #ifdef ASSERT - Unique_Node_List* modified_nodes = _modified_nodes; + Unique_Node_List* modified_nodes = _modified_nodes; _modified_nodes = nullptr; #endif assert(_late_inlines.length() > 0, "sanity"); @@ -3121,7 +3119,7 @@ void Compile::Optimize() { // Iterative Global Value Numbering, including ideal transforms PhaseIterGVN igvn; #ifdef ASSERT - _modified_nodes = new (comp_arena()) Unique_Node_List(comp_arena()); + _modified_nodes = new (comp_arena()) Unique_Node_List(comp_arena()); #endif { TracePhase tp(_t_iterGVN); @@ -3470,6 +3468,9 @@ void Compile::Optimize() { } } + // Unique DeadPath node should not be used anymore + _dead_path = nullptr; + print_method(PHASE_OPTIMIZE_FINISHED, 2); DEBUG_ONLY(set_phase_optimize_finished();) } @@ -3477,10 +3478,10 @@ void Compile::Optimize() { #ifdef ASSERT void Compile::check_no_dead_use() const { ResourceMark rm; - Unique_Node_List wq; + Unique_Node_List wq; wq.push(root()); for (uint i = 0; i < wq.size(); ++i) { - Node* n = wq.at(i); + const Node* n = wq.at(i); for (DUIterator_Fast jmax, j = n->fast_outs(jmax); j < jmax; j++) { Node* u = n->fast_out(j); if (u->outcnt() == 0 && !u->is_Con()) { @@ -3569,7 +3570,7 @@ static bool is_vector_bitwise_cone_root(Node* n) { return true; } -static uint collect_unique_inputs(Node* n, Unique_Node_List& inputs) { +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(); @@ -3603,8 +3604,8 @@ static uint collect_unique_inputs(Node* n, Unique_Node_List& inputs) { return cnt; } -void Compile::collect_logic_cone_roots(Unique_Node_List& list) { - Unique_Node_List useful_nodes; +void Compile::collect_logic_cone_roots(Unique_Node_List& list) { + Unique_Node_List useful_nodes; C->identify_useful_nodes(useful_nodes); for (uint i = 0; i < useful_nodes.size(); i++) { @@ -3617,8 +3618,8 @@ void Compile::collect_logic_cone_roots(Unique_Node_List& list) { Node* Compile::xform_to_MacroLogicV(PhaseIterGVN& igvn, const TypeVect* vt, - Unique_Node_List& partition, - Unique_Node_List& inputs) { + Unique_Node_List& partition, + Unique_Node_List& inputs) { assert(partition.size() == 2 || partition.size() == 3, "not supported"); assert(inputs.size() == 2 || inputs.size() == 3, "not supported"); assert(Matcher::match_rule_supported_vector(Op_MacroLogicV, vt->length(), vt->element_basic_type()), "not supported"); @@ -3703,7 +3704,7 @@ static void eval_operands(Node* n, } } -uint Compile::compute_truth_table(Unique_Node_List& partition, Unique_Node_List& inputs) { +uint Compile::compute_truth_table(const Unique_Node_List& partition, const Unique_Node_List& inputs) { assert(inputs.size() <= 3, "sanity"); ResourceMark rm; uint res = 0; @@ -3766,7 +3767,7 @@ uint Compile::compute_truth_table(Unique_Node_List& partition, Unique_Node_List& // 3) Masked parent can be packed with right child if its un-predicated // or has matching predication condition. // 4) An unmasked parent can be packed with an unmasked child. -bool Compile::compute_logic_cone(Node* n, Unique_Node_List& partition, Unique_Node_List& inputs) { +bool Compile::compute_logic_cone(Node* n, Unique_Node_List& partition, Unique_Node_List& inputs) { assert(partition.size() == 0, "not empty"); assert(inputs.size() == 0, "not empty"); if (is_vector_ternary_bitwise_op(n)) { @@ -3856,8 +3857,8 @@ void Compile::process_logic_cone_root(PhaseIterGVN &igvn, Node *n, VectorSet &vi // 2) Bottom up traversal: Merge node[s] with // the parent to form macro logic node. - Unique_Node_List partition; - Unique_Node_List inputs; + Unique_Node_List partition; + Unique_Node_List inputs; if (compute_logic_cone(n, partition, inputs)) { const TypeVect* vt = n->bottom_type()->is_vect(); Node* pn = partition.at(partition.size() - 1); @@ -3874,7 +3875,7 @@ void Compile::process_logic_cone_root(PhaseIterGVN &igvn, Node *n, VectorSet &vi void Compile::optimize_logic_cones(PhaseIterGVN &igvn) { ResourceMark rm; if (Matcher::match_rule_supported(Op_MacroLogicV)) { - Unique_Node_List list; + Unique_Node_List list; collect_logic_cone_roots(list); while (list.size() > 0) { @@ -4027,7 +4028,7 @@ struct Final_Reshape_Counts : public StackObj { int _java_call_count; // count non-inlined 'java' calls int _inner_loop_count; // count loops which need alignment VectorSet _visited; // Visitation flags - Node_List _tests; // Set of IfNodes & PCTableNodes + Node_List _tests; // Set of IfNodes & PCTableNodes Final_Reshape_Counts() : _java_call_count(0), _inner_loop_count(0) { } @@ -4041,7 +4042,7 @@ struct Final_Reshape_Counts : public StackObj { //------------------------------final_graph_reshaping_impl---------------------- // Implement items 1-5 from final_graph_reshaping below. -void Compile::final_graph_reshaping_impl(Node *n, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes) { +void Compile::final_graph_reshaping_impl(Node* n, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes) { if ( n->outcnt() == 0 ) return; // dead node uint nop = n->Opcode(); @@ -4101,7 +4102,7 @@ void Compile::final_graph_reshaping_impl(Node *n, Final_Reshape_Counts& frc, Uni // Collect CFG split points if (n->is_MultiBranch() && !n->is_RangeCheck()) { - frc._tests.push(n); + frc._tests.push(n->as_MultiBranch()); } } @@ -4154,7 +4155,7 @@ void Compile::handle_mulhi_mul_op(Node* n, bool is_unsigned) { n->subsume_by(mul_hi_lo->second_proj(), this); } -void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop, Unique_Node_List& dead_nodes) { +void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop, Unique_Node_List& dead_nodes) { switch( nop ) { case Op_Opaque1: // Remove Opaque Nodes before matching n->subsume_by(n->in(1), this); @@ -4346,7 +4347,7 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f // those. if (n->in(0) != nullptr) { ResourceMark rm; - Unique_Node_List wq; + Unique_Node_List wq; wq.push(n); @@ -4720,7 +4721,7 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f RangeCheckNode* rc = n->as_RangeCheck(); Node* iff = new IfNode(rc->in(0), rc->in(1), rc->_prob, rc->_fcnt); n->subsume_by(iff, this); - frc._tests.push(iff); + frc._tests.push(iff->as_MultiBranch()); break; } case Op_ConvI2L: { @@ -4730,7 +4731,7 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f // address computations. n->as_Type()->set_type(TypeLong::INT); ResourceMark rm; - Unique_Node_List wq; + Unique_Node_List wq; wq.push(n); for (uint next = 0; next < wq.size(); next++) { Node *m = wq.at(next); @@ -4785,6 +4786,21 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f break; } #endif + case Op_DeadPath: { + // The CFG inputs are dead paths. Replace the DeadPath with a Region and insert a Halt node. + assert(n->req() > 1, "why not removed if no input other than itself?"); + RegionNode* r = new RegionNode(n->req()); + for (uint i = 1; i < n->req(); ++i) { + r->set_req(i, n->in(i)); + } + n->disconnect_inputs(this); + Node* frame = start()->proj_out(TypeFunc::FramePtr); + stringStream ss; + ss.print("dead path discovered by data nodes during igvn"); + Node* halt = new HaltNode(r, frame, ss.as_string(comp_arena())); + root()->set_req(root()->find_edge(n), halt); + break; + } default: assert(!n->is_Call(), ""); assert(!n->is_Mem(), ""); @@ -4796,8 +4812,8 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f //------------------------------final_graph_reshaping_walk--------------------- // Replacing Opaque nodes with their input in final_graph_reshaping_impl(), // requires that the walk visits a node's inputs before visiting the node. -void Compile::final_graph_reshaping_walk(Node_Stack& nstack, Node* root, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes) { - Unique_Node_List sfpt; +void Compile::final_graph_reshaping_walk(Node_Stack& nstack, Node* root, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes) { + Unique_Node_List sfpt; frc._visited.set(root->_idx); // first, mark node as visited uint cnt = root->req(); @@ -4813,7 +4829,7 @@ void Compile::final_graph_reshaping_walk(Node_Stack& nstack, Node* root, Final_R // compute worst case interpreter size in case of a deoptimization update_interpreter_frame_size(m->as_SafePoint()->jvms()->interpreter_frame_size()); - sfpt.push(m); + sfpt.push(m->as_SafePoint()); } cnt = m->req(); nstack.push(n, i); // put on stack parent and next input's index @@ -4920,13 +4936,13 @@ bool Compile::final_graph_reshaping() { // Visit everybody reachable! // Allocate stack of size C->live_nodes()/2 to avoid frequent realloc - Node_Stack nstack(live_nodes() >> 1); - Unique_Node_List dead_nodes; + Node_Stack nstack(live_nodes() >> 1); + Unique_Node_List dead_nodes; final_graph_reshaping_walk(nstack, root(), frc, dead_nodes); // Check for unreachable (from below) code (i.e., infinite loops). for( uint i = 0; i < frc._tests.size(); i++ ) { - MultiBranchNode *n = frc._tests[i]->as_MultiBranch(); + MultiBranchNode* n = frc._tests[i]; // Get number of CFG targets. // Note that PCTables include exception targets after calls. uint required_outcnt = n->required_outcnt(); @@ -5188,10 +5204,10 @@ bool Compile::needs_clinit_barrier(ciInstanceKlass* holder, ciMethod* accessing_ //------------------------------verify_bidirectional_edges--------------------- // For each input edge to a node (ie - for each Use-Def edge), verify that // there is a corresponding Def-Use edge. -void Compile::verify_bidirectional_edges(Unique_Node_List& visited, const Unique_Node_List* root_and_safepoints) const { +void Compile::verify_bidirectional_edges(Unique_Node_List& visited, const Unique_Node_List* root_and_safepoints) const { // Allocate stack of size C->live_nodes()/16 to avoid frequent realloc uint stack_size = live_nodes() >> 4; - Node_List nstack(MAX2(stack_size, (uint) OptoNodeListSize)); + Node_List nstack(MAX2(stack_size, (uint) OptoNodeListSize)); if (root_and_safepoints != nullptr) { assert(root_and_safepoints->member(_root), "root is not in root_and_safepoints"); for (uint i = 0, limit = root_and_safepoints->size(); i < limit; i++) { @@ -5209,7 +5225,7 @@ void Compile::verify_bidirectional_edges(Unique_Node_List& visited, const Unique } while (nstack.size() > 0) { - Node* n = nstack.pop(); + const Node* n = nstack.pop(); if (visited.member(n)) { continue; } @@ -5257,18 +5273,18 @@ void Compile::verify_bidirectional_edges(Unique_Node_List& visited, const Unique //------------------------------verify_graph_edges--------------------------- // Walk the Graph and verify that there is a one-to-one correspondence // between Use-Def edges and Def-Use edges in the graph. -void Compile::verify_graph_edges(bool no_dead_code, const Unique_Node_List* root_and_safepoints) const { +void Compile::verify_graph_edges(bool no_dead_code, const Unique_Node_List* root_and_safepoints) const { if (VerifyGraphEdges) { - Unique_Node_List visited; + Unique_Node_List visited; // Call graph walk to check edges verify_bidirectional_edges(visited, root_and_safepoints); if (no_dead_code) { // Now make sure that no visited node is used by an unvisited node. bool dead_nodes = false; - Unique_Node_List checked; + Unique_Node_List checked; while (visited.size() > 0) { - Node* n = visited.pop(); + const Node* n = visited.pop(); checked.push(n); for (uint i = 0; i < n->outcnt(); i++) { Node* use = n->raw_out(i); @@ -5664,6 +5680,23 @@ void Compile::cleanup_expensive_nodes(PhaseIterGVN &igvn) { } } +void Compile::add_macro_node(Node* n) { + assert(!_macro_nodes.contains(n), "duplicate entry in expand list"); + n->add_flag(Node::Flag_is_macro); + _macro_nodes.append(n); +} + +void Compile::remove_macro_node(Node* n) { + // This function may be called twice for a node so we can only remove it + // if it's still existing. + _macro_nodes.remove_if_existing(n); + n->remove_flag(Node::Flag_is_macro); + // Remove from coarsened locks list if present + if (coarsened_count() > 0) { + remove_coarsened_lock(n); + } +} + void Compile::add_expensive_node(Node * n) { assert(!_expensive_nodes.contains(n), "duplicate entry in expensive list"); assert(n->is_expensive(), "expensive nodes with non-null control here only"); @@ -5681,7 +5714,7 @@ void Compile::add_expensive_node(Node * n) { * Track coarsened Lock and Unlock nodes. */ -class Lock_List : public Node_List { +class Lock_List : public Node_List { uint _origin_cnt; public: Lock_List(Arena *a, uint cnt) : Node_List(a), _origin_cnt(cnt) {} @@ -5718,13 +5751,12 @@ void Compile::add_coarsened_locks(GrowableArray& locks) { } } -void Compile::remove_useless_coarsened_locks(Unique_Node_List& useful) { +void Compile::remove_useless_coarsened_locks(const Unique_Node_List& useful) { int count = coarsened_count(); for (int i = 0; i < count; i++) { - Node_List* locks_list = _coarsened_locks.at(i); + Node_List* locks_list = _coarsened_locks.at(i); for (uint j = 0; j < locks_list->size(); j++) { - Node* lock = locks_list->at(j); - assert(lock->is_AbstractLock(), "sanity"); + AbstractLockNode* lock = locks_list->at(j); if (!useful.member(lock)) { locks_list->yank(lock); } @@ -5736,8 +5768,8 @@ void Compile::remove_coarsened_lock(Node* n) { if (n->is_AbstractLock()) { int count = coarsened_count(); for (int i = 0; i < count; i++) { - Node_List* locks_list = _coarsened_locks.at(i); - locks_list->yank(n); + Node_List* locks_list = _coarsened_locks.at(i); + locks_list->yank(n->as_AbstractLock()); } } } @@ -5755,9 +5787,9 @@ bool Compile::coarsened_locks_consistent() { unbalanced = true; // Some locks were removed from list } else { for (uint j = 0; j < size; j++) { - Node* lock = locks_list->at(j); + AbstractLockNode* lock = locks_list->at(j); // All nodes in group should have the same state (modified or not) - if (!lock->as_AbstractLock()->is_coarsened()) { + if (!lock->is_coarsened()) { if (j == 0) { // first on list was modified, the rest should be too for consistency modified = true; @@ -5799,18 +5831,18 @@ bool Compile::coarsened_locks_consistent() { void Compile::mark_unbalanced_boxes() const { int count = coarsened_count(); for (int i = 0; i < count; i++) { - Node_List* locks_list = _coarsened_locks.at(i); + Node_List* locks_list = _coarsened_locks.at(i); uint size = locks_list->size(); if (size > 0) { - AbstractLockNode* alock = locks_list->at(0)->as_AbstractLock(); + AbstractLockNode* alock = locks_list->at(0); BoxLockNode* box = alock->box_node()->as_BoxLock(); if (alock->is_coarsened()) { // coarsened_locks_consistent(), which is called before this method, verifies // that the rest of Lock/Unlock nodes on locks_list are also coarsened. assert(!box->is_eliminated(), "regions with coarsened locks should not be marked as eliminated"); for (uint j = 1; j < size; j++) { - assert(locks_list->at(j)->as_AbstractLock()->is_coarsened(), "only coarsened locks are expected here"); - BoxLockNode* this_box = locks_list->at(j)->as_AbstractLock()->box_node()->as_BoxLock(); + assert(locks_list->at(j)->is_coarsened(), "only coarsened locks are expected here"); + BoxLockNode* this_box = locks_list->at(j)->box_node()->as_BoxLock(); if (box != this_box) { assert(!this_box->is_eliminated(), "regions with coarsened locks should not be marked as eliminated"); box->set_unbalanced(); @@ -5827,7 +5859,7 @@ void Compile::mark_unbalanced_boxes() const { */ void Compile::remove_speculative_types(PhaseIterGVN &igvn) { if (UseTypeSpeculation) { - Unique_Node_List worklist; + Unique_Node_List worklist; worklist.push(root()); int modified = 0; // Go over all type nodes that carry a speculative type, drop the @@ -6211,7 +6243,7 @@ void Compile::record_method_not_compilable_oom() { #ifndef PRODUCT // Collects all the control inputs from nodes on the worklist and from their data dependencies -static void find_candidate_control_inputs(Unique_Node_List& worklist, Unique_Node_List& candidates) { +static void find_candidate_control_inputs(Unique_Node_List& worklist, Unique_Node_List& candidates) { // Follow non-control edges until we reach CFG nodes for (uint i = 0; i < worklist.size(); i++) { const Node* n = worklist.at(i); @@ -6247,8 +6279,8 @@ static void find_candidate_control_inputs(Unique_Node_List& worklist, Unique_Nod } // Returns the candidate node that is a descendant to all the other candidates -static Node* pick_control(Unique_Node_List& candidates) { - Unique_Node_List worklist; +static Node* pick_control(Unique_Node_List& candidates) { + Unique_Node_List worklist; worklist.copy(candidates); // Traverse backwards through the CFG @@ -6295,7 +6327,7 @@ Node* Compile::make_debug_print_call(const char* str, address call_addr, PhaseGV Node* call = new CallLeafNode(type, call_addr, "debug_print", nullptr, nullptr); // find the most suitable control input - Unique_Node_List worklist, candidates; + Unique_Node_List worklist, candidates; if (parm0 != nullptr) { worklist.push(parm0); if (parm1 != nullptr) { worklist.push(parm1); if (parm2 != nullptr) { worklist.push(parm2); diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index d2306909d360..2954114747c4 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -60,6 +60,7 @@ class CallStaticJavaNode; class CloneMap; class CompilationFailureInfo; class ConnectionGraph; +class DeadPathNode; class IdealGraphPrinter; class InlineTree; class Matcher; @@ -69,8 +70,8 @@ class MachNode; class MachOper; class MachSafePointNode; class Node; -class Node_Array; -class Node_List; +template class Node_Array; +template class Node_List; class Node_Notes; class NodeHash; class NodeCloneInfo; @@ -98,11 +99,11 @@ class TypeOopPtr; class TypeFunc; class TypeVect; class Type_Array; -class Unique_Node_List; +template class Unique_Node_List; class UnstableIfTrap; class ValueTypeNode; class nmethod; -class Node_Stack; +template class Node_Stack; struct Final_Reshape_Counts; class VerifyMeetJoinResult; @@ -361,7 +362,6 @@ class Compile : public Phase { bool _has_stringbuilder; // True StringBuffers or StringBuilders are allocated bool _has_boxed_value; // True if a boxed object is allocated bool _has_reserved_stack_access; // True if the method or an inlined method is annotated with ReservedStackAccess - bool _has_circular_value_type; // True if method loads a value type with a circular, non-flat field bool _needs_nm_slot; // True if an extra stack slot is needed to hold the null marker at scalarized returns uint _max_vector_size; // Maximum size of generated vectors bool _clear_upper_avx; // Clear upper bits of ymm registers using vzeroupper @@ -383,7 +383,6 @@ class Compile : public Phase { uint _igv_idx; // Counter for IGV node identifiers uint _igv_phase_iter[PHASE_NUM_TYPES]; // Counters for IGV phase iterations bool _trace_opto_output; - bool _parsed_irreducible_loop; // True if ciTypeFlow detected irreducible loops during parsing #endif bool _has_irreducible_loop; // Found irreducible loops bool _has_monitors; // Metadata transfered to nmethod to enable Continuations lock-detection fastpath @@ -416,7 +415,7 @@ class Compile : public Phase { GrowableArray _flat_access_nodes; // List of LoadFlat and StoreFlat nodes GrowableArray _for_merge_stores_igvn; // List of nodes for IGVN merge stores GrowableArray _unstable_if_traps; // List of ifnodes after IGVN - GrowableArray _coarsened_locks; // List of coarsened Lock and Unlock nodes + GrowableArray*> _coarsened_locks; // List of coarsened Lock and Unlock nodes ConnectionGraph* _congraph; Stress _stress; @@ -432,7 +431,7 @@ class Compile : public Phase { uint _dead_node_count; // Number of dead nodes; VectorSet::Size() is O(N). // So use this to keep count and make the call O(1). VectorSet _dead_node_list; // Set of dead nodes - DEBUG_ONLY(Unique_Node_List* _modified_nodes;) // List of nodes which inputs were modified + DEBUG_ONLY(Unique_Node_List* _modified_nodes;) // List of nodes which inputs were modified DEBUG_ONLY(bool _phase_optimize_finished;) // Used for live node verification while creating new nodes DEBUG_ONLY(bool _phase_verify_ideal_loop;) // Are we in PhaseIdealLoop verification? @@ -454,7 +453,7 @@ class Compile : public Phase { private: RootNode* _root; // Unique root of compilation, or null after bail-out. Node* _top; // Unique top node. (Reset by various phases.) - + DeadPathNode* _dead_path; // Unique DeadPath node Node* _immutable_memory; // Initial memory state Node* _recent_alloc_obj; @@ -494,7 +493,7 @@ class Compile : public Phase { // Shared worklist for all IGVN rounds. Nodes can be pushed to it at any time. // If pushed outside IGVN, the Node is processed in the next IGVN round. - Unique_Node_List* _igvn_worklist; + Unique_Node_List* _igvn_worklist; // Shared type array for GVN, IGVN and CCP. It maps node idx -> Type*. Type_Array* _types; @@ -665,8 +664,6 @@ class Compile : public Phase { void set_has_boxed_value(bool z) { _has_boxed_value = z; } bool has_reserved_stack_access() const { return _has_reserved_stack_access; } void set_has_reserved_stack_access(bool z) { _has_reserved_stack_access = z; } - bool has_circular_value_type() const { return _has_circular_value_type; } - void set_has_circular_value_type(bool z) { _has_circular_value_type = z; } uint max_vector_size() const { return _max_vector_size; } void set_max_vector_size(uint s) { _max_vector_size = s; } bool clear_upper_avx() const { return _clear_upper_avx; } @@ -731,8 +728,6 @@ class Compile : public Phase { void print_phase(const char* phase_name); void print_ideal_ir(const char* compile_phase_name) const; bool should_print_ideal() const { return _directive->PrintIdealOption; } - bool parsed_irreducible_loop() const { return _parsed_irreducible_loop; } - void set_parsed_irreducible_loop(bool z) { _parsed_irreducible_loop = z; } int _in_dump_cnt; // Required for dumping ir nodes. #endif bool has_irreducible_loop() const { return _has_irreducible_loop; } @@ -787,20 +782,8 @@ class Compile : public Phase { ConnectionGraph* congraph() { return _congraph;} void set_congraph(ConnectionGraph* congraph) { _congraph = congraph;} - void add_macro_node(Node * n) { - //assert(n->is_macro(), "must be a macro node"); - assert(!_macro_nodes.contains(n), "duplicate entry in expand list"); - _macro_nodes.append(n); - } - void remove_macro_node(Node* n) { - // this function may be called twice for a node so we can only remove it - // if it's still existing. - _macro_nodes.remove_if_existing(n); - // Remove from coarsened locks list if present - if (coarsened_count() > 0) { - remove_coarsened_lock(n); - } - } + void add_macro_node(Node* n); + void remove_macro_node(Node* n); void add_expensive_node(Node* n); void remove_expensive_node(Node* n) { _expensive_nodes.remove_if_existing(n); @@ -862,7 +845,7 @@ class Compile : public Phase { void add_value_type(Node* n); void remove_value_type(Node* n); - bool clear_argument_if_only_used_as_buffer_at_calls(Node* result_cast, PhaseIterGVN& igvn); + static bool clear_argument_if_only_used_as_buffer_at_calls(const Node* result_cast, PhaseIterGVN& igvn); void process_value_types(PhaseIterGVN &igvn, bool remove = false); @@ -881,7 +864,7 @@ class Compile : public Phase { void record_unstable_if_trap(UnstableIfTrap* trap); bool remove_unstable_if_trap(CallStaticJavaNode* unc, bool yield); - void remove_useless_unstable_if_traps(Unique_Node_List &useful); + void remove_useless_unstable_if_traps(const Unique_Node_List& useful); void process_for_unstable_if_traps(PhaseIterGVN& igvn); bool merge_stores_phase() { return _merge_stores_phase; } @@ -977,6 +960,12 @@ class Compile : public Phase { Arena* old_arena() { return (&_node_arena_one == _node_arena) ? &_node_arena_two : &_node_arena_one; } RootNode* root() const { return _root; } void set_root(RootNode* r) { _root = r; } + DeadPathNode* dead_path() const { return _dead_path; } + + void set_dead_path(DeadPathNode* dead_path) { + assert(_dead_path == nullptr, "can only set once"); + _dead_path = dead_path; + } StartNode* start() const; // (Derived from root.) void verify_start(StartNode* s) const NOT_DEBUG_RETURN; Node* immutable_memory(); @@ -1008,7 +997,7 @@ class Compile : public Phase { // Record modified nodes to check that they are put on IGVN worklist void record_modified_node(Node* n) NOT_DEBUG_RETURN; void remove_modified_node(Node* n) NOT_DEBUG_RETURN; - DEBUG_ONLY( Unique_Node_List* modified_nodes() const { return _modified_nodes; } ) + DEBUG_ONLY( Unique_Node_List* modified_nodes() const { return _modified_nodes; } ) MachConstantBaseNode* mach_constant_base_node(); bool has_mach_constant_base_node() const { return _mach_constant_base_node != nullptr; } @@ -1126,7 +1115,7 @@ class Compile : public Phase { // Parsing, optimization PhaseGVN* initial_gvn() { return _initial_gvn; } - Unique_Node_List* igvn_worklist() { + Unique_Node_List* igvn_worklist() { assert(_igvn_worklist != nullptr, "must be created in Compile::Compile"); return _igvn_worklist; } @@ -1147,9 +1136,9 @@ class Compile : public Phase { void gvn_replace_by(Node* n, Node* nn); - void identify_useful_nodes(Unique_Node_List &useful); - void update_dead_node_list(Unique_Node_List &useful); - void disconnect_useless_nodes(Unique_Node_List& useful, Unique_Node_List& worklist, const Unique_Node_List* root_and_safepoints = nullptr); + void identify_useful_nodes(Unique_Node_List& useful); + void update_dead_node_list(Unique_Node_List& useful); + void disconnect_useless_nodes(Unique_Node_List& useful, Unique_Node_List& worklist, const Unique_Node_List* root_and_safepoints = nullptr); void remove_useless_node(Node* dead); @@ -1183,12 +1172,12 @@ class Compile : public Phase { } template::value)> - void remove_useless_nodes(GrowableArray& node_list, Unique_Node_List& useful); + void remove_useless_nodes(GrowableArray& node_list, const Unique_Node_List& useful); - void remove_useless_late_inlines(GrowableArray* inlines, Unique_Node_List &useful); + void remove_useless_late_inlines(GrowableArray* inlines, const Unique_Node_List& useful); void remove_useless_late_inlines(GrowableArray* inlines, Node* dead); - void remove_useless_coarsened_locks(Unique_Node_List& useful); + void remove_useless_coarsened_locks(const Unique_Node_List& useful); void dump_print_inlining(); @@ -1333,20 +1322,20 @@ class Compile : public Phase { #endif // Function calls made by the public function final_graph_reshaping. // No need to be made public as they are not called elsewhere. - void final_graph_reshaping_impl(Node *n, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes); - void final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop, Unique_Node_List& dead_nodes); - void final_graph_reshaping_walk(Node_Stack& nstack, Node* root, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes); + void final_graph_reshaping_impl(Node* n, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes); + void final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop, Unique_Node_List& dead_nodes); + void final_graph_reshaping_walk(Node_Stack& nstack, Node* root, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes); void handle_div_mod_op(Node* n, BasicType bt, bool is_unsigned); void handle_mulhi_mul_op(Node* n, bool is_unsigned); // Logic cone optimization. void optimize_logic_cones(PhaseIterGVN &igvn); - void collect_logic_cone_roots(Unique_Node_List& list); + void collect_logic_cone_roots(Unique_Node_List& list); void process_logic_cone_root(PhaseIterGVN &igvn, Node* n, VectorSet& visited); - bool compute_logic_cone(Node* n, Unique_Node_List& partition, Unique_Node_List& inputs); - uint compute_truth_table(Unique_Node_List& partition, Unique_Node_List& inputs); + bool compute_logic_cone(Node* n, Unique_Node_List& partition, Unique_Node_List& inputs); + uint compute_truth_table(const Unique_Node_List& partition, const Unique_Node_List& inputs); uint eval_macro_logic_op(uint func, uint op1, uint op2, uint op3); - Node* xform_to_MacroLogicV(PhaseIterGVN &igvn, const TypeVect* vt, Unique_Node_List& partitions, Unique_Node_List& inputs); + Node* xform_to_MacroLogicV(PhaseIterGVN& igvn, const TypeVect* vt, Unique_Node_List& partitions, Unique_Node_List& inputs); void check_no_dead_use() const NOT_DEBUG_RETURN; public: @@ -1383,10 +1372,10 @@ class Compile : public Phase { // reachable from Root's input make this check unsound (can miss inconsistencies) // and even incomplete (can make up non-existing problems) if no_dead_code is // true. - void verify_graph_edges(bool no_dead_code = false, const Unique_Node_List* root_and_safepoints = nullptr) const PRODUCT_RETURN; + void verify_graph_edges(bool no_dead_code = false, const Unique_Node_List* root_and_safepoints = nullptr) const PRODUCT_RETURN; // Verify bi-directional correspondence of edges - void verify_bidirectional_edges(Unique_Node_List& visited, const Unique_Node_List* root_and_safepoints = nullptr) const; + void verify_bidirectional_edges(Unique_Node_List& visited, const Unique_Node_List* root_and_safepoints = nullptr) const; // End-of-run dumps. static void print_statistics() PRODUCT_RETURN; @@ -1398,7 +1387,7 @@ class Compile : public Phase { static void pd_compiler2_init(); // Materialize reachability fences from reachability edges on safepoints. - void expand_reachability_edges(Unique_Node_List& safepoints); + void expand_reachability_edges(Unique_Node_List& safepoints); // Static parse-time type checking logic for gen_subtype_check: enum SubTypeCheckResult { SSC_always_false, SSC_always_true, SSC_easy_test, SSC_full_test }; diff --git a/src/hotspot/share/opto/convertnode.cpp b/src/hotspot/share/opto/convertnode.cpp index e21479ae004c..9c8e394d500b 100644 --- a/src/hotspot/share/opto/convertnode.cpp +++ b/src/hotspot/share/opto/convertnode.cpp @@ -761,13 +761,6 @@ bool Compile::push_thru_add(PhaseGVN* phase, Node* z, const TypeInteger* tz, con //------------------------------Ideal------------------------------------------ Node* ConvI2LNode::Ideal(PhaseGVN* phase, bool can_reshape) { - if (in(1) != nullptr && phase->type(in(1)) != Type::TOP) { - Node* progress = TypeNode::Ideal(phase, can_reshape); - if (progress != nullptr) { - return progress; - } - } - const TypeLong* this_type = this->type()->is_long(); if (can_reshape && !phase->C->post_loop_opts_phase()) { // makes sure we run ::Value to potentially remove type assertion after loop opts @@ -870,13 +863,6 @@ const Type* ConvL2INode::Value(PhaseGVN* phase) const { // Return a node which is more "ideal" than the current node. // Blow off prior masking to int Node* ConvL2INode::Ideal(PhaseGVN* phase, bool can_reshape) { - if (in(1) != nullptr && phase->type(in(1)) != Type::TOP) { - Node* progress = TypeNode::Ideal(phase, can_reshape); - if (progress != nullptr) { - return progress; - } - } - Node *andl = in(1); uint andl_op = andl->Opcode(); if( andl_op == Op_AndL ) { diff --git a/src/hotspot/share/opto/divnode.cpp b/src/hotspot/share/opto/divnode.cpp index 7b8a071d39a9..1c2a4c97eb67 100644 --- a/src/hotspot/share/opto/divnode.cpp +++ b/src/hotspot/share/opto/divnode.cpp @@ -43,7 +43,6 @@ #include ModFloatingNode::ModFloatingNode(Compile* C, const TypeFunc* tf, address addr, const char* name) : CallLeafPureNode(tf, addr, name) { - add_flag(Flag_is_macro); C->add_macro_node(this); } @@ -1031,6 +1030,10 @@ const Type* UDivINode::Value(PhaseGVN* phase) const { if( t1 == Type::TOP ) return Type::TOP; if( t2 == Type::TOP ) return Type::TOP; + if (t2 == TypeInt::ZERO) { + return Type::TOP; + } + // x/x == 1 since we always generate the dynamic divisor check for 0. if (in(1) == in(2)) { return TypeInt::ONE; @@ -1067,6 +1070,10 @@ const Type* UDivLNode::Value(PhaseGVN* phase) const { if( t1 == Type::TOP ) return Type::TOP; if( t2 == Type::TOP ) return Type::TOP; + if (t2 == TypeLong::ZERO) { + return Type::TOP; + } + // x/x == 1 since we always generate the dynamic divisor check for 0. if (in(1) == in(2)) { return TypeLong::ONE; @@ -1380,6 +1387,9 @@ Node* UModINode::Ideal(PhaseGVN* phase, bool can_reshape) { } const Type* UModINode::Value(PhaseGVN* phase) const { + if (phase->type(in(2)) == TypeInt::ZERO) { + return Type::TOP; + } return unsigned_mod_value(phase, this); } @@ -1520,6 +1530,9 @@ Node *UModLNode::Ideal(PhaseGVN *phase, bool can_reshape) { } const Type* UModLNode::Value(PhaseGVN* phase) const { + if (phase->type(in(2)) == TypeLong::ZERO) { + return Type::TOP; + } return unsigned_mod_value(phase, this); } diff --git a/src/hotspot/share/opto/divnode.hpp b/src/hotspot/share/opto/divnode.hpp index 1a68e156a0d2..7f66f4715e9c 100644 --- a/src/hotspot/share/opto/divnode.hpp +++ b/src/hotspot/share/opto/divnode.hpp @@ -40,7 +40,9 @@ class DivModIntegerNode : public Node { bool _pinned; protected: - DivModIntegerNode(Node* c, Node* dividend, Node* divisor) : Node(c, dividend, divisor), _pinned(false) {} + DivModIntegerNode(Node* c, Node* dividend, Node* divisor) : Node(c, dividend, divisor), _pinned(false) { + init_class_id(Class_DivModInteger); + } private: virtual uint size_of() const override { return sizeof(DivModIntegerNode); } @@ -52,6 +54,15 @@ class DivModIntegerNode : public Node { res->_pinned = true; return res; } + +public: + const TypeInteger* zero() const { + if (bottom_type() == TypeInt::INT) { + return TypeInt::ZERO; + } + assert(bottom_type() == TypeLong::LONG, "should be int or long"); + return TypeLong::ZERO; + } }; //------------------------------DivINode--------------------------------------- diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 89729574b0e9..9adb376466d5 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -139,8 +139,8 @@ bool ConnectionGraph::compute_escape() { PhaseGVN* igvn = _igvn; // Worklists used by EA. - Unique_Node_List delayed_worklist; - Unique_Node_List reducible_merges; + Unique_Node_List delayed_worklist; + Unique_Node_List reducible_merges; GrowableArray alloc_worklist; GrowableArray ptr_cmp_worklist; GrowableArray storestore_worklist; @@ -399,12 +399,12 @@ bool ConnectionGraph::compute_escape() { if (VerifyReduceAllocationMerges) { for (uint i = 0; i < reducible_merges.size(); i++ ) { - Node* n = reducible_merges.at(i); - if (n->outcnt() > 0 && !can_reduce_phi(n->as_Phi())) { + PhiNode* n = reducible_merges.at(i); + if (n->outcnt() > 0 && !can_reduce_phi(n)) { TraceReduceAllocationMerges = true; n->dump(2); n->dump(-2); - assert(can_reduce_phi(n->as_Phi()), "Sanity: previous reducible Phi is no longer reducible before SUT."); + assert(can_reduce_phi(n), "Sanity: previous reducible Phi is no longer reducible before SUT."); } } } @@ -455,9 +455,9 @@ bool ConnectionGraph::compute_escape() { bool delay = _igvn->delay_transform(); _igvn->set_delay_transform(true); for (uint i = 0; i < reducible_merges.size(); i++) { - Node* n = reducible_merges.at(i); + PhiNode* n = reducible_merges.at(i); if (n->outcnt() > 0) { - if (!reduce_phi_on_safepoints(n->as_Phi())) { + if (!reduce_phi_on_safepoints(n)) { NOT_PRODUCT(escape_state_statistics(java_objects_worklist);) C->record_failure(C2Compiler::retry_no_reduce_allocation_merges()); return false; @@ -465,7 +465,7 @@ bool ConnectionGraph::compute_escape() { // Now we set the scalar replaceable inputs of ophi to null, which is // the last piece that would prevent it from being scalar replaceable. - reset_scalar_replaceable_entries(n->as_Phi()); + reset_scalar_replaceable_entries(n); } } _igvn->set_delay_transform(delay); @@ -1227,7 +1227,7 @@ PhiNode* ConnectionGraph::create_selector(PhiNode* ophi) const { // Returns true if the AddP node 'n' has at least one base that is a reducible // merge. If the base is a CastPP/CheckCastPP then the input of the cast is // checked instead. -bool ConnectionGraph::has_reducible_merge_base(AddPNode* n, Unique_Node_List &reducible_merges) { +bool ConnectionGraph::has_reducible_merge_base(AddPNode* n, Unique_Node_List& reducible_merges) const { PointsToNode* ptn = ptnode_adr(n->_idx); if (ptn == nullptr || !ptn->is_Field() || ptn->as_Field()->base_count() < 2) { return false; @@ -1236,13 +1236,13 @@ bool ConnectionGraph::has_reducible_merge_base(AddPNode* n, Unique_Node_List &re for (BaseIterator i(ptn->as_Field()); i.has_next(); i.next()) { Node* base = i.get()->ideal_node(); - if (reducible_merges.member(base)) { + if (base->is_Phi() && reducible_merges.member(base->as_Phi())) { return true; } if (base->is_CastPP() || base->is_CheckCastPP()) { base = base->in(1); - if (reducible_merges.member(base)) { + if (base->is_Phi() && reducible_merges.member(base->as_Phi())) { return true; } } @@ -1264,17 +1264,17 @@ bool ConnectionGraph::has_reducible_merge_base(AddPNode* n, Unique_Node_List &re // reference is a regular reference. bool ConnectionGraph::reduce_phi_on_safepoints(PhiNode* ophi) { PhiNode* selector = create_selector(ophi); - Unique_Node_List safepoints; - Unique_Node_List casts; + Unique_Node_List safepoints; + Unique_Node_List casts; // Just collect the users of the Phis for later processing // in the needed order. for (uint i = 0; i < ophi->outcnt(); i++) { Node* use = ophi->raw_out(i); if (use->is_SafePoint()) { - safepoints.push(use); + safepoints.push(use->as_SafePoint()); } else if (use->is_CastPP()) { - casts.push(use); + casts.push(use->as_CastPP()); } else { assert(use->outcnt() == 0, "Only CastPP & SafePoint users should be left."); } @@ -1287,13 +1287,13 @@ bool ConnectionGraph::reduce_phi_on_safepoints(PhiNode* ophi) { // Now process CastPP->safepoints for (uint i = 0; i < casts.size(); i++) { - Node* cast = casts.at(i); - Unique_Node_List cast_sfpts; + CastPPNode* cast = casts.at(i); + Unique_Node_List cast_sfpts; for (DUIterator_Fast jmax, j = cast->fast_outs(jmax); j < jmax; j++) { Node* use_use = cast->fast_out(j); if (use_use->is_SafePoint()) { - cast_sfpts.push(use_use); + cast_sfpts.push(use_use->as_SafePoint()); } else { assert(use_use->outcnt() == 0, "Only SafePoint users should be left."); } @@ -1312,7 +1312,7 @@ bool ConnectionGraph::reduce_phi_on_safepoints(PhiNode* ophi) { // SafePointScalarObjectNode for each scalar replaceable input. Each // SafePointScalarMergeNode may describe multiple scalar replaced objects - // check detailed description in SafePointScalarMergeNode class header. -bool ConnectionGraph::reduce_phi_on_safepoints_helper(Node* ophi, Node* cast, Node* selector, Unique_Node_List& safepoints) { +bool ConnectionGraph::reduce_phi_on_safepoints_helper(Node* ophi, Node* cast, Node* selector, const Unique_Node_List& safepoints) const { PhaseMacroExpand mexp(*_igvn); Node* original_sfpt_parent = cast != nullptr ? cast : ophi; const TypeOopPtr* merge_t = _igvn->type(original_sfpt_parent)->make_oopptr(); @@ -1324,7 +1324,7 @@ bool ConnectionGraph::reduce_phi_on_safepoints_helper(Node* ophi, Node* cast, No } for (uint spi = 0; spi < safepoints.size(); spi++) { - SafePointNode* sfpt = safepoints.at(spi)->as_SafePoint(); + SafePointNode* sfpt = safepoints.at(spi); SafePointNode::NodeEdgeTempStorage non_debug_edges_worklist(*_igvn); @@ -1364,10 +1364,10 @@ bool ConnectionGraph::reduce_phi_on_safepoints_helper(Node* ophi, Node* cast, No } AllocateNode* alloc = ptn->ideal_node()->as_Allocate(); - Unique_Node_List value_worklist; + Unique_Node_List value_worklist; #ifdef ASSERT const Type* res_type = alloc->result_cast()->bottom_type(); - if (res_type->is_valueklassptr() && !Compile::current()->has_circular_value_type()) { + if (res_type->is_valueklassptr()) { assert(!ophi->as_Phi()->can_push_value_types_down(_igvn), "missed earlier scalarization opportunity"); } #endif @@ -1392,7 +1392,7 @@ bool ConnectionGraph::reduce_phi_on_safepoints_helper(Node* ophi, Node* cast, No // because Deoptimization::reassign_flat_array_elements needs field values. const bool allow_oop = !merge_t->is_flat(); for (uint j = 0; j < value_worklist.size(); ++j) { - ValueTypeNode* vt = value_worklist.at(j)->as_ValueType(); + ValueTypeNode* vt = value_worklist.at(j); if (!vt->make_scalar_in_safepoints(_igvn, allow_oop)) { sfpt->restore_non_debug_edges(non_debug_edges_worklist); return false; @@ -1425,13 +1425,13 @@ void ConnectionGraph::reduce_phi(PhiNode* ophi, GrowableArray &alloc_work // Copying all users first because some will be removed and others won't. // Ophi also may acquire some new users as part of Cast reduction. // CastPPs also need to be processed before CmpPs. - Unique_Node_List castpps; - Unique_Node_List others; + Unique_Node_List castpps; + Unique_Node_List others; for (DUIterator_Fast imax, i = ophi->fast_outs(imax); i < imax; i++) { Node* use = ophi->fast_out(i); if (use->is_CastPP()) { - castpps.push(use); + castpps.push(use->as_CastPP()); } else if (use->is_AddP() || use->is_Cmp()) { others.push(use); } else { @@ -1446,7 +1446,7 @@ void ConnectionGraph::reduce_phi(PhiNode* ophi, GrowableArray &alloc_work // splitting CastPPs we make reference to the inputs of the Cmp that is used // by the If controlling the CastPP. for (uint i = 0; i < castpps.size(); i++) { - reduce_phi_on_castpp_field_load(castpps.at(i)->as_CastPP(), alloc_worklist); + reduce_phi_on_castpp_field_load(castpps.at(i), alloc_worklist); _compile->print_method(PHASE_EA_AFTER_PHI_CASTPP_REDUCTION, 6, castpps.at(i)); } @@ -1515,12 +1515,12 @@ void ConnectionGraph::reset_scalar_replaceable_entries(PhiNode* ophi) { void ConnectionGraph::verify_ram_nodes(Compile* C, Node* root) { if (!C->do_reduce_allocation_merges()) return; - Unique_Node_List ideal_nodes; + Unique_Node_List ideal_nodes; ideal_nodes.map(C->live_nodes(), nullptr); // preallocate space ideal_nodes.push(root); for (uint next = 0; next < ideal_nodes.size(); ++next) { - Node* n = ideal_nodes.at(next); + const Node* n = ideal_nodes.at(next); if (n->is_SafePointScalarMerge()) { SafePointScalarMergeNode* merge = n->as_SafePointScalarMerge(); @@ -1619,7 +1619,7 @@ bool ConnectionGraph::has_arg_escape(CallJavaNode* call) { // Utility function for nodes that load an object -void ConnectionGraph::add_objload_to_connection_graph(Node *n, Unique_Node_List *delayed_worklist) { +void ConnectionGraph::add_objload_to_connection_graph(Node* n, Unique_Node_List* delayed_worklist) { // Using isa_ptr() instead of isa_oopptr() for LoadP and Phi because // ThreadLocal has RawPtr type. const Type* t = _igvn->type(n); @@ -1638,7 +1638,7 @@ void ConnectionGraph::add_objload_to_connection_graph(Node *n, Unique_Node_List } } -void ConnectionGraph::add_proj(Node* n, Unique_Node_List* delayed_worklist) { +void ConnectionGraph::add_proj(Node* n, Unique_Node_List* delayed_worklist) { if (n->as_Proj()->_con == TypeFunc::Parms && n->in(0)->is_Call() && n->in(0)->as_Call()->returns_pointer()) { add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(0), delayed_worklist); } else if (n->in(0)->is_LoadFlat()) { @@ -1661,7 +1661,7 @@ void ConnectionGraph::add_proj(Node* n, Unique_Node_List* delayed_worklist) { // Populate Connection Graph with PointsTo nodes and create simple // connection graph edges. -void ConnectionGraph::add_node_to_connection_graph(Node *n, Unique_Node_List *delayed_worklist) { +void ConnectionGraph::add_node_to_connection_graph(Node* n, Unique_Node_List* delayed_worklist) { assert(!_verify, "this method should not be called for verification"); PhaseGVN* igvn = _igvn; uint n_idx = n->_idx; @@ -2073,7 +2073,7 @@ void ConnectionGraph::add_final_edges(Node *n) { return; } -void ConnectionGraph::add_to_congraph_unsafe_access(Node* n, uint opcode, Unique_Node_List* delayed_worklist) { +void ConnectionGraph::add_to_congraph_unsafe_access(Node* n, uint opcode, Unique_Node_List* delayed_worklist) { Node* adr = n->in(MemNode::Address); const Type* adr_type = _igvn->type(adr); adr_type = adr_type->make_ptr(); @@ -2598,7 +2598,6 @@ void ConnectionGraph::process_call_arguments(CallNode *call) { strcmp(call->as_CallLeaf()->_name, "stringIndexOf") == 0 || strcmp(call->as_CallLeaf()->_name, "arraysort_stub") == 0 || strcmp(call->as_CallLeaf()->_name, "array_partition_stub") == 0 || - strcmp(call->as_CallLeaf()->_name, "get_class_id_intrinsic") == 0 || strcmp(call->as_CallLeaf()->_name, "unsafe_setmemory") == 0) ))) { call->dump(); @@ -3329,14 +3328,14 @@ int ConnectionGraph::find_init_values_null(JavaObjectNode* pta, PhaseValues* pha } // Adjust scalar_replaceable state after Connection Graph is built. -void ConnectionGraph::adjust_scalar_replaceable_state(JavaObjectNode* jobj, Unique_Node_List &reducible_merges) { +void ConnectionGraph::adjust_scalar_replaceable_state(JavaObjectNode* jobj, Unique_Node_List& reducible_merges) { // A Phi 'x' is a _candidate_ to be reducible if 'can_reduce_phi(x)' // returns true. If one of the constraints in this method set 'jobj' to NSR // then the candidate Phi is discarded. If the Phi has another SR 'jobj' as // input, 'adjust_scalar_replaceable_state' will eventually be called with // that other object and the Phi will become a reducible Phi. // There could be multiple merges involving the same jobj. - Unique_Node_List candidates; + Unique_Node_List candidates; // Search for non-escaping objects which are not scalar replaceable // and mark them to propagate the state to referenced objects. @@ -3386,12 +3385,12 @@ void ConnectionGraph::adjust_scalar_replaceable_state(JavaObjectNode* jobj, Uniq } // If it's already a candidate or confirmed reducible merge we can skip verification - if (candidates.member(use_n) || reducible_merges.member(use_n)) { + if (use_n->is_Phi() && (candidates.member(use_n->as_Phi()) || reducible_merges.member(use_n->as_Phi()))) { continue; } if (use_n->is_Phi() && can_reduce_phi(use_n->as_Phi())) { - candidates.push(use_n); + candidates.push(use_n->as_Phi()); } else { // Mark all objects as NSR if we can't remove the merge set_not_scalar_replaceable(jobj NOT_PRODUCT(COMMA trace_merged_message(ptn))); @@ -3485,22 +3484,22 @@ void ConnectionGraph::adjust_scalar_replaceable_state(JavaObjectNode* jobj, Uniq // same jobj. assert(jobj->scalar_replaceable(), "sanity"); for (uint i = 0; i < candidates.size(); i++ ) { - Node* candidate = candidates.at(i); + PhiNode* candidate = candidates.at(i); reducible_merges.push(candidate); } } -bool ConnectionGraph::has_non_reducible_merge(FieldNode* field, Unique_Node_List& reducible_merges) { +bool ConnectionGraph::has_non_reducible_merge(const FieldNode* field, const Unique_Node_List& reducible_merges) { for (BaseIterator i(field); i.has_next(); i.next()) { Node* base = i.get()->ideal_node(); - if (base->is_Phi() && !reducible_merges.member(base)) { + if (base->is_Phi() && !reducible_merges.member(base->as_Phi())) { return true; } } return false; } -void ConnectionGraph::revisit_reducible_phi_status(JavaObjectNode* jobj, Unique_Node_List& reducible_merges) { +void ConnectionGraph::revisit_reducible_phi_status(JavaObjectNode* jobj, Unique_Node_List& reducible_merges) const { assert(jobj != nullptr && !jobj->scalar_replaceable(), "jobj should be set as NSR before calling this function."); // Look for 'phis' that refer to 'jobj' as the last @@ -3536,7 +3535,7 @@ void ConnectionGraph::revisit_reducible_phi_status(JavaObjectNode* jobj, Unique_ } // Propagate NSR (Not scalar replaceable) state. -void ConnectionGraph::find_scalar_replaceable_allocs(GrowableArray& jobj_worklist, Unique_Node_List &reducible_merges) { +void ConnectionGraph::find_scalar_replaceable_allocs(GrowableArray& jobj_worklist, Unique_Node_List& reducible_merges) const { int jobj_length = jobj_worklist.length(); bool found_nsr_alloc = true; while (found_nsr_alloc) { @@ -3565,9 +3564,9 @@ void ConnectionGraph::find_scalar_replaceable_allocs(GrowableArrayis_LocalVar()) { Node* phi = use->ideal_node(); - if (phi->Opcode() == Op_Phi && reducible_merges.member(phi) && !can_reduce_phi(phi->as_Phi())) { + if (phi->Opcode() == Op_Phi && reducible_merges.member(phi->as_Phi()) && !can_reduce_phi(phi->as_Phi())) { set_not_scalar_replaceable(jobj NOT_PRODUCT(COMMA "is merged in a non-reducible phi")); - reducible_merges.yank(phi); + reducible_merges.yank(phi->as_Phi()); found_nsr_alloc = true; break; } @@ -4433,7 +4432,7 @@ bool ConnectionGraph::split_AddP(Node *addp, Node *base) { // created phi or an existing phi. Sets create_new to indicate whether a new // phi was created. Cache the last newly created phi in the node map. // -PhiNode* ConnectionGraph::create_split_phi(PhiNode* orig_phi, int alias_idx, Unique_Node_List& orig_phi_worklist, bool& new_created) { +PhiNode* ConnectionGraph::create_split_phi(PhiNode* orig_phi, int alias_idx, Unique_Node_List& orig_phi_worklist, bool& new_created) { Compile *C = _compile; PhaseGVN* igvn = _igvn; new_created = false; @@ -4484,7 +4483,7 @@ PhiNode* ConnectionGraph::create_split_phi(PhiNode* orig_phi, int alias_idx, Uni // Return a new version of Memory Phi "orig_phi" with the inputs having the // specified alias index. // -PhiNode* ConnectionGraph::split_memory_phi(PhiNode* orig_phi, int alias_idx, Unique_Node_List& orig_phi_worklist, uint rec_depth) { +PhiNode* ConnectionGraph::split_memory_phi(PhiNode* orig_phi, int alias_idx, Unique_Node_List& orig_phi_worklist, uint rec_depth) { assert(alias_idx != Compile::AliasIdxBot, "can't split out bottom memory"); Compile *C = _compile; PhaseGVN* igvn = _igvn; @@ -4493,7 +4492,7 @@ PhiNode* ConnectionGraph::split_memory_phi(PhiNode* orig_phi, int alias_idx, Uni if (!new_phi_created) { return result; } - Unique_Node_List phi_list; + Unique_Node_List phi_list; GrowableArray cur_input; PhiNode *phi = orig_phi; uint idx = 1; @@ -4535,7 +4534,7 @@ PhiNode* ConnectionGraph::split_memory_phi(PhiNode* orig_phi, int alias_idx, Uni // we have finished processing a Phi, see if there are any more to do finished = (phi_list.size() == 0); if (!finished) { - phi = phi_list.pop()->as_Phi(); + phi = phi_list.pop(); idx = cur_input.pop(); PhiNode *prev_result = get_map_phi(phi->_idx); prev_result->set_req(idx++, result); @@ -4566,7 +4565,7 @@ Node* ConnectionGraph::step_through_mergemem(MergeMemNode *mmem, int alias_idx, // // Move memory users to their memory slices. // -void ConnectionGraph::move_inst_mem(Node* n, Unique_Node_List& orig_phis) { +void ConnectionGraph::move_inst_mem(Node* n, Unique_Node_List& orig_phis) { Compile* C = _compile; PhaseGVN* igvn = _igvn; const TypePtr* tp = igvn->type(n->in(MemNode::Address))->isa_ptr(); @@ -4686,7 +4685,7 @@ bool ConnectionGraph::flat_access_aliases_with(Node* flat_access, const TypeOopP return false; } -Node* ConnectionGraph::find_inst_mem(Node* orig_mem, int alias_idx, Unique_Node_List& orig_phis, uint rec_depth) { +Node* ConnectionGraph::find_inst_mem(Node* orig_mem, int alias_idx, Unique_Node_List& orig_phis, uint rec_depth) { if (rec_depth > FIND_INST_MEM_RECURSION_DEPTH_LIMIT) { _compile->record_failure(_invocation > 0 ? C2Compiler::retry_no_iterative_escape_analysis() : C2Compiler::retry_no_escape_analysis()); return nullptr; @@ -4797,7 +4796,7 @@ Node* ConnectionGraph::find_inst_mem(Node* orig_mem, int alias_idx, Unique_Node_ C->get_alias_index(result->as_Phi()->out_adr_type()) != alias_idx) { Node *un = result->as_Phi()->unique_input(igvn); if (un != nullptr) { - orig_phis.push(result); + orig_phis.push(result->as_Phi()); result = un; } else { break; @@ -4863,12 +4862,12 @@ Node* ConnectionGraph::find_inst_mem(Node* orig_mem, int alias_idx, Unique_Node_ } #ifdef ASSERT -void ConnectionGraph::verify_ram_after_reduce_phi(const Unique_Node_List &reducible_merges, const Unique_Node_List& reduced_merges) { +void ConnectionGraph::verify_ram_after_reduce_phi(const Unique_Node_List& reducible_merges, const Unique_Node_List& reduced_merges) { if (VerifyReduceAllocationMerges) { { // Checks that nodes added by reduce_phi() were properly added to the connection graph ResourceMark rm; - Unique_Node_List wq; + Unique_Node_List wq; wq.push(_compile->root()); for (uint i = 0; i < wq.size(); ++i) { Node* n = wq.at(i); @@ -4908,7 +4907,7 @@ void ConnectionGraph::verify_ram_after_reduce_phi(const Unique_Node_List &reduci } for (uint i = 0; i < reducible_merges.size(); i++) { - Node* phi = reducible_merges.at(i); + PhiNode* phi = reducible_merges.at(i); if (!reduced_merges.member(phi)) { phi->dump(2); @@ -4930,7 +4929,7 @@ void ConnectionGraph::verify_ram_after_reduce_phi(const Unique_Node_List &reduci } #endif -Node* ConnectionGraph::find_inst_mem_assert_no_new_node(Node* orig_mem, int alias_idx, Unique_Node_List& orig_phis) { +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"); @@ -5026,13 +5025,13 @@ Node* ConnectionGraph::find_inst_mem_assert_no_new_node(Node* orig_mem, int alia // 90 LoadP _ 120 30 ... alias_index=6 // 100 LoadP _ 80 20 ... alias_index=4 // -void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, - GrowableArray &arraycopy_worklist, - GrowableArray &mergemem_worklist, - Unique_Node_List &reducible_merges) { - DEBUG_ONLY(Unique_Node_List reduced_merges;) - Unique_Node_List memnode_worklist; - Unique_Node_List orig_phis; +void ConnectionGraph::split_unique_types(GrowableArray& alloc_worklist, + GrowableArray& arraycopy_worklist, + GrowableArray& mergemem_worklist, + Unique_Node_List& reducible_merges) { + DEBUG_ONLY(Unique_Node_List reduced_merges;) + Unique_Node_List memnode_worklist; + Unique_Node_List orig_phis; PhaseIterGVN *igvn = _igvn; uint new_index_start = (uint) _compile->num_alias_types(); VectorSet visited; @@ -5227,15 +5226,15 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, // Reducible Phi's will be removed from the graph after split_unique_types // finishes. For now we just try to split out the SR inputs of the merge. Node* parent = n->in(1); - if (reducible_merges.member(n)) { - reduce_phi(n->as_Phi(), alloc_worklist); + if (PhiNode* phi = n->isa_Phi(); phi != nullptr && reducible_merges.member(phi)) { + reduce_phi(phi, alloc_worklist); #ifdef ASSERT if (VerifyReduceAllocationMerges) { - reduced_merges.push(n); + reduced_merges.push(phi); } #endif continue; - } else if (reducible_merges.member(parent)) { + } else if (parent->is_Phi() && reducible_merges.member(parent->as_Phi())) { // 'n' is an user of a reducible merge (a Phi). It will be simplified as // part of reduce_merge. continue; @@ -5610,7 +5609,7 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, // chains as is done in split_memory_phi() since they will // also be processed here. for (uint j = 0; j < orig_phis.size(); j++) { - PhiNode* phi = orig_phis.at(j)->as_Phi(); + PhiNode* phi = orig_phis.at(j); int alias_idx = _compile->get_alias_index(phi->out_adr_type()); igvn->hash_delete(phi); for (uint i = 1; i < phi->req(); i++) { diff --git a/src/hotspot/share/opto/escape.hpp b/src/hotspot/share/opto/escape.hpp index dfa21836ec51..3a89804ab016 100644 --- a/src/hotspot/share/opto/escape.hpp +++ b/src/hotspot/share/opto/escape.hpp @@ -343,7 +343,7 @@ class ConnectionGraph: public ArenaObj { Compile* _compile; // Compile object for current compilation PhaseIterGVN* _igvn; // Value numbering - Unique_Node_List ideal_nodes; // Used by CG construction and types splitting. + Unique_Node_List ideal_nodes; // Used by CG construction and types splitting. int _invocation; // Current number of analysis invocation int _build_iterations; // Number of iterations took to build graph @@ -383,7 +383,7 @@ class ConnectionGraph: public ArenaObj { void add_call_node(CallNode* call); // Create PointsToNode node and add it to Connection Graph. - void add_node_to_connection_graph(Node *n, Unique_Node_List *delayed_worklist); + void add_node_to_connection_graph(Node* n, Unique_Node_List* delayed_worklist); // Add final simple edges to graph. void add_final_edges(Node *n); @@ -481,13 +481,13 @@ class ConnectionGraph: public ArenaObj { bool print_method = true); // Adjust scalar_replaceable state after Connection Graph is built. - void adjust_scalar_replaceable_state(JavaObjectNode* jobj, Unique_Node_List &reducible_merges); + void adjust_scalar_replaceable_state(JavaObjectNode* jobj, Unique_Node_List& reducible_merges); // Reevaluate Phis reducible status after 'obj' became NSR. - void revisit_reducible_phi_status(JavaObjectNode* jobj, Unique_Node_List& reducible_merges); + void revisit_reducible_phi_status(JavaObjectNode* jobj, Unique_Node_List& reducible_merges) const; // Propagate NSR (Not scalar replaceable) state. - void find_scalar_replaceable_allocs(GrowableArray& jobj_worklist, Unique_Node_List &reducible_merges); + void find_scalar_replaceable_allocs(GrowableArray& jobj_worklist, Unique_Node_List& reducible_merges) const; // Optimize ideal graph. void optimize_ideal_graph(GrowableArray& ptr_cmp_worklist, @@ -557,32 +557,33 @@ class ConnectionGraph: public ArenaObj { bool is_captured_store_address(Node* addp); // Propagate unique types created for non-escaped allocated objects through the graph - void split_unique_types(GrowableArray &alloc_worklist, - GrowableArray &arraycopy_worklist, - GrowableArray &mergemem_worklist, - Unique_Node_List &reducible_merges); + void split_unique_types(GrowableArray& alloc_worklist, + GrowableArray& arraycopy_worklist, + GrowableArray& mergemem_worklist, + Unique_Node_List& reducible_merges); // Helper methods for unique types split. bool split_AddP(Node *addp, Node *base); - PhiNode* create_split_phi(PhiNode* orig_phi, int alias_idx, Unique_Node_List& orig_phi_worklist, bool& new_created); - PhiNode* split_memory_phi(PhiNode* orig_phi, int alias_idx, Unique_Node_List& orig_phi_worklist, uint rec_depth); + PhiNode* create_split_phi(PhiNode* orig_phi, int alias_idx, Unique_Node_List& orig_phi_worklist, bool& new_created); + PhiNode* split_memory_phi(PhiNode* orig_phi, int alias_idx, Unique_Node_List& orig_phi_worklist, uint rec_depth); - void move_inst_mem(Node* n, Unique_Node_List& orig_phis); + 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* 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 - // Used for the following purposes: - // Memory Phi - most recent unique Phi split out - // from this Phi - // MemNode - new memory input for this node - // CheckCastPP - allocation that this is a cast of - // allocation - CheckCastPP of the allocation - // NarrowMem - newly created projection (type includes instance_id) from projection created - // before EA + // used for bookkeeping during type splitting + // Used for the following purposes: + // Memory Phi - most recent unique Phi split out + // from this Phi + // MemNode - new memory input for this node + // CheckCastPP - allocation that this is a cast of + // allocation - CheckCastPP of the allocation + // NarrowMem - newly created projection (type includes instance_id) from projection created + // before EA + Node_Array _node_map; // manage entries in _node_map @@ -611,12 +612,12 @@ class ConnectionGraph: public ArenaObj { // ------------------------------------------- // Methods related to Reduce Allocation Merges - bool has_non_reducible_merge(FieldNode* field, Unique_Node_List& reducible_merges); + static bool has_non_reducible_merge(const FieldNode* field, const Unique_Node_List& reducible_merges); PhiNode* create_selector(PhiNode* ophi) const; void updates_after_load_split(Node* data_phi, Node* previous_load, GrowableArray &alloc_worklist); Node* split_castpp_load_through_phi(Node* curr_addp, Node* curr_load, Node* region, GrowableArray* bases_for_loads, GrowableArray &alloc_worklist); void reset_scalar_replaceable_entries(PhiNode* ophi); - bool has_reducible_merge_base(AddPNode* n, Unique_Node_List &reducible_merges); + bool has_reducible_merge_base(AddPNode* n, Unique_Node_List& reducible_merges) const; Node* specialize_cmp(Node* base, Node* curr_ctrl); Node* specialize_castpp(Node* castpp, Node* base, Node* current_control); @@ -631,10 +632,10 @@ class ConnectionGraph: public ArenaObj { void reduce_phi_on_castpp_field_load(CastPPNode* castpp, GrowableArray &alloc_worklist); void reduce_phi_on_cmp(Node* cmp); bool reduce_phi_on_safepoints(PhiNode* ophi); - bool reduce_phi_on_safepoints_helper(Node* ophi, Node* cast, Node* selector, Unique_Node_List& safepoints); + bool reduce_phi_on_safepoints_helper(Node* ophi, Node* cast, Node* selector, const Unique_Node_List& safepoints) const; void reduce_phi(PhiNode* ophi, GrowableArray &alloc_worklist); #ifdef ASSERT - void verify_ram_after_reduce_phi(const Unique_Node_List &reducible_merges, const Unique_Node_List& reduced_merges); + void verify_ram_after_reduce_phi(const Unique_Node_List& reducible_merges, const Unique_Node_List& reduced_merges); #endif void set_not_scalar_replaceable(PointsToNode* ptn NOT_PRODUCT(COMMA const char* reason)) const { @@ -676,13 +677,13 @@ class ConnectionGraph: public ArenaObj { DEBUG_ONLY(static bool is_load_array_klass_related(const Node* uncast_base)); // Utility function for nodes that load an object - void add_objload_to_connection_graph(Node* n, Unique_Node_List* delayed_worklist); + void add_objload_to_connection_graph(Node* n, Unique_Node_List* delayed_worklist); - void add_proj(Node* n, Unique_Node_List* delayed_worklist); + void add_proj(Node* n, Unique_Node_List* delayed_worklist); // Add LocalVar node and edge if possible void add_local_var_and_edge(Node* n, PointsToNode::EscapeState es, Node* to, - Unique_Node_List *delayed_worklist) { + Unique_Node_List* delayed_worklist) { PointsToNode* ptn = ptnode_adr(to->_idx); if (delayed_worklist != nullptr) { // First iteration of CG construction add_local_var(n, es); @@ -702,7 +703,7 @@ class ConnectionGraph: public ArenaObj { _nodes.at_put(n->_idx, ptn); } - void add_to_congraph_unsafe_access(Node* n, uint opcode, Unique_Node_List* delayed_worklist); + void add_to_congraph_unsafe_access(Node* n, uint opcode, Unique_Node_List* delayed_worklist); bool add_final_edges_unsafe_access(Node* n, uint opcode); static bool compatible_return(CallJavaNode* call, uint k); diff --git a/src/hotspot/share/opto/gcm.cpp b/src/hotspot/share/opto/gcm.cpp index 5d14b608dd7a..3d97d66d63eb 100644 --- a/src/hotspot/share/opto/gcm.cpp +++ b/src/hotspot/share/opto/gcm.cpp @@ -305,9 +305,9 @@ static Block* find_deepest_input(Node* n, const PhaseCFG* cfg) { // Find the earliest Block any instruction can be placed in. Some instructions // are pinned into Blocks. Unpinned instructions can appear in last block in // which all their inputs occur. -bool PhaseCFG::schedule_early(VectorSet &visited, Node_Stack &roots) { +bool PhaseCFG::schedule_early(VectorSet& visited, Node_Stack& roots) { // Allocate stack with enough space to avoid frequent realloc - Node_Stack nstack(roots.size() + 8); + Node_Stack nstack(roots.size() + 8); // _root will be processed among C->top() inputs roots.push(C->top(), 0); visited.set(C->top()->_idx); @@ -595,7 +595,7 @@ class DefUseMemStatesQueue : public StackObj { }; GrowableArray _queue; - Unique_Node_List _worklist_visited; // visited mergemem nodes + Unique_Node_List _worklist_visited; // visited mergemem nodes bool already_enqueued(Node* def_mem, PhiNode* use_phi) const { // def_mem is one of the inputs of use_phi and at least one input of use_phi is @@ -635,11 +635,11 @@ class DefUseMemStatesQueue : public StackObj { void push(Node* def_mem_state, Node* use_mem_state) { if (use_mem_state->is_MergeMem()) { // Be sure we don't get into combinatorial problems. - if (_worklist_visited.member(use_mem_state)) { + if (_worklist_visited.member(use_mem_state->as_MergeMem())) { // already on work list; do not repeat return; } - _worklist_visited.push(use_mem_state); + _worklist_visited.push(use_mem_state->as_MergeMem()); } else if (use_mem_state->is_Phi()) { // A Phi could have the same mem as input multiple times. If that's the case, we don't need to enqueue it // more than once. We otherwise allow phis to be repeated; they can merge two relevant states. @@ -793,7 +793,7 @@ Block* PhaseCFG::raise_above_anti_dependences(Block* LCA, Node* load, const bool // relevant stores is efficiency: we lazily record all possible // anti-dependent stores and add anti-dependence edges only to the relevant // ones at the very end of this method when we know the final updated LCA. - Node_List non_early_stores(area); + Node_List non_early_stores(area); // Whether we must raise the LCA after the main worklist loop below. bool must_raise_LCA_above_marks = false; @@ -1133,19 +1133,19 @@ class Node_Backward_Iterator { public: // Constructor for the iterator - Node_Backward_Iterator(Node *root, VectorSet &visited, Node_Stack &stack, PhaseCFG &cfg); + Node_Backward_Iterator(Node* root, VectorSet& visited, Node_Stack& stack, PhaseCFG& cfg); // Postincrement operator to iterate over the nodes Node *next(); private: - VectorSet &_visited; - Node_Stack &_stack; - PhaseCFG &_cfg; + VectorSet& _visited; + Node_Stack& _stack; + PhaseCFG& _cfg; }; // Constructor for the Node_Backward_Iterator -Node_Backward_Iterator::Node_Backward_Iterator( Node *root, VectorSet &visited, Node_Stack &stack, PhaseCFG &cfg) +Node_Backward_Iterator::Node_Backward_Iterator(Node* root, VectorSet& visited, Node_Stack& stack, PhaseCFG& cfg) : _visited(visited), _stack(stack), _cfg(cfg) { // The stack should contain exactly the root stack.clear(); @@ -1239,7 +1239,7 @@ Node *Node_Backward_Iterator::next() { //------------------------------ComputeLatenciesBackwards---------------------- // Compute the latency of all the instructions. -void PhaseCFG::compute_latencies_backwards(VectorSet &visited, Node_Stack &stack) { +void PhaseCFG::compute_latencies_backwards(VectorSet& visited, Node_Stack& stack) { #ifndef PRODUCT if (trace_opto_pipelining()) tty->print("\n#---- ComputeLatenciesBackwards ----\n"); @@ -1547,7 +1547,7 @@ Block* PhaseCFG::hoist_to_cheaper_block(Block* LCA, Block* early, Node* self) { // dominator tree of all USES of a value. Pick the block with the least // loop nesting depth that is lowest in the dominator tree. extern const char must_clone[]; -void PhaseCFG::schedule_late(VectorSet &visited, Node_Stack &stack) { +void PhaseCFG::schedule_late(VectorSet& visited, Node_Stack& stack) { #ifndef PRODUCT if (trace_opto_pipelining()) tty->print("\n#---- schedule_late ----\n"); @@ -1780,7 +1780,7 @@ void PhaseCFG::global_code_motion() { // instructions are pinned into Blocks. Unpinned instructions can // appear in last block in which all their inputs occur. visited.clear(); - Node_Stack stack((C->live_nodes() >> 2) + 16); // pre-grow + Node_Stack stack((C->live_nodes() >> 2) + 16); // pre-grow if (!schedule_early(visited, stack)) { // Bailout without retry assert(C->failure_is_artificial(), "early schedule failed"); diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 73f69aeafbab..f4dbf16428b6 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -206,7 +206,11 @@ void GraphKit::verify_exception_state(SafePointNode* ex_map) { void GraphKit::stop_and_kill_map() { SafePointNode* dead_map = stop(); if (dead_map != nullptr) { - dead_map->disconnect_inputs(C); // Mark the map as killed. + if (PhaseIterGVN* igvn = _gvn.is_IterGVN()) { + igvn->remove_dead_node(dead_map, PhaseIterGVN::NodeOrigin::Speculative); + } else { + dead_map->disconnect_inputs(C); + } assert(dead_map->is_killed(), "must be so marked"); } } @@ -2275,14 +2279,14 @@ void GraphKit::set_predefined_output_for_runtime_call(Node* call, } // Keep track of MergeMems feeding into other MergeMems -static void add_mergemem_users_to_worklist(Unique_Node_List& wl, Node* mem) { +static void add_mergemem_users_to_worklist(Unique_Node_List& wl, Node* mem) { if (!mem->is_MergeMem()) { return; } for (SimpleDUIterator i(mem); i.has_next(); i.next()) { Node* use = i.get(); if (use->is_MergeMem()) { - wl.push(use); + wl.push(use->as_MergeMem()); } } } @@ -2303,7 +2307,7 @@ void GraphKit::replace_call(CallNode* call, Node* result, bool do_replaced_nodes // Find all the needed outputs of this call CallProjections* callprojs = call->extract_projections(true, do_asserts); - Unique_Node_List wl; + Unique_Node_List wl; Node* final_mem = final_state->in(TypeFunc::Memory); Node* final_ctl = final_state->in(TypeFunc::Control); Node* final_io = final_state->in(TypeFunc::I_O); @@ -2400,7 +2404,7 @@ void GraphKit::replace_call(CallNode* call, Node* result, bool do_replaced_nodes // Clean up any MergeMems that feed other MergeMems since the // optimizer doesn't like that. while (wl.size() > 0) { - Node* old_mem = wl.pop(); + MergeMemNode* old_mem = wl.pop(); Node* new_mem = _gvn.transform(old_mem); if (old_mem != new_mem) { C->gvn_replace_by(old_mem, new_mem); @@ -3782,6 +3786,13 @@ Node* GraphKit::gen_checkcast(Node* obj, Node* superklass, Node** failure_contro assert(safe_for_replace, "must be"); obj = null_check(obj); } + if (!stopped() && toop->is_valueklassptr() && !maybe_larval) { + Node* vt = ValueTypeNode::make_from_oop(this, obj, toop->value_klass()); + if (safe_for_replace) { + replace_in_map(obj, vt); + } + obj = vt; + } return obj; case Compile::SSC_always_false: if (null_free) { @@ -3917,7 +3928,8 @@ Node* GraphKit::gen_checkcast(Node* obj, Node* superklass, Node** failure_contro if (Arguments::is_valhalla_enabled() && (not_value || not_flat_in_array)) { // Check if obj has been loaded from an array. Keep obj unchanged for final // map replacement below. - Node* array_obj = obj->isa_DecodeN() ? obj->in(1) : obj; + Node* array_obj = obj->uncast(); + array_obj = array_obj->isa_DecodeN() ? array_obj->in(1) : array_obj; Node* array = nullptr; if (array_obj->isa_Load()) { Node* address = array_obj->in(MemNode::Address); @@ -3948,8 +3960,8 @@ Node* GraphKit::gen_checkcast(Node* obj, Node* superklass, Node** failure_contro replace_in_map(array, cast); array = cast; } - if (!ary_t->is_not_flat() && !ary_t->is_flat() && not_flat_in_array) { - // Casting array element to a non-flat-in-array type, mark array as not flat. + if (null_ctl == top() && !ary_t->is_not_flat() && !ary_t->is_flat() && not_flat_in_array) { + // Casting a non-null array element to a non-flat-in-array type, mark array as not flat. Node* cast = _gvn.transform(new CheckCastPPNode(control(), array, ary_t->cast_to_not_flat())); if (new_cast_failure_map != nullptr && *new_cast_failure_map == nullptr) { // Same as above. diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index 28932b145ebf..9e26adaf8526 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -79,15 +79,19 @@ class GraphKit : public Phase { GraphKit(); // empty constructor GraphKit(JVMState* jvms, PhaseGVN* gvn = nullptr); // the JVM state on which to operate - // Create a GraphKit from a debug state, useful for various kinds of macro expansion + // Create a GraphKit with a temporary map for parsing from a debug state. + // This is useful for various kinds of macro expansion. GraphKit(const SafePointNode* sft, PhaseIterGVN& igvn); -#ifdef ASSERT ~GraphKit() { assert(failing_internal() || !has_exceptions(), "unless compilation failed, user must call transfer_exceptions_into_jvms"); + if (_gvn.is_IterGVN()) { + // Kill the temporary map again + assert(map() == nullptr || map()->outcnt() == 0, "map should not escape"); + stop_and_kill_map(); + } } -#endif virtual Parse* is_Parse() const { return nullptr; } virtual LibraryCallKit* is_LibraryCallKit() const { return nullptr; } @@ -828,9 +832,6 @@ class GraphKit : public Phase { BoxLockNode* shared_lock(Node* obj); void shared_unlock(BoxLockNode* box, Node* obj); - // helper functions for the fast path/slow path idioms - Node* fast_and_slow(Node* in, const Type *result_type, Node* null_result, IfNode* fast_test, Node* fast_result, address slow_call, const TypeFunc *slow_call_type, Node* slow_arg, Klass* ex_klass, Node* slow_result); - // Generate an instance-of idiom. Used by both the instance-of bytecode // and the reflective instance-of call. Node* gen_instanceof(Node *subobj, Node* superkls, bool safe_for_replace = false); @@ -911,7 +912,6 @@ class GraphKit : public Phase { void add_parse_predicate(Deoptimization::DeoptReason reason, int nargs); Node* make_constant_from_field(ciField* field, Node* obj); - Node* load_mirror_from_klass(Node* klass); // Vector API support (implemented in vectorIntrinsics.cpp) Node* box_vector(Node* in, const TypeInstPtr* vbox_type, BasicType elem_bt, int num_elem, bool deoptimize_on_exception = false); @@ -932,7 +932,7 @@ class GraphKit : public Phase { int _sp; JVMState* _jvms; SafePointNode* _map; - Unique_Node_List _ctrl_succ; + Unique_Node_List _ctrl_succ; bool _discarded; public: diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp index 92c3f66c861b..0567def90bf2 100644 --- a/src/hotspot/share/opto/ifnode.cpp +++ b/src/hotspot/share/opto/ifnode.cpp @@ -1510,7 +1510,7 @@ Node* IfNode::merge_uncommon_traps(IfProjNode* proj, IfProjNode* success, IfProj void IfNode::improve_address_types(Node* l, Node* r, ProjNode* fail, PhaseIterGVN* igvn) { #ifdef _LP64 ResourceMark rm; - Node_Stack stack(2); + Node_Stack stack(2); assert(r->Opcode() == Op_LoadRange, "unexpected range check"); const TypeInt* array_size = igvn->type(r)->is_int(); diff --git a/src/hotspot/share/opto/int128tnode.cpp b/src/hotspot/share/opto/int128tnode.cpp index 8263e56aa52a..9924caf77f2d 100644 --- a/src/hotspot/share/opto/int128tnode.cpp +++ b/src/hotspot/share/opto/int128tnode.cpp @@ -29,7 +29,6 @@ #include "opto/type.hpp" Int128TBinaryNode::Int128TBinaryNode(Node* lo1, Node* hi1, Node* lo2, Node* hi2) : MultiNode(5) { - init_flags(Flag_is_macro); init_req(1, lo1); init_req(2, hi1); init_req(3, lo2); diff --git a/src/hotspot/share/opto/lcm.cpp b/src/hotspot/share/opto/lcm.cpp index 0c67d3965c97..1de1c6ec0a03 100644 --- a/src/hotspot/share/opto/lcm.cpp +++ b/src/hotspot/share/opto/lcm.cpp @@ -147,21 +147,18 @@ void PhaseCFG::implicit_null_check(Block* block, Node *proj, Node *val, int allo bool found_trap = false; for (uint i1 = 0; i1 < null_block->number_of_nodes(); i1++) { Node* nn = null_block->get_node(i1); - if (nn->is_MachCall() && - nn->as_MachCall()->entry_point() == OptoRuntime::uncommon_trap_blob()->entry_point()) { - const Type* trtype = nn->in(TypeFunc::Parms)->bottom_type(); - if (trtype->isa_int() && trtype->is_int()->is_con()) { - jint tr_con = trtype->is_int()->get_con(); - Deoptimization::DeoptReason reason = Deoptimization::trap_request_reason(tr_con); - Deoptimization::DeoptAction action = Deoptimization::trap_request_action(tr_con); - assert((int)reason < (int)BitsPerInt, "recode bit map"); - if (is_set_nth_bit(allowed_reasons, (int) reason) - && action != Deoptimization::Action_none) { - // This uncommon trap is sure to recompile, eventually. - // When that happens, C->too_many_traps will prevent - // this transformation from happening again. - found_trap = true; - } + jint trap_request; + if (nn->is_MachSafePoint() && + nn->as_MachSafePoint()->is_uncommon_trap(&trap_request)) { + Deoptimization::DeoptReason reason = Deoptimization::trap_request_reason(trap_request); + Deoptimization::DeoptAction action = Deoptimization::trap_request_action(trap_request); + assert((int)reason < (int)BitsPerInt, "recode bit map"); + if (is_set_nth_bit(allowed_reasons, (int) reason) + && action != Deoptimization::Action_none) { + // This uncommon trap is sure to recompile, eventually. + // When that happens, C->too_many_traps will prevent + // this transformation from happening again. + found_trap = true; } break; } @@ -281,11 +278,10 @@ void PhaseCFG::implicit_null_check(Block* block, Node *proj, Node *val, int allo continue; } - // Check that node's control edge is not-null block's head or dominates it, + // Check that node's control edge dominates the not-null block, // otherwise we can't hoist it because there are other control dependencies. Node* ctrl = mach->in(0); - if (ctrl != nullptr && !(ctrl == not_null_block->head() || - get_block_for_node(ctrl)->dominates(not_null_block))) { + if (ctrl != nullptr && !get_block_for_node(ctrl)->dominates(not_null_block)) { continue; } @@ -562,9 +558,9 @@ void PhaseCFG::implicit_null_check(Block* block, Node *proj, Node *val, int allo // routine). If there is a tie, choose the instruction with the most inputs. Node* PhaseCFG::select( Block* block, - Node_List &worklist, - GrowableArray &ready_cnt, - VectorSet &next_call, + Node_List& worklist, + GrowableArray& ready_cnt, + VectorSet& next_call, uint sched_slot, intptr_t* recalc_pressure_nodes) { @@ -843,11 +839,11 @@ void PhaseCFG::adjust_register_pressure(Node* n, Block* block, intptr_t* recalc_ //------------------------------set_next_call---------------------------------- void PhaseCFG::set_next_call(const Block* block, Node* init, VectorSet& next_call) const { - Node_List worklist; + Node_List worklist; worklist.push(init); while (worklist.size() > 0) { - Node* n = worklist.pop(); + const Node* n = worklist.pop(); if (next_call.test_set(n->_idx)) continue; for (uint i = 0; i < n->len(); i++) { Node* m = n->in(i); @@ -900,7 +896,7 @@ static void add_call_kills(MachProjNode *proj, RegMask& regs, const char* save_p //------------------------------sched_call------------------------------------- -uint PhaseCFG::sched_call(Block* block, uint node_cnt, Node_List& worklist, GrowableArray& ready_cnt, MachCallNode* mcall, VectorSet& next_call) { +uint PhaseCFG::sched_call(Block* block, uint node_cnt, Node_List& worklist, GrowableArray& ready_cnt, MachCallNode* mcall, VectorSet& next_call) { ResourceMark rm(C->regmask_arena()); RegMask regs(C->regmask_arena()); @@ -1093,9 +1089,9 @@ bool PhaseCFG::schedule_local(Block* block, GrowableArray& ready_cnt, Vecto } } - Node_List delay; + Node_List delay; // Make a worklist - Node_List worklist; + Node_List worklist; for(uint i4=i3; i4get_node(i4); if( !ready_cnt.at(m->_idx) ) { // Zero ready count? @@ -1306,7 +1302,7 @@ Node* PhaseCFG::catch_cleanup_find_cloned_def(Block *use_blk, Node *def, Block * if( j == def_blk->_num_succs ) { // Block at same level in dom-tree is not a successor. It needs a // PhiNode, the PhiNode uses from the def and IT's uses need fixup. - Node_Array inputs; + Node_Array inputs; for(uint k = 1; k < use_blk->num_preds(); k++) { Block* block = get_block_for_node(use_blk->pred(k)); inputs.map(k, catch_cleanup_find_cloned_def(block, def, def_blk, n_clone_idx)); @@ -1420,7 +1416,7 @@ void PhaseCFG::call_catch_cleanup(Block* block) { uint n_clone_idx = i2-beg+1; // Index of clone of n in each successor block Node *n = block->get_node(i2); // Node that got cloned // Need DU safe iterator because of edge manipulation in calls. - Unique_Node_List* out = new Unique_Node_List(); + Unique_Node_List* out = new Unique_Node_List(); for (DUIterator_Fast j1max, j1 = n->fast_outs(j1max); j1 < j1max; j1++) { out->push(n->fast_out(j1)); } diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 1cc670a135f7..b5d6982b6103 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -2472,6 +2472,9 @@ bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, c assert(Unsafe_field_offset_to_byte_offset(11) == 11, "fieldOffset must be byte-scaled"); + // Save state and restore on bailout + SavedState old_state(this); + if (base->is_ValueType()) { assert(!is_store, "ValueTypeNodes are non-larval value objects"); ValueTypeNode* vt = base->as_ValueType(); @@ -2495,6 +2498,7 @@ bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, c value = ValueTypeNode::make_from_oop(this, value, value_type->value_klass()); } set_result(value); + old_state.discard(); return true; } } @@ -2511,9 +2515,6 @@ bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, c // 32-bit machines ignore the high half! offset = ConvL2X(offset); - // Save state and restore on bailout - SavedState old_state(this); - if (const TypeAryPtr* base_ary_type = _gvn.type(base)->isa_aryptr(); type == T_OBJECT && base_ary_type != nullptr) { // Read an object from a primitive array, or a Phi some of whose inputs are primitive arrays if (base_ary_type->elem()->make_ptr() == nullptr) { @@ -3515,85 +3516,6 @@ bool LibraryCallKit::inline_native_notify_jvmti_sync() { #ifdef JFR_HAVE_INTRINSICS -/** - * if oop->klass != null - * // normal class - * epoch = _epoch_state ? 2 : 1 - * if oop->klass->trace_id & ((epoch << META_SHIFT) | epoch)) != epoch { - * ... // enter slow path when the klass is first recorded or the epoch of JFR shifts - * } - * id = oop->klass->trace_id >> TRACE_ID_SHIFT // normal class path - * else - * // primitive class - * if oop->array_klass != null - * id = (oop->array_klass->trace_id >> TRACE_ID_SHIFT) + 1 // primitive class path - * else - * id = LAST_TYPE_ID + 1 // void class path - * if (!signaled) - * signaled = true - */ -bool LibraryCallKit::inline_native_classID() { - Node* cls = argument(0); - - IdealKit ideal(this); -#define __ ideal. - IdealVariable result(ideal); __ declarations_done(); - Node* kls = _gvn.transform(LoadKlassNode::make(_gvn, immutable_memory(), - basic_plus_adr(cls, java_lang_Class::klass_offset()), - TypeRawPtr::BOTTOM, TypeInstKlassPtr::OBJECT_OR_NULL)); - - - __ if_then(kls, BoolTest::ne, null()); { - Node* kls_trace_id_addr = basic_plus_adr(kls, in_bytes(KLASS_TRACE_ID_OFFSET)); - Node* kls_trace_id_raw = ideal.load(ideal.ctrl(), kls_trace_id_addr,TypeLong::LONG, T_LONG, Compile::AliasIdxRaw); - - Node* epoch_address = makecon(TypeRawPtr::make(JfrIntrinsicSupport::epoch_address())); - Node* epoch = ideal.load(ideal.ctrl(), epoch_address, TypeInt::BOOL, T_BOOLEAN, Compile::AliasIdxRaw); - epoch = _gvn.transform(new LShiftLNode(longcon(1), epoch)); - Node* mask = _gvn.transform(new LShiftLNode(epoch, intcon(META_SHIFT))); - mask = _gvn.transform(new OrLNode(mask, epoch)); - Node* kls_trace_id_raw_and_mask = _gvn.transform(new AndLNode(kls_trace_id_raw, mask)); - - float unlikely = PROB_UNLIKELY(0.999); - __ if_then(kls_trace_id_raw_and_mask, BoolTest::ne, epoch, unlikely); { - sync_kit(ideal); - make_runtime_call(RC_LEAF, - OptoRuntime::class_id_load_barrier_Type(), - CAST_FROM_FN_PTR(address, JfrIntrinsicSupport::load_barrier), - "class id load barrier", - TypePtr::BOTTOM, - kls); - ideal.sync_kit(this); - } __ end_if(); - - ideal.set(result, _gvn.transform(new URShiftLNode(kls_trace_id_raw, ideal.ConI(TRACE_ID_SHIFT)))); - } __ else_(); { - Node* array_kls = _gvn.transform(LoadKlassNode::make(_gvn, immutable_memory(), - basic_plus_adr(cls, java_lang_Class::array_klass_offset()), - TypeRawPtr::BOTTOM, TypeInstKlassPtr::OBJECT_OR_NULL)); - __ if_then(array_kls, BoolTest::ne, null()); { - Node* array_kls_trace_id_addr = basic_plus_adr(array_kls, in_bytes(KLASS_TRACE_ID_OFFSET)); - Node* array_kls_trace_id_raw = ideal.load(ideal.ctrl(), array_kls_trace_id_addr, TypeLong::LONG, T_LONG, Compile::AliasIdxRaw); - Node* array_kls_trace_id = _gvn.transform(new URShiftLNode(array_kls_trace_id_raw, ideal.ConI(TRACE_ID_SHIFT))); - ideal.set(result, _gvn.transform(new AddLNode(array_kls_trace_id, longcon(1)))); - } __ else_(); { - // void class case - ideal.set(result, longcon(LAST_TYPE_ID + 1)); - } __ end_if(); - - Node* signaled_flag_address = makecon(TypeRawPtr::make(JfrIntrinsicSupport::signal_address())); - Node* signaled = ideal.load(ideal.ctrl(), signaled_flag_address, TypeInt::BOOL, T_BOOLEAN, Compile::AliasIdxRaw, true, MemNode::acquire); - __ if_then(signaled, BoolTest::ne, ideal.ConI(1)); { - ideal.store(ideal.ctrl(), signaled_flag_address, ideal.ConI(1), T_BOOLEAN, Compile::AliasIdxRaw, MemNode::release, true); - } __ end_if(); - } __ end_if(); - - final_sync(ideal); - set_result(ideal.value(result)); -#undef __ - return true; -} - //------------------------inline_native_jvm_commit------------------ bool LibraryCallKit::inline_native_jvm_commit() { enum { _true_path = 1, _false_path = 2, PATH_LIMIT }; diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index 87ce134747df..b5b138d70c87 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -295,14 +295,12 @@ class LibraryCallKit : public GraphKit { #endif #ifdef JFR_HAVE_INTRINSICS - bool inline_native_classID(); bool inline_native_getEventWriter(); bool inline_native_jvm_commit(); void extend_setCurrentThread(Node* jt, Node* thread); bool inline_native_try_update_epoch(); #endif bool inline_native_Class_query(vmIntrinsics::ID id); - bool inline_primitive_Class_conversion(vmIntrinsics::ID id); bool inline_native_subtype_check(); bool inline_native_getLength(); bool inline_array_copyOf(bool is_copyOfRange); @@ -420,8 +418,6 @@ class LibraryCallKit : public GraphKit { bool inline_profileBoolean(); bool inline_isCompileConstant(); - bool inline_continuation_do_yield(); - // Vector API support bool inline_vector_nary_operation(int n); bool inline_vector_call(int arity); @@ -446,8 +442,6 @@ class LibraryCallKit : public GraphKit { bool inline_index_partially_in_upper_range(); bool inline_vector_select_from_two_vectors(); - Node* gen_call_to_vector_math(int vector_api_op_id, BasicType bt, int num_elem, Node* opd1, Node* opd2); - enum VectorMaskUseType { VecMaskUseLoad = 1 << 0, VecMaskUseStore = 1 << 1, diff --git a/src/hotspot/share/opto/loopPredicate.cpp b/src/hotspot/share/opto/loopPredicate.cpp index f3c45cd497fd..537e2ef97053 100644 --- a/src/hotspot/share/opto/loopPredicate.cpp +++ b/src/hotspot/share/opto/loopPredicate.cpp @@ -216,10 +216,10 @@ IfTrueNode* PhaseIdealLoop::create_new_if_for_predicate(const ParsePredicateSucc // Update ctrl and control inputs of all data nodes starting from 'node' to 'new_ctrl' which have 'old_ctrl' as // current ctrl. -void PhaseIdealLoop::set_ctrl_of_nodes_with_same_ctrl(Node* start_node, ProjNode* old_uncommon_proj, +void PhaseIdealLoop::set_ctrl_of_nodes_with_same_ctrl(Node* start_node, const ProjNode* old_uncommon_proj, Node* new_uncommon_proj) { ResourceMark rm; - const Unique_Node_List nodes_with_same_ctrl = find_nodes_with_same_ctrl(start_node, old_uncommon_proj); + const Unique_Node_List nodes_with_same_ctrl = find_nodes_with_same_ctrl(start_node, old_uncommon_proj); for (uint i = 0; i < nodes_with_same_ctrl.size(); i++) { Node* node = nodes_with_same_ctrl[i]; if (node->in(0) == old_uncommon_proj) { @@ -230,8 +230,8 @@ void PhaseIdealLoop::set_ctrl_of_nodes_with_same_ctrl(Node* start_node, ProjNode } // Recursively find all input nodes with the same ctrl. -Unique_Node_List PhaseIdealLoop::find_nodes_with_same_ctrl(Node* node, const ProjNode* ctrl) { - Unique_Node_List nodes_with_same_ctrl; +Unique_Node_List PhaseIdealLoop::find_nodes_with_same_ctrl(Node* node, const ProjNode* ctrl) { + Unique_Node_List nodes_with_same_ctrl; nodes_with_same_ctrl.push(node); for (uint j = 0; j < nodes_with_same_ctrl.size(); j++) { Node* next = nodes_with_same_ctrl[j]; @@ -247,10 +247,11 @@ Unique_Node_List PhaseIdealLoop::find_nodes_with_same_ctrl(Node* node, const Pro // Clone all data nodes with a ctrl to the old uncommon projection from `start_node' by following its inputs. Rewire the // cloned nodes to the new uncommon projection. Returns the clone of the `start_node`. -Node* PhaseIdealLoop::clone_nodes_with_same_ctrl(Node* start_node, ProjNode* old_uncommon_proj, Node* new_uncommon_proj) { +Node* PhaseIdealLoop::clone_nodes_with_same_ctrl(Node* start_node, const ProjNode* old_uncommon_proj, + Node* new_uncommon_proj) { ResourceMark rm; DEBUG_ONLY(uint last_idx = C->unique();) - const Unique_Node_List nodes_with_same_ctrl = find_nodes_with_same_ctrl(start_node, old_uncommon_proj); + const Unique_Node_List nodes_with_same_ctrl = find_nodes_with_same_ctrl(start_node, old_uncommon_proj); DataNodeGraph data_node_graph(nodes_with_same_ctrl, this); const OrigToNewHashtable& orig_to_clone = data_node_graph.clone(new_uncommon_proj); fix_cloned_data_node_controls(old_uncommon_proj, new_uncommon_proj, orig_to_clone); @@ -272,23 +273,14 @@ void PhaseIdealLoop::fix_cloned_data_node_controls(const ProjNode* old_uncommon_ orig_to_clone.iterate_all(orig_clone_action); } -// Put all OpaqueTemplateAssertionPredicate nodes on a list, starting at 'predicate' and going up in the tree. -void PhaseIdealLoop::get_opaque_template_assertion_predicate_nodes(ParsePredicateSuccessProj* parse_predicate_proj, - Unique_Node_List& list) { - Deoptimization::DeoptReason deopt_reason = parse_predicate_proj->in(0)->as_ParsePredicate()->deopt_reason(); - PredicateBlockIterator predicate_iterator(parse_predicate_proj, deopt_reason); - OpaqueTemplateAssertionPredicateCollector opaque_template_assertion_predicate_collector(list); - predicate_iterator.for_each(opaque_template_assertion_predicate_collector); -} - //------------------------------Invariance----------------------------------- // Helper class for loop_predication_impl to compute invariance on the fly and // clone invariants. class Invariance : public StackObj { VectorSet _visited, _invariant; - Node_Stack _stack; + Node_Stack _stack; VectorSet _clone_visited; - Node_List _old_new; // map of old to new (clone) + Node_List _old_new; // map of old to new (clone) IdealLoopTree* _lpt; PhaseIdealLoop* _phase; Node* _data_dependency_on; // The projection into the loop on which data nodes are dependent or null otherwise @@ -408,7 +400,7 @@ class Invariance : public StackObj { // later check again if a node needs to be treated as non-loop- // invariant as well. _data_dependency_on = entry; - Unique_Node_List wq; + Unique_Node_List wq; wq.push(entry); for (uint next = 0; next < wq.size(); ++next) { Node *n = wq.at(next); @@ -433,7 +425,7 @@ class Invariance : public StackObj { } // Map old to n for invariance computation and clone - void map_ctrl(Node* old, Node* n) { + void map_ctrl(const Node* old, Node* n) { assert(old->is_CFG() && n->is_CFG(), "must be"); _old_new.map(old->_idx, n); // "clone" of old is n _invariant.set(old->_idx); // old is invariant @@ -461,8 +453,8 @@ class Invariance : public StackObj { // scale*iv + offset = 0. // Note: This is used by Loop Predication and PhaseIdealLoop::extract_long_range_check(). -bool IdealLoopTree::is_range_check_if(IfProjNode* if_success_proj, PhaseIdealLoop *phase, BasicType bt, Node *iv, Node *&range, - Node *&offset, jlong &scale) const { +bool IdealLoopTree::is_range_check_if(const IfProjNode* if_success_proj, PhaseIdealLoop* phase, BasicType bt, Node* iv, + Node*& range, Node*& offset, jlong& scale) const { IfNode* iff = if_success_proj->in(0)->as_If(); if (!is_loop_exit(iff)) { return false; @@ -541,7 +533,7 @@ bool IdealLoopTree::is_range_check_if(IfProjNode* if_success_proj, PhaseIdealLoo return true; } -bool IdealLoopTree::is_range_check_if(IfProjNode* if_success_proj, PhaseIdealLoop *phase, Invariance& invar DEBUG_ONLY(COMMA ProjNode *predicate_proj)) const { +bool IdealLoopTree::is_range_check_if(const IfProjNode* if_success_proj, PhaseIdealLoop* phase, Invariance& invar DEBUG_ONLY(COMMA ProjNode* predicate_proj)) const { Node* range = nullptr; Node* offset = nullptr; jlong scale = 0; @@ -793,7 +785,7 @@ float PathFrequency::to(Node* n) { float f = c == _dom ? 1 : _freqs.at(c->_idx); Node* prev = c; while (_stack.size() > 0 && prev == c) { - Node* n = _stack.node(); + const Node* n = _stack.node(); if (!n->is_Region()) { if (_phase->get_loop(n) != _phase->get_loop(n->in(0))) { // Found an inner loop: compute frequency of reaching this @@ -943,14 +935,13 @@ float PathFrequency::to(Node* n) { return -1; } -void PhaseIdealLoop::loop_predication_follow_branches(Node *n, IdealLoopTree *loop, float loop_trip_cnt, - PathFrequency& pf, Node_Stack& stack, VectorSet& seen, - Node_List& if_proj_list) { - assert(n->is_Region(), "start from a region"); +void PhaseIdealLoop::loop_predication_follow_branches(RegionNode* n, IdealLoopTree* loop, float loop_trip_cnt, + PathFrequency& pf, Node_Stack& stack, VectorSet& seen, + Node_List& if_proj_list) { Node* tail = loop->tail(); stack.push(n, 1); do { - Node* c = stack.node(); + const Node* c = stack.node(); assert(c->is_Region() || c->is_IfProj(), "only region here"); uint i = stack.index(); @@ -978,7 +969,7 @@ void PhaseIdealLoop::loop_predication_follow_branches(Node *n, IdealLoopTree *lo } } else { if (c->is_IfProj()) { - if_proj_list.push(c); + if_proj_list.push(c->as_IfProj()); } stack.pop(); } @@ -986,7 +977,7 @@ void PhaseIdealLoop::loop_predication_follow_branches(Node *n, IdealLoopTree *lo } while (stack.size() > 0); } -bool PhaseIdealLoop::loop_predication_impl_helper(IdealLoopTree* loop, IfProjNode* if_success_proj, +bool PhaseIdealLoop::loop_predication_impl_helper(IdealLoopTree* loop, const IfProjNode* if_success_proj, ParsePredicateSuccessProj* parse_predicate_proj, CountedLoopNode* cl, ConNode* zero, Invariance& invar, Deoptimization::DeoptReason deopt_reason) { @@ -1213,23 +1204,23 @@ bool PhaseIdealLoop::loop_predication_impl(IdealLoopTree* loop) { // Create list of if-projs such that a newer proj dominates all older // projs in the list, and they all dominate loop->tail() - Node_List if_proj_list; - Node_List regions; + Node_List if_proj_list; + Node_List regions; Node* current_proj = loop->tail(); // start from tail - Node_List controls; + Node_List controls; while (current_proj != head) { if (loop == get_loop(current_proj) && // still in the loop ? - current_proj->is_Proj() && // is a projection ? + current_proj->is_IfProj() && // is a projection ? (current_proj->in(0)->Opcode() == Op_If || current_proj->in(0)->Opcode() == Op_RangeCheck)) { // is a if projection ? - if_proj_list.push(current_proj); + if_proj_list.push(current_proj->as_IfProj()); } if (follow_branches && current_proj->Opcode() == Op_Region && loop == get_loop(current_proj)) { - regions.push(current_proj); + regions.push(current_proj->as_Region()); } current_proj = idom(current_proj); } @@ -1238,9 +1229,7 @@ bool PhaseIdealLoop::loop_predication_impl(IdealLoopTree* loop) { if (can_create_loop_predicates(profiled_loop_predicate_block)) { while (if_proj_list.size() > 0) { - Node* n = if_proj_list.pop(); - - IfProjNode* if_proj = n->as_IfProj(); + IfProjNode* if_proj = if_proj_list.pop(); IfNode* iff = if_proj->in(0)->as_If(); CallStaticJavaNode* call = if_proj->is_uncommon_trap_if_pattern(); @@ -1279,28 +1268,28 @@ bool PhaseIdealLoop::loop_predication_impl(IdealLoopTree* loop) { // Some projections were skipped due to an early loop exit. Try them with profile data. while (if_proj_list.size() > 0) { - Node* if_proj = if_proj_list.pop(); + IfProjNode* if_proj = if_proj_list.pop(); float f = pf.to(if_proj); - if (if_proj->as_Proj()->is_uncommon_trap_if_pattern() && + if (if_proj->is_uncommon_trap_if_pattern() && f * loop_trip_cnt >= 1) { ParsePredicateSuccessProj* profiled_loop_parse_predicate_proj = profiled_loop_predicate_block->parse_predicate_success_proj(); - hoisted = loop_predication_impl_helper(loop, if_proj->as_IfProj(), profiled_loop_parse_predicate_proj, + hoisted = loop_predication_impl_helper(loop, if_proj, profiled_loop_parse_predicate_proj, cl, zero, invar, Deoptimization::Reason_profile_predicate) | hoisted; } } // And look into all branches - Node_Stack stack(0); + Node_Stack stack(0); VectorSet seen; - Node_List if_proj_list_freq(area); + Node_List if_proj_list_freq(area); while (regions.size() > 0) { - Node* c = regions.pop(); + RegionNode* c = regions.pop(); loop_predication_follow_branches(c, loop, loop_trip_cnt, pf, stack, seen, if_proj_list_freq); } for (uint i = 0; i < if_proj_list_freq.size(); i++) { - IfProjNode* if_proj = if_proj_list_freq.at(i)->as_IfProj(); + const IfProjNode* if_proj = if_proj_list_freq.at(i); ParsePredicateSuccessProj* profiled_loop_parse_predicate_proj = profiled_loop_predicate_block->parse_predicate_success_proj(); hoisted = loop_predication_impl_helper(loop, if_proj, profiled_loop_parse_predicate_proj, cl, zero, diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 371c1d851625..b1180e93ceab 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -314,7 +314,7 @@ void IdealLoopTree::compute_profile_trip_cnt(PhaseIdealLoop *phase) { } } else { ResourceMark rm; - Unique_Node_List wq; + Unique_Node_List wq; wq.push(back); for (uint i = 0; i < wq.size(); i++) { Node *n = wq.at(i); @@ -661,7 +661,7 @@ uint IdealLoopTree::estimate_peeling(PhaseIdealLoop *phase) { // dominates the remaining iterations. This allows us to: // - Remove all loop-invariant dominated tests in the main body. // - Remove all loop-invariant dominated stores in the main body. -void PhaseIdealLoop::elide_redundancies_after_peeling(IdealLoopTree* loop, const Node_List& old_new) { +void PhaseIdealLoop::elide_redundancies_after_peeling(IdealLoopTree* loop, const Node_List& old_new) { elide_redundant_tests_after_peeling(loop, old_new); elide_redundant_stores_after_peeling(loop, old_new); } @@ -671,7 +671,7 @@ void PhaseIdealLoop::elide_redundancies_after_peeling(IdealLoopTree* loop, const // 'prev'. As a result, if the condition of 'test' is a loop invariant, 'test' will be dominated by // the IfProj of 'old_new[test->_idx]', and both 'test' and 'old_new[test->_idx]' will have the // same Bool input. This allows 'test' to be removed using PhaseIdealLoop::dominated_by. -void PhaseIdealLoop::elide_redundant_tests_after_peeling(IdealLoopTree* loop, const Node_List& old_new) { +void PhaseIdealLoop::elide_redundant_tests_after_peeling(IdealLoopTree* loop, const Node_List& old_new) { bool progress = true; while (progress) { progress = false; // Reset for next iteration @@ -769,7 +769,7 @@ void PhaseIdealLoop::elide_redundant_tests_after_peeling(IdealLoopTree* loop, co // remove it from the main body. Note that this is only an analysis of the transformation as a // conceptual explanation. In practice, in try_move_store_before_loop, we don't actually peel and // unpeel the loop but we hoist the store directly. -void PhaseIdealLoop::elide_redundant_stores_after_peeling(IdealLoopTree* loop, const Node_List& old_new) { +void PhaseIdealLoop::elide_redundant_stores_after_peeling(IdealLoopTree* loop, const Node_List& old_new) { for (uint i = 0; i < loop->_body.size(); i++) { Node* n = loop->_body.at(i); if (n->is_Store() && loop->can_elide_store_after_peeling(n->as_Store(), old_new[n->_idx]->as_Store())) { @@ -832,7 +832,7 @@ bool IdealLoopTree::can_elide_store_after_peeling_impl(StoreNode* store, StoreNo // with 'store'. If is_profitability_check is true, walk all the relevant nodes inside 'this'. // Otherwise, walk all the relevant nodes between 'store' and 'dominating_store'. ResourceMark rm; - Unique_Node_List worklist; + Unique_Node_List worklist; worklist.push(store->in(MemNode::Memory)); AccessAnalyzer analyzer(&_phase->_igvn, store); for (uint worklist_idx = 0; worklist_idx < worklist.size(); worklist_idx++) { @@ -1018,7 +1018,7 @@ bool IdealLoopTree::can_elide_store_after_peeling_impl(StoreNode* store, StoreNo // v // exit // -void PhaseIdealLoop::do_peeling(IdealLoopTree *loop, Node_List &old_new) { +void PhaseIdealLoop::do_peeling(IdealLoopTree* loop, Node_List& old_new) { assert(LoopPeeling != 0, "do_peeling called with loop peeling always disabled"); C->set_major_progress(); @@ -1559,7 +1559,7 @@ bool IdealLoopTree::policy_peel_only(PhaseIdealLoop *phase) const { //------------------------------clone_up_backedge_goo-------------------------- // If Node n lives in the back_ctrl block and cannot float, we clone a private // version of n in preheader_ctrl block and return that, otherwise return n. -Node *PhaseIdealLoop::clone_up_backedge_goo(Node *back_ctrl, Node *preheader_ctrl, Node *n, VectorSet &visited, Node_Stack &clones) { +Node* PhaseIdealLoop::clone_up_backedge_goo(Node* back_ctrl, Node* preheader_ctrl, Node* n, VectorSet& visited, Node_Stack& clones) { if (get_ctrl(n) != back_ctrl) return n; // Only visit once @@ -1633,7 +1633,7 @@ void PhaseIdealLoop::ensure_zero_trip_guard_proj(Node* node, bool is_main_loop) // Insert pre and post loops. If peel_only is set, the pre-loop can not have // more iterations added. It acts as a 'peel' only, no lower-bound RCE, no // alignment. Useful to unroll loops that do no array accesses. -void PhaseIdealLoop::insert_pre_post_loops(IdealLoopTree *loop, Node_List &old_new, bool peel_only) { +void PhaseIdealLoop::insert_pre_post_loops(IdealLoopTree* loop, Node_List& old_new, bool peel_only) { #ifndef PRODUCT if (TraceLoopOpts) { @@ -1747,7 +1747,7 @@ void PhaseIdealLoop::insert_pre_post_loops(IdealLoopTree *loop, Node_List &old_n assert(post_head->in(1)->is_IfProj(), "must be zero-trip guard If node projection of the post loop"); VectorSet visited; - Node_Stack clones(main_head->back_control()->outcnt()); + Node_Stack clones(main_head->back_control()->outcnt()); // Step B3: Make the fall-in values to the main-loop come from the // fall-out values of the pre-loop. const uint last_node_index_in_pre_loop_body = Compile::current()->unique() - 1; @@ -1847,7 +1847,7 @@ void PhaseIdealLoop::insert_pre_post_loops(IdealLoopTree *loop, Node_List &old_n // unroll_policy has already informed us that more unrolling is about to // happen to the main loop. The resultant post loop will serve as a // vectorized drain loop. -void PhaseIdealLoop::insert_vector_post_loop(IdealLoopTree *loop, Node_List &old_new) { +void PhaseIdealLoop::insert_vector_post_loop(IdealLoopTree* loop, Node_List& old_new) { if (!loop->_head->is_CountedLoop()) return; CountedLoopNode *cl = loop->_head->as_CountedLoop(); @@ -1931,7 +1931,7 @@ Node* PhaseIdealLoop::find_last_store_in_outer_loop(Node* store, const IdealLoop //------------------------------insert_post_loop------------------------------- // Insert post loops. Add a post loop to the given loop passed. -Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree* loop, Node_List& old_new, +Node* PhaseIdealLoop::insert_post_loop(IdealLoopTree* loop, Node_List& old_new, CountedLoopNode* main_head, CountedLoopEndNode* main_end, Node* incr, Node* limit, CountedLoopNode*& post_head) { IfNode* outer_main_end = main_end; @@ -2003,7 +2003,7 @@ Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree* loop, Node_List& old_new, set_idom(post_head, zer_taken, dd_main_exit); VectorSet visited; - Node_Stack clones(main_head->back_control()->outcnt()); + Node_Stack clones(main_head->back_control()->outcnt()); // Step A3: Make the fall-in values to the post-loop come from the // fall-out values of the main-loop. for (DUIterator i = main_head->outs(); main_head->has_out(i); i++) { @@ -2099,7 +2099,7 @@ void PhaseIdealLoop::update_main_loop_assertion_predicates(CountedLoopNode* new_ void PhaseIdealLoop::initialize_assertion_predicates_for_peeled_loop(CountedLoopNode* peeled_loop_head, CountedLoopNode* remaining_loop_head, const uint first_node_index_in_cloned_loop_body, - const Node_List& old_new) { + const Node_List& old_new) { const NodeInOriginalLoopBody node_in_original_loop_body(first_node_index_in_cloned_loop_body, old_new); create_assertion_predicates_at_loop(peeled_loop_head, remaining_loop_head, node_in_original_loop_body, true); } @@ -2111,7 +2111,7 @@ void PhaseIdealLoop::initialize_assertion_predicates_for_main_loop(CountedLoopNo const uint first_node_index_in_pre_loop_body, const uint last_node_index_in_pre_loop_body, DEBUG_ONLY(const uint last_node_index_from_backedge_goo COMMA) - const Node_List& old_new) { + const Node_List& old_new) { assert(first_node_index_in_pre_loop_body < last_node_index_in_pre_loop_body, "cloned some nodes"); const NodeInMainLoopBody node_in_main_loop_body(first_node_index_in_pre_loop_body, last_node_index_in_pre_loop_body, @@ -2185,7 +2185,7 @@ void PhaseIdealLoop::rewire_old_target_loop_entry_dependency_to_new_entry( //------------------------------do_unroll-------------------------------------- // Unroll the loop body one step - make each trip do 2 iterations. -void PhaseIdealLoop::do_unroll(IdealLoopTree *loop, Node_List &old_new, bool adjust_min_trip) { +void PhaseIdealLoop::do_unroll(IdealLoopTree* loop, Node_List& old_new, bool adjust_min_trip) { assert(LoopUnrollLimit, ""); CountedLoopNode *loop_head = loop->_head->as_CountedLoop(); CountedLoopEndNode *loop_end = loop_head->loopexit(); @@ -2203,8 +2203,8 @@ void PhaseIdealLoop::do_unroll(IdealLoopTree *loop, Node_List &old_new, bool adj } if (C->do_vector_loop() && (PrintOpto && (VerifyLoopOptimizations || TraceLoopOpts))) { - Node_Stack stack(C->live_nodes() >> 2); - Node_List rpo_list; + Node_Stack stack(C->live_nodes() >> 2); + Node_List rpo_list; VectorSet visited; visited.set(loop_head->_idx); rpo(loop_head, stack, visited, rpo_list); @@ -2238,7 +2238,7 @@ void PhaseIdealLoop::do_unroll(IdealLoopTree *loop, Node_List &old_new, bool adj // In rare cases, loop cloning (as for peeling, for instance) can break this by replacing // limit and the input of opaq by equivalent but distinct phis. // Next IGVN should clean it up. Let's try to detect we are in such a case. - Unique_Node_List& worklist = loop->_phase->_igvn._worklist; + Unique_Node_List& worklist = loop->_phase->_igvn._worklist; assert(C->major_progress(), "The operation that replaced limit and opaq->in(1) (e.g. peeling) should have set major_progress"); assert(opaq->in(1)->is_Phi() && limit->is_Phi(), "Nodes limit and opaq->in(1) should have been replaced by PhiNodes by fix_data_uses from clone_loop."); assert(worklist.member(opaq->in(1)) && worklist.member(limit), "Nodes limit and opaq->in(1) differ and should have been recorded for IGVN."); @@ -2453,7 +2453,7 @@ void PhaseIdealLoop::do_unroll(IdealLoopTree *loop, Node_List &old_new, bool adj //------------------------------do_maximally_unroll---------------------------- -void PhaseIdealLoop::do_maximally_unroll(IdealLoopTree *loop, Node_List &old_new) { +void PhaseIdealLoop::do_maximally_unroll(IdealLoopTree* loop, Node_List& old_new) { CountedLoopNode *cl = loop->_head->as_CountedLoop(); assert(cl->has_exact_trip_count(), "trip count is not exact"); assert(cl->trip_count() > 0, ""); @@ -3475,7 +3475,7 @@ Node* PhaseIdealLoop::ensure_node_and_inputs_are_above_pre_end(CountedLoopEndNod } control = pre_end->in(0); ResourceMark rm; - Unique_Node_List wq; + Unique_Node_List wq; wq.push(node); for (uint i = 0; i < wq.size(); i++) { Node* n = wq.at(i); @@ -3641,7 +3641,7 @@ bool IdealLoopTree::do_remove_empty_loop(PhaseIdealLoop *phase) { #ifdef ASSERT // Call collect_loop_core_nodes to exercise the assert that checks that it finds the right number of nodes if (empty_loop_with_extra_nodes_candidate(phase)) { - Unique_Node_List wq; + Unique_Node_List wq; collect_loop_core_nodes(phase, wq); } #endif @@ -3736,7 +3736,7 @@ bool IdealLoopTree::do_remove_empty_loop(PhaseIdealLoop *phase) { return false; } // Peel the loop to ensure there's a zero trip guard - Node_List old_new; + Node_List old_new; phase->do_peeling(this, old_new); } @@ -3785,8 +3785,8 @@ bool IdealLoopTree::empty_loop_with_data_nodes(PhaseIdealLoop* phase) const { if (!cl->is_strip_mined() || !empty_loop_with_extra_nodes_candidate(phase)) { return false; } - Unique_Node_List empty_loop_nodes; - Unique_Node_List wq; + Unique_Node_List empty_loop_nodes; + Unique_Node_List wq; // Start from all data nodes in the loop body that are not one of the EMPTY_LOOP_SIZE nodes expected in an empty body enqueue_data_nodes(phase, empty_loop_nodes, wq); @@ -3827,7 +3827,7 @@ bool IdealLoopTree::empty_loop_with_data_nodes(PhaseIdealLoop* phase) const { return true; } -bool IdealLoopTree::process_safepoint(PhaseIdealLoop* phase, Unique_Node_List& empty_loop_nodes, Unique_Node_List& wq, +bool IdealLoopTree::process_safepoint(PhaseIdealLoop* phase, Unique_Node_List& empty_loop_nodes, Unique_Node_List& wq, Node* sfpt) const { CountedLoopNode* cl = _head->as_CountedLoop(); if (cl->outer_safepoint() == sfpt) { @@ -3871,8 +3871,8 @@ bool IdealLoopTree::empty_loop_with_extra_nodes_candidate(PhaseIdealLoop* phase) return true; } -void IdealLoopTree::enqueue_data_nodes(PhaseIdealLoop* phase, Unique_Node_List& empty_loop_nodes, - Unique_Node_List& wq) const { +void IdealLoopTree::enqueue_data_nodes(PhaseIdealLoop* phase, Unique_Node_List& empty_loop_nodes, + Unique_Node_List& wq) const { collect_loop_core_nodes(phase, empty_loop_nodes); for (uint i = 0; i < _body.size(); ++i) { Node* n = _body.at(i); @@ -3883,7 +3883,7 @@ void IdealLoopTree::enqueue_data_nodes(PhaseIdealLoop* phase, Unique_Node_List& } // This collects the node that would be left if this body was empty -void IdealLoopTree::collect_loop_core_nodes(PhaseIdealLoop* phase, Unique_Node_List& wq) const { +void IdealLoopTree::collect_loop_core_nodes(PhaseIdealLoop* phase, Unique_Node_List& wq) const { uint before = wq.size(); wq.push(_head->in(LoopNode::LoopBackControl)); for (uint i = before; i < wq.size(); ++i) { @@ -3934,7 +3934,7 @@ bool IdealLoopTree::do_one_iteration_loop(PhaseIdealLoop *phase) { //============================================================================= //------------------------------iteration_split_impl--------------------------- -bool IdealLoopTree::iteration_split_impl(PhaseIdealLoop *phase, Node_List &old_new) { +bool IdealLoopTree::iteration_split_impl(PhaseIdealLoop* phase, Node_List& old_new) { if (!_head->is_Loop()) { // Head could be a region with a NeverBranch that was added in beautify loops but the region was not // yet transformed into a LoopNode. Bail out and wait until beautify loops turns it into a Loop node. @@ -4108,7 +4108,7 @@ bool IdealLoopTree::iteration_split_impl(PhaseIdealLoop *phase, Node_List &old_n //============================================================================= //------------------------------iteration_split-------------------------------- -bool IdealLoopTree::iteration_split(PhaseIdealLoop* phase, Node_List &old_new) { +bool IdealLoopTree::iteration_split(PhaseIdealLoop* phase, Node_List& old_new) { // Recursively iteration split nested loops if (_child && !_child->iteration_split(phase, old_new)) { return false; diff --git a/src/hotspot/share/opto/loopUnswitch.cpp b/src/hotspot/share/opto/loopUnswitch.cpp index d3b325db359c..4f98b7b55a37 100644 --- a/src/hotspot/share/opto/loopUnswitch.cpp +++ b/src/hotspot/share/opto/loopUnswitch.cpp @@ -141,7 +141,7 @@ bool IdealLoopTree::policy_unswitching(PhaseIdealLoop* phase) const { // Check the absence of any If node that can be used for Loop Unswitching. In that case, no Loop Unswitching can be done. bool IdealLoopTree::no_unswitch_candidate() const { ResourceMark rm; - Node_List dont_care; + Node_List dont_care; return _phase->find_unswitch_candidates(this, dont_care) == nullptr; } @@ -162,7 +162,7 @@ bool IdealLoopTree::no_unswitch_candidate() const { // // Note that for both (2) and (3a), if there are multiple flat array checks, then the candidate's FlatArrayCheckNode is // later updated in Loop Unswitching to perform a flat array check on all collected flat array checks. -IfNode* PhaseIdealLoop::find_unswitch_candidates(const IdealLoopTree* loop, Node_List& flat_array_checks) const { +IfNode* PhaseIdealLoop::find_unswitch_candidates(const IdealLoopTree* loop, Node_List& flat_array_checks) const { IfNode* unswitch_candidate = find_unswitch_candidate_from_idoms(loop); if (unswitch_candidate != nullptr && !unswitch_candidate->is_flat_array_check(&_igvn)) { // Case (1) @@ -176,7 +176,7 @@ IfNode* PhaseIdealLoop::find_unswitch_candidates(const IdealLoopTree* loop, Node return unswitch_candidate; } else if (flat_array_checks.size() > 0) { // Case (3a): Pick first one found as candidate (there could be multiple). - return flat_array_checks[0]->as_If(); + return flat_array_checks[0]; } // Case (3b): No suitable unswitch candidate found. @@ -212,13 +212,13 @@ IfNode* PhaseIdealLoop::find_unswitch_candidate_from_idoms(const IdealLoopTree* } // Collect all flat array checks in the provided 'flat_array_checks' list. -void PhaseIdealLoop::collect_flat_array_checks(const IdealLoopTree* loop, Node_List& flat_array_checks) const { +void PhaseIdealLoop::collect_flat_array_checks(const IdealLoopTree* loop, Node_List& flat_array_checks) const { assert(flat_array_checks.size() == 0, "should be empty initially"); for (uint i = 0; i < loop->_body.size(); i++) { Node* next = loop->_body.at(i); if (next->is_If() && next->as_If()->is_flat_array_check(&_igvn) && loop->is_invariant(next->in(1)) && !loop->is_loop_exit(next)) { - flat_array_checks.push(next); + flat_array_checks.push(next->as_If()); } } } @@ -236,14 +236,14 @@ void PhaseIdealLoop::collect_flat_array_checks(const IdealLoopTree* loop, Node_L // - False-path-loop: We keep all flat array checks in this loop (i.e. a slow path loop). class UnswitchCandidate : public StackObj { PhaseIdealLoop* const _phase; - const Node_List& _old_new; + const Node_List& _old_new; Node* const _original_loop_entry; // If _candidate is a flat array check, this list contains all non-loop-exiting flat array checks in the loop body. - Node_List _flat_array_check_candidates; + Node_List _flat_array_check_candidates; IfNode* const _candidate; public: - UnswitchCandidate(IdealLoopTree* loop, const Node_List& old_new) + UnswitchCandidate(IdealLoopTree* loop, const Node_List& old_new) : _phase(loop->_phase), _old_new(old_new), _original_loop_entry(loop->_head->as_Loop()->skip_strip_mined()->in(LoopNode::EntryControl)), @@ -298,7 +298,7 @@ class UnswitchCandidate : public StackObj { void remove_flat_array_checks(IfProjNode* dominating_proj) const { for (uint i = 0; i < _flat_array_check_candidates.size(); i++) { - IfNode* flat_array_check = _flat_array_check_candidates.at(i)->as_If(); + IfNode* flat_array_check = _flat_array_check_candidates.at(i); _phase->igvn().rehash_node_delayed(flat_array_check); _phase->dominated_by(dominating_proj, flat_array_check); } @@ -483,11 +483,11 @@ class OriginalLoop : public StackObj { LoopNode* const _loop_head; LoopNode* const _outer_loop_head; // OuterStripMinedLoopNode if loop strip mined, else just the loop head. IdealLoopTree* const _loop; - Node_List& _old_new; + Node_List& _old_new; PhaseIdealLoop* const _phase; public: - OriginalLoop(IdealLoopTree* loop, Node_List& old_new) + OriginalLoop(IdealLoopTree* loop, Node_List& old_new) : _loop_head(loop->_head->as_Loop()), _outer_loop_head(loop->_head->as_Loop()->skip_strip_mined()), _loop(loop), @@ -566,7 +566,7 @@ class OriginalLoop : public StackObj { }; // See comments below file header for more information about Loop Unswitching. -void PhaseIdealLoop::do_unswitching(IdealLoopTree* loop, Node_List& old_new) { +void PhaseIdealLoop::do_unswitching(IdealLoopTree* loop, Node_List& old_new) { assert(LoopUnswitching, "LoopUnswitching must be enabled"); LoopNode* original_head = loop->_head->as_Loop(); @@ -599,7 +599,7 @@ void PhaseIdealLoop::do_unswitching(IdealLoopTree* loop, Node_List& old_new) { C->set_major_progress(); } -void PhaseIdealLoop::do_multiversioning(IdealLoopTree* lpt, Node_List& old_new) { +void PhaseIdealLoop::do_multiversioning(IdealLoopTree* lpt, Node_List& old_new) { #ifndef PRODUCT if (TraceLoopOpts || TraceLoopMultiversioning) { tty->print("Multiversion "); @@ -837,7 +837,7 @@ void PhaseIdealLoop::revert_to_normal_loop(const LoopNode* loop_head) { } // Hoist invariant CheckCastPPNodes out of each unswitched loop version to the appropriate loop selector If projection. -void PhaseIdealLoop::hoist_invariant_check_casts(const IdealLoopTree* loop, const Node_List& old_new, +void PhaseIdealLoop::hoist_invariant_check_casts(const IdealLoopTree* loop, const Node_List& old_new, const UnswitchCandidate& unswitch_candidate, const IfNode* loop_selector) { ResourceMark rm; @@ -870,7 +870,7 @@ void PhaseIdealLoop::hoist_invariant_check_casts(const IdealLoopTree* loop, cons } // Enable more optimizations possibilities in the next IGVN round. -void PhaseIdealLoop::add_unswitched_loop_version_bodies_to_igvn(IdealLoopTree* loop, const Node_List& old_new) { +void PhaseIdealLoop::add_unswitched_loop_version_bodies_to_igvn(IdealLoopTree* loop, const Node_List& old_new) const { loop->record_for_igvn(); for (int i = loop->_body.size() - 1; i >= 0; i--) { Node* n = loop->_body[i]; diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index b524b76a21d8..2869e1dde0f4 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -782,7 +782,7 @@ void PhaseIdealLoop::add_parse_predicates(IdealLoopTree* outer_ilt, LoopNode* in // // The same logic is used to transform an int counted loop that contains long range checks into a loop nest of 2 int // loops with long range checks transformed to int range checks in the inner loop. -bool PhaseIdealLoop::create_loop_nest(IdealLoopTree* loop, Node_List &old_new) { +bool PhaseIdealLoop::create_loop_nest(IdealLoopTree* loop, Node_List& old_new) { Node* x = loop->_head; // Only for inner loops if (loop->_child != nullptr || !x->is_BaseCountedLoop() || x->as_Loop()->is_loop_nest_outer_loop()) { @@ -847,7 +847,7 @@ bool PhaseIdealLoop::create_loop_nest(IdealLoopTree* loop, Node_List &old_new) { assert(back_control->Opcode() == Op_IfTrue, "wrong projection for back edge"); - Node_List range_checks; + Node_List range_checks; iters_limit = extract_long_range_checks(loop, stride_con, iters_limit, phi, range_checks); if (bt == T_INT) { @@ -1170,7 +1170,7 @@ Node* PhaseIdealLoop::new_assertion_predicate_opaque_init(Node* entry_control, N // - LongCountedLoop: Create LoopNode but keep the loop limit type with a CastLL node to avoid that we later try to // create a Loop Limit Check when turning the LoopNode into a CountedLoopNode. // - CountedLoop: Can be reused. -bool PhaseIdealLoop::try_make_short_running_loop(IdealLoopTree* loop, jint stride_con, const Node_List &range_checks, +bool PhaseIdealLoop::try_make_short_running_loop(IdealLoopTree* loop, jint stride_con, const Node_List& range_checks, const uint iters_limit) { if (!ShortRunningLongLoop) { return false; @@ -1362,7 +1362,7 @@ bool PhaseIdealLoop::try_make_short_running_loop(IdealLoopTree* loop, jint strid // - we have the proper form "scale*iv + offset & range_checks) { const jlong min_iters = 2; jlong reduced_iters_limit = iters_limit; const jlong original_iters_limit = iters_limit; @@ -1382,7 +1382,7 @@ int PhaseIdealLoop::extract_long_range_checks(const IdealLoopTree* loop, jint st assert(scale == (jint)scale, "scale should be an int"); // Ensure: reduced_iters_limit * ABS(scale) <= original_iters_limit < max_jint reduced_iters_limit = MIN2(reduced_iters_limit, original_iters_limit/ABS(scale)); - range_checks.push(c); + range_checks.push(if_proj); } } } @@ -2441,7 +2441,7 @@ int PhaseIdealLoop::extract_long_range_checks(const IdealLoopTree* loop, jint st // (LHS < 0) == (RHS < 0) == false // // We proved that the transformation is valid under the stated pre-conditions. -void PhaseIdealLoop::transform_long_range_checks(int stride_con, const Node_List &range_checks, Node* outer_phi, +void PhaseIdealLoop::transform_long_range_checks(int stride_con, const Node_List& range_checks, Node* outer_phi, Node* inner_iters_actual_int, Node* inner_phi, Node* iv_add, LoopNode* inner_head) { Node* long_zero = longcon(0); @@ -2450,7 +2450,7 @@ void PhaseIdealLoop::transform_long_range_checks(int stride_con, const Node_List Node* int_stride = intcon(checked_cast(stride_con)); for (uint i = 0; i < range_checks.size(); i++) { - ProjNode* proj = range_checks.at(i)->as_Proj(); + IfProjNode* proj = range_checks.at(i); RangeCheckNode* rc = proj->in(0)->as_RangeCheck(); jlong scale = 0; Node* offset = nullptr; @@ -2678,7 +2678,7 @@ void PhaseIdealLoop::check_counted_loop_shape(IdealLoopTree* loop, Node* head, B Node* back_control = loop_exit_control(loop); assert(back_control != nullptr, "no back control"); - LoopExitTest exit_test(back_control, loop, this); + LoopExitTest exit_test(back_control, loop); exit_test.build(); assert(exit_test.is_valid_with_bt(bt), "no exit test"); @@ -2940,8 +2940,8 @@ bool CountedLoopConverter::stress_long_counted_loop() { assert(should_stress_long_counted_loop(), "stress condition not satisfied"); PhaseIterGVN* igvn = &_phase->igvn(); - Unique_Node_List iv_nodes; - Node_List old_new; + Unique_Node_List iv_nodes; + Node_List old_new; iv_nodes.push(_structure.exit_test().cmp()); bool failed = false; @@ -3560,7 +3560,7 @@ IdealLoopTree* CountedLoopConverter::convert() { if (_phase->is_deleteable_safept(backedge_sfpt)) { _phase->replace_node_and_forward_ctrl(backedge_sfpt, back_control); if (_loop->_safepts != nullptr) { - _loop->_safepts->yank(backedge_sfpt); + _loop->_safepts->yank(backedge_sfpt->as_SafePoint()); } _loop->_tail = back_control; } @@ -4574,10 +4574,10 @@ void OuterStripMinedLoopNode::adjust_strip_mined_loop(PhaseIterGVN* igvn) { IfTrueNode* cle_tail = inner_cle->true_proj(); ResourceMark rm; - Node_List old_new; + Node_List old_new; if (cle_tail->outcnt() > 1) { // Look for nodes on backedge of inner loop and clone them - Unique_Node_List backedge_nodes; + Unique_Node_List backedge_nodes; for (DUIterator_Fast imax, i = cle_tail->fast_outs(imax); i < imax; i++) { Node* u = cle_tail->fast_out(i); if (u != inner_cl) { @@ -4723,7 +4723,7 @@ void OuterStripMinedLoopNode::transform_to_counted_loop(PhaseIterGVN* igvn, Phas igvn->replace_input_of(this, LoopBackControl, igvn->C->top()); inner_cl->clear_strip_mined(); if (iloop != nullptr) { - Unique_Node_List wq; + Unique_Node_List wq; wq.push(safepoint); IdealLoopTree* outer_loop_ilt = iloop->get_loop(this); @@ -5352,7 +5352,7 @@ bool IdealLoopTree::beautify_loops( PhaseIdealLoop *phase ) { //------------------------------allpaths_check_safepts---------------------------- // Allpaths backwards scan. Starting at the head, traversing all backedges, and the body. Terminating each path at first // safepoint encountered. Helper for check_safepts. -void IdealLoopTree::allpaths_check_safepts(VectorSet &visited, Node_List &stack) { +void IdealLoopTree::allpaths_check_safepts(VectorSet& visited, Node_List& stack) { assert(stack.size() == 0, "empty stack"); stack.push(_head); visited.clear(); @@ -5367,9 +5367,11 @@ void IdealLoopTree::allpaths_check_safepts(VectorSet &visited, Node_List &stack) // to avoid this additional check. } else if (n->Opcode() == Op_SafePoint) { if (_phase->get_loop(n) != this) { - if (_required_safept == nullptr) _required_safept = new Node_List(); + if (_required_safept == nullptr) { + _required_safept = new Node_List(); + } // save the first we run into on that path: closest to the tail if the head has a single backedge - _required_safept->push(n); + _required_safept->push(n->as_SafePoint()); } // Terminate this path } else { @@ -5449,7 +5451,7 @@ void IdealLoopTree::allpaths_check_safepts(VectorSet &visited, Node_List &stack) // from the tail to the head, terminating a path when a call or sfpt // is encountered, to find the ncsfpt's that are closest to the tail. // -void IdealLoopTree::check_safepts(VectorSet &visited, Node_List &stack) { +void IdealLoopTree::check_safepts(VectorSet& visited, Node_List& stack) { // Bottom up traversal IdealLoopTree* ch = _child; if (_child) _child->check_safepts(visited, stack); @@ -5517,8 +5519,10 @@ void IdealLoopTree::check_safepts(VectorSet &visited, Node_List &stack) { // inner loop attempts to delete it's safepoints. if (_child != nullptr && !has_call && !has_local_ncsfpt) { if (nonlocal_ncsfpt != nullptr) { - if (_required_safept == nullptr) _required_safept = new Node_List(); - _required_safept->push(nonlocal_ncsfpt); + if (_required_safept == nullptr) { + _required_safept = new Node_List(); + } + _required_safept->push(nonlocal_ncsfpt->as_SafePoint()); } else { // Failed to find a suitable safept on the dom-path. Now use // an all paths walk from tail to head, looking for safepoints to preserve. @@ -5534,7 +5538,7 @@ bool PhaseIdealLoop::is_deleteable_safept(Node* sfpt) const { assert(sfpt->Opcode() == Op_SafePoint, ""); IdealLoopTree* lp = get_loop(sfpt)->_parent; while (lp != nullptr) { - Node_List* sfpts = lp->_required_safept; + Node_List* sfpts = lp->_required_safept; if (sfpts != nullptr) { for (uint i = 0; i < sfpts->size(); i++) { if (sfpt == sfpts->at(i)) @@ -5725,7 +5729,7 @@ void IdealLoopTree::remove_safepoints(PhaseIdealLoop* phase, bool keep_one) { bool prune = !keep_one || keep != nullptr; // Delete other safepoints in this loop. - Node_List* sfpts = _safepts; + Node_List* sfpts = _safepts; if (prune && sfpts != nullptr) { assert(keep == nullptr || keep->Opcode() == Op_SafePoint, "not safepoint"); for (uint i = 0; i < sfpts->size(); i++) { @@ -5869,7 +5873,7 @@ uint IdealLoopTree::est_loop_flow_merge_sz() const { void IdealLoopTree::register_reachability_fence(ReachabilityFenceNode* rf) { if (_reachability_fences == nullptr) { - _reachability_fences = new Node_List(); + _reachability_fences = new Node_List(); } if (!_reachability_fences->contains(rf)) { _reachability_fences->push(rf); @@ -6018,12 +6022,12 @@ void PhaseIdealLoop::eliminate_useless_zero_trip_guard() { if (_zero_trip_guard_opaque_nodes.size() == 0) { return; } - Unique_Node_List useful_zero_trip_guard_opaques_nodes; + Unique_Node_List useful_zero_trip_guard_opaques_nodes; for (LoopTreeIterator iter(_ltree_root); !iter.done(); iter.next()) { IdealLoopTree* lpt = iter.current(); if (lpt->_child == nullptr && lpt->is_counted()) { CountedLoopNode* head = lpt->_head->as_CountedLoop(); - Node* opaque = head->is_canonical_loop_entry(); + OpaqueZeroTripGuardNode* opaque = head->is_canonical_loop_entry(); if (opaque != nullptr) { useful_zero_trip_guard_opaques_nodes.push(opaque); #ifdef ASSERT @@ -6048,7 +6052,7 @@ void PhaseIdealLoop::eliminate_useless_zero_trip_guard() { } } for (uint i = 0; i < _zero_trip_guard_opaque_nodes.size(); ++i) { - OpaqueZeroTripGuardNode* opaque = ((OpaqueZeroTripGuardNode*)_zero_trip_guard_opaque_nodes.at(i)); + OpaqueZeroTripGuardNode* opaque = _zero_trip_guard_opaque_nodes.at(i); DEBUG_ONLY(CountedLoopNode* guarded_loop = opaque->guarded_loop()); if (!useful_zero_trip_guard_opaques_nodes.member(opaque)) { IfNode* iff = opaque->if_node(); @@ -6076,7 +6080,7 @@ void PhaseIdealLoop::eliminate_useless_multiversion_if() { } ResourceMark rm; - Unique_Node_List useful_multiversioning_opaque_nodes; + Unique_Node_List useful_multiversioning_opaque_nodes; // The OpaqueMultiversioning is only used from the fast main loop in AutoVectorization, to add // speculative runtime-checks to the multiversion_if. Thus, a OpaqueMultiversioning is only @@ -6104,7 +6108,7 @@ void PhaseIdealLoop::eliminate_useless_multiversion_if() { } for (uint i = 0; i < _multiversion_opaque_nodes.size(); i++) { - OpaqueMultiversioningNode* opaque = _multiversion_opaque_nodes.at(i)->as_OpaqueMultiversioning(); + OpaqueMultiversioningNode* opaque = _multiversion_opaque_nodes.at(i); if (!useful_multiversioning_opaque_nodes.member(opaque)) { if (opaque->is_delayed_slow_loop()) { // We cannot hack the node directly, otherwise the slow_loop will complain that it cannot @@ -6336,7 +6340,7 @@ void PhaseIdealLoop::build_and_optimize() { // Given dominators, try to find inner loops with calls that must // always be executed (call dominates loop tail). These loops do // not need a separate safepoint. - Node_List cisstack; + Node_List cisstack; _ltree_root->check_safepts(visited, cisstack); } @@ -6347,10 +6351,10 @@ void PhaseIdealLoop::build_and_optimize() { // Allocate stack with enough space to avoid frequent realloc int stack_size = (C->live_nodes() >> 1) + 16; // (live_nodes>>1)+16 from Java2D stats - Node_Stack nstack(stack_size); + Node_Stack nstack(stack_size); visited.clear(); - Node_List worklist; + Node_List worklist; // Don't need C->root() on worklist since // it will be processed among C->top() inputs worklist.push(C->top()); @@ -6642,7 +6646,7 @@ void PhaseIdealLoop::verify() const { // Perform a BFS starting at n, through all inputs. // Call verify_idom and verify_node on all nodes of BFS traversal. bool PhaseIdealLoop::verify_idom_and_nodes(Node* root, const PhaseIdealLoop* phase_verify) const { - Unique_Node_List worklist; + Unique_Node_List worklist; worklist.push(root); bool success = true; for (uint i = 0; i < worklist.size(); i++) { @@ -7330,8 +7334,8 @@ int PhaseIdealLoop::build_loop_tree_impl(Node* n, int pre_order) { innermost->_has_call = 1; // = true } else if (n->Opcode() == Op_SafePoint) { // Record all safepoints in this loop. - if (innermost->_safepts == nullptr) innermost->_safepts = new Node_List(); - innermost->_safepts->push(n); + if (innermost->_safepts == nullptr) innermost->_safepts = new Node_List(); + innermost->_safepts->push(n->as_SafePoint()); } else if (n->is_ReachabilityFence()) { innermost->register_reachability_fence(n->as_ReachabilityFence()); } @@ -7356,7 +7360,7 @@ void PhaseIdealLoop::verify_regions_in_irreducible_loops() { } RootNode* root = C->root(); - Unique_Node_List worklist; // visit all nodes once + Unique_Node_List worklist; // visit all nodes once worklist.push(root); bool failure = false; for (uint i = 0; i < worklist.size(); i++) { @@ -7416,7 +7420,7 @@ bool PhaseIdealLoop::is_in_irreducible_loop(RegionNode* region) { // Put Data nodes into some loop nest, by setting the _loop_or_ctrl[]->loop mapping. // First pass computes the earliest controlling node possible. This is the // controlling input with the deepest dominating depth. -void PhaseIdealLoop::build_loop_early( VectorSet &visited, Node_List &worklist, Node_Stack &nstack ) { +void PhaseIdealLoop::build_loop_early(VectorSet& visited, Node_List& worklist, Node_Stack& nstack) { while (worklist.size() != 0) { // Use local variables nstack_top_n & nstack_top_i to cache values // on nstack's top. @@ -7453,7 +7457,7 @@ void PhaseIdealLoop::build_loop_early( VectorSet &visited, Node_List &worklist, Node *in = n->in(TypeFunc::Control); replace_node_and_forward_ctrl(n, in); // Pull safepoint now if (ilt->_safepts != nullptr) { - ilt->_safepts->yank(n); + ilt->_safepts->yank(n->as_SafePoint()); } // Carry on with the recursion "as if" we are walking // only the control input @@ -7633,7 +7637,7 @@ Node* PhaseIdealLoop::compute_lca_of_uses(Node* n, Node* early, bool verify) { // loop unswitching, and IGVN, or a combination of them) can freely change // the graph's shape. As a result, the graph shape outlined below cannot // be guaranteed anymore. -Node* CountedLoopNode::is_canonical_loop_entry() { +OpaqueZeroTripGuardNode* CountedLoopNode::is_canonical_loop_entry() { if (!is_main_loop() && !is_post_loop()) { return nullptr; } @@ -7659,8 +7663,8 @@ Node* CountedLoopNode::is_canonical_loop_entry() { if (input >= cmpzm->req() || cmpzm->in(input) == nullptr) { return nullptr; } - bool res = cmpzm->in(input)->Opcode() == Op_OpaqueZeroTripGuard; #ifdef ASSERT + bool res = cmpzm->in(input)->Opcode() == Op_OpaqueZeroTripGuard; bool found_opaque = false; for (uint i = 1; i < cmpzm->req(); i++) { Node* opnd = cmpzm->in(i); @@ -7671,7 +7675,7 @@ Node* CountedLoopNode::is_canonical_loop_entry() { } assert(found_opaque == res, "wrong pattern"); #endif - return res ? cmpzm->in(input) : nullptr; + return cmpzm->in(input)->isa_OpaqueZeroTripGuard(); } // Find pre loop end from main loop. Returns nullptr if none. @@ -7743,7 +7747,7 @@ Node *PhaseIdealLoop::get_late_ctrl( Node *n, Node *early ) { Node* PhaseIdealLoop::get_late_ctrl_with_anti_dep(LoadNode* n, Node* early, Node* LCA) { int load_alias_idx = C->get_alias_index(n->in_adr_type()); if (C->alias_type(load_alias_idx)->is_rewritable()) { - Unique_Node_List worklist; + Unique_Node_List worklist; Node* mem = n->in(MemNode::Memory); for (DUIterator_Fast imax, i = mem->fast_outs(imax); i < imax; i++) { @@ -7751,7 +7755,7 @@ Node* PhaseIdealLoop::get_late_ctrl_with_anti_dep(LoadNode* n, Node* early, Node worklist.push(s); } for (uint i = 0; i < worklist.size() && LCA != early; i++) { - Node* s = worklist.at(i); + const Node* s = worklist.at(i); if (s->is_Load() || s->Opcode() == Op_SafePoint || (s->is_CallStaticJava() && s->as_CallStaticJava()->uncommon_trap_request() != 0) || s->is_Phi()) { @@ -7794,7 +7798,7 @@ Node* PhaseIdealLoop::get_late_ctrl_with_anti_dep(LoadNode* n, Node* early, Node // For Phis only consider Region's inputs that were reached by following the memory edges if (LCA != early) { for (uint i = 0; i < worklist.size(); i++) { - Node* s = worklist.at(i); + const Node* s = worklist.at(i); if (s->is_Phi() && C->can_alias(s->out_adr_type(), load_alias_idx)) { Node* r = s->in(0); for (uint j = 1; j < s->req(); j++) { @@ -7912,7 +7916,7 @@ void PhaseIdealLoop::init_dom_lca_tags() { //------------------------------build_loop_late-------------------------------- // Put Data nodes into some loop nest, by setting the _loop_or_ctrl[]->loop mapping. // Second pass finds latest legal placement, and ideal loop placement. -void PhaseIdealLoop::build_loop_late( VectorSet &visited, Node_List &worklist, Node_Stack &nstack ) { +void PhaseIdealLoop::build_loop_late(VectorSet& visited, Node_List& worklist, Node_Stack& nstack) { while (worklist.size() != 0) { Node *n = worklist.pop(); // Only visit once @@ -7979,10 +7983,10 @@ void PhaseIdealLoop::verify_strip_mined_scheduling(Node *n, Node* least) { head->as_Loop()->outer_loop_end()->in(1)->find_int_con(-1) == 0) { Node* sfpt = head->as_Loop()->outer_safepoint(); ResourceMark rm; - Unique_Node_List wq; + Unique_Node_List wq; wq.push(sfpt); for (uint i = 0; i < wq.size(); i++) { - Node *m = wq.at(i); + const Node* m = wq.at(i); for (uint i = 1; i < m->req(); i++) { Node* nn = m->in(i); if (nn == n) { @@ -8207,12 +8211,12 @@ void PhaseIdealLoop::build_loop_late_post_work(Node *n, bool pinned) { if( !chosen_loop->_child ) // Inner loop? chosen_loop->_body.push(n);// Collect inner loops - if (!_verify_only && n->Opcode() == Op_OpaqueZeroTripGuard) { - _zero_trip_guard_opaque_nodes.push(n); + if (!_verify_only && n->is_OpaqueZeroTripGuard()) { + _zero_trip_guard_opaque_nodes.push(n->as_OpaqueZeroTripGuard()); } - if (!_verify_only && n->Opcode() == Op_OpaqueMultiversioning) { - _multiversion_opaque_nodes.push(n); + if (!_verify_only && n->is_OpaqueMultiversioning()) { + _multiversion_opaque_nodes.push(n->as_OpaqueMultiversioning()); } } @@ -8301,7 +8305,7 @@ class RealLCA { // Given idom chains of early and wrong LCA: Walk through idoms starting at StartNode and find the first node which // is different: Return the previously visited node which must be the real LCA. // The node lists also contain _early and _wrong_lca, respectively. - Node* find_real_lca(Unique_Node_List& early_with_idoms, Unique_Node_List& wrong_lca_with_idoms) { + Node* find_real_lca(const Unique_Node_List& early_with_idoms, const Unique_Node_List& wrong_lca_with_idoms) { int early_index = early_with_idoms.size() - 1; int wrong_lca_index = wrong_lca_with_idoms.size() - 1; bool found_difference = false; @@ -8350,8 +8354,8 @@ class RealLCA { void compute_and_dump() { ResourceMark rm; - Unique_Node_List early_with_idoms; - Unique_Node_List wrong_lca_with_idoms; + Unique_Node_List early_with_idoms; + Unique_Node_List wrong_lca_with_idoms; early_with_idoms.push(_early); wrong_lca_with_idoms.push(_wrong_lca); _phase->get_idoms(_early, 10000, early_with_idoms); @@ -8375,8 +8379,8 @@ void PhaseIdealLoop::dump_idoms(Node* early, Node* wrong_lca) { //------------------------------dump------------------------------------------- void PhaseIdealLoop::dump() const { ResourceMark rm; - Node_Stack stack(C->live_nodes() >> 2); - Node_List rpo_list; + Node_Stack stack(C->live_nodes() >> 2); + Node_List rpo_list; VectorSet visited; visited.set(C->top()->_idx); rpo(C->root(), stack, visited, rpo_list); @@ -8384,7 +8388,7 @@ void PhaseIdealLoop::dump() const { dump(_ltree_root, rpo_list.size(), rpo_list); } -void PhaseIdealLoop::dump(IdealLoopTree* loop, uint idx, Node_List &rpo_list) const { +void PhaseIdealLoop::dump(IdealLoopTree* loop, uint idx, Node_List& rpo_list) const { loop->dump_head(); // Now scan for CFG nodes in the same loop @@ -8445,13 +8449,13 @@ void PhaseIdealLoop::dump_idom(Node* n, const uint count) const { tty->print_cr("No idom for data nodes"); } else { ResourceMark rm; - Unique_Node_List idoms; + Unique_Node_List idoms; get_idoms(n, count, idoms); dump_idoms_in_reverse(n, idoms); } } -void PhaseIdealLoop::get_idoms(Node* n, const uint count, Unique_Node_List& idoms) const { +void PhaseIdealLoop::get_idoms(Node* n, const uint count, Unique_Node_List& idoms) const { Node* next = n; for (uint i = 0; !next->is_Start() && i < count; i++) { next = idom(next); @@ -8460,7 +8464,7 @@ void PhaseIdealLoop::get_idoms(Node* n, const uint count, Unique_Node_List& idom } } -void PhaseIdealLoop::dump_idoms_in_reverse(const Node* n, const Node_List& idom_list) const { +void PhaseIdealLoop::dump_idoms_in_reverse(const Node* n, const Node_List& idom_list) const { Node* next; uint padding = 3; uint node_index_padding_width = (C->unique() == 0 ? 0 : static_cast(log10(static_cast(C->unique())))) + 1; @@ -8477,7 +8481,7 @@ void PhaseIdealLoop::dump_idoms_in_reverse(const Node* n, const Node_List& idom_ // Collect a R-P-O for the whole CFG. // Result list is in post-order (scan backwards for RPO) -void PhaseIdealLoop::rpo(Node* start, Node_Stack &stk, VectorSet &visited, Node_List &rpo_list) const { +void PhaseIdealLoop::rpo(Node* start, Node_Stack& stk, VectorSet& visited, Node_List& rpo_list) { stk.push(start, 0); visited.set(start->_idx); diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 70ee7fc45f35..f1e73b38f2b1 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -372,7 +372,7 @@ class CountedLoopNode : public BaseCountedLoopNode { return T_INT; } - Node* is_canonical_loop_entry(); + OpaqueZeroTripGuardNode* is_canonical_loop_entry(); CountedLoopEndNode* find_pre_loop_end(); Node* uncasted_init_trip(bool uncasted); @@ -574,7 +574,6 @@ class LoopLimitNode : public Node { public: LoopLimitNode( Compile* C, Node *init, Node *limit, Node *stride ) : Node(nullptr,init,limit,stride) { // Put it on the Macro nodes list to optimize during macro nodes expansion. - init_flags(Flag_is_macro); C->add_macro_node(this); } virtual int Opcode() const; @@ -595,7 +594,6 @@ class OuterStripMinedLoopNode : public LoopNode { OuterStripMinedLoopNode(Compile* C, Node *entry, Node *backedge) : LoopNode(entry, backedge) { init_class_id(Class_OuterStripMinedLoop); - init_flags(Flag_is_macro); C->add_macro_node(this); } @@ -655,7 +653,7 @@ class IdealLoopTree : public ResourceObj { int _local_loop_unroll_limit; int _local_loop_unroll_factor; - Node_List _body; // Loop body for inner loops + Node_List _body; // Loop body for inner loops uint16_t _nest; // Nesting depth uint8_t _irreducible:1, // True if irreducible @@ -665,9 +663,9 @@ class IdealLoopTree : public ResourceObj { _has_range_checks:1, _has_range_checks_computed:1; - Node_List* _safepts; // List of safepoints in this loop - Node_List* _required_safept; // A inner loop cannot delete these safepts; - Node_List* _reachability_fences; // List of reachability fences in this loop + Node_List* _safepts; // List of safepoints in this loop + Node_List* _required_safept; // A inner loop cannot delete these safepts; + Node_List* _reachability_fences; // List of reachability fences in this loop bool _allow_optimizations; // Allow loop optimizations IdealLoopTree(PhaseIdealLoop* phase, Node* head, Node* tail); @@ -702,20 +700,20 @@ class IdealLoopTree : public ResourceObj { // Perform iteration-splitting on inner loops. Split iterations to // avoid range checks or one-shot null checks. Returns false if the // current round of loop opts should stop. - bool iteration_split( PhaseIdealLoop *phase, Node_List &old_new ); + bool iteration_split(PhaseIdealLoop* phase, Node_List& old_new); // Driver for various flavors of iteration splitting. Returns false // if the current round of loop opts should stop. - bool iteration_split_impl( PhaseIdealLoop *phase, Node_List &old_new ); + bool iteration_split_impl(PhaseIdealLoop* phase, Node_List& old_new); // Given dominators, try to find loops with calls that must always be // executed (call dominates loop tail). These loops do not need non-call // safepoints (ncsfpt). - void check_safepts(VectorSet &visited, Node_List &stack); + void check_safepts(VectorSet& visited, Node_List& stack); // Allpaths backwards scan from loop tail, terminating each path at first safepoint // encountered. - void allpaths_check_safepts(VectorSet &visited, Node_List &stack); + void allpaths_check_safepts(VectorSet& visited, Node_List& stack); // Remove safepoints from loop. Optionally keeping one. void remove_safepoints(PhaseIdealLoop* phase, bool keep_one); @@ -777,8 +775,8 @@ class IdealLoopTree : public ResourceObj { bool policy_range_check(PhaseIdealLoop* phase, bool provisional, BasicType bt) const; // Return TRUE if "iff" is a range check. - bool is_range_check_if(IfProjNode* if_success_proj, PhaseIdealLoop* phase, Invariance& invar DEBUG_ONLY(COMMA ProjNode* predicate_proj)) const; - bool is_range_check_if(IfProjNode* if_success_proj, PhaseIdealLoop* phase, BasicType bt, Node* iv, Node*& range, Node*& offset, + bool is_range_check_if(const IfProjNode* if_success_proj, PhaseIdealLoop* phase, Invariance& invar DEBUG_ONLY(COMMA ProjNode* predicate_proj)) const; + bool is_range_check_if(const IfProjNode* if_success_proj, PhaseIdealLoop* phase, BasicType bt, Node* iv, Node*& range, Node*& offset, jlong& scale) const; // Estimate the number of nodes required when cloning a loop (body). @@ -863,13 +861,13 @@ class IdealLoopTree : public ResourceObj { return (unroll_cnt - 1) * (100.0 / LoopPercentProfileLimit) > cl->profile_trip_cnt(); } - void collect_loop_core_nodes(PhaseIdealLoop* phase, Unique_Node_List& wq) const; + void collect_loop_core_nodes(PhaseIdealLoop* phase, Unique_Node_List& wq) const; bool empty_loop_with_data_nodes(PhaseIdealLoop* phase) const; - void enqueue_data_nodes(PhaseIdealLoop* phase, Unique_Node_List& empty_loop_nodes, Unique_Node_List& wq) const; + void enqueue_data_nodes(PhaseIdealLoop* phase, Unique_Node_List& empty_loop_nodes, Unique_Node_List& wq) const; - bool process_safepoint(PhaseIdealLoop* phase, Unique_Node_List& empty_loop_nodes, Unique_Node_List& wq, + bool process_safepoint(PhaseIdealLoop* phase, Unique_Node_List& empty_loop_nodes, Unique_Node_List& wq, Node* sfpt) const; bool empty_loop_candidate(PhaseIdealLoop* phase) const; @@ -893,7 +891,7 @@ class PhaseIdealLoop : public PhaseTransform { // // Exception: dead CFG nodes may instead have a ctrl/idom forwarding // installed. See: forward_ctrl - Node_List _loop_or_ctrl; + Node_List _loop_or_ctrl; // Pre-computed def-use info PhaseIterGVN &_igvn; @@ -970,9 +968,9 @@ class PhaseIdealLoop : public PhaseTransform { private: // clear out dead code after build_loop_late - Node_List _deadlist; - Node_List _zero_trip_guard_opaque_nodes; - Node_List _multiversion_opaque_nodes; + Node_List _deadlist; + Node_List _zero_trip_guard_opaque_nodes; + Node_List _multiversion_opaque_nodes; // Support for faster execution of get_late_ctrl()/dom_lca() // when a node has many uses and dominator depth is deep. @@ -1017,19 +1015,17 @@ class PhaseIdealLoop : public PhaseTransform { static void ensure_zero_trip_guard_proj(Node* node, bool is_main_loop); #endif private: - static void get_opaque_template_assertion_predicate_nodes(ParsePredicateSuccessProj* parse_predicate_proj, - Unique_Node_List& list); void update_main_loop_assertion_predicates(CountedLoopNode* new_main_loop_head, int stride_con_before_unroll); void initialize_assertion_predicates_for_peeled_loop(CountedLoopNode* peeled_loop_head, CountedLoopNode* remaining_loop_head, uint first_node_index_in_cloned_loop_body, - const Node_List& old_new); + const Node_List& old_new); void initialize_assertion_predicates_for_main_loop(CountedLoopNode* pre_loop_head, CountedLoopNode* main_loop_head, uint first_node_index_in_pre_loop_body, uint last_node_index_in_pre_loop_body, DEBUG_ONLY(uint last_node_index_from_backedge_goo COMMA) - const Node_List& old_new); + const Node_List& old_new); void initialize_assertion_predicates_for_post_loop(CountedLoopNode* main_loop_head, CountedLoopNode* post_loop_head, uint first_node_index_in_cloned_loop_body); void create_assertion_predicates_at_loop(CountedLoopNode* source_loop_head, CountedLoopNode* target_loop_head, @@ -1210,8 +1206,8 @@ class PhaseIdealLoop : public PhaseTransform { #endif // Place Data nodes in some loop nest - void build_loop_early( VectorSet &visited, Node_List &worklist, Node_Stack &nstack ); - void build_loop_late ( VectorSet &visited, Node_List &worklist, Node_Stack &nstack ); + void build_loop_early(VectorSet& visited, Node_List& worklist, Node_Stack& nstack); + void build_loop_late(VectorSet& visited, Node_List& worklist, Node_Stack& nstack); void build_loop_late_post_work(Node* n, bool pinned); void build_loop_late_post(Node* n); void verify_strip_mined_scheduling(Node *n, Node* least); @@ -1287,8 +1283,8 @@ class PhaseIdealLoop : public PhaseTransform { return n; } - void elide_redundant_tests_after_peeling(IdealLoopTree* loop, const Node_List& old_new); - void elide_redundant_stores_after_peeling(IdealLoopTree* loop, const Node_List& old_new); + void elide_redundant_tests_after_peeling(IdealLoopTree* loop, const Node_List& old_new); + void elide_redundant_stores_after_peeling(IdealLoopTree* loop, const Node_List& old_new); public: Node* idom(Node* n) const { @@ -1375,7 +1371,6 @@ class PhaseIdealLoop : public PhaseTransform { const Node* _back_control; const IdealLoopTree* _loop; - PhaseIdealLoop* _phase; Node* _cmp; Node* _incr; @@ -1384,11 +1379,10 @@ class PhaseIdealLoop : public PhaseTransform { float _cl_prob; public: - LoopExitTest(const Node* back_control, const IdealLoopTree* loop, PhaseIdealLoop* phase) : + LoopExitTest(const Node* back_control, const IdealLoopTree* loop) : _is_valid(false), _back_control(back_control), _loop(loop), - _phase(phase), _cmp(nullptr), _incr(nullptr), _limit(nullptr), @@ -1467,7 +1461,7 @@ class PhaseIdealLoop : public PhaseTransform { bool try_convert_to_counted_loop(Node* head, IdealLoopTree*& loop, BasicType iv_bt); Node* loop_nest_replace_iv(Node* iv_to_replace, Node* inner_iv, Node* outer_phi, Node* inner_head, BasicType bt); - bool create_loop_nest(IdealLoopTree* loop, Node_List &old_new); + bool create_loop_nest(IdealLoopTree* loop, Node_List& old_new); void add_parse_predicate(Deoptimization::DeoptReason reason, Node* inner_head, IdealLoopTree* loop, SafePointNode* sfpt); SafePointNode* find_safepoint(Node* back_control, const Node* head, const IdealLoopTree* loop); @@ -1524,49 +1518,49 @@ class PhaseIdealLoop : public PhaseTransform { // either to inner clone or outer // strip mined loop. }; - void clone_loop( IdealLoopTree *loop, Node_List &old_new, int dom_depth, + void clone_loop(IdealLoopTree* loop, Node_List& old_new, int dom_depth, CloneLoopMode mode, Node* side_by_side_idom = nullptr); - void clone_loop_handle_data_uses(Node* old, Node_List &old_new, + void clone_loop_handle_data_uses(Node* old, const Node_List& old_new, IdealLoopTree* loop, IdealLoopTree* companion_loop, - Node_List*& split_if_set, Node_List*& split_bool_set, - Node_List*& split_cex_set, Node_List& worklist, + Node_List*& split_if_set, Node_List*& split_bool_set, + Node_List*& split_cex_set, Node_List& worklist, uint new_counter, CloneLoopMode mode); void clone_outer_loop(LoopNode* head, CloneLoopMode mode, IdealLoopTree *loop, - IdealLoopTree* outer_loop, int dd, Node_List &old_new, - Node_List& extra_data_nodes); + IdealLoopTree* outer_loop, int dd, Node_List& old_new, + Node_List& extra_data_nodes); - void elide_redundancies_after_peeling(IdealLoopTree* loop, const Node_List& old_new); + void elide_redundancies_after_peeling(IdealLoopTree* loop, const Node_List& old_new); // Generate code to do a loop peel for the given loop (and body). // old_new is a temp array. - void do_peeling( IdealLoopTree *loop, Node_List &old_new ); + void do_peeling(IdealLoopTree* loop, Node_List& old_new); // Add pre and post loops around the given loop. These loops are used // during RCE, unrolling and aligning loops. - void insert_pre_post_loops( IdealLoopTree *loop, Node_List &old_new, bool peel_only ); + void insert_pre_post_loops(IdealLoopTree* loop, Node_List& old_new, bool peel_only); // Find the last store in the body of an OuterStripMinedLoop when following memory uses Node *find_last_store_in_outer_loop(Node* store, const IdealLoopTree* outer_loop); // Add post loop after the given loop. - Node *insert_post_loop(IdealLoopTree* loop, Node_List& old_new, + Node* insert_post_loop(IdealLoopTree* loop, Node_List& old_new, CountedLoopNode* main_head, CountedLoopEndNode* main_end, Node* incr, Node* limit, CountedLoopNode*& post_head); // Add a vector post loop between a vector main loop and the current post loop - void insert_vector_post_loop(IdealLoopTree *loop, Node_List &old_new); + void insert_vector_post_loop(IdealLoopTree* loop, Node_List& old_new); // If Node n lives in the back_ctrl block, we clone a private version of n // in preheader_ctrl block and return that, otherwise return n. - Node *clone_up_backedge_goo( Node *back_ctrl, Node *preheader_ctrl, Node *n, VectorSet &visited, Node_Stack &clones ); + Node* clone_up_backedge_goo(Node* back_ctrl, Node* preheader_ctrl, Node* n, VectorSet& visited, Node_Stack& clones); // Take steps to maximally unroll the loop. Peel any odd iterations, then // unroll to do double iterations. The next round of major loop transforms // will repeat till the doubled loop body does all remaining iterations in 1 // pass. - void do_maximally_unroll( IdealLoopTree *loop, Node_List &old_new ); + void do_maximally_unroll(IdealLoopTree* loop, Node_List& old_new); // Unroll the loop body one step - make each trip do 2 iterations. - void do_unroll( IdealLoopTree *loop, Node_List &old_new, bool adjust_min_trip ); + void do_unroll(IdealLoopTree* loop, Node_List& old_new, bool adjust_min_trip); // Return true if exp is a constant times an induction var bool is_scaled_iv(Node* exp, Node* iv, BasicType bt, jlong* p_scale, int depth = 0); @@ -1599,9 +1593,9 @@ class PhaseIdealLoop : public PhaseTransform { private: // Helper functions for create_new_if_for_predicate() - void set_ctrl_of_nodes_with_same_ctrl(Node* start_node, ProjNode* old_uncommon_proj, Node* new_uncommon_proj); - Unique_Node_List find_nodes_with_same_ctrl(Node* node, const ProjNode* ctrl); - Node* clone_nodes_with_same_ctrl(Node* start_node, ProjNode* old_uncommon_proj, Node* new_uncommon_proj); + void set_ctrl_of_nodes_with_same_ctrl(Node* start_node, const ProjNode* old_uncommon_proj, Node* new_uncommon_proj); + Unique_Node_List find_nodes_with_same_ctrl(Node* node, const ProjNode* ctrl); + Node* clone_nodes_with_same_ctrl(Node* start_node, const ProjNode* old_uncommon_proj, Node* new_uncommon_proj); void fix_cloned_data_node_controls(const ProjNode* orig, Node* new_uncommon_proj, const OrigToNewHashtable& orig_to_clone); @@ -1629,21 +1623,21 @@ class PhaseIdealLoop : public PhaseTransform { // Reachability Fence (RF) support. private: void insert_rf(Node* ctrl, Node* referent); - void replace_rf(Node* old_node, Node* new_node); + void replace_rf(ReachabilityFenceNode* old_node, Node* new_node); void remove_rf(ReachabilityFenceNode* rf); public: bool optimize_reachability_fences(); bool expand_reachability_fences(); private: - bool loop_predication_impl_helper(IdealLoopTree* loop, IfProjNode* if_success_proj, + bool loop_predication_impl_helper(IdealLoopTree* loop, const IfProjNode* if_success_proj, ParsePredicateSuccessProj* parse_predicate_proj, CountedLoopNode* cl, ConNode* zero, Invariance& invar, Deoptimization::DeoptReason deopt_reason); bool can_create_loop_predicates(const PredicateBlock* profiled_loop_predicate_block) const; bool loop_predication_should_follow_branches(IdealLoopTree* loop, float& loop_trip_cnt); - void loop_predication_follow_branches(Node *c, IdealLoopTree *loop, float loop_trip_cnt, - PathFrequency& pf, Node_Stack& stack, VectorSet& seen, - Node_List& if_proj_list); + void loop_predication_follow_branches(RegionNode* c, IdealLoopTree* loop, float loop_trip_cnt, + PathFrequency& pf, Node_Stack& stack, VectorSet& seen, + Node_List& if_proj_list); IfTrueNode* create_template_assertion_predicate(CountedLoopNode* loop_head, ParsePredicateNode* parse_predicate, IfProjNode* new_control, int scale, Node* offset, Node* range); void eliminate_hoisted_range_check(IfTrueNode* hoisted_check_proj, IfTrueNode* template_assertion_predicate_proj); @@ -1671,20 +1665,19 @@ class PhaseIdealLoop : public PhaseTransform { // Clone loop with an invariant test (that does not exit) and // insert a clone of the test that selects which version to // execute. - void do_unswitching(IdealLoopTree* loop, Node_List& old_new); + void do_unswitching(IdealLoopTree* loop, Node_List& old_new); - IfNode* find_unswitch_candidates(const IdealLoopTree* loop, Node_List& flat_array_checks) const; + IfNode* find_unswitch_candidates(const IdealLoopTree* loop, Node_List& flat_array_checks) const; IfNode* find_unswitch_candidate_from_idoms(const IdealLoopTree* loop) const; private: static bool has_control_dependencies_from_predicates(LoopNode* head); static void revert_to_normal_loop(const LoopNode* loop_head); - void hoist_invariant_check_casts(const IdealLoopTree* loop, const Node_List& old_new, + void hoist_invariant_check_casts(const IdealLoopTree* loop, const Node_List& old_new, const UnswitchCandidate& unswitch_candidate, const IfNode* loop_selector); - void add_unswitched_loop_version_bodies_to_igvn(IdealLoopTree* loop, const Node_List& old_new); + void add_unswitched_loop_version_bodies_to_igvn(IdealLoopTree* loop, const Node_List& old_new) const; static void increment_unswitch_counts(LoopNode* original_head, LoopNode* new_head); - void remove_unswitch_candidate_from_loops(const Node_List& old_new, const UnswitchedLoopSelector& unswitched_loop_selector); #ifndef PRODUCT static void trace_loop_unswitching_count(IdealLoopTree* loop, LoopNode* original_head); static void trace_loop_unswitching_impossible(const LoopNode* original_head); @@ -1708,8 +1701,8 @@ class PhaseIdealLoop : public PhaseTransform { Node* adjust_limit(bool reduce, Node* scale, Node* offset, Node* rc_limit, Node* old_limit, Node* pre_ctrl, bool round); // Partially peel loop up through last_peel node. - bool partial_peel( IdealLoopTree *loop, Node_List &old_new ); - bool duplicate_loop_backedge(IdealLoopTree *loop, Node_List &old_new); + bool partial_peel(IdealLoopTree* loop, Node_List& old_new); + bool duplicate_loop_backedge(IdealLoopTree* loop, Node_List& old_new); // AutoVectorize the loop: replace scalar ops with vector ops. enum AutoVectorizeStatus { @@ -1719,30 +1712,30 @@ class PhaseIdealLoop : public PhaseTransform { }; AutoVectorizeStatus auto_vectorize(IdealLoopTree* lpt, VSharedData &vshared); - void maybe_multiversion_for_auto_vectorization_runtime_checks(IdealLoopTree* lpt, Node_List& old_new); - void do_multiversioning(IdealLoopTree* lpt, Node_List& old_new); + void maybe_multiversion_for_auto_vectorization_runtime_checks(IdealLoopTree* lpt, Node_List& old_new); + void do_multiversioning(IdealLoopTree* lpt, Node_List& old_new); IfTrueNode* create_new_if_for_multiversion(IfTrueNode* multiversioning_fast_proj); bool try_resume_optimizations_for_delayed_slow_loop(IdealLoopTree* lpt); // Create a scheduled list of nodes control dependent on ctrl set. - void scheduled_nodelist( IdealLoopTree *loop, VectorSet& ctrl, Node_List &sched ); + static void scheduled_nodelist(const IdealLoopTree* loop, const VectorSet& ctrl, Node_List& sched); // Has a use in the vector set bool has_use_in_set( Node* n, VectorSet& vset ); // Has use internal to the vector set (ie. not in a phi at the loop head) bool has_use_internal_to_set( Node* n, VectorSet& vset, IdealLoopTree *loop ); // clone "n" for uses that are outside of loop - int clone_for_use_outside_loop( IdealLoopTree *loop, Node* n, Node_List& worklist ); + int clone_for_use_outside_loop(IdealLoopTree* loop, Node* n, Node_List& worklist); // clone "n" for special uses that are in the not_peeled region - void clone_for_special_use_inside_loop( IdealLoopTree *loop, Node* n, - VectorSet& not_peel, Node_List& sink_list, Node_List& worklist ); + void clone_for_special_use_inside_loop(IdealLoopTree* loop, Node* n, + VectorSet& not_peel, Node_List& sink_list, Node_List& worklist); // Insert phi(lp_entry_val, back_edge_val) at use->in(idx) for loop lp if phi does not already exist void insert_phi_for_loop( Node* use, uint idx, Node* lp_entry_val, Node* back_edge_val, LoopNode* lp ); #ifdef ASSERT // Validate the loop partition sets: peel and not_peel - bool is_valid_loop_partition( IdealLoopTree *loop, VectorSet& peel, Node_List& peel_list, VectorSet& not_peel ); + static bool is_valid_loop_partition(const IdealLoopTree* loop, const VectorSet& peel, const Node_List& peel_list, const VectorSet& not_peel); // Ensure that uses outside of loop are of the right form - bool is_valid_clone_loop_form( IdealLoopTree *loop, Node_List& peel_list, - uint orig_exit_idx, uint clone_exit_idx); + bool is_valid_clone_loop_form(IdealLoopTree* loop, Node_List& peel_list, + uint orig_exit_idx, uint clone_exit_idx); bool is_valid_clone_loop_exit_use( IdealLoopTree *loop, Node* use, uint exit_idx); #endif @@ -1787,10 +1780,10 @@ class PhaseIdealLoop : public PhaseTransform { // Check for aggressive application of 'split-if' optimization, // using basic block level info. - void split_if_with_blocks ( VectorSet &visited, Node_Stack &nstack); - Node *split_if_with_blocks_pre ( Node *n ); - void split_if_with_blocks_post( Node *n ); - Node *has_local_phi_input( Node *n ); + void split_if_with_blocks(VectorSet& visited, Node_Stack& nstack); + Node* split_if_with_blocks_pre(Node* n); + void split_if_with_blocks_post(Node* n); + Node* has_local_phi_input(Node* n); // Mark an IfNode as being dominated by a prior test, // without actually altering the CFG (and hence IDOM info). void dominated_by(IfProjNode* prevdom, IfNode* iff, bool flip = false, bool prev_dom_not_imply_this = false); @@ -1969,7 +1962,7 @@ class PhaseIdealLoop : public PhaseTransform { bool _created_loop_node; DEBUG_ONLY(void dump_idoms(Node* early, Node* wrong_lca);) - NOT_PRODUCT(void dump_idoms_in_reverse(const Node* n, const Node_List& idom_list) const;) + NOT_PRODUCT(void dump_idoms_in_reverse(const Node* n, const Node_List& idom_list) const;) public: void set_created_loop_node() { _created_loop_node = true; } @@ -1993,8 +1986,8 @@ class PhaseIdealLoop : public PhaseTransform { void dump() const; void dump_idom(Node* n) const { dump_idom(n, 1000); } // For debugging void dump_idom(Node* n, uint count) const; - void get_idoms(Node* n, uint count, Unique_Node_List& idoms) const; - void dump(IdealLoopTree* loop, uint rpo_idx, Node_List &rpo_list) const; + void get_idoms(Node* n, uint count, Unique_Node_List& idoms) const; + void dump(IdealLoopTree* loop, uint rpo_idx, Node_List& rpo_list) const; IdealLoopTree* get_loop_idx(Node* n) const { // Dead nodes have no loop, so return the top level loop instead return _loop_or_ctrl[n->_idx] ? (IdealLoopTree*)_loop_or_ctrl[n->_idx] : _ltree_root; @@ -2014,7 +2007,7 @@ class PhaseIdealLoop : public PhaseTransform { bool verify_loop_ctrl(Node* n, const PhaseIdealLoop* phase_verify) const; #endif - void rpo(Node* start, Node_Stack &stk, VectorSet &visited, Node_List &rpo_list) const; + static void rpo(Node* start, Node_Stack& stk, VectorSet& visited, Node_List& rpo_list); void check_counted_loop_shape(IdealLoopTree* loop, Node* head, BasicType bt) NOT_DEBUG_RETURN; @@ -2022,9 +2015,9 @@ class PhaseIdealLoop : public PhaseTransform { int extract_long_range_checks(const IdealLoopTree* loop, jint stride_con, int iters_limit, PhiNode* phi, - Node_List &range_checks); + Node_List& range_checks); - void transform_long_range_checks(int stride_con, const Node_List &range_checks, Node* outer_phi, + void transform_long_range_checks(int stride_con, const Node_List& range_checks, Node* outer_phi, Node* inner_iters_actual_int, Node* inner_phi, Node* iv_add, LoopNode* inner_head); @@ -2048,19 +2041,19 @@ class PhaseIdealLoop : public PhaseTransform { bool try_merge_identical_ifs(Node* n); - void clone_loop_body(const Node_List& body, Node_List &old_new, CloneMap* cm); + void clone_loop_body(const Node_List& body, Node_List& old_new, CloneMap* cm) const; - void fix_body_edges(const Node_List &body, IdealLoopTree* loop, const Node_List &old_new, int dd, + void fix_body_edges(const Node_List& body, IdealLoopTree* loop, const Node_List& old_new, int dd, IdealLoopTree* parent, bool partial); - void fix_ctrl_uses(const Node_List& body, const IdealLoopTree* loop, Node_List &old_new, CloneLoopMode mode, - Node* side_by_side_idom, CloneMap* cm, Node_List &worklist); + void fix_ctrl_uses(const Node_List& body, const IdealLoopTree* loop, Node_List& old_new, CloneLoopMode mode, + Node* side_by_side_idom, CloneMap* cm, Node_List& worklist); - void fix_data_uses(Node_List& body, IdealLoopTree* loop, CloneLoopMode mode, IdealLoopTree* outer_loop, - uint new_counter, Node_List& old_new, Node_List& worklist, Node_List*& split_if_set, - Node_List*& split_bool_set, Node_List*& split_cex_set); + void fix_data_uses(Node_List& body, IdealLoopTree* loop, CloneLoopMode mode, IdealLoopTree* outer_loop, + uint new_counter, Node_List& old_new, Node_List& worklist, Node_List*& split_if_set, + Node_List*& split_bool_set, Node_List*& split_cex_set); - void finish_clone_loop(Node_List* split_if_set, Node_List* split_bool_set, Node_List* split_cex_set); + void finish_clone_loop(Node_List* split_if_set, Node_List* split_bool_set, Node_List* split_cex_set); bool at_relevant_ctrl(Node* n, const Node* blk1, const Node* blk2); @@ -2071,19 +2064,19 @@ class PhaseIdealLoop : public PhaseTransform { Node* similar_subtype_check(const Node* x, Node* r_in); - void update_addp_chain_base(Node* x, Node* old_base, Node* new_base); + void update_addp_chain_base(const AddPNode* x, const Node* old_base, Node* new_base) const; bool can_move_to_inner_loop(Node* n, LoopNode* n_loop, Node* x); void pin_nodes_dependent_on(Node* ctrl, bool old_iff_is_rangecheck); - void collect_flat_array_checks(const IdealLoopTree* loop, Node_List& flat_array_checks) const; + void collect_flat_array_checks(const IdealLoopTree* loop, Node_List& flat_array_checks) const; Node* ensure_node_and_inputs_are_above_pre_end(CountedLoopEndNode* pre_end, Node* node); Node* new_assertion_predicate_opaque_init(Node* entry_control, Node* init, Node* int_zero); - bool try_make_short_running_loop(IdealLoopTree* loop, jint stride_con, const Node_List& range_checks, const uint iters_limit); + bool try_make_short_running_loop(IdealLoopTree* loop, jint stride_con, const Node_List& range_checks, const uint iters_limit); ConINode* intcon(jint i); @@ -2153,7 +2146,7 @@ class CountedLoopConverter { _phase(phase), _iv_bt(iv_bt), _back_control(_phase->loop_exit_control(_loop)), - _exit_test(_back_control, _loop, _phase), + _exit_test(_back_control, _loop), _iv_incr(_head, _loop), _truncated_increment(_iv_bt), _stride(PhaseIdealLoop::LoopIVStride(_iv_bt)), @@ -2324,7 +2317,7 @@ class LoopTreeIterator : public StackObj { class PathFrequency { private: Node* _dom; // frequencies are computed relative to this node - Node_Stack _stack; + Node_Stack _stack; GrowableArray _freqs_stack; // keep track of intermediate result at regions GrowableArray _freqs; // cache frequencies PhaseIdealLoop* _phase; @@ -2353,11 +2346,11 @@ class PathFrequency { // 2. Fix the cloned data inputs pointing to the old nodes to the cloned inputs by using an old->new mapping. class DataNodeGraph : public StackObj { PhaseIdealLoop* const _phase; - const Unique_Node_List& _data_nodes; + const Unique_Node_List& _data_nodes; OrigToNewHashtable _orig_to_new; public: - DataNodeGraph(const Unique_Node_List& data_nodes, PhaseIdealLoop* phase) + DataNodeGraph(const Unique_Node_List& data_nodes, PhaseIdealLoop* phase) : _phase(phase), _data_nodes(data_nodes), // Use 107 as best guess which is the first resize value in ResizeableHashTable::large_table_sizes. diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index c2699cd4afb9..74bbf7f84738 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -873,7 +873,7 @@ Node *PhaseIdealLoop::conditional_move( Node *region ) { // Now replace all Phis with CMOV's Node *cmov_ctrl = iff->in(0); uint flip = (lp->Opcode() == Op_IfTrue); - Node_List wq; + Node_List wq; while (1) { PhiNode* phi = nullptr; for (DUIterator_Fast imax, i = region->fast_outs(imax); i < imax; i++) { @@ -921,7 +921,7 @@ Node *PhaseIdealLoop::conditional_move( Node *region ) { return iff->in(1); } -static void enqueue_cfg_uses(Node* m, Unique_Node_List& wq) { +static void enqueue_cfg_uses(Node* m, Unique_Node_List& wq) { for (DUIterator_Fast imax, i = m->fast_outs(imax); i < imax; i++) { Node* u = m->fast_out(i); if (u->is_CFG()) { @@ -978,7 +978,7 @@ Node* PhaseIdealLoop::try_move_store_before_loop(Node* n, Node *n_ctrl) { // Follow control from loop head until n, we exit the loop or // we reach the tail ResourceMark rm; - Unique_Node_List wq; + Unique_Node_List wq; wq.push(n_loop->_head); for (uint next = 0; next < wq.size(); ++next) { @@ -1056,10 +1056,10 @@ void PhaseIdealLoop::try_move_store_after_loop(Node* n) { bool mem_ok = true; { ResourceMark rm; - Unique_Node_List wq; + Unique_Node_List wq; wq.push(phi); for (uint next = 0; next < wq.size() && mem_ok; ++next) { - Node *m = wq.at(next); + const Node* m = wq.at(next); for (DUIterator_Fast imax, i = m->fast_outs(imax); i < imax && mem_ok; i++) { Node* u = m->fast_out(i); if (u->is_Store() || u->is_Phi()) { @@ -1902,7 +1902,7 @@ void PhaseIdealLoop::try_sink_out_of_loop(Node* n) { !n->is_OpaqueTemplateAssertionPredicate() && !is_raw_to_oop_cast && // don't extend live ranges of raw oops n->Opcode() != Op_CreateEx && - (KillPathsReachableByDeadTypeNode || !n->is_Type()) + (KillPathsReachableByDeadDataNode || !n->is_Type()) ) { Node *n_ctrl = get_ctrl(n); IdealLoopTree *n_loop = get_loop(n_ctrl); @@ -2045,7 +2045,7 @@ void PhaseIdealLoop::try_sink_out_of_loop(Node* n) { // Chain of AddP nodes: // 2- A CastPP of the base is only added now that all AddP nodes are sunk if (x->is_AddP() && k == AddPNode::Base) { - update_addp_chain_base(x, n->in(AddPNode::Base), cast); + update_addp_chain_base(x->as_AddP(), n->in(AddPNode::Base), cast); } break; } @@ -2060,17 +2060,17 @@ void PhaseIdealLoop::try_sink_out_of_loop(Node* n) { } } -void PhaseIdealLoop::update_addp_chain_base(Node* x, Node* old_base, Node* new_base) { +void PhaseIdealLoop::update_addp_chain_base(const AddPNode* x, const Node* old_base, Node* new_base) const { ResourceMark rm; - Node_List wq; + Node_List wq; wq.push(x); while (wq.size() != 0) { - Node* n = wq.pop(); + const AddPNode* n = wq.pop(); for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { Node* u = n->fast_out(i); if (u->is_AddP() && u->in(AddPNode::Base) == old_base) { _igvn.replace_input_of(u, AddPNode::Base, new_base); - wq.push(u); + wq.push(u->as_AddP()); } } } @@ -2081,7 +2081,7 @@ void PhaseIdealLoop::update_addp_chain_base(Node* x, Node* old_base, Node* new_b Node* PhaseIdealLoop::compute_early_ctrl(Node* n, Node* n_ctrl) { Node* early_ctrl = nullptr; ResourceMark rm; - Unique_Node_List wq; + Unique_Node_List wq; wq.push(n); for (uint i = 0; i < wq.size(); i++) { Node* m = wq.at(i); @@ -2171,7 +2171,7 @@ bool PhaseIdealLoop::ctrl_of_use_out_of_loop(const Node* n, Node* n_ctrl, IdealL //------------------------------split_if_with_blocks--------------------------- // Check for aggressive application of 'split-if' optimization, // using basic block level info. -void PhaseIdealLoop::split_if_with_blocks(VectorSet &visited, Node_Stack &nstack) { +void PhaseIdealLoop::split_if_with_blocks(VectorSet &visited, Node_Stack& nstack) { Node* root = C->root(); visited.set(root->_idx); // first, mark root as visited // Do pre-visit work for root @@ -2388,10 +2388,10 @@ CmpNode*PhaseIdealLoop::clone_bool(PhiNode* phi) { return (CmpNode*)cmp; } -void PhaseIdealLoop::clone_loop_handle_data_uses(Node* old, Node_List &old_new, +void PhaseIdealLoop::clone_loop_handle_data_uses(Node* old, const Node_List& old_new, IdealLoopTree* loop, IdealLoopTree* outer_loop, - Node_List*& split_if_set, Node_List*& split_bool_set, - Node_List*& split_cex_set, Node_List& worklist, + Node_List*& split_if_set, Node_List*& split_bool_set, + Node_List*& split_cex_set, Node_List& worklist, uint new_counter, CloneLoopMode mode) { Node* nnn = old_new[old->_idx]; // Copy uses to a worklist, so I can munge the def-use info @@ -2434,21 +2434,21 @@ void PhaseIdealLoop::clone_loop_handle_data_uses(Node* old, Node_List &old_new, // Since this code is highly unlikely, we lazily build the worklist // of such Nodes to go split. if (!split_if_set) { - split_if_set = new Node_List(); + split_if_set = new Node_List(); } split_if_set->push(use); } if (use->is_Bool()) { if (!split_bool_set) { - split_bool_set = new Node_List(); + split_bool_set = new Node_List(); } - split_bool_set->push(use); + split_bool_set->push(use->as_Bool()); } if (use->Opcode() == Op_CreateEx) { if (!split_cex_set) { - split_cex_set = new Node_List(); + split_cex_set = new Node_List(); } - split_cex_set->push(use); + split_cex_set->push(use->as_CreateEx()); } @@ -2555,8 +2555,8 @@ void PhaseIdealLoop::clone_loop_handle_data_uses(Node* old, Node_List &old_new, } } -static void collect_nodes_in_outer_loop_not_reachable_from_sfpt(Node* n, const IdealLoopTree *loop, const IdealLoopTree* outer_loop, - const Node_List &old_new, Unique_Node_List& wq, PhaseIdealLoop* phase, +static void collect_nodes_in_outer_loop_not_reachable_from_sfpt(Node* n, const IdealLoopTree* loop, const IdealLoopTree* outer_loop, + const Node_List& old_new, Unique_Node_List& wq, PhaseIdealLoop* phase, bool check_old_new) { for (DUIterator_Fast jmax, j = n->fast_outs(jmax); j < jmax; j++) { Node* u = n->fast_out(j); @@ -2582,9 +2582,9 @@ static void collect_nodes_in_outer_loop_not_reachable_from_sfpt(Node* n, const I } } -void PhaseIdealLoop::clone_outer_loop(LoopNode* head, CloneLoopMode mode, IdealLoopTree *loop, - IdealLoopTree* outer_loop, int dd, Node_List &old_new, - Node_List& extra_data_nodes) { +void PhaseIdealLoop::clone_outer_loop(LoopNode* head, CloneLoopMode mode, IdealLoopTree* loop, + IdealLoopTree* outer_loop, int dd, Node_List& old_new, + Node_List& extra_data_nodes) { if (head->is_strip_mined() && mode != IgnoreStripMined) { CountedLoopNode* cl = head->as_CountedLoop(); Node* l = cl->outer_loop(); @@ -2644,7 +2644,7 @@ void PhaseIdealLoop::clone_outer_loop(LoopNode* head, CloneLoopMode mode, IdealL // loop: they are kept in the outer loop by the safepoint so start // from the safepoint node's inputs. IdealLoopTree* outer_loop = get_loop(l); - Node_Stack stack(2); + Node_Stack stack(2); stack.push(sfpt, 1); uint new_counter = C->unique(); while (stack.size() > 0) { @@ -2690,7 +2690,7 @@ void PhaseIdealLoop::clone_outer_loop(LoopNode* head, CloneLoopMode mode, IdealL // data nodes to the outer loop. Set their control so they are out // of the outer loop. ResourceMark rm; - Unique_Node_List wq; + Unique_Node_List wq; for (uint i = 0; i < extra_data_nodes.size(); i++) { Node* old = extra_data_nodes.at(i); collect_nodes_in_outer_loop_not_reachable_from_sfpt(old, loop, outer_loop, old_new, wq, this, true); @@ -2749,7 +2749,7 @@ void PhaseIdealLoop::clone_outer_loop(LoopNode* head, CloneLoopMode mode, IdealL // When nonnull, the clone and original are side-by-side, both are // dominated by the side_by_side_idom node. Used in construction of // unswitched loops. -void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd, +void PhaseIdealLoop::clone_loop(IdealLoopTree* loop, Node_List& old_new, int dd, CloneLoopMode mode, Node* side_by_side_idom) { LoopNode* head = loop->_head->as_Loop(); @@ -2783,7 +2783,7 @@ void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd // new node instead. fix_body_edges(loop->_body, loop, old_new, dd, outer_loop->_parent, false); - Node_List extra_data_nodes; // data nodes in the outer strip mined loop + Node_List extra_data_nodes; // data nodes in the outer strip mined loop clone_outer_loop(head, mode, loop, outer_loop, dd, old_new, extra_data_nodes); // Step 3: Now fix control uses. Loop varying control uses have already @@ -2791,16 +2791,16 @@ void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd // control uses must be either an IfFalse or an IfTrue. Make a merge // point to merge the old and new IfFalse/IfTrue nodes; make the use // refer to this. - Node_List worklist; + Node_List worklist; uint new_counter = C->unique(); fix_ctrl_uses(loop->_body, loop, old_new, mode, side_by_side_idom, &cm, worklist); // Step 4: If loop-invariant use is not control, it must be dominated by a // loop exit IfFalse/IfTrue. Find "proper" loop exit. Make a Region // there if needed. Make a Phi there merging old and new used values. - Node_List *split_if_set = nullptr; - Node_List *split_bool_set = nullptr; - Node_List *split_cex_set = nullptr; + Node_List* split_if_set = nullptr; + Node_List* split_bool_set = nullptr; + Node_List* split_cex_set = nullptr; fix_data_uses(loop->_body, loop, mode, outer_loop, new_counter, old_new, worklist, split_if_set, split_bool_set, split_cex_set); for (uint i = 0; i < extra_data_nodes.size(); i++) { @@ -2819,7 +2819,7 @@ void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd } -void PhaseIdealLoop::finish_clone_loop(Node_List* split_if_set, Node_List* split_bool_set, Node_List* split_cex_set) { +void PhaseIdealLoop::finish_clone_loop(Node_List* split_if_set, Node_List* split_bool_set, Node_List* split_cex_set) { if (split_if_set) { while (split_if_set->size()) { Node *iff = split_if_set->pop(); @@ -2850,9 +2850,9 @@ void PhaseIdealLoop::finish_clone_loop(Node_List* split_if_set, Node_List* split } } -void PhaseIdealLoop::fix_data_uses(Node_List& body, IdealLoopTree* loop, CloneLoopMode mode, IdealLoopTree* outer_loop, - uint new_counter, Node_List &old_new, Node_List &worklist, Node_List*& split_if_set, - Node_List*& split_bool_set, Node_List*& split_cex_set) { +void PhaseIdealLoop::fix_data_uses(Node_List& body, IdealLoopTree* loop, CloneLoopMode mode, IdealLoopTree* outer_loop, + uint new_counter, Node_List& old_new, Node_List& worklist, Node_List*& split_if_set, + Node_List*& split_bool_set, Node_List*& split_cex_set) { for(uint i = 0; i < body.size(); i++ ) { Node* old = body.at(i); clone_loop_handle_data_uses(old, old_new, loop, outer_loop, split_if_set, @@ -2861,8 +2861,8 @@ void PhaseIdealLoop::fix_data_uses(Node_List& body, IdealLoopTree* loop, CloneLo } } -void PhaseIdealLoop::fix_ctrl_uses(const Node_List& body, const IdealLoopTree* loop, Node_List &old_new, CloneLoopMode mode, - Node* side_by_side_idom, CloneMap* cm, Node_List &worklist) { +void PhaseIdealLoop::fix_ctrl_uses(const Node_List& body, const IdealLoopTree* loop, Node_List& old_new, CloneLoopMode mode, + Node* side_by_side_idom, CloneMap* cm, Node_List& worklist) { LoopNode* head = loop->_head->as_Loop(); for(uint i = 0; i < body.size(); i++ ) { Node* old = body.at(i); @@ -2975,7 +2975,7 @@ void PhaseIdealLoop::fix_ctrl_uses(const Node_List& body, const IdealLoopTree* l } } -void PhaseIdealLoop::fix_body_edges(const Node_List &body, IdealLoopTree* loop, const Node_List &old_new, int dd, +void PhaseIdealLoop::fix_body_edges(const Node_List& body, IdealLoopTree* loop, const Node_List& old_new, int dd, IdealLoopTree* parent, bool partial) { for(uint i = 0; i < body.size(); i++ ) { Node *old = body.at(i); @@ -3012,7 +3012,7 @@ void PhaseIdealLoop::fix_body_edges(const Node_List &body, IdealLoopTree* loop, } } -void PhaseIdealLoop::clone_loop_body(const Node_List& body, Node_List &old_new, CloneMap* cm) { +void PhaseIdealLoop::clone_loop_body(const Node_List& body, Node_List& old_new, CloneMap* cm) const { for (uint i = 0; i < body.size(); i++) { Node* old = body.at(i); Node* nnn = old->clone(); @@ -3476,11 +3476,11 @@ void PhaseIdealLoop::remove_cmpi_loop_exit(IfNode* if_cmp, IdealLoopTree *loop) // nodes which have no inputs in the "member" set, and then // followed by the nodes that have an immediate input dependence // on a node in "sched". -void PhaseIdealLoop::scheduled_nodelist( IdealLoopTree *loop, VectorSet& member, Node_List &sched ) { +void PhaseIdealLoop::scheduled_nodelist(const IdealLoopTree* loop, const VectorSet& member, Node_List& sched) { assert(member.test(loop->_head->_idx), "loop head must be in member set"); VectorSet visited; - Node_Stack nstack(loop->_body.size()); + Node_Stack nstack(loop->_body.size()); Node* n = loop->_head; // top of stack is cached in "n" uint idx = 0; @@ -3560,7 +3560,7 @@ bool PhaseIdealLoop::has_use_internal_to_set( Node* n, VectorSet& vset, IdealLoo //------------------------------ clone_for_use_outside_loop ------------------------------------- // clone "n" for uses that are outside of loop -int PhaseIdealLoop::clone_for_use_outside_loop( IdealLoopTree *loop, Node* n, Node_List& worklist ) { +int PhaseIdealLoop::clone_for_use_outside_loop(IdealLoopTree* loop, Node* n, Node_List& worklist) { int cloned = 0; assert(worklist.size() == 0, "should be empty"); for (DUIterator_Fast jmax, j = n->fast_outs(jmax); j < jmax; j++) { @@ -3615,8 +3615,8 @@ int PhaseIdealLoop::clone_for_use_outside_loop( IdealLoopTree *loop, Node* n, No // marks the method as not compilable. For example, if a "BoolNode" // is in a different basic block than the "IfNode" that uses it, then // the compilation is aborted in the code generator. -void PhaseIdealLoop::clone_for_special_use_inside_loop( IdealLoopTree *loop, Node* n, - VectorSet& not_peel, Node_List& sink_list, Node_List& worklist ) { +void PhaseIdealLoop::clone_for_special_use_inside_loop(IdealLoopTree* loop, Node* n, + VectorSet& not_peel, Node_List& sink_list, Node_List& worklist) { if (n->is_Phi() || n->is_Load()) { return; } @@ -3676,8 +3676,8 @@ void PhaseIdealLoop::insert_phi_for_loop( Node* use, uint idx, Node* lp_entry_va #ifdef ASSERT //------------------------------ is_valid_loop_partition ------------------------------------- // Validate the loop partition sets: peel and not_peel -bool PhaseIdealLoop::is_valid_loop_partition( IdealLoopTree *loop, VectorSet& peel, Node_List& peel_list, - VectorSet& not_peel ) { +bool PhaseIdealLoop::is_valid_loop_partition(const IdealLoopTree* loop, const VectorSet& peel, const Node_List& peel_list, + const VectorSet& not_peel) { uint i; // Check that peel_list entries are in the peel set for (i = 0; i < peel_list.size(); i++) { @@ -3730,8 +3730,8 @@ bool PhaseIdealLoop::is_valid_clone_loop_exit_use( IdealLoopTree *loop, Node* us //------------------------------ is_valid_clone_loop_form ------------------------------------- // Ensure that all uses outside of loop are of the right form -bool PhaseIdealLoop::is_valid_clone_loop_form( IdealLoopTree *loop, Node_List& peel_list, - uint orig_exit_idx, uint clone_exit_idx) { +bool PhaseIdealLoop::is_valid_clone_loop_form(IdealLoopTree* loop, Node_List& peel_list, + uint orig_exit_idx, uint clone_exit_idx) { uint len = peel_list.size(); for (uint i = 0; i < len; i++) { Node *def = peel_list.at(i); @@ -3962,7 +3962,7 @@ bool PhaseIdealLoop::is_valid_clone_loop_form( IdealLoopTree *loop, Node_List& p // v // exitA // -bool PhaseIdealLoop::partial_peel( IdealLoopTree *loop, Node_List &old_new ) { +bool PhaseIdealLoop::partial_peel(IdealLoopTree* loop, Node_List& old_new) { assert(!loop->_head->is_CountedLoop(), "Non-counted loop only"); if (!loop->_head->is_Loop()) { @@ -4048,7 +4048,7 @@ bool PhaseIdealLoop::partial_peel( IdealLoopTree *loop, Node_List &old_new ) { if (TracePartialPeeling) { tty->print_cr("before partial peel one iteration"); - Node_List wl; + Node_List wl; Node* t = head->in(2); while (true) { wl.push(t); @@ -4067,9 +4067,9 @@ bool PhaseIdealLoop::partial_peel( IdealLoopTree *loop, Node_List &old_new ) { VectorSet peel; VectorSet not_peel; - Node_List peel_list; - Node_List worklist; - Node_List sink_list; + Node_List peel_list; + Node_List worklist; + Node_List sink_list; uint estimate = loop->est_loop_clone_sz(1); if (exceeding_node_budget(estimate)) { @@ -4354,7 +4354,7 @@ bool PhaseIdealLoop::partial_peel( IdealLoopTree *loop, Node_List &old_new ) { #ifndef PRODUCT if (TracePartialPeeling) { tty->print_cr("\nafter partial peel one iteration"); - Node_List wl; + Node_List wl; Node* t = last_peel; while (true) { wl.push(t); @@ -4451,7 +4451,7 @@ class MoveAssertionPredicatesVisitor : public PredicateVisitor { // Conditions for this transformation to trigger: // - the path through stmt1 is frequent enough // - the inner loop will be turned into a counted loop after transformation -bool PhaseIdealLoop::duplicate_loop_backedge(IdealLoopTree *loop, Node_List &old_new) { +bool PhaseIdealLoop::duplicate_loop_backedge(IdealLoopTree* loop, Node_List& old_new) { if (!DuplicateBackedge) { return false; } @@ -4499,7 +4499,7 @@ bool PhaseIdealLoop::duplicate_loop_backedge(IdealLoopTree *loop, Node_List &old return false; } - LoopExitTest loop_exit(back_control, loop, this); + LoopExitTest loop_exit(back_control, loop); loop_exit.build(); if (!loop_exit.is_valid_with_bt(T_INT)) { return false; @@ -4560,7 +4560,7 @@ bool PhaseIdealLoop::duplicate_loop_backedge(IdealLoopTree *loop, Node_List &old } // Collect all control nodes that need to be cloned (shared_stmt in the diagram) - Unique_Node_List wq; + Unique_Node_List wq; wq.push(head->in(LoopNode::LoopBackControl)); for (uint i = 0; i < wq.size(); i++) { Node* c = wq.at(i); @@ -4659,13 +4659,13 @@ bool PhaseIdealLoop::duplicate_loop_backedge(IdealLoopTree *loop, Node_List &old } // create control and data nodes for out of loop uses (including region2) - Node_List worklist; + Node_List worklist; uint new_counter = C->unique(); fix_ctrl_uses(wq, loop, old_new, ControlAroundStripMined, outer_head, nullptr, worklist); - Node_List *split_if_set = nullptr; - Node_List *split_bool_set = nullptr; - Node_List *split_cex_set = nullptr; + Node_List* split_if_set = nullptr; + Node_List* split_bool_set = nullptr; + Node_List* split_cex_set = nullptr; fix_data_uses(wq, loop, ControlAroundStripMined, loop->skip_strip_mined(), new_counter, old_new, worklist, split_if_set, split_bool_set, split_cex_set); @@ -4773,7 +4773,7 @@ PhaseIdealLoop::auto_vectorize(IdealLoopTree* lpt, VSharedData &vshared) { // // Note: new runtime checks can be added to the multiversion_if with // PhaseIdealLoop::create_new_if_for_multiversion -void PhaseIdealLoop::maybe_multiversion_for_auto_vectorization_runtime_checks(IdealLoopTree* lpt, Node_List& old_new) { +void PhaseIdealLoop::maybe_multiversion_for_auto_vectorization_runtime_checks(IdealLoopTree* lpt, Node_List& old_new) { CountedLoopNode* cl = lpt->_head->as_CountedLoop(); LoopNode* outer_loop = cl->skip_strip_mined(); Node* entry = outer_loop->in(LoopNode::EntryControl); diff --git a/src/hotspot/share/opto/machnode.cpp b/src/hotspot/share/opto/machnode.cpp index 7fb49c506221..28fdd5076ffe 100644 --- a/src/hotspot/share/opto/machnode.cpp +++ b/src/hotspot/share/opto/machnode.cpp @@ -460,8 +460,8 @@ const TypePtr* MachNode::compute_adr_type_from_inputs() const { } if (t->isa_intptr_t() && -#if !defined(AARCH64) - // AArch64 supports the addressing mode: +#if !defined(AARCH64) && !defined(RISCV) + // AArch64 and RISC-V support the addressing mode: // [base, 0], in which [base] is converted from a long value offset != 0 && #endif @@ -768,6 +768,94 @@ const RegMask &MachSafePointNode::in_RegMask( uint idx ) const { return *Compile::current()->matcher()->idealreg2spillmask[in(idx)->ideal_reg()]; } +bool MachSafePointNode::is_uncommon_trap(int32_t* out_trap_request) const { + int32_t trap_request = 0; + bool is_trap_request = false; + if (is_MachCallStaticJava()) { + trap_request = as_MachCallStaticJava()->uncommon_trap_request(); + is_trap_request = (trap_request != 0); + } else if (is_MachUncommonTrap()) { + trap_request = as_MachUncommonTrap()->trap_request(); + is_trap_request = true; + } + + if (is_trap_request && out_trap_request != nullptr) { + *out_trap_request = trap_request; + } + + return is_trap_request; +} + +#ifndef PRODUCT +void MachSafePointNode::format_jvms_and_oopmap(PhaseRegAlloc* ra, outputStream* st) const { + if (_jvms) { + _jvms->format(ra, this, st); + } else { + st->print_cr(" No JVM State Info"); + } + st->print(" # "); + if (_jvms && _oop_map) { + _oop_map->print_on(st); + } +} + +// Helper for summarizing uncommon_trap arguments. +void MachSafePointNode::dump_trap_args(outputStream* st) const { + int trap_req; + if (is_uncommon_trap(&trap_req)) { + char buf[100]; + st->print("(%s)", + Deoptimization::format_trap_request(buf, sizeof(buf), + trap_req)); + } +} + +#endif + +//============================================================================= + +const RegMask& MachUncommonTrapNode::in_RegMask(uint idx) const { + // Control, I_O, Memory, FramePtr, ReturnAdr + if (idx < TypeFunc::Parms) return _in_rms[idx]; + + // Debug inputs + assert(in(idx)->ideal_reg() != Op_RegFlags, "flags register is not spillable"); + return *Compile::current()->matcher()->idealreg2spillmask[in(idx)->ideal_reg()]; +} + +void MachUncommonTrapNode::emit(C2_MacroAssembler* masm, PhaseRegAlloc* ra_) const { + if constexpr (Matcher::use_mach_uncommon_trap_node) { + emit_impl(masm, ra_); + } else { + Unimplemented(); + } +} + +uint MachUncommonTrapNode::size(PhaseRegAlloc* ra_) const { + if constexpr (Matcher::use_mach_uncommon_trap_node) { + return size_impl(ra_); + } else { + Unimplemented(); + } +} + +int MachUncommonTrapNode::ret_addr_offset() const { + if constexpr (Matcher::use_mach_uncommon_trap_node) { + return ret_addr_offset_impl(); + } else { + Unimplemented(); + } +} + +#ifndef PRODUCT +void MachUncommonTrapNode::format(PhaseRegAlloc* ra, outputStream* st) const { + st->print("UNCOMMON TRAP"); + + dump_trap_args(st); + st->cr(); + format_jvms_and_oopmap(ra, st); +} +#endif //============================================================================= @@ -884,17 +972,6 @@ int MachCallStaticJavaNode::uncommon_trap_request() const { } #ifndef PRODUCT -// Helper for summarizing uncommon_trap arguments. -void MachCallStaticJavaNode::dump_trap_args(outputStream *st) const { - int trap_req = uncommon_trap_request(); - if (trap_req != 0) { - char buf[100]; - st->print("(%s)", - Deoptimization::format_trap_request(buf, sizeof(buf), - trap_req)); - } -} - void MachCallStaticJavaNode::dump_spec(outputStream *st) const { st->print("Static "); if (_name != nullptr) { diff --git a/src/hotspot/share/opto/machnode.hpp b/src/hotspot/share/opto/machnode.hpp index ee94e84cfeb4..4414b8c8a9ae 100644 --- a/src/hotspot/share/opto/machnode.hpp +++ b/src/hotspot/share/opto/machnode.hpp @@ -345,7 +345,7 @@ class MachNode : public Node { // Expand method for MachNode, replaces nodes representing pseudo // instructions with a set of nodes which represent real machine // instructions and compute the same value. - virtual MachNode *Expand( State *, Node_List &proj_list, Node* mem ) { return this; } + virtual MachNode* Expand(State*, Node_List& proj_list, Node* mem) { return this; } // Bottom_type call; value comes from operand0 virtual const Type* bottom_type() const { @@ -945,6 +945,53 @@ class MachSafePointNode : public MachReturnNode { set_req(_jvmadj + jvms->monoff() + idx, c); } virtual uint size_of() const { return sizeof(MachSafePointNode); } + + // Returns true if this is an uncommon trap. In that case, the request + // value is stored back to *out_trap_request unless the pointer is null. + bool is_uncommon_trap(int32_t* out_trap_request = nullptr) const; +#ifndef PRODUCT + void dump_trap_args(outputStream* st) const; + +protected: + void format_jvms_and_oopmap(PhaseRegAlloc* ra, outputStream* st) const; +#endif +}; + +//------------------------------MachUncommonTrapNode---------------------------------- +class MachUncommonTrapNode : public MachSafePointNode { +private: + void emit_impl(C2_MacroAssembler* masm, PhaseRegAlloc* ra_) const; + uint size_impl(PhaseRegAlloc* ra_) const; + int ret_addr_offset_impl() const; + + int _trap_request; +protected: + // Uncommon trap nodes must not compare equal to any other node. + virtual uint hash() const { return NO_HASH; } +public: + MachUncommonTrapNode(int trap_request) + : MachSafePointNode(), _trap_request(trap_request) { + init_class_id(Class_MachUncommonTrap); + } + + int trap_request() const { return _trap_request; } + + virtual int ideal_Opcode() const { return Op_CallStaticJava; } + virtual uint size_of() const { return sizeof(MachUncommonTrapNode); } + virtual uint rule() const { return 9999999; /* This node is constructed by match_sfpt. */ } + virtual const class Type* bottom_type() const { return TypeTuple::CONTROL; } + virtual uint ideal_reg() const { return NotAMachineReg; } + virtual int reloc() const { return 1; /* The relocInfo::runtime_call_type relocation */ } + virtual const RegMask& in_RegMask(uint) const; + virtual const RegMask& out_RegMask() const { return RegMask::EMPTY; } + virtual void emit(C2_MacroAssembler* masm, PhaseRegAlloc* ra_) const; + virtual uint size(PhaseRegAlloc* ra_) const; + virtual int ret_addr_offset() const; + +#ifndef PRODUCT + virtual const char* Name() const { return "UncommonTrap"; } + virtual void format(PhaseRegAlloc* ra, outputStream* st) const; +#endif }; //------------------------------MachCallNode---------------------------------- @@ -1042,7 +1089,6 @@ class MachCallStaticJavaNode : public MachCallJavaNode { virtual int ret_addr_offset() const; #ifndef PRODUCT virtual void dump_spec(outputStream *st) const; - void dump_trap_args(outputStream *st) const; #endif }; diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 745b7a63c9d4..3be18a8ace49 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -69,30 +69,6 @@ #include "gc/g1/g1ThreadLocalData.hpp" #endif // INCLUDE_G1GC - -// -// Replace any references to "oldref" in inputs to "use" with "newref". -// Returns the number of replacements made. -// -int PhaseMacroExpand::replace_input(Node *use, Node *oldref, Node *newref) { - int nreplacements = 0; - uint req = use->req(); - for (uint j = 0; j < use->len(); j++) { - Node *uin = use->in(j); - if (uin == oldref) { - if (j < req) - use->set_req(j, newref); - else - use->set_prec(j, newref); - nreplacements++; - } else if (j >= req && uin == nullptr) { - break; - } - } - return nreplacements; -} - - Node* PhaseMacroExpand::opt_bits_test(Node* ctrl, Node* region, int edge, CmpNode* cmp) { Node* bol = transform_later(new BoolNode(cmp, BoolTest::ne)); IfNode* iff = new IfNode( ctrl, bol, PROB_MIN, COUNT_UNKNOWN ); @@ -268,7 +244,7 @@ bool has_interfering_store(const ArrayCopyNode* ac, LoadNode* load, PhaseGVN* ph assert(ac != nullptr && load != nullptr, "sanity"); AccessAnalyzer acc(phase, load); ResourceMark rm; - Unique_Node_List to_visit; + Unique_Node_List to_visit; to_visit.push(load->in(MemNode::Memory)); for (uint worklist_idx = 0; worklist_idx < to_visit.size(); worklist_idx++) { @@ -402,7 +378,7 @@ Node* PhaseMacroExpand::make_arraycopy_load(ArrayCopyNode* ac, intptr_t offset, // on the input paths. // Note: this function is recursive, its depth is limited by the "level" argument // Returns the computed Phi, or null if it cannot compute it. -Node *PhaseMacroExpand::value_from_mem_phi(Node *mem, BasicType ft, const Type *phi_type, const TypeOopPtr *adr_t, AllocateNode *alloc, Node_Stack *value_phis, int level) { +Node* PhaseMacroExpand::value_from_mem_phi(Node* mem, BasicType ft, const Type* phi_type, const TypeOopPtr* adr_t, AllocateNode* alloc, Node_Stack* value_phis, int level) { assert(mem->is_Phi(), "sanity"); int alias_idx = C->get_alias_index(adr_t); int offset = adr_t->flat_offset(); @@ -418,7 +394,7 @@ Node *PhaseMacroExpand::value_from_mem_phi(Node *mem, BasicType ft, const Type * } } // Check if an appropriate new value phi already exists. - Node* new_phi = value_phis->find(mem->_idx); + PhiNode* new_phi = value_phis->find(mem->_idx); if (new_phi != nullptr) return new_phi; @@ -650,7 +626,7 @@ Node* PhaseMacroExpand::value_from_mem(Node* origin, Node* ctl, BasicType ft, co return n; } else if (mem->is_Phi()) { // attempt to produce a Phi reflecting the values on the input paths of the Phi - Node_Stack value_phis(8); + Node_Stack value_phis(8); Node* phi = value_from_mem_phi(mem, ft, ftype, adr_t, alloc, &value_phis, ValueSearchLimit); if (phi != nullptr) { return phi; @@ -765,7 +741,7 @@ Node* PhaseMacroExpand::value_type_from_mem(ciValueKlass* vk, const TypeAryPtr* } // Check the possibility of scalar replacement. -bool PhaseMacroExpand::can_eliminate_allocation(PhaseIterGVN* igvn, AllocateNode* alloc, Unique_Node_List* safepoints) { +bool PhaseMacroExpand::can_eliminate_allocation(PhaseIterGVN* igvn, AllocateNode* alloc, Unique_Node_List* safepoints) { // Scan the uses of the allocation to check for anything that would // prevent us from eliminating it. NOT_PRODUCT( const char* fail_eliminate = nullptr; ) @@ -773,8 +749,8 @@ bool PhaseMacroExpand::can_eliminate_allocation(PhaseIterGVN* igvn, AllocateNode bool can_eliminate = true; bool reduce_merge_precheck = (safepoints == nullptr); - Unique_Node_List worklist; - Node* res = alloc->result_cast(); + Unique_Node_List worklist; + const Node* res = alloc->result_cast(); const TypeOopPtr* res_type = nullptr; if (res == nullptr) { // All users were eliminated. @@ -957,15 +933,15 @@ bool PhaseMacroExpand::can_eliminate_allocation(PhaseIterGVN* igvn, AllocateNode return can_eliminate; } -void PhaseMacroExpand::undo_previous_scalarizations(Node_List& safepoints_done, Node_List& scalar_objects_done, AllocateNode* alloc) { +void PhaseMacroExpand::undo_previous_scalarizations(Node_List& safepoints_done, Node_List& scalar_objects_done, AllocateNode* alloc) const { Node* res = alloc->result_cast(); assert(res == nullptr || res->is_CheckCastPP(), "unexpected AllocateNode result"); assert(safepoints_done.size() == scalar_objects_done.size(), "inconsistent count"); // rollback processed safepoints while (safepoints_done.size() > 0) { - SafePointNode* sfpt_done = safepoints_done.pop()->as_SafePoint(); - SafePointScalarObjectNode* scobj = scalar_objects_done.pop()->as_SafePointScalarObject(); + SafePointNode* sfpt_done = safepoints_done.pop(); + const SafePointScalarObjectNode* scobj = scalar_objects_done.pop(); assert(scobj->alloc() == alloc, "sanity"); SafePointNode::NodeEdgeTempStorage non_debug_edges_worklist(igvn()); @@ -1035,7 +1011,7 @@ void PhaseMacroExpand::undo_previous_scalarizations(Node_List& safepoints_done, } #endif -void PhaseMacroExpand::process_field_value_at_safepoint(const Type* field_type, Node* field_val, SafePointNode* sfpt, Unique_Node_List* value_worklist) { +void PhaseMacroExpand::process_field_value_at_safepoint(const Type* field_type, Node* field_val, SafePointNode* sfpt, Unique_Node_List* value_worklist) { if (UseCompressedOops && field_type->isa_narrowoop()) { // Enable "DecodeN(EncodeP(Allocate)) --> Allocate" transformation // to be able scalar replace the allocation. @@ -1048,21 +1024,21 @@ void PhaseMacroExpand::process_field_value_at_safepoint(const Type* field_type, // Keep track of value types to scalarize them later if (field_val->is_ValueType()) { - value_worklist->push(field_val); + value_worklist->push(field_val->as_ValueType()); } else if (field_val->is_Phi()) { PhiNode* phi = field_val->as_Phi(); // Eagerly replace value type phis now since we could be removing a value type allocation where we must // scalarize all its fields in safepoints. field_val = phi->try_push_value_types_down(&_igvn, true); if (field_val->is_ValueType()) { - value_worklist->push(field_val); + value_worklist->push(field_val->as_ValueType()); } } DEBUG_ONLY(verify_value_type_compatibility(field_val->bottom_type(), field_type);) sfpt->add_req(field_val); } -bool PhaseMacroExpand::add_array_elems_to_safepoint(AllocateNode* alloc, const TypeAryPtr* array_type, SafePointNode* sfpt, Unique_Node_List* value_worklist) { +bool PhaseMacroExpand::add_array_elems_to_safepoint(AllocateNode* alloc, const TypeAryPtr* array_type, SafePointNode* sfpt, Unique_Node_List* value_worklist) { const Type* elem_type = array_type->elem(); BasicType basic_elem_type = elem_type->array_element_basic_type(); @@ -1116,7 +1092,7 @@ bool PhaseMacroExpand::add_array_elems_to_safepoint(AllocateNode* alloc, const T // Recursively adds all flattened fields of a type 'iklass' inside 'base' to 'sfpt'. // 'offset_minus_header' refers to the offset of the payload of 'iklass' inside 'base' minus the // payload offset of 'iklass'. If 'base' is of type 'iklass' then 'offset_minus_header' == 0. -bool PhaseMacroExpand::add_inst_fields_to_safepoint(ciInstanceKlass* iklass, AllocateNode* alloc, Node* base, int offset_minus_header, SafePointNode* sfpt, Unique_Node_List* value_worklist) { +bool PhaseMacroExpand::add_inst_fields_to_safepoint(ciInstanceKlass* iklass, AllocateNode* alloc, Node* base, int offset_minus_header, SafePointNode* sfpt, Unique_Node_List* value_worklist) { const TypeInstPtr* base_type = _igvn.type(base)->is_instptr(); auto report_failure = [&](int offset, bool is_forced_failure) { #ifndef PRODUCT @@ -1199,7 +1175,7 @@ bool PhaseMacroExpand::add_inst_fields_to_safepoint(ciInstanceKlass* iklass, All } SafePointScalarObjectNode* PhaseMacroExpand::create_scalarized_object_description(AllocateNode* alloc, SafePointNode* sfpt, - Unique_Node_List* value_worklist) { + Unique_Node_List* value_worklist) { assert(sfpt->jvms()->endoff() == sfpt->req(), "no extra edges past debug info allowed"); // Fields of scalar objs are referenced only at the end @@ -1269,9 +1245,9 @@ SafePointScalarObjectNode* PhaseMacroExpand::create_scalarized_object_descriptio } // Do scalar replacement. -bool PhaseMacroExpand::scalar_replacement(AllocateNode* alloc, Unique_Node_List& safepoints) { - Node_List safepoints_done; - Node_List scalar_objects_done; +bool PhaseMacroExpand::scalar_replacement(AllocateNode* alloc, Unique_Node_List& safepoints) { + Node_List safepoints_done; + Node_List scalar_objects_done; Node* res = alloc->result_cast(); assert(res == nullptr || res->is_CheckCastPP(), "unexpected AllocateNode result"); const TypeOopPtr* res_type = nullptr; @@ -1280,9 +1256,9 @@ bool PhaseMacroExpand::scalar_replacement(AllocateNode* alloc, Unique_Node_List& } // Process the safepoint uses - Unique_Node_List value_worklist; + Unique_Node_List value_worklist; while (safepoints.size() > 0) { - SafePointNode* sfpt = safepoints.pop()->as_SafePoint(); + SafePointNode* sfpt = safepoints.pop(); SafePointNode::NodeEdgeTempStorage non_debug_edges_worklist(igvn()); @@ -1315,7 +1291,7 @@ bool PhaseMacroExpand::scalar_replacement(AllocateNode* alloc, Unique_Node_List& // because Deoptimization::reassign_flat_array_elements needs field values. bool allow_oop = (res_type != nullptr) && !res_type->is_flat(); for (uint i = 0; i < value_worklist.size(); ++i) { - ValueTypeNode* vt = value_worklist.at(i)->as_ValueType(); + ValueTypeNode* vt = value_worklist.at(i); if (!vt->make_scalar_in_safepoints(&_igvn, allow_oop)) { C->record_failure("out of nodes during scalarization"); return false; @@ -1337,7 +1313,7 @@ static void disconnect_projections(MultiNode* n, PhaseIterGVN& igvn) { // Process users of eliminated allocation. void PhaseMacroExpand::process_users_of_allocation(CallNode *alloc, bool value_type_alloc) { - Unique_Node_List worklist; + Unique_Node_List worklist; Node* res = alloc->result_cast(); if (res != nullptr) { worklist.push(res); @@ -1550,7 +1526,7 @@ bool PhaseMacroExpand::eliminate_allocate_node(AllocateNode *alloc) { _callprojs = alloc->extract_projections(false /*separate_io_proj*/, false /*do_asserts*/); - Unique_Node_List safepoints; + Unique_Node_List safepoints; if (!can_eliminate_allocation(&_igvn, alloc, &safepoints)) { return false; } @@ -3090,12 +3066,12 @@ void PhaseMacroExpand::expand_subtypecheck_node(SubTypeCheckNode *check) { void PhaseMacroExpand::expand_flatarraycheck_node(FlatArrayCheckNode* check) { assert(UseArrayFlattening, "Should not be called if flat arrays are not enabled"); bool use_mark_word = _igvn.type(check->in(FlatArrayCheckNode::ArrayOrKlass))->isa_oopptr() != nullptr; - Unique_Node_List outputs; + Unique_Node_List outputs; for (DUIterator_Fast imax, i = check->fast_outs(imax); i < imax; i++) { Node* bol = check->fast_out(i); assert(bol->is_Bool(), "expected Bool node, but got: %s", bol->Name()); - outputs.push(bol); + outputs.push(bol->as_Bool()); for (DUIterator_Fast jmax, j = bol->fast_outs(jmax); j < jmax; j++) { if (!bol->fast_out(j)->is_If()) { // No control input, fall back to layout helper check @@ -3126,8 +3102,8 @@ void PhaseMacroExpand::expand_flatarraycheck_node(FlatArrayCheckNode* check) { // Replace the bool nodes for (uint i = 0; i < outputs.size(); ++i) { - Node* bol = outputs.at(i); - assert(bol->as_Bool()->_test._test == BoolTest::ne || bol->as_Bool()->_test._test == BoolTest::eq, "unexpected condition"); + BoolNode* bol = outputs.at(i); + assert(bol->_test._test == BoolTest::ne || bol->_test._test == BoolTest::eq, "unexpected condition"); // FlatArrayCheckNode returns true if one of the inputs is flat. // - Bool(FlatArrayCheckNode, !=) is testing that the arrays are not flat, @@ -3163,11 +3139,11 @@ void PhaseMacroExpand::expand_flatarraycheck_node(FlatArrayCheckNode* check) { Node* cmp = transform_later(new CmpINode(masked, intcon(0))); for (uint i = 0; i < outputs.size(); ++i) { - Node* bol = outputs.at(i); + BoolNode* bol = outputs.at(i); for (DUIterator_Last jmin, j = bol->last_outs(jmin); j >= jmin; --j) { Node* user = bol->last_out(j); // See comment above for the logic of the .negate(). - Node* new_bol = transform_later(new BoolNode(cmp, bol->as_Bool()->_test.negate())); + Node* new_bol = transform_later(new BoolNode(cmp, bol->_test.negate())); assert(user->is_If() || user->is_CMove(), "must be a legit user of Bool node"); for (uint k = 0; k < user->req(); k++) { if (user->in(k) == bol) { @@ -3182,7 +3158,6 @@ void PhaseMacroExpand::expand_flatarraycheck_node(FlatArrayCheckNode* check) { void PhaseMacroExpand::expand_add_sub_i128t_node(Int128TBinaryNode* addsub) { if (Matcher::match_rule_supported(addsub->Opcode())) { - addsub->remove_flag(Node::Flag_is_macro); C->remove_macro_node(addsub); return; } @@ -3636,12 +3611,12 @@ int PhaseMacroExpand::count_MemBar(Compile *C) { if (!PrintOptoStatistics) { return 0; } - Unique_Node_List ideal_nodes; + Unique_Node_List ideal_nodes; int total = 0; ideal_nodes.map(C->live_nodes(), nullptr); ideal_nodes.push(C->root()); for (uint next = 0; next < ideal_nodes.size(); ++next) { - Node* n = ideal_nodes.at(next); + const Node* n = ideal_nodes.at(next); if (n->is_MemBar()) { total++; } diff --git a/src/hotspot/share/opto/macro.hpp b/src/hotspot/share/opto/macro.hpp index 89a9cda6293e..ad8b0bafa9f8 100644 --- a/src/hotspot/share/opto/macro.hpp +++ b/src/hotspot/share/opto/macro.hpp @@ -110,18 +110,18 @@ class PhaseMacroExpand : public Phase { Node* valid_length_test); void yank_alloc_node(AllocateNode* alloc); - void process_field_value_at_safepoint(const Type* field_type, Node* field_val, SafePointNode* sfpt, Unique_Node_List* value_worklist); - bool add_array_elems_to_safepoint(AllocateNode* alloc, const TypeAryPtr* array_type, SafePointNode* sfpt, Unique_Node_List* value_worklist); - bool add_inst_fields_to_safepoint(ciInstanceKlass* iklass, AllocateNode* alloc, Node* base, int offset_minus_header, SafePointNode* sfpt, Unique_Node_List* value_worklist); + void process_field_value_at_safepoint(const Type* field_type, Node* field_val, SafePointNode* sfpt, Unique_Node_List* value_worklist); + bool add_array_elems_to_safepoint(AllocateNode* alloc, const TypeAryPtr* array_type, SafePointNode* sfpt, Unique_Node_List* value_worklist); + bool add_inst_fields_to_safepoint(ciInstanceKlass* iklass, AllocateNode* alloc, Node* base, int offset_minus_header, SafePointNode* sfpt, Unique_Node_List* value_worklist); Node* value_from_alloc(BasicType ft, const TypeOopPtr* adr_t, AllocateNode* alloc); Node* value_from_mem(Node* start, Node* ctl, BasicType ft, const Type* ftype, const TypeOopPtr* adr_t, AllocateNode* alloc); - Node* value_from_mem_phi(Node* mem, BasicType ft, const Type* ftype, const TypeOopPtr* adr_t, AllocateNode* alloc, Node_Stack* value_phis, int level); + Node* value_from_mem_phi(Node* mem, BasicType ft, const Type* ftype, const TypeOopPtr* adr_t, AllocateNode* alloc, Node_Stack* value_phis, int level); Node* value_type_from_mem(ciValueKlass* vk, const TypeAryPtr* elem_adr_type, int elem_idx, int offset_in_element, bool null_free, AllocateNode* alloc, SafePointNode* sfpt); bool eliminate_boxing_node(CallStaticJavaNode* call); bool eliminate_allocate_node(AllocateNode *alloc); - void undo_previous_scalarizations(Node_List& safepoints_done, Node_List& scalar_objects_done, AllocateNode* alloc); - bool scalar_replacement(AllocateNode* alloc, Unique_Node_List& safepoints); + void undo_previous_scalarizations(Node_List& safepoints_done, Node_List& scalar_objects_done, AllocateNode* alloc) const; + bool scalar_replacement(AllocateNode* alloc, Unique_Node_List& safepoints); void process_users_of_allocation(CallNode *alloc, bool value_type_alloc = false); void eliminate_gc_barrier(Node *p2x); @@ -223,8 +223,6 @@ class PhaseMacroExpand : public Phase { void expand_add_sub_i128t_node(Int128TBinaryNode* addsub); - int replace_input(Node *use, Node *oldref, Node *newref); - void migrate_outs(Node *old, Node *target); Node* opt_bits_test(Node* ctrl, Node* region, int edge, CmpNode* cmp); void copy_predefined_input_for_runtime_call(Node * ctrl, CallNode* oldcall, CallNode* call); CallNode* make_slow_call(CallNode *oldcall, const TypeFunc* slow_call_type, address slow_call, @@ -248,8 +246,8 @@ class PhaseMacroExpand : public Phase { bool expand_macro_nodes(); void eliminate_opaque_looplimit_macro_nodes(); - SafePointScalarObjectNode* create_scalarized_object_description(AllocateNode *alloc, SafePointNode* sfpt, Unique_Node_List* value_worklist); - static bool can_eliminate_allocation(PhaseIterGVN* igvn, AllocateNode* alloc, Unique_Node_List* safepoints); + SafePointScalarObjectNode* create_scalarized_object_description(AllocateNode* alloc, SafePointNode* sfpt, Unique_Node_List* value_worklist); + static bool can_eliminate_allocation(PhaseIterGVN* igvn, AllocateNode* alloc, Unique_Node_List* safepoints); PhaseIterGVN &igvn() const { return _igvn; } diff --git a/src/hotspot/share/opto/matcher.cpp b/src/hotspot/share/opto/matcher.cpp index c780710796dc..463ae32e134d 100644 --- a/src/hotspot/share/opto/matcher.cpp +++ b/src/hotspot/share/opto/matcher.cpp @@ -74,7 +74,6 @@ Matcher::Matcher() _reused(C->comp_arena()), #endif // !PRODUCT _allocation_started(false), - _ruleName(ruleName), _register_save_policy(register_save_policy), _c_reg_save_policy(c_reg_save_policy), _register_save_type(register_save_type), @@ -141,11 +140,11 @@ OptoReg::Name Compile::compute_old_SP() { void Matcher::verify_new_nodes_only(Node* xroot) { // Make sure that the new graph only references new nodes ResourceMark rm; - Unique_Node_List worklist; + Unique_Node_List worklist; VectorSet visited; worklist.push(xroot); while (worklist.size() > 0) { - Node* n = worklist.pop(); + const Node* n = worklist.pop(); if (visited.test_set(n->_idx)) { continue; } @@ -1212,15 +1211,45 @@ OptoReg::Name Matcher::warp_outgoing_stk_arg( VMReg reg, OptoReg::Name begin_out // Helper function to match call instructions. Calls match special. // They match alone with no children. Their children, the incoming // arguments, match normally. -MachNode *Matcher::match_sfpt( SafePointNode *sfpt ) { - MachSafePointNode *msfpt = nullptr; - MachCallNode *mcall = nullptr; +MachNode* Matcher::match_sfpt(SafePointNode* sfpt) { + MachSafePointNode* msfpt = nullptr; + MachCallNode* mcall = nullptr; + JVMState* sfpt_jvms = nullptr; uint cnt; // Split out case for SafePoint vs Call - CallNode *call; - const TypeTuple *domain; + CallNode* call = nullptr; + const TypeTuple* domain = nullptr; ciMethod* method = nullptr; - if( sfpt->is_Call() ) { + if (Matcher::use_mach_uncommon_trap_node && + !PreferCallBasedUncommonTraps && + sfpt->is_CallStaticJava() && + sfpt->as_CallStaticJava()->is_uncommon_trap()) { + const address entry_point = OptoRuntime::uncommon_trap_blob()->entry_point(); + const CallStaticJavaNode* static_java_call = sfpt->as_CallStaticJava(); + const int trap_request = static_java_call->uncommon_trap_request(); + + assert(trap_request != 0, "malformed uncommon trap"); + assert(static_java_call->tf() == OptoRuntime::uncommon_trap_Type(), "unexpected uncommon trap call shape"); + assert(static_java_call->entry_point() == entry_point, "unexpected uncommon trap entry point"); + assert(static_java_call->jvms() != nullptr, "no map back into the interpreter state"); + assert(static_java_call->req() > TypeFunc::Parms, "does not have the trap request input"); + assert(static_java_call->in(TypeFunc::Parms) != nullptr, "missing trap request input edge"); + + MachUncommonTrapNode* mn = new MachUncommonTrapNode(trap_request); + for (uint i = 0; i < static_java_call->req(); i++) { + if (i == TypeFunc::Parms) { + // Remove the trap request input edge. + assert(static_java_call->in(i)->bottom_type()->isa_int() && + static_java_call->in(i)->bottom_type()->is_int()->is_con(), "incorrect trap request input"); + } else { + mn->add_req(static_java_call->in(i)); + } + } + msfpt = mn->as_MachSafePoint(); + sfpt_jvms = sfpt->jvms()->clone_deep(C); + sfpt_jvms->adapt_position(-1); + cnt = TypeFunc::Parms; + } else if (sfpt->is_Call()) { call = sfpt->as_Call(); domain = call->tf()->domain_cc(); cnt = domain->cnt(); @@ -1259,14 +1288,14 @@ MachNode *Matcher::match_sfpt( SafePointNode *sfpt ) { mach_call_rt->_name = call->as_CallRuntime()->_name; } msfpt = mcall; + sfpt_jvms = sfpt->jvms(); } // This is a non-call safepoint else { - call = nullptr; - domain = nullptr; MachNode *mn = match_tree(sfpt); if (C->failing()) return nullptr; msfpt = mn->as_MachSafePoint(); + sfpt_jvms = sfpt->jvms(); cnt = TypeFunc::Parms; } msfpt->_has_ea_local_in_scope = sfpt->has_ea_local_in_scope(); @@ -1396,7 +1425,7 @@ MachNode *Matcher::match_sfpt( SafePointNode *sfpt ) { } // Transfer the safepoint information from the call to the mcall // Move the JVMState list - msfpt->set_jvms(sfpt->jvms()); + msfpt->set_jvms(sfpt_jvms); for (JVMState* jvms = msfpt->jvms(); jvms; jvms = jvms->caller()) { jvms->set_map(sfpt); } @@ -2722,7 +2751,7 @@ void Matcher::specialize_generic_vector_operands() { // Replace generic vector operands (vec/legVec) with concrete ones (vec[SDXYZ]/legVec[SDXYZ]) // and remove reg-to-reg vector moves (MoveVec2Leg and MoveLeg2Vec). - Unique_Node_List live_nodes; + Unique_Node_List live_nodes; C->identify_useful_nodes(live_nodes); while (live_nodes.size() > 0) { @@ -2797,7 +2826,7 @@ bool Matcher::is_encode_and_store_pattern(const Node* n, const Node* m) { bool Matcher::verify_after_postselect_cleanup() { assert(!C->failing_internal() || C->failure_is_artificial(), "sanity"); if (supports_generic_vector_operands) { - Unique_Node_List useful; + Unique_Node_List useful; C->identify_useful_nodes(useful); for (uint i = 0; i < useful.size(); i++) { MachNode* m = useful.at(i)->isa_Mach(); diff --git a/src/hotspot/share/opto/matcher.hpp b/src/hotspot/share/opto/matcher.hpp index c5f656b738f6..fd91046588a3 100644 --- a/src/hotspot/share/opto/matcher.hpp +++ b/src/hotspot/share/opto/matcher.hpp @@ -54,7 +54,7 @@ class Matcher : public PhaseTransform { Alt_Post_Visit // alternative post-visit path }; - class MStack: public Node_Stack { + class MStack: public Node_Stack { public: MStack(int size) : Node_Stack(size) { } @@ -82,7 +82,7 @@ class Matcher : public PhaseTransform { ResourceArea _states_arena; // Map old nodes to new nodes - Node_List _new_nodes; + Node_List _new_nodes; VectorSet _visited; // Visit bits @@ -129,13 +129,13 @@ class Matcher : public PhaseTransform { Node *transform( Node *dummy ); - Node_List _projection_list; // For Machine nodes killing many values + Node_List _projection_list; // For Machine nodes killing many values - Node_Array _shared_nodes; + Node_Array _shared_nodes; #ifndef PRODUCT - Node_Array _old2new_map; // Map roots of ideal-trees to machine-roots - Node_Array _new2old_map; // Maps machine nodes back to ideal + Node_Array _old2new_map; // Map roots of ideal-trees to machine-roots + Node_Array _new2old_map; // Maps machine nodes back to ideal VectorSet _reused; // Ideal IGV identifiers reused by machine nodes #endif // !PRODUCT @@ -200,8 +200,6 @@ class Matcher : public PhaseTransform { static const char *regName[]; // Machine register encodings static const unsigned char _regEncode[]; - // Machine Node names - const char **_ruleName; // Rules that are cheaper to rematerialize than to spill static const uint _begin_rematerialize; static const uint _end_rematerialize; @@ -229,26 +227,22 @@ class Matcher : public PhaseTransform { // List of IfFalse or IfTrue Nodes that indicate a taken null test. // List is valid in the post-matching space. - Node_List _null_check_tests; + Node_List _null_check_tests; void collect_null_checks( Node *proj, Node *orig_proj ); void validate_null_checks( ); Matcher(); // Get a projection node at position pos - Node* get_projection(uint pos) { + MachProjNode* get_projection(uint pos) { return _projection_list[pos]; } // Push a projection node onto the projection list - void push_projection(Node* node) { + void push_projection(MachProjNode* node) { _projection_list.push(node); } - Node* pop_projection() { - return _projection_list.pop(); - } - // Number of nodes in the projection list uint number_of_projections() const { return _projection_list.size(); diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 2659eff3fa92..7bad6b504061 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -613,8 +613,8 @@ Node::DomResult MemNode::maybe_all_controls_dominate(Node* dom, Node* sub, Phase // Check all control edges of 'dom'. ResourceMark rm; - Node_List nlist; - Unique_Node_List dom_list; + Node_List nlist; + Unique_Node_List dom_list; dom_list.push(dom); bool only_dominating_controls = false; @@ -3280,9 +3280,9 @@ class MergePrimitiveStores : public StackObj { Status find_use_store_unidirectional(const StoreNode* def_store) const; Status find_def_store_unidirectional(const StoreNode* use_store) const; - void collect_merge_list(Node_List& merge_list) const; - Node* make_merged_input_value(const Node_List& merge_list); - StoreNode* make_merged_store(const Node_List& merge_list, Node* merged_input_value); + void collect_merge_list(Node_List& merge_list) const; + Node* make_merged_input_value(const Node_List& merge_list) const; + StoreNode* make_merged_store(const Node_List& merge_list, Node* merged_input_value) const; #ifndef PRODUCT // Access to TraceMergeStores tags @@ -3311,7 +3311,7 @@ class MergePrimitiveStores : public StackObj { } #endif - NOT_PRODUCT( void trace(const Node_List& merge_list, const Node* merged_input_value, const StoreNode* merged_store) const; ) + NOT_PRODUCT(static void trace(const Node_List& merge_list, const Node* merged_input_value, const StoreNode* merged_store); ) }; StoreNode* MergePrimitiveStores::run() { @@ -3346,7 +3346,7 @@ StoreNode* MergePrimitiveStores::run() { assert(_value_order != ValueOrder::NotAdjacent && _value_order != ValueOrder::Unknown, "Order should be checked"); ResourceMark rm; - Node_List merge_list; + Node_List merge_list; collect_merge_list(merge_list); Node* merged_input_value = make_merged_input_value(merge_list); @@ -3609,7 +3609,7 @@ MergePrimitiveStores::Status MergePrimitiveStores::find_def_store_unidirectional return Status::make(def_store, cfg_status_for_pair(use_store, def_store)); } -void MergePrimitiveStores::collect_merge_list(Node_List& merge_list) const { +void MergePrimitiveStores::collect_merge_list(Node_List& merge_list) const { // The merged store can be at most 8 bytes. const uint merge_list_max_size = 8 / _store->memory_size(); assert(merge_list_max_size >= 2 && @@ -3647,9 +3647,9 @@ void MergePrimitiveStores::collect_merge_list(Node_List& merge_list) const { } // Merge the input values of the smaller stores to a single larger input value. -Node* MergePrimitiveStores::make_merged_input_value(const Node_List& merge_list) { +Node* MergePrimitiveStores::make_merged_input_value(const Node_List& merge_list) const { int new_memory_size = _store->memory_size() * merge_list.size(); - Node* first = merge_list.at(merge_list.size()-1); + const StoreNode* first = merge_list.at(merge_list.size()-1); Node* merged_input_value = nullptr; if (_store->in(MemNode::ValueIn)->Opcode() == Op_ConI) { assert(_value_order == ValueOrder::Const, "must match"); @@ -3749,8 +3749,8 @@ Node* MergePrimitiveStores::make_merged_input_value(const Node_List& merge_list) // | | | | | | | | // // last_store (= _store) merged_store // // // -StoreNode* MergePrimitiveStores::make_merged_store(const Node_List& merge_list, Node* merged_input_value) { - Node* first_store = merge_list.at(merge_list.size()-1); +StoreNode* MergePrimitiveStores::make_merged_store(const Node_List& merge_list, Node* merged_input_value) const { + const StoreNode* first_store = merge_list.at(merge_list.size()-1); Node* last_ctrl = _store->in(MemNode::Control); // after (optional) RangeCheck Node* first_mem = first_store->in(MemNode::Memory); Node* first_adr = first_store->in(MemNode::Address); @@ -3779,7 +3779,7 @@ StoreNode* MergePrimitiveStores::make_merged_store(const Node_List& merge_list, } #ifndef PRODUCT -void MergePrimitiveStores::trace(const Node_List& merge_list, const Node* merged_input_value, const StoreNode* merged_store) const { +void MergePrimitiveStores::trace(const Node_List& merge_list, const Node* merged_input_value, const StoreNode* merged_store) { stringStream ss; ss.print_cr("[TraceMergeStores]: Replace"); for (int i = (int)merge_list.size() - 1; i >= 0; i--) { @@ -3968,14 +3968,23 @@ Node* StoreNode::Identity(PhaseGVN* phase) { } } - // Store of zero anywhere into a freshly-allocated object? - // Then the store is useless. + // Check if this store is redundant with the initialization of a freshly allocated object // (It must already have been captured by the InitializeNode.) if (result == this && ReduceFieldZeroing) { - // a newly allocated object is already all-zeroes everywhere - if (mem->is_Proj() && mem->in(0)->is_Allocate() && - (phase->type(val)->is_zero_type() || mem->in(0)->in(AllocateNode::InitValue) == val)) { - result = mem; + if (mem->is_Proj() && mem->in(0)->is_Allocate()) { + AllocateNode* alloc = mem->in(0)->as_Allocate(); + Node* init_value = alloc->in(AllocateNode::InitValue); + if (init_value == nullptr && phase->type(val)->is_zero_type()) { + // No initial value. A zero store is redundant because memory is already zeroed. + result = mem; + } else if (init_value == val && alloc->is_AllocateArray()) { + // Initial value. A store with the same value is redundant if it initializes + // the entire array element. This is only guaranteed with a non-flat array. + const TypeAryKlassPtr* ary_klass = phase->type(alloc->in(AllocateNode::KlassNode))->isa_aryklassptr(); + if (ary_klass != nullptr && ary_klass->is_not_flat()) { + result = mem; + } + } } if (result == this && phase->type(val)->is_zero_type()) { @@ -4254,27 +4263,6 @@ Node* StoreNode::Ideal_sign_extended_input(PhaseGVN* phase, int num_rejected_bit return nullptr; } -//------------------------------value_never_loaded----------------------------------- -// Determine whether there are any possible loads of the value stored. -// For simplicity, we actually check if there are any loads from the -// address stored to, not just for loads of the value stored by this node. -// -bool StoreNode::value_never_loaded(PhaseValues* phase) const { - Node *adr = in(Address); - const TypeOopPtr *adr_oop = phase->type(adr)->isa_oopptr(); - if (adr_oop == nullptr) - return false; - if (!adr_oop->is_known_instance_field()) - return false; // if not a distinct instance, there may be aliases of the address - for (DUIterator_Fast imax, i = adr->fast_outs(imax); i < imax; i++) { - Node *use = adr->fast_out(i); - if (use->is_Load() || use->is_LoadStore()) { - return false; - } - } - return true; -} - MemBarNode* StoreNode::trailing_membar() const { if (is_release()) { MemBarNode* trailing_mb = nullptr; @@ -4881,9 +4869,9 @@ MemBarNode* MemBarNode::trailing_membar() const { ResourceMark rm; Node* trailing = (Node*)this; VectorSet seen; - Node_Stack multis(0); + Node_Stack multis(0); do { - Node* c = trailing; + const Node* c = trailing; uint i = 0; do { trailing = nullptr; @@ -4894,7 +4882,7 @@ MemBarNode* MemBarNode::trailing_membar() const { if (multis.node() == c) { multis.set_index(i+1); } else { - multis.push(c, i+1); + multis.push(c->as_MultiBranch(), i+1); } } trailing = next; @@ -4925,13 +4913,13 @@ MemBarNode* MemBarNode::trailing_membar() const { MemBarNode* MemBarNode::leading_membar() const { ResourceMark rm; VectorSet seen; - Node_Stack regions(0); + Node_Stack regions(0); Node* leading = in(0); while (leading != nullptr && (!leading->is_MemBar() || !leading->as_MemBar()->leading())) { while (leading == nullptr || leading->is_top() || seen.test_set(leading->_idx)) { leading = nullptr; while (regions.size() > 0 && leading == nullptr) { - Node* r = regions.node(); + const RegionNode* r = regions.node(); uint i = regions.index(); if (i < r->req()) { leading = r->in(i); @@ -4946,18 +4934,18 @@ MemBarNode* MemBarNode::leading_membar() const { } } if (leading->is_Region()) { - regions.push(leading, 2); + regions.push(leading->as_Region(), 2); leading = leading->in(1); } else { leading = leading->in(0); } } #ifdef ASSERT - Unique_Node_List wq; - wq.push((Node*)this); + Unique_Node_List wq; + wq.push(this); uint found = 0; for (uint i = 0; i < wq.size(); i++) { - Node* n = wq.at(i); + const Node* n = wq.at(i); if (n->is_Region()) { for (uint j = 1; j < n->req(); j++) { Node* in = n->in(j); @@ -5179,7 +5167,7 @@ intptr_t InitializeNode::get_store_offset(Node* st, PhaseValues* phase) { // True for constants and parameters and small combinations thereof. bool InitializeNode::detect_init_independence(Node* value, PhaseGVN* phase) { ResourceMark rm; - Unique_Node_List worklist; + Unique_Node_List worklist; worklist.push(value); uint complexity_limit = 20; @@ -5277,7 +5265,7 @@ intptr_t InitializeNode::can_capture_store(StoreNode* st, PhaseGVN* phase, bool const TypePtr* t_adr = phase->type(adr)->isa_ptr(); int alias_idx = phase->C->get_alias_index(t_adr); ResourceMark rm; - Unique_Node_List mems; + Unique_Node_List mems; mems.push(mem); Node* unique_merge = nullptr; for (uint next = 0; next < mems.size(); ++next) { diff --git a/src/hotspot/share/opto/memnode.hpp b/src/hotspot/share/opto/memnode.hpp index 033f5d438f2b..36e78361c1fe 100644 --- a/src/hotspot/share/opto/memnode.hpp +++ b/src/hotspot/share/opto/memnode.hpp @@ -104,7 +104,6 @@ class MemNode : public Node { static bool detect_ptr_independence(Node* p1, AllocateNode* a1, Node* p2, AllocateNode* a2, PhaseGVN* phase); - static bool adr_phi_is_loop_invariant(Node* adr_phi, Node* cast); static Node *optimize_simple_memory_chain(Node *mchain, const TypeOopPtr *t_oop, Node *load, PhaseGVN *phase); static Node *optimize_memory_chain(Node *mchain, const TypePtr *t_adr, Node *load, PhaseGVN *phase); @@ -713,9 +712,6 @@ class StoreNode : public MemNode { // Map a store opcode to its corresponding own opcode, trivially. virtual int store_Opcode() const { return Opcode(); } - // have all possible loads of the value stored been optimized away? - bool value_never_loaded(PhaseValues* phase) const; - Node* convert_to_reinterpret_store(PhaseGVN& gvn, Node* val, const Type* vt); MemBarNode* trailing_membar() const; diff --git a/src/hotspot/share/opto/movenode.cpp b/src/hotspot/share/opto/movenode.cpp index 28cc47077677..68a79b5dbc3d 100644 --- a/src/hotspot/share/opto/movenode.cpp +++ b/src/hotspot/share/opto/movenode.cpp @@ -90,11 +90,6 @@ Node *CMoveNode::Ideal(PhaseGVN *phase, bool can_reshape) { phase->type(in(IfTrue)) == Type::TOP) { return nullptr; } - Node* progress = TypeNode::Ideal(phase, can_reshape); - if (progress != nullptr) { - return progress; - } - // Check for Min/Max patterns. This is called before constants are pushed to the true-case input, as that transform can // make BoolTests non-canonical. Node* minmax = Ideal_minmax(phase, this); diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index 0619fadd23d1..ef6d3bfbfaae 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -29,6 +29,7 @@ #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "opto/ad.hpp" +#include "opto/addnode.hpp" #include "opto/callGenerator.hpp" #include "opto/castnode.hpp" #include "opto/cfgnode.hpp" @@ -603,6 +604,7 @@ void Node::setup_is_top() { //------------------------------~Node------------------------------------------ // Fancy destructor; eagerly attempt to reclaim Node numberings and storage void Node::destruct(PhaseValues* phase) { + assert(this != Compile::current()->dead_path(), "we want to keep the unique DeadPath node around"); Compile* compile = (phase != nullptr) ? phase->C : Compile::current(); if (phase != nullptr && phase->is_IterGVN()) { phase->is_IterGVN()->_worklist.remove(this); @@ -742,11 +744,14 @@ void Node::out_grow(uint len) { //------------------------------is_dead---------------------------------------- bool Node::is_dead() const { // Mach and pinch point nodes may look like dead. - if( is_top() || is_Mach() || (Opcode() == Op_Node && _outcnt > 0) ) + if (is_top() || is_Mach() || (Opcode() == Op_Node && _outcnt > 0) || this == Compile::current()->dead_path()) { return false; - for( uint i = 0; i < _max; i++ ) - if( _in[i] != nullptr ) + } + for (uint i = 0; i < _max; i++) { + if (_in[i] != nullptr) { return false; + } + } return true; } @@ -756,11 +761,11 @@ bool Node::is_not_dead(const Node* n) { bool Node::is_reachable_from_root() const { ResourceMark rm; - Unique_Node_List wq; - wq.push((Node*)this); + Unique_Node_List wq; + wq.push(this); RootNode* root = Compile::current()->root(); for (uint i = 0; i < wq.size(); i++) { - Node* m = wq.at(i); + const Node* m = wq.at(i); if (m == root) { return true; } @@ -1368,7 +1373,7 @@ Node* Node::find_exact_control(Node* ctrl) { // We already know that if any path back to Root or Start reaches 'this', // then all paths so, so this is a simple search for one example, // not an exhaustive search for a counterexample. -Node::DomResult Node::dominates(Node* sub, Node_List &nlist) { +Node::DomResult Node::dominates(Node* sub, Node_List& nlist) { assert(this->is_CFG(), "expecting control"); assert(sub != nullptr && sub->is_CFG(), "expecting control"); @@ -1497,7 +1502,7 @@ static void kill_dead_code( Node *dead, PhaseIterGVN *igvn ) { if( dead->is_Con() ) return; ResourceMark rm; - Node_List nstack; + Node_List nstack; VectorSet dead_set; // notify uses only once Node *top = igvn->C->top(); @@ -2826,7 +2831,7 @@ void Node::dump_comp(const char* suffix, outputStream *st) const { // VERIFICATION CODE // Verify all nodes if verify_depth is negative -void Node::verify(int verify_depth, VectorSet& visited, Node_List& worklist) { +void Node::verify(int verify_depth, VectorSet& visited, Node_List& worklist) { assert(verify_depth != 0, "depth should not be 0"); Compile* C = Compile::current(); uint last_index_on_current_depth = worklist.size() - 1; @@ -2916,32 +2921,36 @@ const RegMask &Node::in_RegMask(uint) const { return RegMask::EMPTY; } -void Node_Array::grow(uint i) { +template +void Node_Array::grow(uint i) { assert(i >= _max, "Should have been checked before, use maybe_grow?"); assert(_max > 0, "invariant"); uint old = _max; _max = next_power_of_2(i); - _nodes = (Node**)_a->Arealloc( _nodes, old*sizeof(Node*),_max*sizeof(Node*)); - Copy::zero_to_bytes( &_nodes[old], (_max-old)*sizeof(Node*) ); + _nodes = (T**)_a->Arealloc( _nodes, old*sizeof(T*),_max*sizeof(T*)); + Copy::zero_to_bytes( &_nodes[old], (_max-old)*sizeof(T*) ); } -void Node_Array::insert(uint i, Node* n) { +template +void Node_Array::insert(uint i, T* n) { if (_nodes[_max - 1]) { grow(_max); } - Copy::conjoint_words_to_higher((HeapWord*)&_nodes[i], (HeapWord*)&_nodes[i + 1], ((_max - i - 1) * sizeof(Node*))); + Copy::conjoint_words_to_higher((HeapWord*)&_nodes[i], (HeapWord*)&_nodes[i + 1], ((_max - i - 1) * sizeof(T*))); _nodes[i] = n; } -void Node_Array::remove(uint i) { - Copy::conjoint_words_to_lower((HeapWord*)&_nodes[i + 1], (HeapWord*)&_nodes[i], ((_max - i - 1) * sizeof(Node*))); +template +void Node_Array::remove(uint i) { + Copy::conjoint_words_to_lower((HeapWord*)&_nodes[i + 1], (HeapWord*)&_nodes[i], ((_max - i - 1) * sizeof(T*))); _nodes[_max - 1] = nullptr; } -void Node_Array::dump() const { +template +void Node_Array::dump() const { #ifndef PRODUCT for (uint i = 0; i < _max; i++) { - Node* nn = _nodes[i]; + T* nn = _nodes[i]; if (nn != nullptr) { tty->print("%5d--> ",i); nn->dump(); } @@ -3163,36 +3172,39 @@ bool Node::has_non_debug_uses() const { //============================================================================= //------------------------------yank------------------------------------------- // Find and remove -void Node_List::yank( Node *n ) { +template +void Node_List::yank(const T* n) { uint i; for (i = 0; i < _cnt; i++) { - if (_nodes[i] == n) { + if (Base::_nodes[i] == n) { break; } } if (i < _cnt) { - _nodes[i] = _nodes[--_cnt]; + Base::_nodes[i] = Base::_nodes[--_cnt]; } } //------------------------------dump------------------------------------------- -void Node_List::dump() const { +template +void Node_List::dump() const { #ifndef PRODUCT for (uint i = 0; i < _cnt; i++) { - if (_nodes[i]) { + if (Base::_nodes[i]) { tty->print("%5d--> ", i); - _nodes[i]->dump(); + Base::_nodes[i]->dump(); } } #endif } -void Node_List::dump_simple() const { +template +void Node_List::dump_simple() const { #ifndef PRODUCT for (uint i = 0; i < _cnt; i++) { - if( _nodes[i] ) { - tty->print(" %d", _nodes[i]->_idx); + if (Base::_nodes[i]) { + tty->print(" %d", Base::_nodes[i]->_idx); } else { tty->print(" null"); } @@ -3202,11 +3214,12 @@ void Node_List::dump_simple() const { //============================================================================= //------------------------------remove----------------------------------------- -void Unique_Node_List::remove(Node* n) { +template +void Unique_Node_List::remove(const T* n) { if (_in_worklist.test(n->_idx)) { - for (uint i = 0; i < size(); i++) { - if (_nodes[i] == n) { - map(i, Node_List::pop()); + for (uint i = 0; i < Base::size(); i++) { + if (Base::_nodes[i] == n) { + Base::map(i, Base::pop()); _in_worklist.remove(n->_idx); return; } @@ -3215,23 +3228,23 @@ void Unique_Node_List::remove(Node* n) { } } -//-----------------------remove_useless_nodes---------------------------------- // Remove useless nodes from worklist -void Unique_Node_List::remove_useless_nodes(const VectorSet& useful) { - for (uint i = 0; i < size(); ++i) { - Node *n = at(i); +template +void Unique_Node_List::remove_useless_nodes(const VectorSet& useful) { + for (uint i = 0; i < Base::size(); ++i) { + T* n = Base::at(i); assert( n != nullptr, "Did not expect null entries in worklist"); if (!useful.test(n->_idx)) { _in_worklist.remove(n->_idx); - map(i, Node_List::pop()); + Base::map(i, Base::pop()); --i; // Visit popped node // If it was last entry, loop terminates since size() was also reduced } } } -//============================================================================= -void Node_Stack::grow() { +template +void Node_Stack::grow() { size_t old_top = pointer_delta(_inode_top,_inodes,sizeof(INode)); // save _top size_t old_max = pointer_delta(_inode_max,_inodes,sizeof(INode)); size_t max = old_max << 1; // max * 2 @@ -3241,7 +3254,8 @@ void Node_Stack::grow() { } // Node_Stack is used to map nodes. -Node* Node_Stack::find(uint idx) const { +template +T* Node_Stack::find(uint idx) const { uint sz = size(); for (uint i = 0; i < sz; i++) { if (idx == index_at(i)) { @@ -3280,10 +3294,11 @@ uint TypeNode::ideal_reg() const { return _type->ideal_reg(); } -void TypeNode::make_path_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, Node* ctrl_use, uint j, const char* phase_str) { +void Node::make_path_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, Node* ctrl_use, uint j, const char* phase_str) { Node* c = ctrl_use->in(j); - if (igvn->type(c) != Type::TOP) { - igvn->replace_input_of(ctrl_use, j, igvn->C->top()); + Node* top = igvn->C->top(); + if (c != top) { + igvn->replace_input_of(ctrl_use, j, top); create_halt_path(igvn, c, loop, phase_str); } } @@ -3295,14 +3310,18 @@ void TypeNode::make_path_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, Node* ct // constant folds and the control flow that leads to the Type node becomes unreachable. There are cases where that // doesn't happen, however. They are handled here by following uses of the Type node until a CFG or a Phi to find dead // paths. The dead paths are then replaced by a Halt node. -void TypeNode::make_paths_from_here_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, const char* phase_str) { - Unique_Node_List wq; +void Node::make_paths_from_here_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, const char* phase_str) { + Unique_Node_List wq; wq.push(this); for (uint i = 0; i < wq.size(); ++i) { Node* n = wq.at(i); + if (n->is_CFG()) { + n->remove_dead_region(igvn, true); + } for (DUIterator_Fast kmax, k = n->fast_outs(kmax); k < kmax; k++) { Node* u = n->fast_out(k); if (u->is_CFG()) { + wq.push(u); assert(!u->is_Region(), "Can't reach a Region without going through a Phi"); make_path_dead(igvn, loop, u, 0, phase_str); } else if (u->is_Phi()) { @@ -3322,7 +3341,7 @@ void TypeNode::make_paths_from_here_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loo } } -void TypeNode::create_halt_path(PhaseIterGVN* igvn, Node* c, PhaseIdealLoop* loop, const char* phase_str) const { +void Node::create_halt_path(PhaseIterGVN* igvn, Node* c, PhaseIdealLoop* loop, const char* phase_str) { Node* frame = new ParmNode(igvn->C->start(), TypeFunc::FramePtr); if (loop == nullptr) { igvn->register_new_node_with_optimizer(frame); @@ -3342,14 +3361,43 @@ void TypeNode::create_halt_path(PhaseIterGVN* igvn, Node* c, PhaseIdealLoop* loo igvn->add_input_to(igvn->C->root(), halt); } -Node* TypeNode::Ideal(PhaseGVN* phase, bool can_reshape) { - if (KillPathsReachableByDeadTypeNode && can_reshape && Value(phase) == Type::TOP) { - PhaseIterGVN* igvn = phase->is_IterGVN(); - Node* top = igvn->C->top(); - ResourceMark rm; - make_paths_from_here_dead(igvn, nullptr, "igvn"); - return top; - } - - return Node::Ideal(phase, can_reshape); -} +#define INSTANTIATE_NODE_CONTAINERS(node) \ +template class Node_Array; \ +template class Node_Array; \ +template class Node_List; \ +template class Node_List; \ +template class Unique_Node_List; \ +template class Unique_Node_List; \ +template class Node_Stack; \ +template class Node_Stack; \ + +INSTANTIATE_NODE_CONTAINERS(Node) + +INSTANTIATE_NODE_CONTAINERS(AbstractLockNode) +INSTANTIATE_NODE_CONTAINERS(AddPNode) +INSTANTIATE_NODE_CONTAINERS(BoolNode) +INSTANTIATE_NODE_CONTAINERS(CallJavaNode) +INSTANTIATE_NODE_CONTAINERS(CallStaticJavaNode) +INSTANTIATE_NODE_CONTAINERS(CastPPNode) +INSTANTIATE_NODE_CONTAINERS(ConstraintCastNode) +INSTANTIATE_NODE_CONTAINERS(CountedLoopNode) +INSTANTIATE_NODE_CONTAINERS(CreateExNode) +INSTANTIATE_NODE_CONTAINERS(IfNode) +INSTANTIATE_NODE_CONTAINERS(IfProjNode) +INSTANTIATE_NODE_CONTAINERS(MachNode) +INSTANTIATE_NODE_CONTAINERS(MachProjNode) +INSTANTIATE_NODE_CONTAINERS(MergeMemNode) +INSTANTIATE_NODE_CONTAINERS(MemNode) +INSTANTIATE_NODE_CONTAINERS(MultiBranchNode) +INSTANTIATE_NODE_CONTAINERS(PhiNode) +INSTANTIATE_NODE_CONTAINERS(OpaqueMultiversioningNode) +INSTANTIATE_NODE_CONTAINERS(OpaqueTemplateAssertionPredicateNode) +INSTANTIATE_NODE_CONTAINERS(OpaqueZeroTripGuardNode) +INSTANTIATE_NODE_CONTAINERS(ReachabilityFenceNode) +INSTANTIATE_NODE_CONTAINERS(RegionNode) +INSTANTIATE_NODE_CONTAINERS(SafePointNode) +INSTANTIATE_NODE_CONTAINERS(SafePointScalarObjectNode) +INSTANTIATE_NODE_CONTAINERS(StoreNode) +INSTANTIATE_NODE_CONTAINERS(TypeNode) +INSTANTIATE_NODE_CONTAINERS(ValueTypeNode) +#undef INSTANTIATE_NODE_CONTAINERS diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index cbf817aac4fb..3e0d5601214f 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -80,9 +80,11 @@ class CompareAndSwapNode; class CompareAndExchangeNode; class CountedLoopNode; class CountedLoopEndNode; +class CreateExNode; class DecodeNarrowPtrNode; class DecodeNNode; class DecodeNKlassNode; +class DivModIntegerNode; class EncodeNarrowPtrNode; class EncodePNode; class EncodePKlassNode; @@ -126,6 +128,7 @@ class MachReturnNode; class MachSafePointNode; class MachSpillCopyNode; class MachTempNode; +class MachUncommonTrapNode; class MachMergeNode; class MachMemBarNode; class MachVEPNode; @@ -146,16 +149,17 @@ class NeverBranchNode; class Opaque1Node; class OpaqueLoopInitNode; class OpaqueLoopStrideNode; -class OpaqueMultiversioningNode; class OpaqueConstantBoolNode; class OpaqueInitializedAssertionPredicateNode; +class OpaqueMultiversioningNode; class OpaqueTemplateAssertionPredicateNode; +class OpaqueZeroTripGuardNode; class OuterStripMinedLoopNode; class OuterStripMinedLoopEndNode; class Node; -class Node_Array; -class Node_List; -class Node_Stack; +template class Node_Array; +template class Node_List; +template class Node_Stack; class OopMap; class ParmNode; class ParsePredicateNode; @@ -730,6 +734,7 @@ class Node { DEFINE_CLASS_ID(MachCallDynamicJava, MachCallJava, 1) DEFINE_CLASS_ID(MachCallRuntime, MachCall, 1) DEFINE_CLASS_ID(MachCallLeaf, MachCallRuntime, 0) + DEFINE_CLASS_ID(MachUncommonTrap, MachSafePoint, 1) DEFINE_CLASS_ID(MachBranch, Mach, 1) DEFINE_CLASS_ID(MachIf, MachBranch, 0) DEFINE_CLASS_ID(MachGoto, MachBranch, 1) @@ -780,6 +785,7 @@ class Node { DEFINE_CLASS_ID(ConI, Con, 0) DEFINE_CLASS_ID(SafePointScalarMerge, Type, 10) DEFINE_CLASS_ID(Convert, Type, 11) + DEFINE_CLASS_ID(CreateEx, Type, 12) DEFINE_CLASS_ID(Proj, Node, 3) @@ -836,6 +842,7 @@ class Node { DEFINE_CLASS_ID(OpaqueLoopInit, Opaque1, 0) DEFINE_CLASS_ID(OpaqueLoopStride, Opaque1, 1) DEFINE_CLASS_ID(OpaqueMultiversioning, Opaque1, 2) + DEFINE_CLASS_ID(OpaqueZeroTripGuard, Opaque1, 3) DEFINE_CLASS_ID(OpaqueConstantBool, Node, 17) DEFINE_CLASS_ID(OpaqueInitializedAssertionPredicate, Node, 18) DEFINE_CLASS_ID(OpaqueTemplateAssertionPredicate, Node, 19) @@ -843,8 +850,9 @@ class Node { DEFINE_CLASS_ID(LShift, Node, 21) DEFINE_CLASS_ID(Neg, Node, 22) DEFINE_CLASS_ID(ReachabilityFence, Node, 23) + DEFINE_CLASS_ID(DivModInteger, Node, 24) - _max_classes = ClassMask_Neg + _max_classes = ClassMask_DivModInteger }; #undef DEFINE_CLASS_ID @@ -959,9 +967,11 @@ class Node { DEFINE_CLASS_QUERY(Convert) DEFINE_CLASS_QUERY(CountedLoop) DEFINE_CLASS_QUERY(CountedLoopEnd) + DEFINE_CLASS_QUERY(CreateEx) DEFINE_CLASS_QUERY(DecodeNarrowPtr) DEFINE_CLASS_QUERY(DecodeN) DEFINE_CLASS_QUERY(DecodeNKlass) + DEFINE_CLASS_QUERY(DivModInteger) DEFINE_CLASS_QUERY(EncodeNarrowPtr) DEFINE_CLASS_QUERY(EncodeP) DEFINE_CLASS_QUERY(EncodePKlass) @@ -1003,6 +1013,7 @@ class Node { DEFINE_CLASS_QUERY(MachProlog) DEFINE_CLASS_QUERY(MachReturn) DEFINE_CLASS_QUERY(MachSafePoint) + DEFINE_CLASS_QUERY(MachUncommonTrap) DEFINE_CLASS_QUERY(MachSpillCopy) DEFINE_CLASS_QUERY(MachTemp) DEFINE_CLASS_QUERY(MachMemBar) @@ -1029,6 +1040,7 @@ class Node { DEFINE_CLASS_QUERY(OpaqueLoopInit) DEFINE_CLASS_QUERY(OpaqueLoopStride) DEFINE_CLASS_QUERY(OpaqueMultiversioning) + DEFINE_CLASS_QUERY(OpaqueZeroTripGuard) DEFINE_CLASS_QUERY(OuterStripMinedLoop) DEFINE_CLASS_QUERY(OuterStripMinedLoopEnd) DEFINE_CLASS_QUERY(Parm) @@ -1328,7 +1340,7 @@ class Node { EncounteredDeadCode // Result is undefined due to encountering dead code. }; // Check if 'this' node dominates or equal to 'sub'. - DomResult dominates(Node* sub, Node_List &nlist); + DomResult dominates(Node* sub, Node_List& nlist); bool remove_dead_region(PhaseGVN *phase, bool can_reshape); public: @@ -1516,7 +1528,7 @@ class Node { // Print compact per-node info virtual void dump_compact_spec(outputStream *st) const { dump_spec(st); } - static void verify(int verify_depth, VectorSet& visited, Node_List& worklist); + static void verify(int verify_depth, VectorSet& visited, Node_List& worklist); // This call defines a class-unique string used to identify class instances virtual const char *Name() const; @@ -1548,6 +1560,10 @@ class Node { uint _del_tick; // Bumped when a deletion happens.. #endif #endif + void make_paths_from_here_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, const char* phase_str); + + static void create_halt_path(PhaseIterGVN* igvn, Node* c, PhaseIdealLoop* loop, const char* phase_str); + void make_path_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, Node* ctrl_use, uint j, const char* phase_str); }; inline bool not_a_node(const Node* n) { @@ -1814,11 +1830,12 @@ class SimpleDUIterator : public StackObj { // Abstractly provides an infinite array of Node*'s, initialized to null. // Note that the constructor just zeros things, and since I use Arena // allocation I do not need a destructor to reclaim storage. +template class Node_Array : public AnyObj { protected: Arena* _a; // Arena to allocate in uint _max; - Node** _nodes; + T** _nodes; ReallocMark _nesting; // Safety checks for arena reallocation // Grow array to required capacity @@ -1832,7 +1849,7 @@ class Node_Array : public AnyObj { public: Node_Array(Arena* a, uint max = OptoNodeListSize) : _a(a), _max(max) { - _nodes = NEW_ARENA_ARRAY(a, Node*, max); + _nodes = NEW_ARENA_ARRAY(a, T*, max); clear(); } Node_Array() : Node_Array(Thread::current()->resource_area()) {} @@ -1842,52 +1859,54 @@ class Node_Array : public AnyObj { // Allow move constructor for && (eg. capture return of function) Node_Array(Node_Array&&) = default; - Node *operator[] ( uint i ) const // Lookup, or null for not mapped - { return (i<_max) ? _nodes[i] : (Node*)nullptr; } - Node* at(uint i) const { assert(i<_max,"oob"); return _nodes[i]; } - Node** adr() { return _nodes; } + // Lookup, or null for not mapped + T* operator[] (uint i) const { return (i < _max) ? _nodes[i] : (T*)nullptr; } + T* at(uint i) const { assert(i<_max,"oob"); return _nodes[i]; } + T** adr() { return _nodes; } // Extend the mapping: index i maps to Node *n. - void map( uint i, Node *n ) { maybe_grow(i); _nodes[i] = n; } - void insert( uint i, Node *n ); - void remove( uint i ); // Remove, preserving order + void map(uint i, T* n) { maybe_grow(i); _nodes[i] = n; } + void insert(uint i, T* n); + void remove(uint i ); // Remove, preserving order // Clear all entries in _nodes to null but keep storage void clear() { - Copy::zero_to_bytes(_nodes, _max * sizeof(Node*)); + Copy::zero_to_bytes(_nodes, _max * sizeof(T*)); } uint max() const { return _max; } void dump() const; }; -class Node_List : public Node_Array { +template +class Node_List : public Node_Array { + typedef Node_Array Base; uint _cnt; public: - Node_List(uint max = OptoNodeListSize) : Node_Array(Thread::current()->resource_area(), max), _cnt(0) {} - Node_List(Arena *a, uint max = OptoNodeListSize) : Node_Array(a, max), _cnt(0) {} + Node_List(uint max = OptoNodeListSize) : Base(Thread::current()->resource_area(), max), _cnt(0) {} + Node_List(Arena* a, uint max = OptoNodeListSize) : Base(a, max), _cnt(0) {} NONCOPYABLE(Node_List); Node_List& operator=(Node_List&&) = delete; // Allow move constructor for && (eg. capture return of function) Node_List(Node_List&&) = default; - bool contains(const Node* n) const { + bool contains(const T* n) const { for (uint e = 0; e < size(); e++) { - if (at(e) == n) return true; + if (Base::at(e) == n) return true; } return false; } - void insert( uint i, Node *n ) { Node_Array::insert(i,n); _cnt++; } - void remove( uint i ) { Node_Array::remove(i); _cnt--; } - void push( Node *b ) { map(_cnt++,b); } - void yank( Node *n ); // Find and remove - Node *pop() { return _nodes[--_cnt]; } - void clear() { _cnt = 0; Node_Array::clear(); } // retain storage - void copy(const Node_List& from) { - if (from._max > _max) { - grow(from._max); + void insert(uint i, T* n) { Base::insert(i,n); _cnt++; } + void remove(uint i) { Base::remove(i); _cnt--; } + void push(T* b) { Base::map(_cnt++,b); } + void yank(const T* n); // Find and remove + T* pop() { return Base::_nodes[--_cnt]; } + void clear() { _cnt = 0; Base::clear(); } // retain storage + void copy(const Node_List& from) { + if (from._max > Base::_max) { + Base::grow(from._max); } _cnt = from._cnt; - Copy::conjoint_words_to_higher((HeapWord*)&from._nodes[0], (HeapWord*)&_nodes[0], from._max * sizeof(Node*)); + Copy::conjoint_words_to_higher((HeapWord*)&from._nodes[0], (HeapWord*)&Base::_nodes[0], from._max * sizeof(Node*)); } uint size() const { return _cnt; } @@ -1900,7 +1919,7 @@ template void Node::visit_uses(Callback callback, Check is_boundary, bool always_callback) const { ResourceMark rm; VectorSet visited; - Node_List worklist; + Node_List worklist; // The initial worklist consists of the direct uses for (DUIterator_Fast kmax, k = fast_outs(kmax); k < kmax; k++) { @@ -1924,69 +1943,54 @@ void Node::visit_uses(Callback callback, Check is_boundary, bool always_callback } } - -//------------------------------Unique_Node_List------------------------------- -class Unique_Node_List : public Node_List { +template +class Unique_Node_List : public Node_List { + typedef Node_List Base; VectorSet _in_worklist; uint _clock_index; // Index in list where to pop from next public: - Unique_Node_List() : Node_List(), _clock_index(0) {} - Unique_Node_List(Arena *a) : Node_List(a), _in_worklist(a), _clock_index(0) {} + Unique_Node_List() : Base(), _clock_index(0) {} + Unique_Node_List(Arena* a) : Base(a), _in_worklist(a), _clock_index(0) {} NONCOPYABLE(Unique_Node_List); Unique_Node_List& operator=(Unique_Node_List&&) = delete; // Allow move constructor for && (eg. capture return of function) Unique_Node_List(Unique_Node_List&&) = default; - void remove( Node *n ); - bool member(const Node* n) const { return _in_worklist.test(n->_idx) != 0; } - VectorSet& member_set(){ return _in_worklist; } + void remove(const T* n); + bool member(const T* n) const { return _in_worklist.test(n->_idx) != 0; } + const VectorSet& member_set() const { return _in_worklist; } - void push(Node* b) { - if( !_in_worklist.test_set(b->_idx) ) - Node_List::push(b); - } - void push_non_cfg_inputs_of(const Node* node) { - for (uint i = 1; i < node->req(); i++) { - Node* input = node->in(i); - if (input != nullptr && !input->is_CFG()) { - push(input); - } - } - } - - void push_outputs_of(const Node* node) { - for (DUIterator_Fast imax, i = node->fast_outs(imax); i < imax; i++) { - Node* output = node->fast_out(i); - push(output); - } + void push(T* b) { + if (!_in_worklist.test_set(b->_idx)) + Base::push(b); } - Node *pop() { - if( _clock_index >= size() ) _clock_index = 0; - Node *b = at(_clock_index); - map( _clock_index, Node_List::pop()); - if (size() != 0) _clock_index++; // Always start from 0 + T* pop() { + if( _clock_index >= Base::size() ) _clock_index = 0; + T* b = Base::at(_clock_index); + Base::map( _clock_index, Base::pop()); + if (Base::size() != 0) _clock_index++; // Always start from 0 _in_worklist.remove(b->_idx); return b; } - Node *remove(uint i) { - Node *b = Node_List::at(i); + T* remove(uint i) { + T* b = Base::at(i); _in_worklist.remove(b->_idx); - map(i,Node_List::pop()); + Base::map(i,Base::pop()); return b; } - void yank(Node *n) { + void yank(const T* n) { _in_worklist.remove(n->_idx); - Node_List::yank(n); + Base::yank(n); } - void clear() { + void clear() { _in_worklist.clear(); // Discards storage but grows automatically - Node_List::clear(); + Base::clear(); _clock_index = 0; } void ensure_empty() { - assert(size() == 0, "must be empty"); + assert(Base::size() == 0, "must be empty"); clear(); // just in case } @@ -1996,16 +2000,16 @@ class Unique_Node_List : public Node_List { // If the idx of the Nodes change, we must recompute the VectorSet void recompute_idx_set() { _in_worklist.clear(); - for (uint i = 0; i < size(); i++) { - Node* n = at(i); + for (uint i = 0; i < Base::size(); i++) { + T* n = Base::at(i); _in_worklist.set(n->_idx); } } #ifdef ASSERT - bool is_subset_of(Unique_Node_List& other) { - for (uint i = 0; i < size(); i++) { - Node* n = at(i); + bool is_subset_of(const Unique_Node_List& other) const { + for (uint i = 0; i < Base::size(); i++) { + T* n = Base::at(i); if (!other.member(n)) { return false; } @@ -2014,7 +2018,7 @@ class Unique_Node_List : public Node_List { } #endif - bool contains(const Node* n) const { + bool contains(const T* n) const { fatal("use faster member() instead"); return false; } @@ -2051,7 +2055,7 @@ class Unique_Mixed_Node_List : public ResourceObj { private: Dict _visited_set; - Node_List _worklist; + Node_List _worklist; }; // Inline definition of Compile::record_for_igvn must be deferred to this point. @@ -2064,17 +2068,17 @@ inline void Compile::remove_for_igvn(Node* n) { _igvn_worklist->remove(n); } -//------------------------------Node_Stack------------------------------------- +template class Node_Stack { protected: struct INode { - Node *node; // Processed node + T* node; // Processed node uint indx; // Index of next node's child }; - INode *_inode_top; // tos, stack grows up - INode *_inode_max; // End of _inodes == _inodes + _max - INode *_inodes; // Array storage for the stack - Arena *_a; // Arena to allocate in + INode* _inode_top; // tos, stack grows up + INode* _inode_max; // End of _inodes == _inodes + _max + INode* _inodes; // Array storage for the stack + Arena* _a; // Arena to allocate in ReallocMark _nesting; // Safety checks for arena reallocation void maybe_grow() { @@ -2105,17 +2109,17 @@ class Node_Stack { assert(_inode_top >= _inodes, "node stack underflow"); --_inode_top; } - void push(Node *n, uint i) { + void push(T* n, uint i) { ++_inode_top; maybe_grow(); INode *top = _inode_top; // optimization top->node = n; top->indx = i; } - Node *node() const { + T* node() const { return _inode_top->node; } - Node* node_at(uint i) const { + T* node_at(uint i) const { assert(_inodes + i <= _inode_top, "in range"); return _inodes[i].node; } @@ -2126,7 +2130,7 @@ class Node_Stack { assert(_inodes + i <= _inode_top, "in range"); return _inodes[i].indx; } - void set_node(Node *n) { + void set_node(T* n) { _inode_top->node = n; } void set_index(uint i) { @@ -2139,7 +2143,7 @@ class Node_Stack { void clear() { _inode_top = _inodes - 1; } // retain storage // Node_Stack is used to map nodes. - Node* find(uint idx) const; + T* find(uint idx) const; NONCOPYABLE(Node_Stack); }; @@ -2246,17 +2250,13 @@ class TypeNode : public Node { init_class_id(Class_Type); } virtual const Type* Value(PhaseGVN* phase) const; - virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); virtual const Type *bottom_type() const; virtual uint ideal_reg() const; - void make_path_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, Node* ctrl_use, uint j, const char* phase_str); #ifndef PRODUCT virtual void dump_spec(outputStream *st) const; virtual void dump_compact_spec(outputStream *st) const; #endif - void make_paths_from_here_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, const char* phase_str); - void create_halt_path(PhaseIterGVN* igvn, Node* c, PhaseIdealLoop* loop, const char* phase_str) const; }; #include "opto/opcodes.hpp" @@ -2373,7 +2373,7 @@ class DataNodeBFS : public StackObj { // Run the BFS starting from 'start_node' and apply the actions provided to this class. void run(Node* start_node) { ResourceMark rm; - Unique_Node_List _nodes_to_visit; + Unique_Node_List _nodes_to_visit; _nodes_to_visit.push(start_node); for (uint i = 0; i < _nodes_to_visit.size(); i++) { Node* next = _nodes_to_visit[i]; diff --git a/src/hotspot/share/opto/opaquenode.cpp b/src/hotspot/share/opto/opaquenode.cpp index a3b50d269fbd..3510d451cff0 100644 --- a/src/hotspot/share/opto/opaquenode.cpp +++ b/src/hotspot/share/opto/opaquenode.cpp @@ -49,7 +49,7 @@ Node* Opaque1Node::Identity(PhaseGVN* phase) { CountedLoopNode* OpaqueZeroTripGuardNode::guarded_loop() const { Node* iff = if_node(); ResourceMark rm; - Unique_Node_List wq; + Unique_Node_List wq; wq.push(iff); for (uint i = 0; i < wq.size(); ++i) { Node* nn = wq.at(i); diff --git a/src/hotspot/share/opto/opaquenode.hpp b/src/hotspot/share/opto/opaquenode.hpp index 7ec7e23144a3..2a1730e219c0 100644 --- a/src/hotspot/share/opto/opaquenode.hpp +++ b/src/hotspot/share/opto/opaquenode.hpp @@ -40,7 +40,6 @@ class Opaque1Node : public Node { public: Opaque1Node(Compile* C, Node *n) : Node(nullptr, n) { // Put it on the Macro nodes list to removed during macro nodes expansion. - init_flags(Flag_is_macro); init_class_id(Class_Opaque1); C->add_macro_node(this); } @@ -48,7 +47,6 @@ class Opaque1Node : public Node { // which is consumed by range check elimination. Opaque1Node(Compile* C, Node *n, Node* orig_limit) : Node(nullptr, n, orig_limit) { // Put it on the Macro nodes list to removed during macro nodes expansion. - init_flags(Flag_is_macro); init_class_id(Class_Opaque1); C->add_macro_node(this); } @@ -82,6 +80,7 @@ class OpaqueZeroTripGuardNode : public Opaque1Node { BoolTest::mask _loop_entered_mask; OpaqueZeroTripGuardNode(Compile* C, Node* n, BoolTest::mask loop_entered_test) : Opaque1Node(C, n), _loop_entered_mask(loop_entered_test) { + init_class_id(Class_OpaqueZeroTripGuard); } DEBUG_ONLY(CountedLoopNode* guarded_loop() const); @@ -150,7 +149,6 @@ class OpaqueConstantBoolNode : public Node { OpaqueConstantBoolNode(Compile* C, Node* tst, bool constant) : Node(nullptr, tst), _constant(constant) { assert(tst->is_Bool() || tst->is_Con(), "Test node must be a BoolNode or a constant"); init_class_id(Class_OpaqueConstantBool); - init_flags(Flag_is_macro); C->add_macro_node(this); } @@ -241,7 +239,6 @@ class OpaqueInitializedAssertionPredicateNode : public Node { OpaqueInitializedAssertionPredicateNode(BoolNode* bol, Compile* C) : Node(nullptr, bol), _useless(false) { init_class_id(Class_OpaqueInitializedAssertionPredicate); - init_flags(Flag_is_macro); C->add_macro_node(this); } diff --git a/src/hotspot/share/opto/output.cpp b/src/hotspot/share/opto/output.cpp index e1b995c4bc87..11bd4ab0526e 100644 --- a/src/hotspot/share/opto/output.cpp +++ b/src/hotspot/share/opto/output.cpp @@ -67,9 +67,6 @@ class Scheduling { private: - // Arena to use - Arena *_arena; - // Control-Flow Graph info PhaseCFG *_cfg; @@ -80,20 +77,20 @@ class Scheduling { uint _node_bundling_limit; // List of scheduled nodes. Generated in reverse order - Node_List _scheduled; + Node_List _scheduled; // List of nodes currently available for choosing for scheduling - Node_List _available; + Node_List _available; // For each instruction beginning a bundle, the number of following // nodes to be bundled with it. Bundle *_node_bundling_base; // Mapping from register to Node - Node_List _reg_node; + Node_List _reg_node; // Free list for pinch nodes. - Node_List _pinch_free_list; + Node_List _pinch_free_list; // Number of uses of this node within the containing basic block. short *_uses; @@ -510,7 +507,17 @@ void PhaseOutput::shorten_branches(uint* blk_starts) { MachNode* mach = nj->as_Mach(); blk_size += (mach->alignment_required() - 1) * relocInfo::addr_unit(); // assume worst case padding reloc_size += mach->reloc(); - if (mach->is_MachCall()) { + if (mach->is_MachUncommonTrap()) { + // add size information for trampoline stub + // class CallStubImpl is platform-specific and defined in the *.ad files. + stub_size += CallStubImpl::size_call_trampoline(); + reloc_size += CallStubImpl::reloc_call_trampoline(); + + // No nop is needed to disambiguate from another safepoint. + assert(mach->as_MachUncommonTrap()->ret_addr_offset() > 0 && + (uint)mach->as_MachUncommonTrap()->ret_addr_offset() < mach->size(C->regalloc()), + "Safepoint address should be offset from the first instruction and should not point after the node"); + } else if (mach->is_MachCall()) { // add size information for trampoline stub // class CallStubImpl is platform-specific and defined in the *.ad files. stub_size += CallStubImpl::size_call_trampoline(); @@ -532,6 +539,7 @@ void PhaseOutput::shorten_branches(uint* blk_starts) { // nop to disambiguate the two safepoints. // ScheduleAndBundle() can rearrange nodes in a block, // check for all offsets inside this block. + assert(mach->ideal_Opcode() == Op_SafePoint, "An unexpected safepoint node, which might need handling above"); if (last_call_adr >= blk_starts[i]) { blk_size += nop_size; } @@ -1058,7 +1066,6 @@ bool PhaseOutput::contains_as_scalarized_obj(JVMState* jvms, MachSafePointNode* void PhaseOutput::Process_OopMap_Node(MachNode *mach, int current_offset) { // Handle special safepoint nodes for synchronization MachSafePointNode *sfn = mach->as_MachSafePoint(); - MachCallNode *mcall; int safepoint_pc_offset = current_offset; bool return_oop = false; @@ -1067,11 +1074,10 @@ void PhaseOutput::Process_OopMap_Node(MachNode *mach, int current_offset) { bool arg_escape = false; // Add the safepoint in the DebugInfoRecorder - if( !mach->is_MachCall() ) { - mcall = nullptr; - C->debug_info()->add_safepoint(safepoint_pc_offset, sfn->_oop_map); - } else { - mcall = mach->as_MachCall(); + if (mach->is_MachUncommonTrap()) { + safepoint_pc_offset += mach->as_MachUncommonTrap()->ret_addr_offset(); + } else if (mach->is_MachCall()) { + MachCallNode* mcall = mach->as_MachCall(); if (mcall->is_MachCallJava()) { arg_escape = mcall->as_MachCallJava()->_arg_escape; @@ -1085,8 +1091,8 @@ void PhaseOutput::Process_OopMap_Node(MachNode *mach, int current_offset) { return_scalarized = true; } safepoint_pc_offset += mcall->ret_addr_offset(); - C->debug_info()->add_safepoint(safepoint_pc_offset, mcall->_oop_map); } + C->debug_info()->add_safepoint(safepoint_pc_offset, sfn->_oop_map); // Loop over the JVMState list to add scope information // Do not skip safepoints with a null method, they need monitor info @@ -1596,8 +1602,18 @@ void PhaseOutput::fill_buffer(C2_MacroAssembler* masm, uint* blk_starts) { int padding = mach->compute_padding(current_offset); // Make sure safepoint node for polling is distinct from a call's // return by adding a nop if needed. - if (is_sfn && !is_mcall && padding == 0 && current_offset == last_call_offset) { - padding = nop_size; + if (padding == 0 && current_offset == last_call_offset) { + if (is_mcall) { + assert(mach->as_MachCall()->ret_addr_offset() > 0, + "Safepoint address should be offset from the first instruction"); + } else if (mach->is_MachUncommonTrap()) { + assert(mach->as_MachUncommonTrap()->ret_addr_offset() > 0 && + (uint)mach->as_MachUncommonTrap()->ret_addr_offset() < mach->size(C->regalloc()), + "Safepoint address should be offset from the first instruction and should not point after the node"); + } else if (is_sfn) { + assert(mach->ideal_Opcode() == Op_SafePoint, "An unexpected safepoint node, which might need handling above"); + padding = nop_size; + } } if (padding == 0 && mach->avoid_back_to_back(MachNode::AVOID_BEFORE) && current_offset == last_avoid_back_to_back_offset) { @@ -2045,8 +2061,7 @@ uint Scheduling::_total_instructions_per_bundle[Pipeline::_max_instrs_per_cycle+ // Initializer for class Scheduling Scheduling::Scheduling(Arena *arena, Compile &compile) - : _arena(arena), - _cfg(compile.cfg()), + : _cfg(compile.cfg()), _regalloc(compile.regalloc()), _scheduled(arena), _available(arena), diff --git a/src/hotspot/share/opto/output.hpp b/src/hotspot/share/opto/output.hpp index b0d43e31fb03..e63a7b03842b 100644 --- a/src/hotspot/share/opto/output.hpp +++ b/src/hotspot/share/opto/output.hpp @@ -167,7 +167,6 @@ class PhaseOutput : public Phase { // Scratch buffer BufferBlob* scratch_buffer_blob() { return _scratch_buffer_blob; } void init_scratch_buffer_blob(int const_size); - void clear_scratch_buffer_blob(); void set_scratch_buffer_blob(BufferBlob* b) { _scratch_buffer_blob = b; } relocInfo* scratch_locs_memory() { return _scratch_locs_memory; } void set_scratch_locs_memory(relocInfo* b) { _scratch_locs_memory = b; } diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp index c7710947a307..53eadf3e1dbb 100644 --- a/src/hotspot/share/opto/parse1.cpp +++ b/src/hotspot/share/opto/parse1.cpp @@ -594,9 +594,6 @@ Parse::Parse(JVMState* caller, ciMethod* parse_method, float expected_uses) #endif #ifndef PRODUCT - if (_flow->has_irreducible_entry()) { - C->set_parsed_irreducible_loop(true); - } methods_parsed++; // add method size here to guarantee that inlined methods are added too diff --git a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp index 0aae6e6bc441..f5c6348dcd55 100644 --- a/src/hotspot/share/opto/parse2.cpp +++ b/src/hotspot/share/opto/parse2.cpp @@ -1305,7 +1305,7 @@ bool Parse::create_jump_tables(Node* key_val, SwitchRange* lo, SwitchRange* hi) i = 0; for (SwitchRange* r = lo; r <= hi; r++) { for (int64_t j = r->lo(); j <= r->hi(); j++, i++) { - Node* input = _gvn.transform(new JumpProjNode(jtn, i, r->dest(), (int)(j - lowval))); + Node* input = _gvn.transform(new JumpProjNode(jtn, i, r->dest())); { PreserveJVMState pjvms(this); set_control(input); @@ -2818,7 +2818,7 @@ void Parse::sharpen_type_after_if(BoolTest::mask btest, const Type* obj_type = _gvn.type(obj); const Type* tboth = obj_type->filter_speculative(cast_type); assert(tboth->higher_equal(obj_type) && tboth->higher_equal(cast_type), "sanity"); - if (tboth == Type::TOP && KillPathsReachableByDeadTypeNode) { + if (tboth == Type::TOP && KillPathsReachableByDeadDataNode) { // Let dead type node cleaning logic prune effectively dead path for us. // CheckCastPP::Value() == TOP and it will trigger the cleanup during GVN. // Don't materialize the cast when cleanup is disabled, because diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 51f83f816ffb..335388a4282c 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -32,6 +32,7 @@ #include "opto/castnode.hpp" #include "opto/cfgnode.hpp" #include "opto/convertnode.hpp" +#include "opto/divnode.hpp" #include "opto/idealGraphPrinter.hpp" #include "opto/loopnode.hpp" #include "opto/machnode.hpp" @@ -39,6 +40,7 @@ #include "opto/phaseX.hpp" #include "opto/regalloc.hpp" #include "opto/rootnode.hpp" +#include "opto/vectornode.hpp" #include "utilities/macros.hpp" #include "utilities/powerOfTwo.hpp" @@ -281,7 +283,7 @@ void NodeHash::clear() { //-----------------------remove_useless_nodes---------------------------------- // Remove useless nodes from value table, // implementation does not depend on hash function -void NodeHash::remove_useless_nodes(VectorSet &useful) { +void NodeHash::remove_useless_nodes(const VectorSet& useful) { // Dead nodes in the hash table inherited from GVN should not replace // existing nodes, remove dead nodes. @@ -300,7 +302,7 @@ void NodeHash::remove_useless_nodes(VectorSet &useful) { void NodeHash::check_no_speculative_types() { #ifdef ASSERT uint max = size(); - Unique_Node_List live_nodes; + Unique_Node_List live_nodes; Compile::current()->identify_useful_nodes(live_nodes); Node *sentinel_node = sentinel(); for (uint i = 0; i < max; ++i) { @@ -362,7 +364,7 @@ NodeHash::~NodeHash() { // Add users of 'n' that match 'predicate' to worklist template -static void add_users_to_worklist_if(Unique_Node_List& worklist, const Node* n, Predicate predicate) { +static void add_users_to_worklist_if(Unique_Node_List& worklist, const Node* n, Predicate predicate) { for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { Node* u = n->fast_out(i); if (predicate(u)) { @@ -374,7 +376,7 @@ static void add_users_to_worklist_if(Unique_Node_List& worklist, const Node* n, //============================================================================= //------------------------------PhaseRemoveUseless----------------------------- // 1) Use a breadthfirst walk to collect useful nodes reachable from root. -PhaseRemoveUseless::PhaseRemoveUseless(PhaseGVN* gvn, Unique_Node_List& worklist, PhaseNumber phase_num) : Phase(phase_num) { +PhaseRemoveUseless::PhaseRemoveUseless(PhaseGVN* gvn, Unique_Node_List& worklist, PhaseNumber phase_num) : Phase(phase_num) { C->print_method(PHASE_BEFORE_REMOVEUSELESS, 3); // Implementation requires an edge from root to each SafePointNode // at a backward branch. Inserted in add_safepoint(). @@ -416,7 +418,7 @@ PhaseRemoveUseless::PhaseRemoveUseless(PhaseGVN* gvn, Unique_Node_List& worklist // numbering ("C->node_hash()", referenced by PhaseValue::_table) is not updated because // computing the hash values is not based on node IDs. PhaseRenumberLive::PhaseRenumberLive(PhaseGVN* gvn, - Unique_Node_List& worklist, + Unique_Node_List& worklist, PhaseNumber phase_num) : PhaseRemoveUseless(gvn, worklist, Remove_Useless_And_Renumber_Live), _new_type_array(C->comp_arena()), @@ -887,7 +889,7 @@ void PhaseIterGVN::verify_step(Node* n) { if (is_verify_def_use()) { ResourceMark rm; VectorSet visited; - Node_List worklist; + Node_List worklist; _verify_window[_verify_counter % _verify_window_size] = n; ++_verify_counter; @@ -973,7 +975,7 @@ void PhaseIterGVN::init_verifyPhaseIterGVN() { } #ifdef ASSERT // Verify that all modified nodes are on _worklist - Unique_Node_List* modified_list = C->modified_nodes(); + Unique_Node_List* modified_list = C->modified_nodes(); while (modified_list != nullptr && modified_list->size()) { Node* n = modified_list->pop(); if (!n->is_Con() && !_worklist.member(n)) { @@ -987,7 +989,7 @@ void PhaseIterGVN::init_verifyPhaseIterGVN() { void PhaseIterGVN::verify_PhaseIterGVN(bool deep_revisit_converged) { #ifdef ASSERT // Verify nodes with changed inputs. - Unique_Node_List* modified_list = C->modified_nodes(); + Unique_Node_List* modified_list = C->modified_nodes(); while (modified_list != nullptr && modified_list->size()) { Node* n = modified_list->pop(); if (!n->is_Con()) { // skip Con nodes @@ -1091,6 +1093,12 @@ bool PhaseIterGVN::needs_deep_revisit(const Node* n) const { default: break; } + // ValueType::Ideal -> is_loaded searches through many hops, via nested + // ValueType, object references, casts, DecodeN, to eventually find if + // the field loads are all at desired offsets and from the same object. + if (n->is_ValueType()) { + return true; + } return false; } @@ -1136,7 +1144,7 @@ bool PhaseIterGVN::drain_worklist() { void PhaseIterGVN::push_deep_revisit_candidates() { ResourceMark rm; - Unique_Node_List all_nodes; + Unique_Node_List all_nodes; all_nodes.push(C->root()); for (uint j = 0; j < all_nodes.size(); j++) { Node* n = all_nodes.at(j); @@ -1254,7 +1262,7 @@ void PhaseIterGVN::verify_optimize(bool deep_revisit_converged) { is_verify_Identity() || is_verify_invariants()) { ResourceMark rm; - Unique_Node_List worklist; + Unique_Node_List worklist; // BFS all nodes, starting at root worklist.push(C->root()); for (uint j = 0; j < worklist.size(); ++j) { @@ -1969,6 +1977,116 @@ Node *PhaseIterGVN::transform( Node *n ) { return transform_old(n); } +DeadPathNode* PhaseIterGVN::dead_path() { + DeadPathNode* dead_path_node = C->dead_path(); + if (!dead_path_node->is_active()) { + dead_path_node->activate(this); + } + assert(C->root()->find_edge(dead_path_node) > 0, "should be reachable from root"); + return dead_path_node; +} + + +// If dead_node is a data node, all CFG nodes reachable from dead_node are dead cfg paths. This method follows uses from +// dead_node until it encounters a cfg node or a phi and eagerly kills these dead cfg paths. This is needed because, in +// some corner cases, a data node dies but some data paths that use it (and are unreachable at runtime) are not proven +// dead by igvn, possibly leading to incorrect IR graphs. +// Also see comment at DeadPathNode declaration. +void PhaseIterGVN::make_dependent_paths_dead_if_top(Node* dead_node, const Type* t) { + if (t != Type::TOP) { + return; + } + if (!KillPathsReachableByDeadDataNode) { + return; + } + // dead_node is going dead, follow uses + ResourceMark rm; + Unique_Node_List wq; + wq.push(dead_node); + for (uint i = 0; i < wq.size(); i++) { + Node* n = wq.at(i); + if ((n != dead_node && n->is_Phi()) || n->is_CFG()) { + continue; + } + for (DUIterator_Fast kmax, k = n->fast_outs(kmax); k < kmax; k++) { + Node* u = n->fast_out(k); + wq.push(u); + } + } + for (uint i = 0; i < wq.size(); i++) { + Node* n = wq.at(i); + if (n->is_Phi()) { + Node* region = n->in(0); + // Find out through which of the Phi's input, we reached that Phi and mark the corresponding CFG path dead + for (uint j = 1; j < n->req(); j++) { + Node* in = n->in(j); + // We don't follow uses beyond Phis so if 'in' is a Phi (unless it's dead_node), we couldn't reach this Phi through it + if (in == dead_node || (in != nullptr && !in->is_Phi() && wq.member(in))) { + if (!region->is_top() && region->in(j) != nullptr && !region->in(j)->is_top()) { + // We reached this CFG path through data nodes, record it in dead path to later insert a Halt node, if it + // doesn't die in the meantime + dead_path()->add_req(region->in(j)); + _worklist.push(dead_path()); + replace_input_of(region, j, C->top()); + } + replace_input_of(n, j, C->top()); + if (in->outcnt() == 0) { + remove_dead_node(in, NodeOrigin::Graph); + } + } + } + continue; + } + if (n == dead_node) { + continue; + } + // We don't want to follow CFG nodes but is_CFG() can return false for a cfg projection if its input is top. So + // there's no foolproof way of telling if dead_node is a cfg or not and as a consequence we can reach a Region. + if (n->is_Region()) { + // Find out through which of the Region's input, we reached that Region and mark it dead + for (uint j = 1; j < n->req(); j++) { + Node* in = n->in(j); + // We don't follow uses beyond Regions so if 'in' is a Region, we couldn't reach this Region through it + if (in != nullptr && !in->is_Region() && wq.member(in)) { + replace_input_of(n, j, C->top()); + in->remove_dead_region(this, true); + } + } + continue; + } + // If we reached this CFG node through a data input... + if (n->is_CFG()) { + Node* control_input = n->in(0); + if (control_input != nullptr && !control_input->is_top()) { + // record it in dead path to later insert a Halt node, if it doesn't die in the meantime + dead_path()->add_req(control_input); + _worklist.push(dead_path()); + replace_input_of(n, 0, C->top()); + } + n->remove_dead_region(this, true); + continue; + } + if (n->outcnt() == 0) { + remove_dead_node(n, NodeOrigin::Graph); + } + } + // make sure all visited nodes are indeed dead + for (uint i = 0; i < wq.size(); i++) { + Node* n = wq.at(i); + if (n->outcnt() == 0 || n->is_Phi() || n->is_CFG()) { + continue; + } +#ifdef ASSERT + for (DUIterator_Fast jmax, j = n->fast_outs(jmax); j < jmax; j++) { + Node* u = n->fast_out(j); + assert(wq.member(u) || u->Opcode() == Op_DeadPath, ""); + } +#endif + replace_in_uses(n, C->top()); + remove_dead_node(n, NodeOrigin::Graph); + } +} + Node *PhaseIterGVN::transform_old(Node* n) { NOT_PRODUCT(set_transforms()); // Remove 'n' from hash table in case it gets modified @@ -2049,6 +2167,7 @@ Node *PhaseIterGVN::transform_old(Node* n) { } // If 'k' computes a constant, replace it with a constant if (t->singleton() && !k->is_Con()) { + make_dependent_paths_dead_if_top(k, t); set_progress(); Node* con = makecon(t); // Make a constant add_users_to_worklist(k); @@ -2094,7 +2213,7 @@ void PhaseIterGVN::remove_globally_dead_node(Node* dead, NodeOrigin origin) { PROCESS_OUTPUTS }; ResourceMark rm; - Node_Stack stack(32); + Node_Stack stack(32); stack.push(dead, PROCESS_INPUTS); while (stack.is_nonempty()) { @@ -2253,7 +2372,7 @@ void PhaseIterGVN::replace_in_uses(Node* n, Node* m) { } //------------------------------add_users_to_worklist-------------------------- -void PhaseIterGVN::add_users_to_worklist0(Node* n, Unique_Node_List& worklist) { +void PhaseIterGVN::add_users_to_worklist0(Node* n, Unique_Node_List& worklist) { for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { worklist.push(n->fast_out(i)); // Push on worklist } @@ -2284,7 +2403,7 @@ void PhaseIterGVN::add_users_to_worklist(Node* n) const { add_users_to_worklist(n, _worklist); } -void PhaseIterGVN::add_users_to_worklist(Node* n, Unique_Node_List& worklist) { +void PhaseIterGVN::add_users_to_worklist(Node* n, Unique_Node_List& worklist) { add_users_to_worklist0(n, worklist); // Move users of node to worklist @@ -2294,7 +2413,7 @@ void PhaseIterGVN::add_users_to_worklist(Node* n, Unique_Node_List& worklist) { } } -void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_List& worklist) { +void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_List& worklist) { if(use->is_Multi() || // Multi-definer? Push projs on worklist use->is_Store() ) // Enable store/load same address add_users_to_worklist0(use, worklist); @@ -2578,13 +2697,17 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ } // If changed AndI/AndL inputs, check RShift/URShift users for "(x & mask) >> shift" optimization opportunity // LShiftNode::IdealIL optimizes patterns like: LShift(And(RShift(x, c), Y), c) + // Additionally, check for nested And reassociation: + // e.g. (x & c1) & c2 -> x & (c1 & c2) if (use_op == Op_AndI || use_op == Op_AndL) { - add_users_to_worklist_if(worklist, use, [](Node* u) { + add_users_to_worklist_if(worklist, use, [&](Node* u) { return u->Opcode() == Op_RShiftI || u->Opcode() == Op_RShiftL || u->Opcode() == Op_URShiftI || u->Opcode() == Op_URShiftL || - u->Opcode() == Op_LShiftI || u->Opcode() == Op_LShiftL; + u->Opcode() == Op_LShiftI || u->Opcode() == Op_LShiftL || + u->Opcode() == use_op; }); } + // MulNode::AndIL_sum_and_mask can optimize: // AndX(AddX(x, y), mask) -> AndX(x, mask) if (use_op == Op_AddI || use_op == Op_AddL) { @@ -2645,10 +2768,14 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ }); } // Check for "abs(0-x)" into "abs(x)" conversion + // Check for "(0 - x) * (0 - y)" into "x * y" conversion if (use->is_Sub()) { add_users_to_worklist_if(worklist, use, [](Node* u) { return u->Opcode() == Op_AbsD || u->Opcode() == Op_AbsF || - u->Opcode() == Op_AbsL || u->Opcode() == Op_AbsI; + u->Opcode() == Op_AbsL || u->Opcode() == Op_AbsI || + u->Opcode() == Op_MulI || u->Opcode() == Op_MulL || + u->Opcode() == Op_MulF || u->Opcode() == Op_MulD || + u->Opcode() == Op_MulHF; }); } // Check for Max/Min(A, Max/Min(B, C)) where A == B or A == C @@ -2791,6 +2918,20 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ }); } + // VectorNode::reassociate_vector_operation matches a two-hop pattern: + // VectorOp (Replicate INP1) (VectorOp (Replicate INP2) INP3) + // => VectorOp (VectorOp (Replicate INP1) (Replicate INP2)) INP3 + // The outer VectorOp only becomes idealizable once an input of the inner + // VectorOp is a Replicate, and the change of that input notifies the inner + // VectorOp alone. + if (VectorNode::is_associative_and_commutative_vector_operation(use_op) && + !use->as_Vector()->is_predicated_vector()) { + add_users_to_worklist_if(worklist, use, [use_op](Node* u) { + return u->Opcode() == use_op && !u->as_Vector()->is_predicated_vector() && + (u->in(1)->Opcode() == Op_Replicate || u->in(2)->Opcode() == Op_Replicate); + }); + } + // From CastX2PNode::Ideal // CastX2P(AddX(x, y)) // CastX2P(SubX(x, y)) @@ -2993,9 +3134,9 @@ void PhaseCCP::analyze() { // We also do not want to put the worklist on "Compile::current()->comp_arena()", as that one only gets de-allocated after // Compile is over. The local arena gets de-allocated at the end of its scope. ResourceArea local_arena(mtCompiler); - Unique_Node_List worklist(&local_arena); - Unique_Node_List worklist_revisit(&local_arena); - DEBUG_ONLY(Unique_Node_List worklist_verify(&local_arena);) + Unique_Node_List worklist(&local_arena); + Unique_Node_List worklist_revisit(&local_arena); + DEBUG_ONLY(Unique_Node_List worklist_verify(&local_arena);) // Push root onto worklist worklist.push(C->root()); @@ -3043,7 +3184,7 @@ void PhaseCCP::analyze() { DEBUG_ONLY(verify_analyze(worklist_verify);) } -bool PhaseCCP::analyze_step(Unique_Node_List& worklist, Node* n) { +bool PhaseCCP::analyze_step(Unique_Node_List& worklist, Node* n) { const Type* new_type = n->Value(this); if (klass_bottom_type_invalidated(n, new_type)) { C->record_failure("concurrent class loading"); @@ -3055,10 +3196,14 @@ bool PhaseCCP::analyze_step(Unique_Node_List& worklist, Node* n) { set_type(n, new_type); push_child_nodes_to_worklist(worklist, n); } - if (KillPathsReachableByDeadTypeNode && n->is_Type() && new_type == Type::TOP) { + if (KillPathsReachableByDeadDataNode && n->is_Type() && new_type == Type::TOP) { // Keep track of Type nodes to kill CFG paths that use Type // nodes that become dead. - _maybe_top_type_nodes.push(n); + _maybe_top_type_or_div_mod_nodes.push(n); + } + if (KillPathsReachableByDeadDataNode && new_type == Type::TOP && n->is_DivModInteger() && + type(n->in(2)) == n->as_DivModInteger()->zero()) { + _maybe_top_type_or_div_mod_nodes.push(n); } return true; } @@ -3083,7 +3228,7 @@ bool PhaseCCP::needs_revisit(Node* n) const { // For every node n on verify list, check if type(n) == n->Value() // Note for CCP the non-convergence can lead to unsound analysis and mis-compilation. // Therefore, we are verifying Value convergence strictly. -void PhaseCCP::verify_analyze(Unique_Node_List& worklist_verify) { +void PhaseCCP::verify_analyze(Unique_Node_List& worklist_verify) { while (worklist_verify.size()) { Node* n = worklist_verify.pop(); @@ -3105,7 +3250,7 @@ void PhaseCCP::verify_analyze(Unique_Node_List& worklist_verify) { #endif // Fetch next node from worklist to be examined in this iteration. -Node* PhaseCCP::fetch_next_node(Unique_Node_List& worklist) { +Node* PhaseCCP::fetch_next_node(Unique_Node_List& worklist) { if (StressCCP) { return worklist.remove(C->stress().random() % worklist.size()); } else { @@ -3132,7 +3277,7 @@ bool PhaseCCP::not_bottom_type(Node* n) const { // We need to propagate the type change of 'n' to all its uses. Depending on the kind of node, additional nodes // (grandchildren or even further down) need to be revisited as their types could also be improved as a result // of the new type of 'n'. Push these nodes to the worklist. -void PhaseCCP::push_child_nodes_to_worklist(Unique_Node_List& worklist, Node* n) const { +void PhaseCCP::push_child_nodes_to_worklist(Unique_Node_List& worklist, Node* n) const { for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { Node* use = n->fast_out(i); push_if_not_bottom_type(worklist, use); @@ -3140,7 +3285,7 @@ void PhaseCCP::push_child_nodes_to_worklist(Unique_Node_List& worklist, Node* n) } } -void PhaseCCP::push_if_not_bottom_type(Unique_Node_List& worklist, Node* n) const { +void PhaseCCP::push_if_not_bottom_type(Unique_Node_List& worklist, Node* n) const { if (not_bottom_type(n)) { worklist.push(n); } @@ -3148,7 +3293,7 @@ void PhaseCCP::push_if_not_bottom_type(Unique_Node_List& worklist, Node* n) cons // For some nodes, we need to propagate the type change to grandchildren or even further down. // Add them back to the worklist. -void PhaseCCP::push_more_uses(Unique_Node_List& worklist, Node* parent, const Node* use) const { +void PhaseCCP::push_more_uses(Unique_Node_List& worklist, Node* parent, const Node* use) const { push_phis(worklist, use); push_catch(worklist, use); push_cmpu(worklist, use); @@ -3163,7 +3308,7 @@ void PhaseCCP::push_more_uses(Unique_Node_List& worklist, Node* parent, const No // We must recheck Phis too if use is a Region. -void PhaseCCP::push_phis(Unique_Node_List& worklist, const Node* use) const { +void PhaseCCP::push_phis(Unique_Node_List& worklist, const Node* use) const { if (use->is_Region()) { add_users_to_worklist_if(worklist, use, [&](Node* u) { return not_bottom_type(u); @@ -3174,7 +3319,7 @@ void PhaseCCP::push_phis(Unique_Node_List& worklist, const Node* use) const { // If we changed the receiver type to a call, we need to revisit the Catch node following the call. It's looking for a // non-null receiver to know when to enable the regular fall-through path in addition to the NullPtrException path. // Same is true if the type of a ValidLengthTest input to an AllocateArrayNode changes. -void PhaseCCP::push_catch(Unique_Node_List& worklist, const Node* use) { +void PhaseCCP::push_catch(Unique_Node_List& worklist, const Node* use) { if (use->is_Call()) { for (DUIterator_Fast imax, i = use->fast_outs(imax); i < imax; i++) { Node* proj = use->fast_out(i); @@ -3191,7 +3336,7 @@ void PhaseCCP::push_catch(Unique_Node_List& worklist, const Node* use) { // CmpU nodes can get their type information from two nodes up in the graph (instead of from the nodes immediately // above). Make sure they are added to the worklist if nodes they depend on are updated since they could be missed // and get wrong types otherwise. -void PhaseCCP::push_cmpu(Unique_Node_List& worklist, const Node* use) const { +void PhaseCCP::push_cmpu(Unique_Node_List& worklist, const Node* use) const { uint use_op = use->Opcode(); if (use_op == Op_AddI || use_op == Op_SubI) { // Got a CmpU or CmpU3 which might need the new type information from node n. @@ -3215,7 +3360,7 @@ void PhaseCCP::push_cmpu(Unique_Node_List& worklist, const Node* use) const { // | // Bool // -void PhaseCCP::push_bool_with_cmpu_and_mask(Unique_Node_List& worklist, const Node* use) const { +void PhaseCCP::push_bool_with_cmpu_and_mask(Unique_Node_List& worklist, const Node* use) const { uint use_op = use->Opcode(); if (use_op != Op_AndI && (use_op != Op_AddI || use->in(2)->find_int_con(0) != 1)) { // Not "m & x" or "m + 1" @@ -3230,7 +3375,7 @@ void PhaseCCP::push_bool_with_cmpu_and_mask(Unique_Node_List& worklist, const No } // Push any Bool below 'cmpu' that matches case (1b) of BoolNode::Value_cmpu_and_mask(). -void PhaseCCP::push_bool_matching_case1b(Unique_Node_List& worklist, const Node* cmpu) const { +void PhaseCCP::push_bool_matching_case1b(Unique_Node_List& worklist, const Node* cmpu) const { assert(cmpu->Opcode() == Op_CmpU, "must be"); for (DUIterator_Fast imax, i = cmpu->fast_outs(imax); i < imax; i++) { Node* bol = cmpu->fast_out(i); @@ -3255,7 +3400,7 @@ void PhaseCCP::push_bool_matching_case1b(Unique_Node_List& worklist, const Node* // If n is used in a counted loop exit condition, then the type of the counted loop's Phi depends on the type of 'n'. // Seem PhiNode::Value(). -void PhaseCCP::push_counted_loop_phi(Unique_Node_List& worklist, Node* parent, const Node* use) { +void PhaseCCP::push_counted_loop_phi(Unique_Node_List& worklist, Node* parent, const Node* use) { uint use_op = use->Opcode(); if (use_op == Op_CmpI || use_op == Op_CmpL) { PhiNode* phi = countedloop_phi_from_cmp(use->as_Cmp(), parent); @@ -3266,7 +3411,7 @@ void PhaseCCP::push_counted_loop_phi(Unique_Node_List& worklist, Node* parent, c } // Needed because of PhaseMacroExpand::expand_mh_intrinsic_return -void PhaseCCP::push_cast(Unique_Node_List& worklist, const Node* use) { +void PhaseCCP::push_cast(Unique_Node_List& worklist, const Node* use) { uint use_op = use->Opcode(); if (use_op == Op_CastP2X) { for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { @@ -3280,7 +3425,7 @@ void PhaseCCP::push_cast(Unique_Node_List& worklist, const Node* use) { // Loading the java mirror from a Klass requires two loads and the type of the mirror load depends on the type of 'n'. // See LoadNode::Value(). -void PhaseCCP::push_loadp(Unique_Node_List& worklist, const Node* use) const { +void PhaseCCP::push_loadp(Unique_Node_List& worklist, const Node* use) const { if (use->Opcode() == Op_LoadP && use->bottom_type()->isa_rawptr()) { for (DUIterator_Fast imax, i = use->fast_outs(imax); i < imax; i++) { Node* loadp = use->fast_out(i); @@ -3295,7 +3440,7 @@ void PhaseCCP::push_loadp(Unique_Node_List& worklist, const Node* use) const { // AndI/L::Value() optimizes patterns similar to (v << 2) & 3, or CON & 3 to zero if they are bitwise disjoint. // Add the AndI/L nodes back to the worklist to re-apply Value() in case the value is now a constant or shift // value changed. -void PhaseCCP::push_and(Unique_Node_List& worklist, const Node* parent, const Node* use) const { +void PhaseCCP::push_and(Unique_Node_List& worklist, const Node* parent, const Node* use) const { const TypeInteger* parent_type = type(parent)->isa_integer(type(parent)->basic_type()); uint use_op = use->Opcode(); if ( @@ -3322,7 +3467,7 @@ void PhaseCCP::push_and(Unique_Node_List& worklist, const Node* parent, const No // CastII node back to the worklist to re-apply Value() to either not miss this optimization or to undo it because it // cannot be applied anymore. We could have optimized the type of the CastII before but now the type of the right input // of the CmpI (i.e. 'parent') is no longer constant. The type of the CastII must be widened in this case. -void PhaseCCP::push_cast_ii(Unique_Node_List& worklist, const Node* parent, const Node* use) const { +void PhaseCCP::push_cast_ii(Unique_Node_List& worklist, const Node* parent, const Node* use) const { if (use->Opcode() == Op_CmpI && use->in(2) == parent) { Node* other_cmp_input = use->in(1); add_users_to_worklist_if(worklist, other_cmp_input, [&](Node* u) { @@ -3331,7 +3476,7 @@ void PhaseCCP::push_cast_ii(Unique_Node_List& worklist, const Node* parent, cons } } -void PhaseCCP::push_opaque_zero_trip_guard(Unique_Node_List& worklist, const Node* use) const { +void PhaseCCP::push_opaque_zero_trip_guard(Unique_Node_List& worklist, const Node* use) const { if (use->Opcode() == Op_OpaqueZeroTripGuard) { push_if_not_bottom_type(worklist, use->unique_out()); } @@ -3355,22 +3500,22 @@ Node *PhaseCCP::transform( Node *n ) { ResourceMark rm; // Map: old node idx -> node after CCP (or nullptr if not yet transformed or useless). - Node_List node_map; + Node_List node_map; // Pre-allocate to avoid frequent realloc GrowableArray transform_stack(C->live_nodes() >> 1); // track all visited nodes, so that we can remove the complement - Unique_Node_List useful; + Unique_Node_List useful; - if (KillPathsReachableByDeadTypeNode) { - for (uint i = 0; i < _maybe_top_type_nodes.size(); ++i) { - Node* type_node = _maybe_top_type_nodes.at(i); - if (type(type_node) == Type::TOP) { + if (KillPathsReachableByDeadDataNode) { + for (uint i = 0; i < _maybe_top_type_or_div_mod_nodes.size(); ++i) { + Node* data_node = _maybe_top_type_or_div_mod_nodes.at(i); + if (type(data_node) == Type::TOP) { ResourceMark rm; - type_node->as_Type()->make_paths_from_here_dead(this, nullptr, "ccp"); + data_node->make_paths_from_here_dead(this, nullptr, "ccp"); } } } else { - assert(_maybe_top_type_nodes.size() == 0, "we don't need type nodes"); + assert(_maybe_top_type_or_div_mod_nodes.size() == 0, "we don't need type nodes"); } // Initialize the traversal. diff --git a/src/hotspot/share/opto/phaseX.hpp b/src/hotspot/share/opto/phaseX.hpp index f6bdce80cd31..5ac0bde26956 100644 --- a/src/hotspot/share/opto/phaseX.hpp +++ b/src/hotspot/share/opto/phaseX.hpp @@ -90,7 +90,7 @@ class NodeHash : public AnyObj { return _table[table_index]; } - void remove_useless_nodes(VectorSet& useful); // replace with sentinel + void remove_useless_nodes(const VectorSet& useful); // replace with sentinel void check_no_speculative_types(); // Check no speculative part for type nodes in table Node *sentinel() { return _sentinel; } @@ -150,12 +150,12 @@ class Type_Array : public AnyObj { // Remove useless nodes from GVN hash-table, worklist, and graph class PhaseRemoveUseless : public Phase { protected: - Unique_Node_List _useful; // Nodes reachable from root + Unique_Node_List _useful; // Nodes reachable from root // list is allocated from current resource area public: - PhaseRemoveUseless(PhaseGVN* gvn, Unique_Node_List& worklist, PhaseNumber phase_num = Remove_Useless); + PhaseRemoveUseless(PhaseGVN* gvn, Unique_Node_List& worklist, PhaseNumber phase_num = Remove_Useless); - Unique_Node_List *get_useful() { return &_useful; } + Unique_Node_List* get_useful() { return &_useful; } }; //------------------------------PhaseRenumber---------------------------------- @@ -165,7 +165,7 @@ class PhaseRenumberLive : public PhaseRemoveUseless { protected: Type_Array _new_type_array; // Storage for the updated type information. GrowableArray _old2new_map; - Node_List _delayed; + Node_List _delayed; bool _is_pass_finished; uint _live_node_count; @@ -174,7 +174,7 @@ class PhaseRenumberLive : public PhaseRemoveUseless { public: PhaseRenumberLive(PhaseGVN* gvn, - Unique_Node_List& worklist, + Unique_Node_List& worklist, PhaseNumber phase_num = Remove_Useless_And_Renumber_Live); }; @@ -290,7 +290,7 @@ class PhaseValues : public PhaseTransform { Node* hash_find(const Node* n) { return _table.hash_find(n); } // Used after parsing to eliminate values that are no longer in program - void remove_useless_nodes(VectorSet &useful) { + void remove_useless_nodes(const VectorSet& useful) { _table.remove_useless_nodes(useful); // this may invalidate cached cons so reset the cache init_con_caches(); @@ -510,6 +510,10 @@ class PhaseIterGVN : public PhaseGVN { // Usually returns new_type. Returns old_type if new_type is only a slight // improvement, such that it would take many (>>10) steps to reach 2**32. + DeadPathNode* dead_path(); + + void make_dependent_paths_dead_if_top(Node* dead_node, const Type* t); + public: PhaseIterGVN(PhaseIterGVN* igvn); // Used by CCP constructor @@ -536,7 +540,7 @@ class PhaseIterGVN : public PhaseGVN { virtual void record_for_igvn(Node *n) { _worklist.push(n); } // Iterative worklist. Reference to "C->igvn_worklist()". - Unique_Node_List &_worklist; + Unique_Node_List& _worklist; // Given def-use info and an initial worklist, apply Node::Ideal, // Node::Value, Node::Identity, hash-based value numbering, Node::Ideal_DU @@ -593,7 +597,7 @@ class PhaseIterGVN : public PhaseGVN { } // Add users of 'n' to worklist - static void add_users_to_worklist0(Node* n, Unique_Node_List& worklist); + static void add_users_to_worklist0(Node* n, Unique_Node_List& worklist); // Add one or more users of 'use' to the worklist if it appears that a // known optimization could be applied to those users. @@ -602,7 +606,7 @@ class PhaseIterGVN : public PhaseGVN { // Certain optimizations have dependencies that extend beyond a node's // direct inputs, so it is necessary to ensure the appropriate // notifications are made here. - static void add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_List& worklist); + static void add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_List& worklist); // Add users of 'n', and any other nodes that could be directly // affected by changes to 'n', to the worklist. @@ -614,7 +618,7 @@ class PhaseIterGVN : public PhaseGVN { void add_users_to_worklist(Node* n) const; // The generic version that will add it to a given worklist instead of // the current this->_worklist. - static void add_users_to_worklist(Node* n, Unique_Node_List& worklist); + static void add_users_to_worklist(Node* n, Unique_Node_List& worklist); // Replace old node with new one. void replace_node( Node *old, Node *nn ) { @@ -704,29 +708,29 @@ class PhaseIterGVN : public PhaseGVN { // Phase for performing global Conditional Constant Propagation. // Should be replaced with combined CCP & GVN someday. class PhaseCCP : public PhaseIterGVN { - Unique_Node_List _root_and_safepoints; - Unique_Node_List _maybe_top_type_nodes; + Unique_Node_List _root_and_safepoints; + Unique_Node_List _maybe_top_type_or_div_mod_nodes; // Non-recursive. Use analysis to transform single Node. virtual Node* transform_once(Node* n); - Node* fetch_next_node(Unique_Node_List& worklist); + Node* fetch_next_node(Unique_Node_List& worklist); static void dump_type_and_node(const Node* n, const Type* t) PRODUCT_RETURN; bool not_bottom_type(Node* n) const; - void push_child_nodes_to_worklist(Unique_Node_List& worklist, Node* n) const; - void push_if_not_bottom_type(Unique_Node_List& worklist, Node* n) const; - void push_more_uses(Unique_Node_List& worklist, Node* parent, const Node* use) const; - void push_phis(Unique_Node_List& worklist, const Node* use) const; - static void push_catch(Unique_Node_List& worklist, const Node* use); - void push_cmpu(Unique_Node_List& worklist, const Node* use) const; - static void push_counted_loop_phi(Unique_Node_List& worklist, Node* parent, const Node* use); - static void push_cast(Unique_Node_List& worklist, const Node* use); - void push_loadp(Unique_Node_List& worklist, const Node* use) const; - void push_and(Unique_Node_List& worklist, const Node* parent, const Node* use) const; - void push_cast_ii(Unique_Node_List& worklist, const Node* parent, const Node* use) const; - void push_opaque_zero_trip_guard(Unique_Node_List& worklist, const Node* use) const; - void push_bool_with_cmpu_and_mask(Unique_Node_List& worklist, const Node* use) const; - void push_bool_matching_case1b(Unique_Node_List& worklist, const Node* cmpu) const; + void push_child_nodes_to_worklist(Unique_Node_List& worklist, Node* n) const; + void push_if_not_bottom_type(Unique_Node_List& worklist, Node* n) const; + void push_more_uses(Unique_Node_List& worklist, Node* parent, const Node* use) const; + void push_phis(Unique_Node_List& worklist, const Node* use) const; + static void push_catch(Unique_Node_List& worklist, const Node* use); + void push_cmpu(Unique_Node_List& worklist, const Node* use) const; + static void push_counted_loop_phi(Unique_Node_List& worklist, Node* parent, const Node* use); + static void push_cast(Unique_Node_List& worklist, const Node* use); + void push_loadp(Unique_Node_List& worklist, const Node* use) const; + void push_and(Unique_Node_List& worklist, const Node* parent, const Node* use) const; + void push_cast_ii(Unique_Node_List& worklist, const Node* parent, const Node* use) const; + void push_opaque_zero_trip_guard(Unique_Node_List& worklist, const Node* use) const; + void push_bool_with_cmpu_and_mask(Unique_Node_List& worklist, const Node* use) const; + void push_bool_matching_case1b(Unique_Node_List& worklist, const Node* cmpu) const; public: PhaseCCP( PhaseIterGVN *igvn ); // Compute conditional constants @@ -734,12 +738,12 @@ class PhaseCCP : public PhaseIterGVN { // Worklist algorithm identifies constants void analyze(); - bool analyze_step(Unique_Node_List& worklist, Node* n); + bool analyze_step(Unique_Node_List& worklist, Node* n); bool needs_revisit(Node* n) const; #ifdef ASSERT void verify_type(Node* n, const Type* tnew, const Type* told); // For every node n on verify list, check if type(n) == n->Value() - void verify_analyze(Unique_Node_List& worklist_verify); + void verify_analyze(Unique_Node_List& worklist_verify); #endif // Recursive traversal of program. Used analysis to modify program. virtual Node *transform( Node *n ); diff --git a/src/hotspot/share/opto/postaloc.cpp b/src/hotspot/share/opto/postaloc.cpp index c961340e71ac..c15533d764ae 100644 --- a/src/hotspot/share/opto/postaloc.cpp +++ b/src/hotspot/share/opto/postaloc.cpp @@ -29,7 +29,7 @@ // See if this register (or pairs, or vector) already contains the value. static bool register_contains_value(Node* val, OptoReg::Name reg, int n_regs, - const Node_List &value) { + const Node_List& value) { for (int i = 0; i < n_regs; i++) { OptoReg::Name nreg = OptoReg::add(reg,-i); if (value[nreg] != val) @@ -76,7 +76,7 @@ bool PhaseChaitin::may_be_copy_of_callee( Node *def ) const { //------------------------------yank----------------------------------- // Helper function for yank_if_dead -int PhaseChaitin::yank(Node *old, Block *current_block, Node_List *value, Node_List *regnd) { +int PhaseChaitin::yank(Node* old, Block* current_block, Node_List* value, Node_List* regnd) { int blk_adjust=0; Block *oldb = _cfg.get_block_for_node(old); oldb->find_remove(old); @@ -126,8 +126,8 @@ static bool expected_yanked_node(Node *old, Node *orig_old) { //------------------------------yank_if_dead----------------------------------- // Removed edges from 'old'. Yank if dead. Return adjustment counts to // iterators in the current block. -int PhaseChaitin::yank_if_dead_recurse(Node *old, Node *orig_old, Block *current_block, - Node_List *value, Node_List *regnd) { +int PhaseChaitin::yank_if_dead_recurse(Node* old, Node* orig_old, Block* current_block, + Node_List* value, Node_List* regnd) { int blk_adjust=0; if (old->outcnt() == 0 && old != C->top()) { #ifdef ASSERT @@ -161,7 +161,7 @@ int PhaseChaitin::yank_if_dead_recurse(Node *old, Node *orig_old, Block *current // Use the prior value instead of the current value, in an effort to make // the current value go dead. Return block iterator adjustment, in case // we yank some instructions from this block. -int PhaseChaitin::use_prior_register( Node *n, uint idx, Node *def, Block *current_block, Node_List *value, Node_List *regnd ) { +int PhaseChaitin::use_prior_register(Node* n, uint idx, Node* def, Block* current_block, Node_List* value, Node_List* regnd) { // No effect? if( def == n->in(idx) ) return 0; // Def is currently dead and can be removed? Do not resurrect @@ -228,7 +228,7 @@ Node *PhaseChaitin::skip_copies( Node *c ) { //------------------------------elide_copy------------------------------------- // Remove (bypass) copies along Node n, edge k. -int PhaseChaitin::elide_copy( Node *n, int k, Block *current_block, Node_List *value, Node_List *regnd, bool can_change_regs ) { +int PhaseChaitin::elide_copy(Node* n, int k, Block* current_block, Node_List* value, Node_List* regnd, bool can_change_regs) { int blk_adjust = 0; uint nk_idx = _lrg_map.live_range_id(n->in(k)); @@ -355,7 +355,7 @@ int PhaseChaitin::elide_copy( Node *n, int k, Block *current_block, Node_List *v // bool PhaseChaitin::eliminate_copy_of_constant(Node* val, Node* n, Block *current_block, - Node_List& value, Node_List& regnd, + Node_List& value, Node_List& regnd, OptoReg::Name nreg, OptoReg::Name nreg2) { if (value[nreg] != val && val->is_Con() && value[nreg] != nullptr && value[nreg]->is_Con() && @@ -503,16 +503,16 @@ void PhaseChaitin::post_allocate_copy_removal() { // Need a mapping from basic block Node_Lists. We need a Node_List to // map from register number to value-producing Node. - Node_List **blk2value = NEW_RESOURCE_ARRAY( Node_List *, _cfg.number_of_blocks() + 1); - memset(blk2value, 0, sizeof(Node_List*) * (_cfg.number_of_blocks() + 1)); + Node_List** blk2value = NEW_RESOURCE_ARRAY(Node_List*, _cfg.number_of_blocks() + 1); + memset(blk2value, 0, sizeof(Node_List*) * (_cfg.number_of_blocks() + 1)); // Need a mapping from basic block Node_Lists. We need a Node_List to // map from register number to register-defining Node. - Node_List **blk2regnd = NEW_RESOURCE_ARRAY( Node_List *, _cfg.number_of_blocks() + 1); - memset(blk2regnd, 0, sizeof(Node_List*) * (_cfg.number_of_blocks() + 1)); + Node_List** blk2regnd = NEW_RESOURCE_ARRAY(Node_List*, _cfg.number_of_blocks() + 1); + memset(blk2regnd, 0, sizeof(Node_List*) * (_cfg.number_of_blocks() + 1)); // We keep unused Node_Lists on a free_list to avoid wasting // memory. - GrowableArray free_list = GrowableArray(16); + GrowableArray*> free_list = GrowableArray*>(16); // For all blocks for (uint i = 0; i < _cfg.number_of_blocks(); i++) { @@ -561,8 +561,8 @@ void PhaseChaitin::post_allocate_copy_removal() { // Extract Node_List mappings. If 'freed' is non-zero, we just popped // 'freed's blocks off the list - Node_List ®nd = *(free_list.is_empty() ? new Node_List(_max_reg) : free_list.pop()); - Node_List &value = *(free_list.is_empty() ? new Node_List(_max_reg) : free_list.pop()); + Node_List& regnd = *(free_list.is_empty() ? new Node_List(_max_reg) : free_list.pop()); + Node_List& value = *(free_list.is_empty() ? new Node_List(_max_reg) : free_list.pop()); assert( !freed || blk2value[freed->_pre_order] == &value, "" ); // Set mappings as OUR mappings blk2value[block->_pre_order] = &value; @@ -588,7 +588,7 @@ void PhaseChaitin::post_allocate_copy_removal() { if (pb == freed) { continue; // Did self already via freelist } - Node_List &p_regnd = *blk2regnd[pb->_pre_order]; + Node_List& p_regnd = *blk2regnd[pb->_pre_order]; for (uint k = 0; k < (uint)_max_reg; k++) { if (regnd[k] != p_regnd[k]) { // Conflict on reaching defs? value.map(k, nullptr); // Then no value handy diff --git a/src/hotspot/share/opto/predicates.cpp b/src/hotspot/share/opto/predicates.cpp index 2e9c19f72ba6..1b266f45cf5f 100644 --- a/src/hotspot/share/opto/predicates.cpp +++ b/src/hotspot/share/opto/predicates.cpp @@ -547,11 +547,11 @@ class DataNodesOnPathsToTargets : public StackObj { // trivially pass the _node_filter. NodeCheck _is_target_node; // The resulting node collection of all nodes on paths from source->target(s). - Unique_Node_List _collected_nodes; + Unique_Node_List _collected_nodes; // List to track all nodes visited on the search for target nodes starting at a start node. These nodes are then used // in backtracking to find the nodes actually being on a start->target(s) path. This list also serves as visited set // to avoid double visits of a node which could happen with diamonds shapes. - Unique_Node_List _nodes_to_visit; + Unique_Node_List _nodes_to_visit; public: DataNodesOnPathsToTargets(NodeCheck node_filter, NodeCheck is_target_node) @@ -563,7 +563,7 @@ class DataNodesOnPathsToTargets : public StackObj { // the target node predicate to stop discovering more inputs and start backtracking. The implementation is done // with two BFS traversal: One to collect the target nodes (if any) and one to backtrack from the target nodes to // find all other nodes on the start->target(s) paths. - const Unique_Node_List& collect(Node* start_node) { + const Unique_Node_List& collect(Node* start_node) { assert(_collected_nodes.size() == 0 && _nodes_to_visit.size() == 0, "should not call this method twice in a row"); assert(!_is_target_node(start_node), "no trivial paths where start node is also a target node"); @@ -582,7 +582,7 @@ class DataNodesOnPathsToTargets : public StackObj { void collect_target_nodes(Node* start_node) { _nodes_to_visit.push(start_node); for (uint i = 0; i < _nodes_to_visit.size(); i++) { - Node* next = _nodes_to_visit[i]; + const Node* next = _nodes_to_visit[i]; for (uint j = 1; j < next->req(); j++) { Node* input = next->in(j); if (_is_target_node(input)) { @@ -623,7 +623,7 @@ TemplateAssertionExpression::clone(const TransformStrategyForOpaqueLoopNodes& tr DataNodesOnPathsToTargets data_nodes_on_path_to_targets(TemplateAssertionExpressionNode::is_maybe_in_expression, is_opaque_loop_node); Node* bool_into_opaque_node = _opaque_node->in(1); - const Unique_Node_List& collected_nodes = data_nodes_on_path_to_targets.collect(bool_into_opaque_node); + const Unique_Node_List& collected_nodes = data_nodes_on_path_to_targets.collect(bool_into_opaque_node); DataNodeGraph data_node_graph(collected_nodes, _phase); const OrigToNewHashtable& orig_to_new = data_node_graph.clone_with_opaque_loop_transform_strategy(transform_strategy, new_control); @@ -659,18 +659,27 @@ TemplateAssertionExpression::clone_and_fold_opaque_loop_nodes(Node* new_control) return opaque_initialized_opaque; } +static void push_non_cfg_inputs_of(Unique_Node_List& list, const Node* node) { + for (uint i = 1; i < node->req(); i++) { + Node* input = node->in(i); + if (input != nullptr && !input->is_CFG()) { + list.push(input); + } + } +} + // Check if this node belongs a Template Assertion Expression (including OpaqueLoop* nodes). bool TemplateAssertionExpressionNode::is_in_expression(Node* node) { if (is_maybe_in_expression(node)) { ResourceMark rm; - Unique_Node_List list; + Unique_Node_List list; list.push(node); for (uint i = 0; i < list.size(); i++) { Node* next = list.at(i); if (next->is_OpaqueLoopInit() || next->is_OpaqueLoopStride()) { return true; } else if (is_maybe_in_expression(next)) { - list.push_non_cfg_inputs_of(next); + push_non_cfg_inputs_of(list, next); } } } @@ -1346,8 +1355,8 @@ void EliminateUselessPredicates::verify_loop_nodes_of_useless_templates_assertio verify_associated_loop_nodes_are_dead(loop_nodes_of_useless_template_assertion_predicates); } -Unique_Node_List EliminateUselessPredicates::collect_loop_nodes_of_useless_template_assertion_predicates() const { - Unique_Node_List loop_nodes_of_useless_template_assertion_predicates; +Unique_Node_List EliminateUselessPredicates::collect_loop_nodes_of_useless_template_assertion_predicates() const { + Unique_Node_List loop_nodes_of_useless_template_assertion_predicates; for (int i = 0; i < _template_assertion_predicate_opaques.length(); i++) { OpaqueTemplateAssertionPredicateNode* opaque_node = _template_assertion_predicate_opaques.at(i); if (opaque_node->is_useless()) { @@ -1358,7 +1367,7 @@ Unique_Node_List EliminateUselessPredicates::collect_loop_nodes_of_useless_templ } void EliminateUselessPredicates::verify_associated_loop_nodes_are_dead( - const Unique_Node_List& loop_nodes_of_useless_template_assertion_predicates) const { + const Unique_Node_List& loop_nodes_of_useless_template_assertion_predicates) const { if (loop_nodes_of_useless_template_assertion_predicates.size() == 0) { return; } @@ -1366,7 +1375,7 @@ void EliminateUselessPredicates::verify_associated_loop_nodes_are_dead( IdealLoopTree* loop = iterator.current(); Node* loop_head = loop->head(); if (loop_head->is_CountedLoop()) { - assert(!loop_nodes_of_useless_template_assertion_predicates.member(loop_head), + assert(!loop_nodes_of_useless_template_assertion_predicates.member(loop_head->as_CountedLoop()), "CountedLoopNode should be dead when found in OpaqueTemplateAssertionPredicateNode being marked useless"); } } diff --git a/src/hotspot/share/opto/predicates.hpp b/src/hotspot/share/opto/predicates.hpp index cf295432104c..0c3835d165e9 100644 --- a/src/hotspot/share/opto/predicates.hpp +++ b/src/hotspot/share/opto/predicates.hpp @@ -593,12 +593,19 @@ class TemplateAssertionExpressionNode : public StackObj { opcode == Op_CastII); } + static void push_outputs_of(Unique_Node_List& list, const Node* node) { + for (DUIterator_Fast imax, i = node->fast_outs(imax); i < imax; i++) { + Node* output = node->fast_out(i); + list.push(output); + } + } + // Apply the given function to all Template Assertion Predicates (if any) to which this Template Assertion Predicate // Expression Node belongs to. template void for_each_template_assertion_predicate(Callback callback) { ResourceMark rm; - Unique_Node_List list; + Unique_Node_List list; list.push(_node); DEBUG_ONLY(int template_counter = 0;) for (uint i = 0; i < list.size(); i++) { @@ -608,7 +615,7 @@ class TemplateAssertionExpressionNode : public StackObj { DEBUG_ONLY(template_counter++;) } else { assert(!next->is_CFG(), "no CFG expected in Template Assertion Expression"); - list.push_outputs_of(next); + push_outputs_of(list, next); } } @@ -1077,10 +1084,10 @@ class Predicates : public StackObj { // This class checks whether a node is in the original loop body and not the cloned one. class NodeInOriginalLoopBody : public NodeInLoopBody { const uint _first_node_index_in_cloned_loop_body; - const Node_List& _old_new; + const Node_List& _old_new; public: - NodeInOriginalLoopBody(const uint first_node_index_in_cloned_loop_body, const Node_List& old_new) + NodeInOriginalLoopBody(const uint first_node_index_in_cloned_loop_body, const Node_List& old_new) : _first_node_index_in_cloned_loop_body(first_node_index_in_cloned_loop_body), _old_new(old_new) {} NONCOPYABLE(NodeInOriginalLoopBody); @@ -1104,11 +1111,11 @@ class NodeInMainLoopBody : public NodeInLoopBody { const uint _first_node_index_in_pre_loop_body; const uint _last_node_index_in_pre_loop_body; DEBUG_ONLY(const uint _last_node_index_from_backedge_goo;) - const Node_List& _old_new; + const Node_List& _old_new; public: NodeInMainLoopBody(const uint first_node_index_in_pre_loop_body, const uint last_node_index_in_pre_loop_body, - DEBUG_ONLY(const uint last_node_index_from_backedge_goo COMMA) const Node_List& old_new) + DEBUG_ONLY(const uint last_node_index_from_backedge_goo COMMA) const Node_List& old_new) : _first_node_index_in_pre_loop_body(first_node_index_in_pre_loop_body), _last_node_index_in_pre_loop_body(last_node_index_in_pre_loop_body), DEBUG_ONLY(_last_node_index_from_backedge_goo(last_node_index_from_backedge_goo) COMMA) @@ -1283,10 +1290,10 @@ class CloneUnswitchedLoopPredicatesVisitor : public PredicateVisitor { // This visitor collects all OpaqueTemplateAssertionNodes of Template Assertion Predicates. This is used for cleaning // up unused Template Assertion Predicates. class OpaqueTemplateAssertionPredicateCollector : public PredicateVisitor { - Unique_Node_List& _list; + Unique_Node_List& _list; public: - explicit OpaqueTemplateAssertionPredicateCollector(Unique_Node_List& list) : _list(list) {} + explicit OpaqueTemplateAssertionPredicateCollector(Unique_Node_List& list) : _list(list) {} using PredicateVisitor::visit; @@ -1364,8 +1371,8 @@ class EliminateUselessPredicates : public StackObj { #ifdef ASSERT void verify_loop_nodes_of_useless_templates_assertion_predicates_are_dead() const; - Unique_Node_List collect_loop_nodes_of_useless_template_assertion_predicates() const; - void verify_associated_loop_nodes_are_dead(const Unique_Node_List& loop_nodes_of_useless_template_assertion_predicates) const; + Unique_Node_List collect_loop_nodes_of_useless_template_assertion_predicates() const; + void verify_associated_loop_nodes_are_dead(const Unique_Node_List& loop_nodes_of_useless_template_assertion_predicates) const; #endif // ASSERT public: EliminateUselessPredicates(PhaseIterGVN& igvn, IdealLoopTree* ltree_root) diff --git a/src/hotspot/share/opto/reachability.cpp b/src/hotspot/share/opto/reachability.cpp index a40f51e31259..31e3403705f8 100644 --- a/src/hotspot/share/opto/reachability.cpp +++ b/src/hotspot/share/opto/reachability.cpp @@ -178,11 +178,7 @@ void PhaseIdealLoop::insert_rf(Node* ctrl, Node* referent) { } } -void PhaseIdealLoop::replace_rf(Node* old_node, Node* new_node) { - assert(old_node->is_ReachabilityFence() || - (old_node->is_Proj() && old_node->in(0)->is_ReachabilityFence()), - "%s", NodeClassNames[old_node->Opcode()]); - +void PhaseIdealLoop::replace_rf(ReachabilityFenceNode* old_node, Node* new_node) { IdealLoopTree* lpt = get_loop(old_node); lpt->_body.yank(old_node); assert(lpt->_reachability_fences != nullptr, "missing"); @@ -210,7 +206,7 @@ bool PhaseIdealLoop::optimize_reachability_fences() { assert(OptimizeReachabilityFences, "required"); // ResourceMark rm; // NB! not safe because insert_rf may trigger _idom reallocation - Unique_Node_List redundant_rfs; + Unique_Node_List redundant_rfs; typedef Pair LoopExit; GrowableArray worklist; @@ -269,7 +265,7 @@ bool PhaseIdealLoop::optimize_reachability_fences() { // Eliminate redundant RFs. bool progress = (redundant_rfs.size() > 0); while (redundant_rfs.size() > 0) { - remove_rf(redundant_rfs.pop()->as_ReachabilityFence()); + remove_rf(redundant_rfs.pop()); } return progress; @@ -281,20 +277,20 @@ bool PhaseIdealLoop::optimize_reachability_fences() { // Linearly traverse CFG upwards starting at ctrl_start until first merge point. // All encountered safepoints are recorded in safepoints list, except // the ones filtered out by is_interfering_sfpt_candidate(). -static void enumerate_interfering_sfpts_linear_traversal(Node* ctrl_start, Node_Stack& stack, VectorSet& visited, - Node_List& interfering_sfpts) { +static void enumerate_interfering_sfpts_linear_traversal(Node* ctrl_start, Node_Stack& stack, VectorSet& visited, + Node_List& interfering_sfpts) { for (Node* ctrl = ctrl_start; ctrl != nullptr; ctrl = ctrl->in(0)) { assert(ctrl->is_CFG(), ""); if (visited.test_set(ctrl->_idx)) { return; } else { if (ctrl->is_Region()) { - stack.push(ctrl, 1); + stack.push(ctrl->as_Region(), 1); return; // stop at merge points } else if (ctrl->is_SafePoint() && is_interfering_sfpt_candidate(ctrl->as_SafePoint())) { assert(!ctrl->is_CallStaticJava() || !ctrl->as_CallStaticJava()->is_uncommon_trap(), "uncommon traps should not be enumerated"); - interfering_sfpts.push(ctrl); + interfering_sfpts.push(ctrl->as_SafePoint()); } } } @@ -303,8 +299,8 @@ static void enumerate_interfering_sfpts_linear_traversal(Node* ctrl_start, Node_ // Enumerate all safepoints which are reachable from the RF to its referent through CFG. // Start at RF node and traverse CFG upwards until referent's control node is reached. static void enumerate_interfering_sfpts(ReachabilityFenceNode* rf, PhaseIdealLoop* phase, - Node_Stack& stack, VectorSet& visited, - Node_List& interfering_sfpts) { + Node_Stack& stack, VectorSet& visited, + Node_List& interfering_sfpts) { assert(stack.is_empty(), "required"); assert(visited.is_empty(), "required"); @@ -315,11 +311,10 @@ static void enumerate_interfering_sfpts(ReachabilityFenceNode* rf, PhaseIdealLoo visited.set(referent_ctrl->_idx); // end point enumerate_interfering_sfpts_linear_traversal(rf, stack, visited, interfering_sfpts); // starting point in CFG while (stack.is_nonempty()) { - Node* cur = stack.node(); - uint idx = stack.index(); + RegionNode* cur = stack.node(); + uint idx = stack.index(); assert(cur != nullptr, ""); - assert(cur->is_Region(), "%s", NodeClassNames[cur->Opcode()]); assert(phase->is_dominator(referent_ctrl, cur), ""); assert(idx > 0 && idx <= cur->req(), "%d %d", idx, cur->req()); @@ -365,13 +360,13 @@ bool PhaseIdealLoop::expand_reachability_fences() { DEBUG_ONLY( int no_of_constant_rfs = 0; ) ResourceMark rm; - Unique_Node_List for_removal; + Unique_Node_List for_removal; typedef Pair ReachabilityEdge; GrowableArray reachability_edges; { // Reuse temporary structures to avoid allocating them for every single RF node. - Node_List sfpt_worklist; - Node_Stack stack(0); + Node_List sfpt_worklist; + Node_Stack stack(0); VectorSet visited; for (int i = 0; i < C->reachability_fences_count(); i++) { @@ -402,7 +397,7 @@ bool PhaseIdealLoop::expand_reachability_fences() { Node* referent = rf->referent(); while (sfpt_worklist.size() > 0) { - SafePointNode* sfpt = sfpt_worklist.pop()->as_SafePoint(); + SafePointNode* sfpt = sfpt_worklist.pop(); assert(is_dominator(get_ctrl(referent), sfpt), ""); assert(sfpt->req() == rf_base_offset(sfpt), "no extra edges allowed"); if (sfpt->find_edge(referent) == -1) { @@ -427,7 +422,7 @@ bool PhaseIdealLoop::expand_reachability_fences() { // Eliminate processed RFs. They become redundant once reachability edges are added. bool progress = (for_removal.size() > 0); while (for_removal.size() > 0) { - remove_rf(for_removal.pop()->as_ReachabilityFence()); + remove_rf(for_removal.pop()); } assert(C->reachability_fences_count() == no_of_constant_rfs, ""); @@ -470,9 +465,9 @@ static Node* sfpt_ctrl_out(SafePointNode* sfpt) { // after macro expansion where runtime calls during array allocation are annotated with // valid_length_test_input as an extra edges. Until there's a mechanism to distinguish between // different types of non-debug edges, unrelated cases are filtered out explicitly and in ad-hoc manner. -void Compile::expand_reachability_edges(Unique_Node_List& safepoints) { +void Compile::expand_reachability_edges(Unique_Node_List& safepoints) { for (uint i = 0; i < safepoints.size(); i++) { - SafePointNode* sfpt = safepoints.at(i)->as_SafePoint(); + SafePointNode* sfpt = safepoints.at(i); uint rf_offset = rf_base_offset(sfpt); if (sfpt->jvms() != nullptr && sfpt->req() > rf_offset) { diff --git a/src/hotspot/share/opto/reg_split.cpp b/src/hotspot/share/opto/reg_split.cpp index 7b2597d74146..f1958009bbf9 100644 --- a/src/hotspot/share/opto/reg_split.cpp +++ b/src/hotspot/share/opto/reg_split.cpp @@ -575,8 +575,8 @@ uint PhaseChaitin::Split(uint maxlrg, ResourceArea* split_arena) { } // Keep track of DEFS & Phis for later passes - Node_List defs(split_arena, 8); - Node_List phis(split_arena, 16); + Node_List defs(split_arena, 8); + Node_List phis(split_arena, 16); //----------PASS 1---------- //----------Propagation & Node Insertion Code---------- @@ -1328,8 +1328,7 @@ uint PhaseChaitin::Split(uint maxlrg, ResourceArea* split_arena) { // Walk the phis list to patch inputs, split phis, and name phis uint lrgs_before_phi_split = maxlrg; for( insidx = 0; insidx < phis.size(); insidx++ ) { - Node *phi = phis.at(insidx); - assert(phi->is_Phi(),"This list must only contain Phi Nodes"); + PhiNode* phi = phis.at(insidx); Block *b = _cfg.get_block_for_node(phi); // Grab the live range number uint lidx = _lrg_map.find_id(phi); diff --git a/src/hotspot/share/opto/replacednodes.cpp b/src/hotspot/share/opto/replacednodes.cpp index 96216f9d7778..408291f5d4a5 100644 --- a/src/hotspot/share/opto/replacednodes.cpp +++ b/src/hotspot/share/opto/replacednodes.cpp @@ -112,8 +112,8 @@ void ReplacedNodes::apply(Compile* C, Node* ctl) { return; } ResourceMark rm; - Node_Stack stack(0); - Unique_Node_List to_fix; // nodes to clone + uses at the end of the chain that need to updated + Node_Stack stack(0); + Unique_Node_List to_fix; // nodes to clone + uses at the end of the chain that need to updated VectorSet seen; VectorSet valid_control; @@ -315,7 +315,7 @@ void ReplacedNodes::merge_with(const ReplacedNodes& other) { } } -void ReplacedNodes::collect_nodes_to_clone(const Node_Stack& stack, Unique_Node_List& to_fix) { +void ReplacedNodes::collect_nodes_to_clone(const Node_Stack& stack, Unique_Node_List& to_fix) { for (uint i = stack.size() - 1; i >= 1; i--) { Node* n = stack.node_at(i); to_fix.push(n); diff --git a/src/hotspot/share/opto/replacednodes.hpp b/src/hotspot/share/opto/replacednodes.hpp index 9b66a2c5f49b..d169ba03ca43 100644 --- a/src/hotspot/share/opto/replacednodes.hpp +++ b/src/hotspot/share/opto/replacednodes.hpp @@ -79,7 +79,7 @@ class ReplacedNodes { bool is_dominator(const Node* ctl, Node* n) const; - void collect_nodes_to_clone(const Node_Stack& stack, Unique_Node_List& to_fix); + static void collect_nodes_to_clone(const Node_Stack& stack, Unique_Node_List& to_fix); }; #endif // SHARE_OPTO_REPLACEDNODES_HPP diff --git a/src/hotspot/share/opto/rootnode.cpp b/src/hotspot/share/opto/rootnode.cpp index bdac33faa443..9c9898114a51 100644 --- a/src/hotspot/share/opto/rootnode.cpp +++ b/src/hotspot/share/opto/rootnode.cpp @@ -92,3 +92,48 @@ const Type* HaltNode::Value(PhaseGVN* phase) const { const RegMask &HaltNode::out_RegMask() const { return RegMask::EMPTY; } + +Node* DeadPathNode::Ideal(PhaseGVN* phase, bool can_reshape) { + assert(unique_ctrl_out() == phase->C->root(), "only referenced from root"); + assert(can_reshape, "only used once igvn executes"); + bool modified = false; + for (uint i = 1; i < req(); i++) { // For all inputs + // Check for and remove dead inputs + if (phase->type(in(i)) == Type::TOP) { + del_req(i--); // Delete TOP inputs + modified = true; + } + } + if (req() == 1 && is_active()) { + assert(modified, "only if some inputs were removed"); + deactivate(); + } + return modified ? this : nullptr; +} + +const Type* DeadPathNode::Value(PhaseGVN* phase) const { + if (req() == 1) { + return Type::TOP; + } + return bottom_type(); +} + +void DeadPathNode::activate(PhaseIterGVN* igvn) { + assert(Compile::current()->root()->find_edge(this) < 0, "should be disconnected from root"); + set_req(0, this); + // If an entire subgraph died such as with Node::remove_dead_region(), some dead inputs to the DeadPath node will have + // been left behind + while (req() > 1) { + uint last = req() - 1; + assert(in(last) == nullptr || in(last)->is_top(), "only dead inputs should remain"); + del_req(last); + } + Node* root_node = Compile::current()->root(); + root_node->add_req(this); + igvn->_worklist.push(root_node); + igvn->set_type(this, bottom_type()); +} + +void DeadPathNode::deactivate() { + set_req(0, nullptr); +} diff --git a/src/hotspot/share/opto/rootnode.hpp b/src/hotspot/share/opto/rootnode.hpp index 76f0ec440a9b..b63405811eec 100644 --- a/src/hotspot/share/opto/rootnode.hpp +++ b/src/hotspot/share/opto/rootnode.hpp @@ -69,4 +69,35 @@ class HaltNode : public Node { virtual uint match_edge(uint idx) const { return 0; } }; + +// This node collects paths that are found dead by PhaseIterGVN::make_dependent_paths_dead_if_top() + +// There is a single DeadPath node for the lifetime of optimizations. It's initially not active (i.e. unreachable from +// the IR graph). When a cfg path becomes dead it's added as an input to the unique DeadPath node. If after some +// optimizations run, the DeadPath node gets disconnected, it's not destroyed. It becomes inactive and can possibly be +// activated again on a subsequent igvn. When optimizations are over, the DeadPath node, if it is active, is expanded to +// a Region and Halt node in Compile::final_graph_reshaping(). + +// Rather than having this dedicated node, igvn could add a Halt node every time it finds a dead cfg path from a data +// node. What's likely, however, is that as igvn progresses, that same cfg path is found dead by following cfg edges. +// The Halt node then becomes dead. To avoid this unnecessary cycle of creation of a Halt node only to have it be found +// dead shortly after, dead cfg paths are added to the unique DeadPath node. +class DeadPathNode : public RegionNode { +public: + DeadPathNode() : RegionNode(1) { + deactivate(); + assert(Compile::current()->dead_path() == nullptr, "only one"); + } + virtual int Opcode() const; + virtual const Type* bottom_type() const { return Type::BOTTOM; } + virtual Node* Identity(PhaseGVN* phase) { return this; } + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); + virtual const Type* Value(PhaseGVN* phase) const; + bool is_active() const { + return in(0) == this; + } + void activate(PhaseIterGVN* igvn); + void deactivate(); +}; + #endif // SHARE_OPTO_ROOTNODE_HPP diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp index 4a0f53f42aec..0f6cdb669751 100644 --- a/src/hotspot/share/opto/runtime.cpp +++ b/src/hotspot/share/opto/runtime.cpp @@ -251,9 +251,6 @@ const TypeFunc* OptoRuntime::_updateBytesAdler32_Type = nullptr; const TypeFunc* OptoRuntime::_osr_end_Type = nullptr; const TypeFunc* OptoRuntime::_register_finalizer_Type = nullptr; const TypeFunc* OptoRuntime::_vthread_transition_Type = nullptr; -#if INCLUDE_JFR -const TypeFunc* OptoRuntime::_class_id_load_barrier_Type = nullptr; -#endif // INCLUDE_JFR const TypeFunc* OptoRuntime::_dtrace_method_entry_exit_Type = nullptr; const TypeFunc* OptoRuntime::_dtrace_object_alloc_Type = nullptr; @@ -2239,22 +2236,6 @@ static const TypeFunc* make_register_finalizer_Type() { return TypeFunc::make(domain, range); } -#if INCLUDE_JFR -static const TypeFunc* make_class_id_load_barrier_Type() { - // create input type (domain) - const Type **fields = TypeTuple::fields(1); - fields[TypeFunc::Parms+0] = TypeInstPtr::KLASS; - const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms + 1, fields); - - // create result type (range) - fields = TypeTuple::fields(0); - - const TypeTuple *range = TypeTuple::make(TypeFunc::Parms + 0, fields); - - return TypeFunc::make(domain,range); -} -#endif // INCLUDE_JFR - //----------------------------------------------------------------------------- static const TypeFunc* make_dtrace_method_entry_exit_Type() { // create input type (domain) @@ -2450,9 +2431,6 @@ void OptoRuntime::initialize_types() { _osr_end_Type = make_osr_end_Type(); _register_finalizer_Type = make_register_finalizer_Type(); _vthread_transition_Type = make_vthread_transition_Type(); - JFR_ONLY( - _class_id_load_barrier_Type = make_class_id_load_barrier_Type(); - ) _dtrace_method_entry_exit_Type = make_dtrace_method_entry_exit_Type(); _dtrace_object_alloc_Type = make_dtrace_object_alloc_Type(); } diff --git a/src/hotspot/share/opto/runtime.hpp b/src/hotspot/share/opto/runtime.hpp index 8985a69f5e8f..e18b8d0a0782 100644 --- a/src/hotspot/share/opto/runtime.hpp +++ b/src/hotspot/share/opto/runtime.hpp @@ -200,9 +200,6 @@ class OptoRuntime : public AllStatic { static const TypeFunc* _osr_end_Type; static const TypeFunc* _register_finalizer_Type; static const TypeFunc* _vthread_transition_Type; -#if INCLUDE_JFR - static const TypeFunc* _class_id_load_barrier_Type; -#endif // INCLUDE_JFR static const TypeFunc* _dtrace_method_entry_exit_Type; static const TypeFunc* _dtrace_object_alloc_Type; @@ -247,9 +244,6 @@ class OptoRuntime : public AllStatic { private: - // Implicit exception support - static void throw_null_exception_C(JavaThread* thread); - // Exception handling static address handle_exception_C (JavaThread* current); static address handle_exception_C_helper(JavaThread* current, nmethod*& nm); @@ -313,10 +307,6 @@ class OptoRuntime : public AllStatic { static UncommonTrapBlob* uncommon_trap_blob() { return _uncommon_trap_blob; } static ExceptionBlob* exception_blob() { return _exception_blob; } - // Implicit exception support - static void throw_div0_exception_C (JavaThread* thread); - static void throw_stack_overflow_error_C(JavaThread* thread); - // Exception handling static address rethrow_stub() { return _rethrow_Java; } @@ -772,13 +762,6 @@ class OptoRuntime : public AllStatic { return vthread_transition_Type(); } -#if INCLUDE_JFR - static inline const TypeFunc* class_id_load_barrier_Type() { - assert(_class_id_load_barrier_Type != nullptr, "should be initialized"); - return _class_id_load_barrier_Type; - } -#endif // INCLUDE_JFR - static const TypeFunc* load_unknown_value_Type(); static const TypeFunc* store_unknown_value_Type(); diff --git a/src/hotspot/share/opto/split_if.cpp b/src/hotspot/share/opto/split_if.cpp index 284752b87af8..ba93c8310879 100644 --- a/src/hotspot/share/opto/split_if.cpp +++ b/src/hotspot/share/opto/split_if.cpp @@ -238,7 +238,7 @@ bool PhaseIdealLoop::clone_cmp_loadklass_down(Node* n, const Node* blk1, const N tty->print_cr(" Cloning down (LoadKlass): %d %s", n->_idx, n->Name()); } #endif - Node_List cmp_nodes; + Node_List cmp_nodes; uint old = C->unique(); for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { Node* u1 = n->fast_out(i); diff --git a/src/hotspot/share/opto/stringopts.cpp b/src/hotspot/share/opto/stringopts.cpp index ceea0136b52d..09c797714e42 100644 --- a/src/hotspot/share/opto/stringopts.cpp +++ b/src/hotspot/share/opto/stringopts.cpp @@ -49,13 +49,13 @@ class StringConcat : public ResourceObj { Node* _arguments; // The list of arguments to be concatenated GrowableArray _mode; // into a String along with a mode flag // indicating how to treat the value. - Node_List _constructors; // List of constructors (many in case of stacked concat) - Node_List _control; // List of control nodes that will be deleted - Node_List _uncommon_traps; // Uncommon traps that needs to be rewritten - // to restart at the initial JVMState. - Unique_Node_List _allowed_compares; // validate_control_flow() needs to know which compare nodes are - // accepted users of call results. In case of stacked concats, - // these need to be persisted across merges for validation. + Node_List _constructors; // List of constructors (many in case of stacked concat) + Node_List _control; // List of control nodes that will be deleted + Node_List _uncommon_traps; // Uncommon traps that needs to be rewritten + // to restart at the initial JVMState. + Unique_Node_List _allowed_compares; // validate_control_flow() needs to know which compare nodes are + // accepted users of call results. In case of stacked concats, + // these need to be persisted across merges for validation. static constexpr uint STACKED_CONCAT_UPPER_BOUND = 256; // argument limit for a merged concat. // The value 256 was derived by measuring @@ -182,7 +182,7 @@ class StringConcat : public ResourceObj { assert(!_control.contains(ctrl), "only push once"); _control.push(ctrl); } - void add_constructor(Node* init) { + void add_constructor(const Node* init) { assert(!_constructors.contains(init), "only push once"); _constructors.push(init); } @@ -291,8 +291,8 @@ void StringConcat::eliminate_unneeded_control() { StringConcat* StringConcat::merge(StringConcat* other, Node* arg) { StringConcat* result = new StringConcat(_stringopts, _end); - Unique_Node_List null_check_ifs; - Unique_Node_List skipped_phis; + Unique_Node_List null_check_ifs; + Unique_Node_List skipped_phis; for (uint x = 0; x < _control.size(); x++) { Node* n = _control.at(x); @@ -350,7 +350,7 @@ StringConcat* StringConcat::merge(StringConcat* other, Node* arg) { // Verify that the diamond region isn't shared with non-null check phis; // and that the associated bool doesn't have external uses. for (uint i = 0; i < skipped_phis.size(); i++) { - Node* n = skipped_phis.at(i); + const Node* n = skipped_phis.at(i); Node* r = n->in(0); for (SimpleDUIterator j(r); j.has_next(); j.next()) { Node* n2 = j.get(); @@ -452,9 +452,9 @@ void StringConcat::eliminate_initialize(InitializeNode* init) { init->disconnect_inputs(C); } -Node_List PhaseStringOpts::collect_toString_calls() { - Node_List string_calls; - Node_List worklist; +Node_List PhaseStringOpts::collect_toString_calls() { + Node_List string_calls; + Node_List worklist; _visited.clear(); @@ -730,9 +730,9 @@ PhaseStringOpts::PhaseStringOpts(PhaseGVN* gvn): // if it's possible to fuse the usage of the SB into a single String // construction. GrowableArray concats; - Node_List toStrings = collect_toString_calls(); + Node_List toStrings = collect_toString_calls(); while (toStrings.size() > 0) { - StringConcat* sc = build_candidate(toStrings.pop()->as_CallStaticJava()); + StringConcat* sc = build_candidate(toStrings.pop()); if (sc != nullptr) { concats.push(sc); } @@ -854,7 +854,7 @@ bool StringConcat::validate_mem_flow() { for (uint i = 0; i < _control.size(); i++) { #ifndef PRODUCT - Node_List path; + Node_List path; #endif Node* curr = _control.at(i); if (curr->is_Call() && curr != _begin) { // For all calls except the first allocation @@ -998,11 +998,11 @@ bool StringConcat::validate_control_flow() { // trap. int null_check_count = 0; - Unique_Node_List ctrl_path; + Unique_Node_List ctrl_path; // Local version of _allowed_compares that stores allowed comparisons discovered during traversal // but that we won't persist across merges. - Unique_Node_List local_allowed_compares; + Unique_Node_List local_allowed_compares; assert(_control.contains(_begin), "missing"); assert(_control.contains(_end), "missing"); @@ -1212,7 +1212,7 @@ bool StringConcat::validate_control_flow() { // this cluster of objects. First collect all the results produced // by calls in the region. _stringopts->_visited.clear(); - Node_List worklist; + Node_List worklist; Node* final_result = _end->proj_out_or_null(TypeFunc::Parms); for (uint i = 0; i < _control.size(); i++) { CallNode* cnode = _control.at(i)->isa_Call(); diff --git a/src/hotspot/share/opto/stringopts.hpp b/src/hotspot/share/opto/stringopts.hpp index b893ac8c2780..667a2ceaa269 100644 --- a/src/hotspot/share/opto/stringopts.hpp +++ b/src/hotspot/share/opto/stringopts.hpp @@ -38,7 +38,7 @@ class PhaseStringOpts : public Phase { PhaseGVN* _gvn; // List of dead nodes to clean up aggressively at the end - Unique_Node_List dead_worklist; + Unique_Node_List dead_worklist; // Memory slices needed for code gen int byte_adr_idx; @@ -47,7 +47,7 @@ class PhaseStringOpts : public Phase { VectorSet _visited; // Collect a list of all SB.toString calls - Node_List collect_toString_calls(); + Node_List collect_toString_calls(); // Examine the use of the SB alloc to see if it can be replace with // a single string construction. diff --git a/src/hotspot/share/opto/subnode.hpp b/src/hotspot/share/opto/subnode.hpp index d4706affb796..4037ecc7389e 100644 --- a/src/hotspot/share/opto/subnode.hpp +++ b/src/hotspot/share/opto/subnode.hpp @@ -323,7 +323,6 @@ class FlatArrayCheckNode : public CmpNode { FlatArrayCheckNode(Compile* C, Node* mem, Node* array_or_klass) : CmpNode(mem, array_or_klass) { assert(UseArrayFlattening, "Should not be used without flat arrays enabled"); init_class_id(Class_FlatArrayCheck); - init_flags(Flag_is_macro); C->add_macro_node(this); } virtual int Opcode() const; diff --git a/src/hotspot/share/opto/subtypenode.hpp b/src/hotspot/share/opto/subtypenode.hpp index 2dac38662098..e131460c2eb9 100644 --- a/src/hotspot/share/opto/subtypenode.hpp +++ b/src/hotspot/share/opto/subtypenode.hpp @@ -38,7 +38,6 @@ class SubTypeCheckNode : public CmpNode { SubTypeCheckNode(Compile* C, Node* obj_or_subklass, Node* superklass, ciMethod* method, int bci) : CmpNode(obj_or_subklass, superklass), _method(method), _bci(bci) { init_class_id(Class_SubTypeCheck); - init_flags(Flag_is_macro); C->add_macro_node(this); } diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp index 9ab788a344eb..0d4738134e2c 100644 --- a/src/hotspot/share/opto/superword.cpp +++ b/src/hotspot/share/opto/superword.cpp @@ -49,9 +49,9 @@ SuperWord::SuperWord(const VLoopAnalyzer &vloop_analyzer) : // Collect ignored loop nodes during VPointer parsing. class SuperWordUnrollingAnalysisIgnoredNodes : public MemPointerParserCallback { private: - const VLoop& _vloop; - const Node_List& _body; - bool* _ignored; + const VLoop& _vloop; + const Node_List& _body; + bool* _ignored; public: SuperWordUnrollingAnalysisIgnoredNodes(const VLoop& vloop) : @@ -813,10 +813,10 @@ bool VLoopDependencyGraph::independent(Node* s1, Node* s2) const { bool speculate_away_weak_edges = _vloop.use_speculative_aliasing_checks(); ResourceMark rm; - Unique_Node_List worklist; + Unique_Node_List worklist; worklist.push(deep); for (uint i = 0; i < worklist.size(); i++) { - Node* n = worklist.at(i); + const Node* n = worklist.at(i); for (PredsIterator preds(*this, n); !preds.done(); preds.next()) { if (speculate_away_weak_edges && preds.is_current_weak_memory_edge()) { continue; } Node* pred = preds.current(); @@ -839,9 +839,9 @@ bool VLoopDependencyGraph::independent(Node* s1, Node* s2) const { // is the smallest depth of all nodes from the nodes list. Once we have // traversed all those nodes, and have not found another node from the // nodes list, we know that all nodes in the nodes list are independent. -bool VLoopDependencyGraph::mutually_independent(const Node_List* nodes) const { +bool VLoopDependencyGraph::mutually_independent(const Node_List* nodes) const { ResourceMark rm; - Unique_Node_List worklist; + Unique_Node_List worklist; VectorSet nodes_set; int min_d = depth(nodes->at(0)); for (uint k = 0; k < nodes->size(); k++) { @@ -857,7 +857,7 @@ bool VLoopDependencyGraph::mutually_independent(const Node_List* nodes) const { bool speculate_away_weak_edges = _vloop.use_speculative_aliasing_checks(); for (uint i = 0; i < worklist.size(); i++) { - Node* n = worklist.at(i); + const Node* n = worklist.at(i); for (PredsIterator preds(*this, n); !preds.done(); preds.next()) { if (speculate_away_weak_edges && preds.is_current_weak_memory_edge()) { continue; } Node* pred = preds.current(); @@ -1197,13 +1197,13 @@ void SuperWord::combine_pairs_to_longer_packs() { #endif // Iterate pair-chain by pair-chain, each from left-most to right-most. - Node_List* pack = nullptr; + Node_List* pack = nullptr; for (PairSetIterator pair(_pairset); !pair.done(); pair.next()) { Node* left = pair.left(); Node* right = pair.right(); if (_pairset.is_left_in_a_left_most_pair(left)) { assert(pack == nullptr, "no unfinished pack"); - pack = new (arena()) Node_List(arena()); + pack = new (arena()) Node_List(arena()); pack->push(left); } assert(pack != nullptr, "must have unfinished pack"); @@ -1226,7 +1226,7 @@ void SuperWord::combine_pairs_to_longer_packs() { } SplitStatus PackSet::split_pack(const char* split_name, - Node_List* pack, + Node_List* pack, SplitTask task) { uint pack_size = pack->size(); @@ -1310,7 +1310,7 @@ SplitStatus PackSet::split_pack(const char* split_name, // We will have two packs assert(old_size >= 2 && new_size >= 2, "implied"); - Node_List* new_pack = new Node_List(new_size); + Node_List* new_pack = new Node_List(new_size); for (uint i = 0; i < new_size; i++) { Node* n = pack->at(old_size + i); @@ -1335,13 +1335,13 @@ void PackSet::split_packs(const char* split_name, changed = false; int new_packset_length = 0; for (int i = 0; i < _packs.length(); i++) { - Node_List* pack = _packs.at(i); + Node_List* pack = _packs.at(i); assert(pack != nullptr && pack->size() >= 2, "no nullptr, at least size 2"); SplitTask task = strategy(pack); SplitStatus status = split_pack(split_name, pack, task); changed |= !status.is_unchanged(); - Node_List* first_pack = status.first_pack(); - Node_List* second_pack = status.second_pack(); + Node_List* first_pack = status.first_pack(); + Node_List* second_pack = status.second_pack(); _packs.at_put(i, nullptr); // take out pack if (first_pack != nullptr) { // The first pack can be put at the current position @@ -1366,7 +1366,7 @@ void PackSet::split_packs(const char* split_name, // Split packs at boundaries where left and right have different use or def packs. void SuperWord::split_packs_at_use_def_boundaries() { - auto split_strategy = [&](const Node_List* pack) { + auto split_strategy = [&](const Node_List* pack) { uint pack_size = pack->size(); uint boundary = find_use_def_boundary(pack); assert(boundary < pack_size, "valid boundary %d", boundary); @@ -1381,7 +1381,7 @@ void SuperWord::split_packs_at_use_def_boundaries() { // Split packs that are only implemented with a smaller pack size. Also splits packs // such that they eventually have power of 2 size. void SuperWord::split_packs_only_implemented_with_smaller_size() { - auto split_strategy = [&](const Node_List* pack) { + auto split_strategy = [&](const Node_List* pack) { uint pack_size = pack->size(); uint implemented_size = max_implemented_size(pack); if (implemented_size == 0) { @@ -1398,7 +1398,7 @@ void SuperWord::split_packs_only_implemented_with_smaller_size() { // Split packs that have a mutual dependency, until all packs are mutually_independent. void SuperWord::split_packs_to_break_mutual_dependence() { - auto split_strategy = [&](const Node_List* pack) { + auto split_strategy = [&](const Node_List* pack) { uint pack_size = pack->size(); assert(is_power_of_2(pack_size), "ensured by earlier splits %d", pack_size); if (!is_marked_reduction(pack->at(0)) && @@ -1416,7 +1416,7 @@ template void PackSet::filter_packs(const char* filter_name, const char* rejection_message, FilterPredicate filter) { - auto split_strategy = [&](const Node_List* pack) { + auto split_strategy = [&](const Node_List* pack) { if (filter(pack)) { return SplitTask::make_unchanged(); } else { @@ -1427,7 +1427,7 @@ void PackSet::filter_packs(const char* filter_name, } void SuperWord::filter_packs_for_power_of_2_size() { - auto filter = [&](const Node_List* pack) { + auto filter = [&](const Node_List* pack) { return is_power_of_2(pack->size()); }; _packset.filter_packs("SuperWord::filter_packs_for_power_of_2_size", @@ -1455,7 +1455,7 @@ void SuperWord::filter_packs_for_power_of_2_size() { // for (int i ...) { v[i] = v[i + 1] + 5; } // for (int i ...) { v[i - 1] = v[i] + 5; } void SuperWord::filter_packs_for_mutual_independence() { - auto filter = [&](const Node_List* pack) { + auto filter = [&](const Node_List* pack) { // reductions are trivially connected return is_marked_reduction(pack->at(0)) || mutually_independent(pack); @@ -1465,7 +1465,7 @@ void SuperWord::filter_packs_for_mutual_independence() { } // Find the set of alignment solutions for load/store pack. -const AlignmentSolution* SuperWord::pack_alignment_solution(const Node_List* pack) { +const AlignmentSolution* SuperWord::pack_alignment_solution(const Node_List* pack) const { assert(pack != nullptr && (pack->at(0)->is_Load() || pack->at(0)->is_Store()), "only load/store packs"); const MemNode* mem_ref = pack->at(0)->as_Mem(); @@ -1507,7 +1507,7 @@ void SuperWord::filter_packs_for_alignment() { int mem_ops_count = 0; int mem_ops_rejected = 0; - auto filter = [&](const Node_List* pack) { + auto filter = [&](const Node_List* pack) { // Only memops need to be aligned. if (!pack->at(0)->is_Load() && !pack->at(0)->is_Store()) { @@ -1552,7 +1552,7 @@ void SuperWord::filter_packs_for_alignment() { // Solution is constrained (not trivial) // -> must change pre-limit to achieve alignment MemNode const* mem = current->as_constrained()->mem_ref(); - Node_List* pack = get_pack(mem); + Node_List* pack = get_pack(mem); assert(pack != nullptr, "memop of final solution must still be packed"); _vpointer_for_main_loop_alignment = &vpointer(mem); _aw_for_main_loop_alignment = pack->size() * mem->memory_size(); @@ -1561,7 +1561,7 @@ void SuperWord::filter_packs_for_alignment() { // Remove packs that are not implemented void SuperWord::filter_packs_for_implemented() { - auto filter = [&](const Node_List* pack) { + auto filter = [&](const Node_List* pack) { return implemented(pack, pack->size()); }; _packset.filter_packs("SuperWord::filter_packs_for_implemented", @@ -1571,7 +1571,7 @@ void SuperWord::filter_packs_for_implemented() { // Remove packs that are not profitable. void SuperWord::filter_packs_for_profitable() { // Remove packs that are not profitable - auto filter = [&](const Node_List* pack) { + auto filter = [&](const Node_List* pack) { return profitable(pack); }; _packset.filter_packs("Superword::filter_packs_for_profitable", @@ -1579,7 +1579,7 @@ void SuperWord::filter_packs_for_profitable() { } // Can code be generated for the pack, restricted to size nodes? -bool SuperWord::implemented(const Node_List* pack, const uint size) const { +bool SuperWord::implemented(const Node_List* pack, const uint size) const { assert(size >= 2 && size <= pack->size() && is_power_of_2(size), "valid size"); bool retValue = false; Node* p0 = pack->at(0); @@ -1616,7 +1616,7 @@ bool SuperWord::implemented(const Node_List* pack, const uint size) const { } // Find the maximal implemented size smaller or equal to the packs size -uint SuperWord::max_implemented_size(const Node_List* pack) { +uint SuperWord::max_implemented_size(const Node_List* pack) const { uint size = round_down_power_of_2(pack->size()); if (implemented(pack, size)) { return size; @@ -1632,7 +1632,7 @@ uint SuperWord::max_implemented_size(const Node_List* pack) { } // If the j-th input for all nodes in the pack is the same input: return it, else nullptr. -Node* PackSet::same_inputs_at_index_or_null(const Node_List* pack, const int index) const { +Node* PackSet::same_inputs_at_index_or_null(const Node_List* pack, const int index) { Node* p0_in = pack->at(0)->in(index); for (uint i = 1; i < pack->size(); i++) { if (pack->at(i)->in(index) != p0_in) { @@ -1642,7 +1642,7 @@ Node* PackSet::same_inputs_at_index_or_null(const Node_List* pack, const int ind return p0_in; } -VTransformBoolTest PackSet::get_bool_test(const Node_List* bool_pack) const { +VTransformBoolTest PackSet::get_bool_test(const Node_List* bool_pack) const { BoolNode* bol = bool_pack->at(0)->as_Bool(); BoolTest::mask mask = bol->_test._test; bool is_negated = false; @@ -1735,7 +1735,7 @@ VTransformBoolTest PackSet::get_bool_test(const Node_List* bool_pack) const { //------------------------------profitable--------------------------- // For pack p, are all operands and all uses (with in the block) vector? -bool SuperWord::profitable(const Node_List* p) const { +bool SuperWord::profitable(const Node_List* p) const { Node* p0 = p->at(0); uint start, end; VectorNode::vector_operands(p0, &start, &end); @@ -1753,7 +1753,7 @@ bool SuperWord::profitable(const Node_List* p) const { // Check if reductions are connected if (is_marked_reduction(p0)) { Node* second_in = p0->in(2); - Node_List* second_pk = get_pack(second_in); + Node_List* second_pk = get_pack(second_in); if (second_pk == nullptr) { // The second input has to be the vector we wanted to reduce, // but it was not packed. @@ -1766,7 +1766,7 @@ bool SuperWord::profitable(const Node_List* p) const { // For now, return false if shift count is vector or not scalar promotion // case (different shift counts) because it is not supported yet. Node* cnt = p0->in(2); - Node_List* cnt_pk = get_pack(cnt); + Node_List* cnt_pk = get_pack(cnt); if (cnt_pk != nullptr || _packset.same_inputs_at_index_or_null(p, 2) == nullptr) { return false; } @@ -1836,7 +1836,7 @@ void SuperWord::verify_packs() const { // All packs must be: for (int i = 0; i < _packset.length(); i++) { - Node_List* pack = _packset.at(i); + Node_List* pack = _packset.at(i); // 1. Mutually independent (or a reduction). if (!is_marked_reduction(pack->at(0)) && @@ -1865,9 +1865,9 @@ void SuperWord::verify_packs() const { void PackSet::verify() const { // Verify all nodes in packset have pack set correctly. ResourceMark rm; - Unique_Node_List processed; + Unique_Node_List processed; for (int i = 0; i < _packs.length(); i++) { - Node_List* p = _packs.at(i); + Node_List* p = _packs.at(i); for (uint k = 0; k < p->size(); k++) { Node* n = p->at(k); assert(_vloop.in_bb(n), "only nodes in bb can be in packset"); @@ -2065,7 +2065,7 @@ void VTransform::apply_vectorization() const { // which are proper vector uses of def. void SuperWord::verify_no_extract() { for (int i = 0; i < _packset.length(); i++) { - Node_List* p_def = _packset.at(i); + Node_List* p_def = _packset.at(i); // A vector store has no uses if (p_def->at(0)->is_Store()) { continue; } @@ -2079,7 +2079,7 @@ void SuperWord::verify_no_extract() { for (uint k = 0; k < use->req(); k++) { Node* maybe_def = use->in(k); if (def == maybe_def) { - Node_List* p_use = get_pack(use); + Node_List* p_use = get_pack(use); if (is_marked_reduction(def)) { continue; } assert(p_use != nullptr && is_vector_use(use, k), "all uses must be vector uses"); } @@ -2092,13 +2092,13 @@ void SuperWord::verify_no_extract() { // Check if n_super's pack uses are a superset of n_sub's pack uses. bool SuperWord::has_use_pack_superset(const Node* n_super, const Node* n_sub) const { - Node_List* pack = get_pack(n_super); + Node_List* pack = get_pack(n_super); assert(pack != nullptr && pack == get_pack(n_sub), "must have the same pack"); // For all uses of n_sub that are in a pack (use_sub) ... for (DUIterator_Fast jmax, j = n_sub->fast_outs(jmax); j < jmax; j++) { Node* use_sub = n_sub->fast_out(j); - Node_List* pack_use_sub = get_pack(use_sub); + Node_List* pack_use_sub = get_pack(use_sub); if (pack_use_sub == nullptr) { continue; } // ... and all input edges: use_sub->in(i) == n_sub. @@ -2111,7 +2111,7 @@ bool SuperWord::has_use_pack_superset(const Node* n_super, const Node* n_sub) co bool found = false; for (DUIterator_Fast kmax, k = n_super->fast_outs(kmax); k < kmax; k++) { Node* use_super = n_super->fast_out(k); - Node_List* pack_use_super = get_pack(use_super); + Node_List* pack_use_super = get_pack(use_super); if (pack_use_sub != pack_use_super) { continue; } // ... and where there is an edge use_super->in(i) == n_super. @@ -2137,7 +2137,7 @@ bool SuperWord::has_use_pack_superset(const Node* n_super, const Node* n_sub) co // Find a boundary in the pack, where left and right have different pack uses and defs. // This is a natural boundary to split a pack, to ensure that use and def packs match. // If no boundary is found, return zero. -uint SuperWord::find_use_def_boundary(const Node_List* pack) const { +uint SuperWord::find_use_def_boundary(const Node_List* pack) const { Node* p0 = pack->at(0); Node* p1 = pack->at(1); @@ -2181,7 +2181,7 @@ uint SuperWord::find_use_def_boundary(const Node_List* pack) const { //------------------------------is_vector_use--------------------------- // Is use->in(u_idx) a vector use? bool SuperWord::is_vector_use(Node* use, int u_idx) const { - Node_List* u_pk = get_pack(use); + Node_List* u_pk = get_pack(use); if (u_pk == nullptr) return false; // Reduction: first input is internal connection. @@ -2195,7 +2195,7 @@ bool SuperWord::is_vector_use(Node* use, int u_idx) const { } Node* def = use->in(u_idx); - Node_List* d_pk = get_pack(def); + Node_List* d_pk = get_pack(def); if (d_pk == nullptr) { Node* n = u_pk->at(0)->in(u_idx); if (n == iv()) { @@ -2251,14 +2251,14 @@ bool SuperWord::is_vector_use(Node* use, int u_idx) const { // and (b0, b1) in the other input pack. Thus, both a and b are strided, // with stride = 2. Further, a0 and b0 have offset 0, whereas a1 and b1 // have offset 1. -bool PackSet::is_muladds2i_pack_with_pack_inputs(const Node_List* pack) const { +bool PackSet::is_muladds2i_pack_with_pack_inputs(const Node_List* pack) const { assert(VectorNode::is_muladds2i(pack->at(0)), "must be MulAddS2I"); bool pack1_has_offset_0 = (strided_pack_input_at_index_or_null(pack, 1, 2, 0) != nullptr); - Node_List* pack1 = strided_pack_input_at_index_or_null(pack, 1, 2, pack1_has_offset_0 ? 0 : 1); - Node_List* pack2 = strided_pack_input_at_index_or_null(pack, 2, 2, pack1_has_offset_0 ? 0 : 1); - Node_List* pack3 = strided_pack_input_at_index_or_null(pack, 3, 2, pack1_has_offset_0 ? 1 : 0); - Node_List* pack4 = strided_pack_input_at_index_or_null(pack, 4, 2, pack1_has_offset_0 ? 1 : 0); + Node_List* pack1 = strided_pack_input_at_index_or_null(pack, 1, 2, pack1_has_offset_0 ? 0 : 1); + Node_List* pack2 = strided_pack_input_at_index_or_null(pack, 2, 2, pack1_has_offset_0 ? 0 : 1); + Node_List* pack3 = strided_pack_input_at_index_or_null(pack, 3, 2, pack1_has_offset_0 ? 1 : 0); + Node_List* pack4 = strided_pack_input_at_index_or_null(pack, 4, 2, pack1_has_offset_0 ? 1 : 0); return pack1 != nullptr && pack2 != nullptr && @@ -2268,10 +2268,10 @@ bool PackSet::is_muladds2i_pack_with_pack_inputs(const Node_List* pack) const { (pack1 == pack4 && pack2 == pack3)); // case 2 or 4 } -Node_List* PackSet::strided_pack_input_at_index_or_null(const Node_List* pack, const int index, const int stride, const int offset) const { +Node_List* PackSet::strided_pack_input_at_index_or_null(const Node_List* pack, const int index, const int stride, const int offset) const { Node* def0 = pack->at(0)->in(index); - Node_List* pack_in = get_pack(def0); + Node_List* pack_in = get_pack(def0); if (pack_in == nullptr || pack->size() * stride != pack_in->size()) { return nullptr; // size mismatch } @@ -2525,6 +2525,8 @@ static bool can_subword_truncate(Node* in, const Type* type) { case Op_CountLeadingZerosL: case Op_CountTrailingZerosI: case Op_CountTrailingZerosL: + case Op_CompressBits: + case Op_ExpandBits: case Op_IsFiniteF: case Op_IsFiniteD: case Op_IsInfiniteF: @@ -2691,7 +2693,7 @@ bool VLoopMemorySlices::same_memory_slice(MemNode* m1, MemNode* m2) const { _vloop.phase()->C->get_alias_index(m2->in_adr_type()); } -LoadNode::ControlDependency SuperWordVTransformBuilder::load_control_dependency(const Node_List* pack) const { +LoadNode::ControlDependency SuperWordVTransformBuilder::load_control_dependency(const Node_List* pack) { LoadNode::ControlDependency dep = LoadNode::DependsOnlyOnTest; for (uint i = 0; i < pack->size(); i++) { Node* n = pack->at(i); @@ -3165,7 +3167,7 @@ void PackSet::print() const { tty->print_cr("\nPackSet::print: %d packs", _packs.length()); for (int i = 0; i < _packs.length(); i++) { tty->print_cr(" Pack: %d", i); - Node_List* pack = _packs.at(i); + Node_List* pack = _packs.at(i); if (pack == nullptr) { tty->print_cr(" nullptr"); } else { @@ -3174,7 +3176,7 @@ void PackSet::print() const { } } -void PackSet::print_pack(Node_List* pack) { +void PackSet::print_pack(const Node_List* pack) { for (uint i = 0; i < pack->size(); i++) { tty->print(" %3d: ", i); pack->at(i)->dump(); @@ -3201,7 +3203,3 @@ void VLoopBody::print() const { bool SuperWord::same_origin_idx(Node* a, Node* b) const { return a != nullptr && b != nullptr && _clone_map.same_idx(a->_idx, b->_idx); } -bool SuperWord::same_generation(Node* a, Node* b) const { - return a != nullptr && b != nullptr && _clone_map.same_gen(a->_idx, b->_idx); -} - diff --git a/src/hotspot/share/opto/superword.hpp b/src/hotspot/share/opto/superword.hpp index 4e6fce70e5c4..5ffab37ff095 100644 --- a/src/hotspot/share/opto/superword.hpp +++ b/src/hotspot/share/opto/superword.hpp @@ -265,10 +265,10 @@ class SplitStatus { Split, // The pack was split into two packs. pack1 pack2 }; Kind _kind; - Node_List* _first_pack; - Node_List* _second_pack; + Node_List* _first_pack; + Node_List* _second_pack; - SplitStatus(Kind kind, Node_List* first_pack, Node_List* second_pack) : + SplitStatus(Kind kind, Node_List* first_pack, Node_List* second_pack) : _kind(kind), _first_pack(first_pack), _second_pack(second_pack) { assert(_kind != Unchanged || (first_pack != nullptr && second_pack == nullptr), "unchanged status conditions"); @@ -278,7 +278,7 @@ class SplitStatus { } public: - static SplitStatus make_unchanged(Node_List* old_pack) { + static SplitStatus make_unchanged(Node_List* old_pack) { return SplitStatus(Unchanged, old_pack, nullptr); } @@ -286,17 +286,17 @@ class SplitStatus { return SplitStatus(Rejected, nullptr, nullptr); } - static SplitStatus make_modified(Node_List* first_pack) { + static SplitStatus make_modified(Node_List* first_pack) { return SplitStatus(Modified, first_pack, nullptr); } - static SplitStatus make_split(Node_List* first_pack, Node_List* second_pack) { + static SplitStatus make_split(Node_List* first_pack, Node_List* second_pack) { return SplitStatus(Split, first_pack, second_pack); } bool is_unchanged() const { return _kind == Unchanged; } - Node_List* first_pack() const { return _first_pack; } - Node_List* second_pack() const { return _second_pack; } + Node_List* first_pack() const { return _first_pack; } + Node_List* second_pack() const { return _second_pack; } }; class PackSet : public StackObj { @@ -305,10 +305,10 @@ class PackSet : public StackObj { const VLoopBody& _body; // Set of all packs: - GrowableArray _packs; + GrowableArray*> _packs; // Mapping from nodes to their pack: bb_idx -> pack - GrowableArray _node_to_pack; + GrowableArray*> _node_to_pack; NOT_PRODUCT(const bool _trace_packset;) NOT_PRODUCT(const bool _trace_rejections;) @@ -329,15 +329,15 @@ class PackSet : public StackObj { // Accessors to iterate over packs. int length() const { return _packs.length(); } bool is_empty() const { return _packs.is_empty(); } - Node_List* at(int i) const { return _packs.at(i); } + Node_List* at(int i) const { return _packs.at(i); } private: - void map_node_in_pack(const Node* n, Node_List* new_pack) { + void map_node_in_pack(const Node* n, Node_List* new_pack) { assert(get_pack(n) == nullptr, "was previously unmapped"); _node_to_pack.at_put(_body.bb_idx(n), new_pack); } - void remap_node_in_pack(const Node* n, Node_List* new_pack) { + void remap_node_in_pack(const Node* n, Node_List* new_pack) { assert(get_pack(n) != nullptr && new_pack != nullptr && get_pack(n) != new_pack, "was previously mapped"); _node_to_pack.at_put(_body.bb_idx(n), new_pack); } @@ -347,15 +347,15 @@ class PackSet : public StackObj { _node_to_pack.at_put(_body.bb_idx(n), nullptr); } - void unmap_all_nodes_in_pack(Node_List* old_pack) { + void unmap_all_nodes_in_pack(Node_List* old_pack) { for (uint i = 0; i < old_pack->size(); i++) { unmap_node_in_pack(old_pack->at(i)); } } public: - Node_List* get_pack(const Node* n) const { return !_vloop.in_bb(n) ? nullptr : _node_to_pack.at(_body.bb_idx(n)); } + Node_List* get_pack(const Node* n) const { return !_vloop.in_bb(n) ? nullptr : _node_to_pack.at(_body.bb_idx(n)); } - void add_pack(Node_List* pack) { + void add_pack(Node_List* pack) { _packs.append(pack); for (uint i = 0; i < pack->size(); i++) { Node* n = pack->at(i); @@ -363,17 +363,17 @@ class PackSet : public StackObj { } } - Node_List* strided_pack_input_at_index_or_null(const Node_List* pack, const int index, const int stride, const int offset) const; - bool is_muladds2i_pack_with_pack_inputs(const Node_List* pack) const; - Node* same_inputs_at_index_or_null(const Node_List* pack, const int index) const; - VTransformBoolTest get_bool_test(const Node_List* bool_pack) const; + Node_List* strided_pack_input_at_index_or_null(const Node_List* pack, const int index, const int stride, const int offset) const; + bool is_muladds2i_pack_with_pack_inputs(const Node_List* pack) const; + static Node* same_inputs_at_index_or_null(const Node_List* pack, const int index); + VTransformBoolTest get_bool_test(const Node_List* bool_pack) const; - Node_List* pack_input_at_index_or_null(const Node_List* pack, const int index) const { + Node_List* pack_input_at_index_or_null(const Node_List* pack, const int index) const { return strided_pack_input_at_index_or_null(pack, index, 1, 0); } private: - SplitStatus split_pack(const char* split_name, Node_List* pack, SplitTask task); + SplitStatus split_pack(const char* split_name, Node_List* pack, SplitTask task); public: template void split_packs(const char* split_name, SplitStrategy strategy); @@ -391,7 +391,7 @@ class PackSet : public StackObj { public: DEBUG_ONLY(void verify() const;) NOT_PRODUCT(void print() const;) - NOT_PRODUCT(static void print_pack(Node_List* pack);) + NOT_PRODUCT(static void print_pack(const Node_List* pack);) }; // -----------------------------SuperWord--------------------------------- @@ -495,7 +495,7 @@ class SuperWord : public ResourceObj { return _vloop_analyzer.dependency_graph().independent(n1, n2); } - bool mutually_independent(const Node_List* nodes) const { + bool mutually_independent(const Node_List* nodes) const { return _vloop_analyzer.dependency_graph().mutually_independent(nodes); } @@ -545,7 +545,7 @@ class SuperWord : public ResourceObj { bool do_vector_loop() { return _do_vector_loop; } const PackSet& packset() const { return _packset; } - Node_List* get_pack(const Node* n) const { return _packset.get_pack(n); } + Node_List* get_pack(const Node* n) const { return _packset.get_pack(n); } private: bool _do_vector_loop; // whether to do vectorization/simd style @@ -555,7 +555,6 @@ class SuperWord : public ResourceObj { // CloneMap utilities bool same_origin_idx(Node* a, Node* b) const; - bool same_generation(Node* a, Node* b) const; private: bool SLP_extract(); @@ -628,19 +627,19 @@ class SuperWord : public ResourceObj { void filter_packs_for_power_of_2_size(); void filter_packs_for_mutual_independence(); void filter_packs_for_alignment(); - const AlignmentSolution* pack_alignment_solution(const Node_List* pack); + const AlignmentSolution* pack_alignment_solution(const Node_List* pack) const; void filter_packs_for_implemented(); void filter_packs_for_profitable(); DEBUG_ONLY(void verify_packs() const;) // Can code be generated for the pack, restricted to size nodes? - bool implemented(const Node_List* pack, const uint size) const; + bool implemented(const Node_List* pack, const uint size) const; // Find the maximal implemented size smaller or equal to the packs size - uint max_implemented_size(const Node_List* pack); + uint max_implemented_size(const Node_List* pack) const; // For pack p, are all operands and all uses (with in the block) vector? - bool profitable(const Node_List* p) const; + bool profitable(const Node_List* p) const; // Verify that all uses of packs are also packs, i.e. we do not need extract operations. DEBUG_ONLY(void verify_no_extract();) @@ -648,7 +647,7 @@ class SuperWord : public ResourceObj { // Check if n_super's pack uses are a superset of n_sub's pack uses. bool has_use_pack_superset(const Node* n1, const Node* n2) const; // Find a boundary in the pack, where left and right have different pack uses and defs. - uint find_use_def_boundary(const Node_List* pack) const; + uint find_use_def_boundary(const Node_List* pack) const; // Is use->in(u_idx) a vector use? bool is_vector_use(Node* use, int u_idx) const; diff --git a/src/hotspot/share/opto/superwordVTransformBuilder.cpp b/src/hotspot/share/opto/superwordVTransformBuilder.cpp index b42397177c34..4b185c0eb493 100644 --- a/src/hotspot/share/opto/superwordVTransformBuilder.cpp +++ b/src/hotspot/share/opto/superwordVTransformBuilder.cpp @@ -45,7 +45,7 @@ void SuperWordVTransformBuilder::build() { void SuperWordVTransformBuilder::build_vector_vtnodes_for_packed_nodes() { for (int i = 0; i < _packset.length(); i++) { - Node_List* pack = _packset.at(i); + Node_List* pack = _packset.at(i); VTransformVectorNode* vtn = make_vector_vtnode_for_pack(pack); for (uint k = 0; k < pack->size(); k++) { map_node_to_vtnode(pack->at(k), vtn); @@ -78,7 +78,7 @@ void SuperWordVTransformBuilder::build_scalar_vtnodes_for_non_packed_nodes() { void SuperWordVTransformBuilder::build_inputs_for_vector_vtnodes(VectorSet& vtn_memory_dependencies) { for (int i = 0; i < _packset.length(); i++) { - Node_List* pack = _packset.at(i); + Node_List* pack = _packset.at(i); Node* p0 = pack->at(0); VTransformVectorNode* vtn = get_vtnode(p0)->isa_Vector(); @@ -177,7 +177,7 @@ void SuperWordVTransformBuilder::build_uses_after_loop() { } // Create a vtnode for each pack. No in/out edges set yet. -VTransformVectorNode* SuperWordVTransformBuilder::make_vector_vtnode_for_pack(const Node_List* pack) const { +VTransformVectorNode* SuperWordVTransformBuilder::make_vector_vtnode_for_pack(const Node_List* pack) const { Node* p0 = pack->at(0); const VTransformVectorNodeProperties properties = VTransformVectorNodeProperties::make_from_pack(pack, _vloop_analyzer); const int sopc = properties.scalar_opcode(); @@ -249,10 +249,10 @@ void SuperWordVTransformBuilder::init_req_with_scalar(Node* n, VTransformNode* v // Either get the existing vtnode vector input (when input is a pack), or else make a // new vector vtnode for the input (e.g. for Replicate or PopulateIndex). -VTransformNode* SuperWordVTransformBuilder::get_or_make_vtnode_vector_input_at_index(const Node_List* pack, const int index) { +VTransformNode* SuperWordVTransformBuilder::get_or_make_vtnode_vector_input_at_index(const Node_List* pack, const int index) { Node* p0 = pack->at(0); - Node_List* pack_in = _packset.pack_input_at_index_or_null(pack, index); + Node_List* pack_in = _packset.pack_input_at_index_or_null(pack, index); if (pack_in != nullptr) { Node* in_p0 = pack_in->at(0); BasicType def_bt = _vloop_analyzer.types().velt_basic_type(in_p0); @@ -276,11 +276,11 @@ VTransformNode* SuperWordVTransformBuilder::get_or_make_vtnode_vector_input_at_i if (VectorNode::is_muladds2i(p0)) { assert(_packset.is_muladds2i_pack_with_pack_inputs(pack), "inputs must all be packs"); // All inputs are strided (stride = 2), either with offset 0 or 1. - Node_List* pack_in0 = _packset.strided_pack_input_at_index_or_null(pack, index, 2, 0); + Node_List* pack_in0 = _packset.strided_pack_input_at_index_or_null(pack, index, 2, 0); if (pack_in0 != nullptr) { return get_vtnode(pack_in0->at(0)); } - Node_List* pack_in1 = _packset.strided_pack_input_at_index_or_null(pack, index, 2, 1); + Node_List* pack_in1 = _packset.strided_pack_input_at_index_or_null(pack, index, 2, 1); if (pack_in1 != nullptr) { return get_vtnode(pack_in1->at(0)); } @@ -354,7 +354,7 @@ VTransformNode* SuperWordVTransformBuilder::get_vtnode_or_wrap_as_outer(Node* n) return vtn; } -void SuperWordVTransformBuilder::init_req_with_vector(const Node_List* pack, VTransformNode* vtn, int j) { +void SuperWordVTransformBuilder::init_req_with_vector(const Node_List* pack, VTransformNode* vtn, int j) { VTransformNode* req = get_or_make_vtnode_vector_input_at_index(pack, j); vtn->init_req(j, req); } @@ -368,7 +368,7 @@ void SuperWordVTransformBuilder::init_all_req_with_scalars(Node* n, VTransformNo } } -void SuperWordVTransformBuilder::init_all_req_with_vectors(const Node_List* pack, VTransformNode* vtn) { +void SuperWordVTransformBuilder::init_all_req_with_vectors(const Node_List* pack, VTransformNode* vtn) { Node* p0 = pack->at(0); assert(vtn->req() <= p0->req(), "must have at at most as many reqs"); // Vectors have no ctrl, so ignore it. diff --git a/src/hotspot/share/opto/superwordVTransformBuilder.hpp b/src/hotspot/share/opto/superwordVTransformBuilder.hpp index b3a47b5ba232..b1b39eecb7b4 100644 --- a/src/hotspot/share/opto/superwordVTransformBuilder.hpp +++ b/src/hotspot/share/opto/superwordVTransformBuilder.hpp @@ -75,15 +75,15 @@ class SuperWordVTransformBuilder : public StackObj { _idx_to_vtnode.put_when_absent(n->_idx, vtn); } - VTransformVectorNode* make_vector_vtnode_for_pack(const Node_List* pack) const; - VTransformNode* get_or_make_vtnode_vector_input_at_index(const Node_List* pack, const int index); + VTransformVectorNode* make_vector_vtnode_for_pack(const Node_List* pack) const; + VTransformNode* get_or_make_vtnode_vector_input_at_index(const Node_List* pack, const int index); VTransformNode* get_vtnode_or_wrap_as_outer(Node* n); void init_req_with_scalar(Node* n, VTransformNode* vtn, const int index); - void init_req_with_vector(const Node_List* pack, VTransformNode* vtn, const int index); + void init_req_with_vector(const Node_List* pack, VTransformNode* vtn, const int index); void init_all_req_with_scalars(Node* n, VTransformNode* vtn); - void init_all_req_with_vectors(const Node_List* pack, VTransformNode* vtn); + void init_all_req_with_vectors(const Node_List* pack, VTransformNode* vtn); void add_memory_dependencies_of_node_to_vtnode(Node* n, VTransformNode* vtn, VectorSet& vtn_memory_dependencies); - LoadNode::ControlDependency load_control_dependency(const Node_List* pack) const; + static LoadNode::ControlDependency load_control_dependency(const Node_List* pack); }; #endif // SHARE_OPTO_SUPERWORDVTRANSFORMBUILDER_HPP diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index 384e126bf67e..f30910273a24 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -230,33 +230,6 @@ BasicType Type::array_element_basic_type() const { return bt; } -// For two instance arrays of same dimension, return the base element types. -// Otherwise or if the arrays have different dimensions, return null. -void Type::get_arrays_base_elements(const Type *a1, const Type *a2, - const TypeInstPtr **e1, const TypeInstPtr **e2) { - - if (e1) *e1 = nullptr; - if (e2) *e2 = nullptr; - const TypeAryPtr* a1tap = (a1 == nullptr) ? nullptr : a1->isa_aryptr(); - const TypeAryPtr* a2tap = (a2 == nullptr) ? nullptr : a2->isa_aryptr(); - - if (a1tap != nullptr && a2tap != nullptr) { - // Handle multidimensional arrays - const TypePtr* a1tp = a1tap->elem()->make_ptr(); - const TypePtr* a2tp = a2tap->elem()->make_ptr(); - while (a1tp && a1tp->isa_aryptr() && a2tp && a2tp->isa_aryptr()) { - a1tap = a1tp->is_aryptr(); - a2tap = a2tp->is_aryptr(); - a1tp = a1tap->elem()->make_ptr(); - a2tp = a2tap->elem()->make_ptr(); - } - if (a1tp && a1tp->isa_instptr() && a2tp && a2tp->isa_instptr()) { - if (e1) *e1 = a1tp->is_instptr(); - if (e2) *e2 = a2tp->is_instptr(); - } - } -} - //---------------------------get_typeflow_type--------------------------------- // Import a type produced by ciTypeFlow. const Type* Type::get_typeflow_type(ciType* type) { @@ -614,30 +587,34 @@ void Type::Initialize_shared(Compile* current) { TypeLong::INT = TypeLong::make((jlong)min_jint, (jlong)max_jint,WidenMin); TypeLong::TYPE_DOMAIN = TypeLong::LONG; - const Type **fboth =(const Type**)shared_type_arena->AmallocWords(2*sizeof(Type*)); + const Type** fcontrol = (const Type**)shared_type_arena->AmallocWords(1*sizeof(Type*)); + fcontrol[0] = Type::CONTROL; + TypeTuple::CONTROL = TypeTuple::make(1, fcontrol); + + const Type** fboth = (const Type**)shared_type_arena->AmallocWords(2*sizeof(Type*)); fboth[0] = Type::CONTROL; fboth[1] = Type::CONTROL; - TypeTuple::IFBOTH = TypeTuple::make( 2, fboth ); + TypeTuple::IFBOTH = TypeTuple::make(2, fboth); - const Type **ffalse =(const Type**)shared_type_arena->AmallocWords(2*sizeof(Type*)); + const Type** ffalse = (const Type**)shared_type_arena->AmallocWords(2*sizeof(Type*)); ffalse[0] = Type::CONTROL; ffalse[1] = Type::TOP; - TypeTuple::IFFALSE = TypeTuple::make( 2, ffalse ); + TypeTuple::IFFALSE = TypeTuple::make(2, ffalse); - const Type **fneither =(const Type**)shared_type_arena->AmallocWords(2*sizeof(Type*)); + const Type** fneither = (const Type**)shared_type_arena->AmallocWords(2*sizeof(Type*)); fneither[0] = Type::TOP; fneither[1] = Type::TOP; - TypeTuple::IFNEITHER = TypeTuple::make( 2, fneither ); + TypeTuple::IFNEITHER = TypeTuple::make(2, fneither); - const Type **ftrue =(const Type**)shared_type_arena->AmallocWords(2*sizeof(Type*)); + const Type** ftrue = (const Type**)shared_type_arena->AmallocWords(2*sizeof(Type*)); ftrue[0] = Type::TOP; ftrue[1] = Type::CONTROL; - TypeTuple::IFTRUE = TypeTuple::make( 2, ftrue ); + TypeTuple::IFTRUE = TypeTuple::make(2, ftrue); - const Type **floop =(const Type**)shared_type_arena->AmallocWords(2*sizeof(Type*)); + const Type** floop = (const Type**)shared_type_arena->AmallocWords(2*sizeof(Type*)); floop[0] = Type::CONTROL; floop[1] = TypeInt::INT; - TypeTuple::LOOPBODY = TypeTuple::make( 2, floop ); + TypeTuple::LOOPBODY = TypeTuple::make(2, floop); TypePtr::NULL_PTR= TypePtr::make(AnyPtr, TypePtr::Null, Offset(0)); TypePtr::NOTNULL = TypePtr::make(AnyPtr, TypePtr::NotNull, Offset::bottom); @@ -646,10 +623,10 @@ void Type::Initialize_shared(Compile* current) { TypeRawPtr::BOTTOM = TypeRawPtr::make( TypePtr::BotPTR ); TypeRawPtr::NOTNULL= TypeRawPtr::make( TypePtr::NotNull ); - const Type **fmembar = TypeTuple::fields(0); + const Type** fmembar = TypeTuple::fields(0); TypeTuple::MEMBAR = TypeTuple::make(TypeFunc::Parms+0, fmembar); - const Type **fsc = (const Type**)shared_type_arena->AmallocWords(2*sizeof(Type*)); + const Type** fsc = (const Type**)shared_type_arena->AmallocWords(2*sizeof(Type*)); fsc[0] = TypeInt::CC; fsc[1] = Type::MEMORY; TypeTuple::STORECONDITIONAL = TypeTuple::make(2, fsc); @@ -2241,18 +2218,19 @@ void TypeLong::dump_verbose() const { //============================================================================= // Convenience common pre-built types. -const TypeTuple *TypeTuple::IFBOTH; // Return both arms of IF as reachable -const TypeTuple *TypeTuple::IFFALSE; -const TypeTuple *TypeTuple::IFTRUE; -const TypeTuple *TypeTuple::IFNEITHER; -const TypeTuple *TypeTuple::LOOPBODY; -const TypeTuple *TypeTuple::MEMBAR; -const TypeTuple *TypeTuple::STORECONDITIONAL; -const TypeTuple *TypeTuple::START_I2C; -const TypeTuple *TypeTuple::INT_PAIR; -const TypeTuple *TypeTuple::LONG_PAIR; -const TypeTuple *TypeTuple::INT_CC_PAIR; -const TypeTuple *TypeTuple::LONG_CC_PAIR; +const TypeTuple* TypeTuple::CONTROL; +const TypeTuple* TypeTuple::IFBOTH; // Return both arms of IF as reachable +const TypeTuple* TypeTuple::IFFALSE; +const TypeTuple* TypeTuple::IFTRUE; +const TypeTuple* TypeTuple::IFNEITHER; +const TypeTuple* TypeTuple::LOOPBODY; +const TypeTuple* TypeTuple::MEMBAR; +const TypeTuple* TypeTuple::STORECONDITIONAL; +const TypeTuple* TypeTuple::START_I2C; +const TypeTuple* TypeTuple::INT_PAIR; +const TypeTuple* TypeTuple::LONG_PAIR; +const TypeTuple* TypeTuple::INT_CC_PAIR; +const TypeTuple* TypeTuple::LONG_CC_PAIR; static void collect_value_fields(ciValueKlass* vk, const Type** field_array, uint& pos) { for (int i = 0; i < vk->nof_declared_nonstatic_fields(); i++) { diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 9c347492e192..ce4bf5bb38a6 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -430,11 +430,6 @@ class Type { return _const_basic_type[type]; } - // For two instance arrays of same dimension, return the base element types. - // Otherwise or if the arrays have different dimensions, return null. - static void get_arrays_base_elements(const Type *a1, const Type *a2, - const TypeInstPtr **e1, const TypeInstPtr **e2); - // Mapping to the array element's basic type. BasicType array_element_basic_type() const; @@ -990,18 +985,19 @@ class TypeTuple : public Type { 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; - static const TypeTuple *IFTRUE; - static const TypeTuple *IFNEITHER; - static const TypeTuple *LOOPBODY; - static const TypeTuple *MEMBAR; - static const TypeTuple *STORECONDITIONAL; - static const TypeTuple *START_I2C; - static const TypeTuple *INT_PAIR; - static const TypeTuple *LONG_PAIR; - static const TypeTuple *INT_CC_PAIR; - static const TypeTuple *LONG_CC_PAIR; + static const TypeTuple* CONTROL; + static const TypeTuple* IFBOTH; + static const TypeTuple* IFFALSE; + static const TypeTuple* IFTRUE; + static const TypeTuple* IFNEITHER; + static const TypeTuple* LOOPBODY; + static const TypeTuple* MEMBAR; + static const TypeTuple* STORECONDITIONAL; + static const TypeTuple* START_I2C; + static const TypeTuple* INT_PAIR; + static const TypeTuple* LONG_PAIR; + static const TypeTuple* INT_CC_PAIR; + static const TypeTuple* LONG_CC_PAIR; #ifndef PRODUCT virtual void dump2( Dict &d, uint, outputStream *st ) const; // Specialized per-Type dumping #endif @@ -2097,7 +2093,6 @@ class TypeAryKlassPtr : public TypeKlassPtr { static const TypeAryKlassPtr* make(ciKlass* klass, InterfaceHandling interface_handling); const TypeAryKlassPtr* cast_to_non_refined() const; - const TypeAryKlassPtr* cast_to_default_refined() const; const TypeAryKlassPtr* cast_to_refined_array_klass_ptr(bool refined = true) const; const Type *elem() const { return _elem; } diff --git a/src/hotspot/share/opto/valuetypenode.cpp b/src/hotspot/share/opto/valuetypenode.cpp index 15494a1cca01..a8312111aed2 100644 --- a/src/hotspot/share/opto/valuetypenode.cpp +++ b/src/hotspot/share/opto/valuetypenode.cpp @@ -254,10 +254,6 @@ void ValueTypeNode::set_field_value(uint index, Node* value) { set_req(Values + index, value); } -void ValueTypeNode::set_field_value_by_offset(int offset, Node* value) { - set_field_value(field_index(offset), value); -} - uint ValueTypeNode::field_index(int offset) const { uint i = 0; for (; i < field_count() && field(i)->offset_in_bytes() != offset; i++) { } @@ -290,7 +286,7 @@ static uint add_top_to_safepoint(ciValueKlass* vk, SafePointNode* sfpt) { return cnt; } -uint ValueTypeNode::add_fields_to_safepoint(Unique_Node_List& worklist, SafePointNode* sfpt) const { +uint ValueTypeNode::add_fields_to_safepoint(Unique_Node_List& worklist, SafePointNode* sfpt) const { uint cnt = 0; for (uint i = 0; i < field_count(); ++i) { Node* value = field_value(i); @@ -319,7 +315,7 @@ uint ValueTypeNode::add_fields_to_safepoint(Unique_Node_List& worklist, SafePoin } if (value->is_ValueType()) { // Add value type to the worklist to process later - worklist.push(value); + worklist.push(value->as_ValueType()); } sfpt->add_req(value); cnt++; @@ -327,7 +323,7 @@ uint ValueTypeNode::add_fields_to_safepoint(Unique_Node_List& worklist, SafePoin return cnt; } -void ValueTypeNode::make_scalar_in_safepoint(PhaseIterGVN* igvn, Unique_Node_List& worklist, SafePointNode* sfpt) const { +void ValueTypeNode::make_scalar_in_safepoint(PhaseIterGVN* igvn, Unique_Node_List& worklist, SafePointNode* sfpt) const { JVMState* jvms = sfpt->jvms(); assert(jvms != nullptr, "missing JVMS"); uint first_ind = (sfpt->req() - jvms->scloff()); @@ -365,17 +361,17 @@ bool ValueTypeNode::make_scalar_in_safepoints(PhaseIterGVN* igvn, bool allow_oop Node* oop = get_oop(); bool use_oop = false; if (allow_oop && is_allocated(igvn) && oop->is_Phi()) { - Unique_Node_List worklist; + Unique_Node_List worklist; VectorSet visited; visited.set(oop->_idx); - worklist.push(oop); + worklist.push(oop->as_Phi()); use_oop = true; while (worklist.size() > 0 && use_oop) { - Node* n = worklist.pop(); + const PhiNode* n = worklist.pop(); for (uint i = 1; i < n->req(); i++) { Node* in = n->in(i); if (in->is_Phi() && !visited.test_set(in->_idx)) { - worklist.push(in); + worklist.push(in->as_Phi()); } else if (!(in->is_Con() || in->is_Parm())) { use_oop = false; break; @@ -388,17 +384,17 @@ bool ValueTypeNode::make_scalar_in_safepoints(PhaseIterGVN* igvn, bool allow_oop } ResourceMark rm; - Unique_Node_List safepoints; + Unique_Node_List safepoints; if (safepoint == nullptr) { // Gathering all SafePoint users of `this`... - Unique_Node_List worklist; + Unique_Node_List worklist; worklist.push(this); while (worklist.size() > 0) { - Node* n = worklist.pop(); + const Node* n = worklist.pop(); for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { Node* use = n->fast_out(i); if (use->is_SafePoint() && !use->is_CallLeaf() && (!use->is_Call() || use->as_Call()->has_debug_use(n))) { - safepoints.push(use); + safepoints.push(use->as_SafePoint()); } else if (use->is_ConstraintCast()) { worklist.push(use); } @@ -415,9 +411,9 @@ bool ValueTypeNode::make_scalar_in_safepoints(PhaseIterGVN* igvn, bool allow_oop if ((C->live_nodes() + safepoints.size() + NodeLimitFudgeFactor) > C->max_node_limit()) { return false; } - Unique_Node_List vt_worklist; + Unique_Node_List vt_worklist; while (safepoints.size() > 0) { - SafePointNode* sfpt = safepoints.pop()->as_SafePoint(); + SafePointNode* sfpt = safepoints.pop(); if (use_oop) { for (uint i = sfpt->jvms()->debug_start(); i < sfpt->jvms()->debug_end(); i++) { Node* debug = sfpt->in(i); @@ -432,7 +428,7 @@ bool ValueTypeNode::make_scalar_in_safepoints(PhaseIterGVN* igvn, bool allow_oop } // Now scalarize non-flat fields for (uint i = 0; i < vt_worklist.size(); ++i) { - ValueTypeNode* vt = vt_worklist.at(i)->isa_ValueType(); + ValueTypeNode* vt = vt_worklist.at(i); if (!vt->make_scalar_in_safepoints(igvn)) { return false; } diff --git a/src/hotspot/share/opto/valuetypenode.hpp b/src/hotspot/share/opto/valuetypenode.hpp index c53fbba3bb7c..2f3d6278a3c7 100644 --- a/src/hotspot/share/opto/valuetypenode.hpp +++ b/src/hotspot/share/opto/valuetypenode.hpp @@ -60,8 +60,8 @@ class ValueTypeNode : public TypeNode { // Get the klass defining the field layout of the value type ciValueKlass* value_klass() const { return type()->value_klass(); } - void make_scalar_in_safepoint(PhaseIterGVN* igvn, Unique_Node_List& worklist, SafePointNode* sfpt) const; - uint add_fields_to_safepoint(Unique_Node_List& worklist, SafePointNode* sfpt) const; + void make_scalar_in_safepoint(PhaseIterGVN* igvn, Unique_Node_List& worklist, SafePointNode* sfpt) const; + uint add_fields_to_safepoint(Unique_Node_List& worklist, SafePointNode* sfpt) const; // Checks if the value type is loaded from memory and if so returns the oop Node* is_loaded(PhaseGVN* phase, ciValueKlass* vk = nullptr, Node* base = nullptr, int holder_offset = 0) const; @@ -119,7 +119,6 @@ class ValueTypeNode : public TypeNode { Node* field_value(uint index) const; Node* field_value_by_offset(int offset, bool recursive) const; void set_field_value(uint index, Node* value); - void set_field_value_by_offset(int offset, Node* value); uint field_index(int offset) const; // Replace ValueTypeNodes in debug info at safepoints with SafePointScalarObjectNodes diff --git a/src/hotspot/share/opto/vector.cpp b/src/hotspot/share/opto/vector.cpp index 7853a2919cec..1df70a236f4d 100644 --- a/src/hotspot/share/opto/vector.cpp +++ b/src/hotspot/share/opto/vector.cpp @@ -176,14 +176,14 @@ static JVMState* clone_jvms(Compile* C, SafePointNode* sfpt) { return new_jvms; } -void PhaseVector::scalarize_vbox_node(VectorBoxNode* vec_box) { +void PhaseVector::scalarize_vbox_node(VectorBoxNode* vec_box) const { Node* vec_value = vec_box->in(VectorBoxNode::Value); PhaseGVN& gvn = *C->initial_gvn(); // Process merged VBAs if (EnableVectorAggressiveReboxing) { - Unique_Node_List calls; + Unique_Node_List calls; for (DUIterator_Fast imax, i = vec_box->fast_outs(imax); i < imax; i++) { Node* use = vec_box->fast_out(i); if (use->is_CallJava()) { @@ -195,7 +195,7 @@ void PhaseVector::scalarize_vbox_node(VectorBoxNode* vec_box) { } while (calls.size() > 0) { - CallJavaNode* call = calls.pop()->as_CallJava(); + CallJavaNode* call = calls.pop(); // Attach new VBA to the call and use it instead of Phi (VBA ... VBA). JVMState* jvms = clone_jvms(C, call); @@ -253,12 +253,12 @@ void PhaseVector::scalarize_vbox_node(VectorBoxNode* vec_box) { } // Process debug uses at safepoints - Unique_Node_List safepoints; + Unique_Node_List safepoints; - Unique_Node_List worklist; + Unique_Node_List worklist; worklist.push(vec_box); while (worklist.size() > 0) { - Node* n = worklist.pop(); + const Node* n = worklist.pop(); for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { Node* use = n->fast_out(i); if (use->is_SafePoint()) { @@ -288,7 +288,7 @@ void PhaseVector::scalarize_vbox_node(VectorBoxNode* vec_box) { } while (safepoints.size() > 0) { - SafePointNode* sfpt = safepoints.pop()->as_SafePoint(); + SafePointNode* sfpt = safepoints.pop(); uint first_ind = (sfpt->req() - sfpt->jvms()->scloff()); Node* sobj = new SafePointScalarObjectNode(vec_box->box_type(), vec_box, first_ind, sfpt->jvms()->depth(), n_fields); diff --git a/src/hotspot/share/opto/vector.hpp b/src/hotspot/share/opto/vector.hpp index e76b4df376d8..b0f1ce7337f6 100644 --- a/src/hotspot/share/opto/vector.hpp +++ b/src/hotspot/share/opto/vector.hpp @@ -46,14 +46,12 @@ class PhaseVector : public Phase { const TypeInstPtr* box_type, const TypeVect* vect_type); void scalarize_vbox_nodes(); - void scalarize_vbox_node(VectorBoxNode* vec_box); + void scalarize_vbox_node(VectorBoxNode* vec_box) const; void expand_vunbox_nodes(); void expand_vunbox_node(VectorUnboxNode* vec_box); void eliminate_vbox_alloc_nodes(); void eliminate_vbox_alloc_node(VectorBoxAllocateNode* vbox_alloc); void do_cleanup(); - void scalarize_vector_boxes(); - void expand_vector_boxes(); public: PhaseVector(PhaseIterGVN& igvn) : Phase(Vector), _igvn(igvn) {} diff --git a/src/hotspot/share/opto/vectorization.cpp b/src/hotspot/share/opto/vectorization.cpp index 0fe022615300..c8bfc3687af5 100644 --- a/src/hotspot/share/opto/vectorization.cpp +++ b/src/hotspot/share/opto/vectorization.cpp @@ -395,7 +395,7 @@ void VLoopDependencyGraph::construct() { void VLoopDependencyGraph::add_node(MemNode* n, GrowableArray& strong_memory_edges, GrowableArray& weak_memory_edges) { assert(_dependency_nodes.at_grow(_body.bb_idx(n), nullptr) == nullptr, "not yet created"); - DependencyNode* dn = new (_arena) DependencyNode(n, strong_memory_edges, weak_memory_edges, _arena); + DependencyNode* dn = new (_arena) DependencyNode(strong_memory_edges, weak_memory_edges, _arena); _dependency_nodes.at_put_grow(_body.bb_idx(n), dn, nullptr); } @@ -482,11 +482,9 @@ void VLoopDependencyGraph::print() const { } #endif -VLoopDependencyGraph::DependencyNode::DependencyNode(MemNode* n, - GrowableArray& strong_memory_edges, +VLoopDependencyGraph::DependencyNode::DependencyNode(GrowableArray& strong_memory_edges, GrowableArray& weak_memory_edges, Arena* arena) : - _node(n), _num_strong_memory_edges(strong_memory_edges.length()), _num_weak_memory_edges(weak_memory_edges.length()), _memory_edges(nullptr) diff --git a/src/hotspot/share/opto/vectorization.hpp b/src/hotspot/share/opto/vectorization.hpp index b187435c04d9..d7c0abd9cc20 100644 --- a/src/hotspot/share/opto/vectorization.hpp +++ b/src/hotspot/share/opto/vectorization.hpp @@ -568,7 +568,6 @@ class VLoopTypes : public StackObj { NONCOPYABLE(VLoopTypes); void compute_vector_element_type(); - NOT_PRODUCT( void print() const; ) const Type* velt_type(const Node* n) const { assert(_vloop.in_bb(n), "only call on nodes in loop"); @@ -741,7 +740,7 @@ class VLoopDependencyGraph : public StackObj { void construct(); bool independent(Node* s1, Node* s2) const; - bool mutually_independent(const Node_List* nodes) const; + bool mutually_independent(const Node_List* nodes) const; private: void add_node(MemNode* n, GrowableArray& strong_memory_edges, GrowableArray& weak_memory_edges); @@ -757,12 +756,11 @@ class VLoopDependencyGraph : public StackObj { class DependencyNode : public ArenaObj { private: - MemNode* _node; // Corresponding ideal node const uint _num_strong_memory_edges; const uint _num_weak_memory_edges; int* _memory_edges; // memory pred-edges, mapping to bb_idx public: - DependencyNode(MemNode* n, GrowableArray& strong_memory_edges, GrowableArray& weak_memory_edges, Arena* arena); + DependencyNode(GrowableArray& strong_memory_edges, GrowableArray& weak_memory_edges, Arena* arena); NONCOPYABLE(DependencyNode); uint num_strong_memory_edges() const { return _num_strong_memory_edges; } uint num_weak_memory_edges() const { return _num_weak_memory_edges; } diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index d90b75e7579b..7c3d7d70ef81 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -1188,7 +1188,7 @@ static bool is_commutative_vector_operation(int opcode) { } } -static bool is_associative_and_commutative_vector_operation(int opcode) { +bool VectorNode::is_associative_and_commutative_vector_operation(int opcode) { switch (opcode) { case Op_AddVB: case Op_AddVS: @@ -2427,7 +2427,7 @@ Node* VectorMaskOpNode::Ideal(PhaseGVN* phase, bool can_reshape) { if (n != nullptr) { return n; } - return TypeNode::Ideal(phase, can_reshape); + return nullptr; } Node* VectorMaskCastNode::Identity(PhaseGVN* phase) { diff --git a/src/hotspot/share/opto/vectornode.hpp b/src/hotspot/share/opto/vectornode.hpp index b5db43326539..3e5bc776af54 100644 --- a/src/hotspot/share/opto/vectornode.hpp +++ b/src/hotspot/share/opto/vectornode.hpp @@ -146,6 +146,7 @@ class VectorNode : public TypeNode { static bool is_minmax_opcode(int opc); bool should_swap_inputs_to_help_global_value_numbering(); + static bool is_associative_and_commutative_vector_operation(int opcode); Node* reassociate_vector_operation(PhaseGVN* phase); static Node* create_reassociated_node(Node* parent, Node* child, Node* cinput1, Node* cinput2, Node* pinput2, PhaseGVN* phase); @@ -1452,11 +1453,10 @@ class VectorMaskGenNode : public TypeNode { // be either "BVectMask" or "PVectMask" depending on the platform. class VectorMaskOpNode : public TypeNode { private: - int _mopc; const TypeVect* _vect_type; public: - VectorMaskOpNode(Node* mask, const Type* ty, int mopc): - TypeNode(ty, 2), _mopc(mopc), _vect_type(mask->bottom_type()->is_vect()) { + VectorMaskOpNode(Node* mask, const Type* ty): + TypeNode(ty, 2), _vect_type(mask->bottom_type()->is_vect()) { assert(Matcher::has_predicated_vectors() || _vect_type->element_basic_type() == T_BOOLEAN, ""); init_req(1, mask); } @@ -1466,7 +1466,6 @@ class VectorMaskOpNode : public TypeNode { virtual uint size_of() const { return sizeof(VectorMaskOpNode); } virtual uint ideal_reg() const { return Op_RegI; } virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); - int get_mask_Opcode() const { return _mopc;} static Node* make(Node* mask, const Type* ty, int mopc); }; @@ -1474,7 +1473,7 @@ class VectorMaskOpNode : public TypeNode { class VectorMaskTrueCountNode : public VectorMaskOpNode { public: VectorMaskTrueCountNode(Node* mask, const Type* ty): - VectorMaskOpNode(mask, ty, Op_VectorMaskTrueCount) {} + VectorMaskOpNode(mask, ty) {} virtual int Opcode() const; }; @@ -1483,7 +1482,7 @@ class VectorMaskTrueCountNode : public VectorMaskOpNode { class VectorMaskFirstTrueNode : public VectorMaskOpNode { public: VectorMaskFirstTrueNode(Node* mask, const Type* ty): - VectorMaskOpNode(mask, ty, Op_VectorMaskFirstTrue) {} + VectorMaskOpNode(mask, ty) {} virtual int Opcode() const; }; @@ -1492,7 +1491,7 @@ class VectorMaskFirstTrueNode : public VectorMaskOpNode { class VectorMaskLastTrueNode : public VectorMaskOpNode { public: VectorMaskLastTrueNode(Node* mask, const Type* ty): - VectorMaskOpNode(mask, ty, Op_VectorMaskLastTrue) {} + VectorMaskOpNode(mask, ty) {} virtual int Opcode() const; }; @@ -1503,7 +1502,7 @@ class VectorMaskLastTrueNode : public VectorMaskOpNode { class VectorMaskToLongNode : public VectorMaskOpNode { public: VectorMaskToLongNode(Node* mask, const Type* ty): - VectorMaskOpNode(mask, ty, Op_VectorMaskToLong) {} + VectorMaskOpNode(mask, ty) {} virtual int Opcode() const; Node* Ideal(PhaseGVN* phase, bool can_reshape); Node* Ideal_MaskAll(PhaseGVN* phase); @@ -2187,7 +2186,6 @@ class VectorBoxNode : public Node { VectorBoxNode(Compile* C, Node* box, Node* val, const TypeInstPtr* box_type, const TypeVect* vt) : Node(nullptr, box, val), _box_type(box_type), _vec_type(vt) { - init_flags(Flag_is_macro); C->add_macro_node(this); } @@ -2208,7 +2206,6 @@ class VectorBoxAllocateNode : public CallStaticJavaNode { public: VectorBoxAllocateNode(Compile* C, const TypeInstPtr* vbox_type) : CallStaticJavaNode(C, VectorBoxNode::vec_box_type(vbox_type), nullptr, nullptr) { - init_flags(Flag_is_macro); C->add_macro_node(this); } @@ -2227,7 +2224,6 @@ class VectorUnboxNode : public VectorNode { : VectorNode(mem, obj, vec_type) { init_req(0, ctrl); init_class_id(Class_VectorUnbox); - init_flags(Flag_is_macro); C->add_macro_node(this); } diff --git a/src/hotspot/share/opto/vtransform.hpp b/src/hotspot/share/opto/vtransform.hpp index 2c535eca6d1d..d28330f3ec57 100644 --- a/src/hotspot/share/opto/vtransform.hpp +++ b/src/hotspot/share/opto/vtransform.hpp @@ -787,7 +787,7 @@ class VTransformVectorNodeProperties : public StackObj { _element_basic_type(element_basic_type) {} public: - static VTransformVectorNodeProperties make_from_pack(const Node_List* pack, const VLoopAnalyzer& vloop_analyzer) { + static VTransformVectorNodeProperties make_from_pack(const Node_List* pack, const VLoopAnalyzer& vloop_analyzer) { Node* first = pack->at(0); int opc = first->Opcode(); int vlen = pack->size(); @@ -814,7 +814,6 @@ class VTransformVectorNode : public VTransformNode { VTransformNode(vtransform, req), _properties(properties) {} virtual VTransformVectorNode* isa_Vector() override { return this; } - void register_new_node_from_vectorization_and_replace_scalar_nodes(VTransformApplyState& apply_state, Node* vn) const; NOT_PRODUCT(virtual void print_spec() const override;) protected: @@ -956,7 +955,6 @@ class VTransformLoadVectorNode : public VTransformMemVectorNode { const TypePtr* adr_type, const LoadNode::ControlDependency control_dependency) : VTransformMemVectorNode(vtransform, 3, properties, vpointer, adr_type), _control_dependency(control_dependency) {} - LoadNode::ControlDependency control_dependency() const; virtual VTransformLoadVectorNode* isa_LoadVector() override { return this; } virtual bool is_load_in_loop() const override { return true; } virtual float cost(const VLoopAnalyzer& vloop_analyzer) const override; diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index c15c48042f6e..31fa693b9d07 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -113,7 +113,7 @@ #include "utilities/utf8.hpp" #include "utilities/zipLibrary.hpp" #if INCLUDE_CDS -#include "classfile/systemDictionaryShared.hpp" +#include "cds/aotSystemDictionary.hpp" #endif #if INCLUDE_JFR #include "jfr/jfr.hpp" @@ -1059,7 +1059,7 @@ JVM_ENTRY(jclass, JVM_FindClassFromClass(JNIEnv *env, const char *name, #if INCLUDE_CDS if (CDSConfig::is_preserving_verification_constraints() && from_class->is_instance_klass()) { InstanceKlass* ik = InstanceKlass::cast(from_class); - SystemDictionaryShared::add_old_verification_constraint(THREAD, ik, h_name); + AOTSystemDictionary::add_old_verification_constraint(THREAD, ik, h_name); } #endif @@ -1301,7 +1301,7 @@ JVM_ENTRY(jclass, JVM_FindLoadedClass(JNIEnv *env, jobject loader, jstring name) if (k == nullptr) { // If the class is not already loaded, try to see if it's in the shared // archive for the current classloader (h_loader). - k = SystemDictionaryShared::find_or_load_shared_class(klass_name, h_loader, CHECK_NULL); + k = AOTSystemDictionary::find_or_load_shared_class(klass_name, h_loader, CHECK_NULL); } #endif return (k == nullptr) ? nullptr : diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp index f4b0383a1b90..86427c291e4c 100644 --- a/src/hotspot/share/runtime/javaThread.cpp +++ b/src/hotspot/share/runtime/javaThread.cpp @@ -314,6 +314,7 @@ JavaThread::JavaThread(MemTag mem_tag) : Thread(mem_tag), // Initialize fields _on_thread_list(false), + _smr_delete_is_safe(false), DEBUG_ONLY(_java_call_counter(0) COMMA) _entry_point(nullptr), _deopt_mark(nullptr), diff --git a/src/hotspot/share/runtime/javaThread.hpp b/src/hotspot/share/runtime/javaThread.hpp index f3e3617a40b9..79bca8bbbc3f 100644 --- a/src/hotspot/share/runtime/javaThread.hpp +++ b/src/hotspot/share/runtime/javaThread.hpp @@ -96,6 +96,12 @@ class JavaThread: public Thread { private: bool _on_thread_list; // Is set when this JavaThread is added to the Threads list + // When ThreadsSMRSupport::free_list() is called by ThreadsSMRSupport::remove_thread(), + // it may set this flag to indicate that this JavaThread is no longer protected by a + // ThreadsListHandle. This avoids a costly re-scan of hazard pointers in + // ThreadsSMRSupport::wait_until_not_protected(). + bool _smr_delete_is_safe; + // All references to Java objects managed via OopHandles. These // have to be released by the ServiceThread after the JavaThread has // terminated - see add_oop_handles_for_release(). @@ -577,6 +583,8 @@ class JavaThread: public Thread { // JavaThread termination and lifecycle support: void smr_delete(); + bool smr_delete_is_safe() const { return _smr_delete_is_safe; } + void set_smr_delete_is_safe() { _smr_delete_is_safe = true; } bool on_thread_list() const { return _on_thread_list; } void set_on_thread_list() { _on_thread_list = true; } diff --git a/src/hotspot/share/runtime/mountUnmountDisabler.cpp b/src/hotspot/share/runtime/mountUnmountDisabler.cpp index 277a841a88c7..58eec72020e5 100644 --- a/src/hotspot/share/runtime/mountUnmountDisabler.cpp +++ b/src/hotspot/share/runtime/mountUnmountDisabler.cpp @@ -27,10 +27,12 @@ #include "prims/jvmtiExport.hpp" #include "prims/jvmtiThreadState.inline.hpp" #include "runtime/handles.inline.hpp" +#include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaThread.hpp" #include "runtime/jniHandles.hpp" #include "runtime/mountUnmountDisabler.hpp" #include "runtime/threadSMR.hpp" +#include "utilities/spinYield.hpp" volatile int MountUnmountDisabler::_global_vthread_transition_disable_count = 0; volatile int MountUnmountDisabler::_active_disablers = 0; @@ -93,7 +95,7 @@ class JVMTIEndTransition : public StackObj { if (!_is_mount && _current->is_carrier_thread_suspended()) { MonitorLocker ml(VThreadTransition_lock); while (_current->is_carrier_thread_suspended()) { - ml.wait(200); + ml.wait(); } } @@ -152,7 +154,7 @@ void MountUnmountDisabler::start_transition(JavaThread* current, oop vthread, bo // Block while transitions are disabled MonitorLocker ml(VThreadTransition_lock); while (is_start_transition_disabled(current, vth())) { - ml.wait(200); + ml.wait(); } } @@ -273,19 +275,25 @@ MountUnmountDisabler::~MountUnmountDisabler() { // disable transitions for one virtual thread void MountUnmountDisabler::disable_transition_for_one() { - MonitorLocker ml(VThreadTransition_lock); - while (exclusive_operation_ongoing()) { - ml.wait(10); - } + JavaThread* current = JavaThread::current(); - inc_active_disablers(); - java_lang_Thread::inc_vthread_transition_disable_count(_vthread()); + { + MonitorLocker ml(VThreadTransition_lock); + while (exclusive_operation_ongoing()) { + ml.wait(); + } + inc_active_disablers(); + java_lang_Thread::inc_vthread_transition_disable_count(_vthread()); + } // Prevent load of transition flag from floating up. OrderAccess::storeload(); + SpinYield spy; while (java_lang_Thread::is_in_vthread_transition(_vthread())) { - ml.wait(10); // wait while the virtual thread is in transition + // Wait while the virtual thread is in transition + ThreadBlockInVM tbivm(current); + spy.wait(); } // Start of the critical section. If the target is unmounted, we need an acquire @@ -295,37 +303,40 @@ MountUnmountDisabler::disable_transition_for_one() { // carrierThread to float up. // This pairs with the release barrier in end_transition(). OrderAccess::acquire(); - JVMTI_ONLY(JavaThread::current()->set_is_vthread_transition_disabler(true);) + JVMTI_ONLY(current->set_is_vthread_transition_disabler(true);) } // disable transitions for all virtual threads void MountUnmountDisabler::disable_transition_for_all() { - DEBUG_ONLY(JavaThread* thread = JavaThread::current();) - DEBUG_ONLY(thread->set_is_disabler_at_start(true);) + JavaThread* current = JavaThread::current(); + DEBUG_ONLY(current->set_is_disabler_at_start(true);) - MonitorLocker ml(VThreadTransition_lock); - while (exclusive_operation_ongoing()) { - ml.wait(10); - } - if (_is_exclusive) { - set_exclusive_operation_ongoing(true); - while (active_disablers() > 0) { - ml.wait(10); + { + MonitorLocker ml(VThreadTransition_lock); + while (exclusive_operation_ongoing()) { + ml.wait(); + } + if (_is_exclusive) { + set_exclusive_operation_ongoing(true); + while (active_disablers() > 0) { + ml.wait(); + } } + inc_active_disablers(); + inc_global_vthread_transition_disable_count(); } - inc_active_disablers(); - inc_global_vthread_transition_disable_count(); // Prevent loads of transition flag from floating up. Technically not // required since JavaThreadIteratorWithHandle includes full fence. OrderAccess::storeload(); - // Block while some mount/unmount transitions are in progress. - // Debug version fails and prints diagnostic information. + SpinYield spy; for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) { while (jt->is_in_vthread_transition()) { - ml.wait(10); + // Wait while the virtual thread is in transition + ThreadBlockInVM tbivm(current); + spy.wait(); } } @@ -336,8 +347,8 @@ MountUnmountDisabler::disable_transition_for_all() { // carrierThread to float up. // This pairs with the release barrier in end_transition(). OrderAccess::acquire(); - JVMTI_ONLY(JavaThread::current()->set_is_vthread_transition_disabler(true);) - DEBUG_ONLY(thread->set_is_disabler_at_start(false);) + JVMTI_ONLY(current->set_is_vthread_transition_disabler(true);) + DEBUG_ONLY(current->set_is_disabler_at_start(false);) } // enable transitions for one virtual thread diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp index 16fa8ebd5d03..060d79a2f970 100644 --- a/src/hotspot/share/runtime/mutexLocker.hpp +++ b/src/hotspot/share/runtime/mutexLocker.hpp @@ -126,7 +126,7 @@ extern Mutex* NmtVirtualMemory_lock; // guards NMT virtual memory up #if INCLUDE_JVMTI extern Mutex* CDSClassFileStream_lock; // FileMapInfo::open_stream_for_jvmti #endif -extern Mutex* DumpTimeTable_lock; // SystemDictionaryShared::_dumptime_table +extern Mutex* DumpTimeTable_lock; // AOTSystemDictionary::_dumptime_table extern Mutex* CDSLambda_lock; // LambdaProxyClassDictionary::find_lambda_proxy_class extern Mutex* DumpRegion_lock; // Symbol::operator new(size_t sz, int len) extern Mutex* ClassListFile_lock; // ClassListWriter() diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index 9e9ac9c65cd2..a11cfaa8cc1e 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -2845,7 +2845,7 @@ bool CompiledEntrySignature::check_supers_and_deoptimize(int arg_num) { assert(JavaThread::current()->thread_state() == _thread_in_vm, "must be in vm state"); bool scalar_super = false; - bool non_scalar_super = false; + bool non_scalar = _method->is_native(); GrowableArray* supers = get_supers(); for (int i = 0; i < supers->length(); ++i) { @@ -2853,20 +2853,20 @@ bool CompiledEntrySignature::check_supers_and_deoptimize(int arg_num) { if (super_method->is_scalarized_arg(arg_num)) { scalar_super = true; } else { - non_scalar_super = true; + non_scalar = true; } } #ifdef ASSERT // Randomly enable below code paths for stress testing bool stress = StressCallingConvention; if (stress && (os::random() & 1) == 1) { - non_scalar_super = true; + non_scalar = true; if ((os::random() & 1) == 1) { scalar_super = true; } } #endif - if (non_scalar_super) { + if (non_scalar) { // Found a super method with a non-scalarized argument. Fall back to the non-scalarized calling convention. if (scalar_super) { // Found non-scalar *and* scalar super methods. We can't handle both. @@ -2891,7 +2891,7 @@ bool CompiledEntrySignature::check_supers_and_deoptimize(int arg_num) { } } - return non_scalar_super; + return non_scalar; } // Iterate over arguments and compute scalarized and non-scalarized signatures diff --git a/src/hotspot/share/runtime/threadSMR.cpp b/src/hotspot/share/runtime/threadSMR.cpp index d70b7dcbd34e..b3ab2b56ad02 100644 --- a/src/hotspot/share/runtime/threadSMR.cpp +++ b/src/hotspot/share/runtime/threadSMR.cpp @@ -137,6 +137,11 @@ uint ThreadsSMRSupport::_to_delete_list_cnt = 0; // Impl note: See _to_delete_list_cnt note. uint ThreadsSMRSupport::_to_delete_list_max = 0; +// Track the number of ThreadsLists (and total size in bytes) that have been +// retired since the last reclaim attempt in free_list(). +uint ThreadsSMRSupport::_retired_since_reclaim_cnt = 0; +size_t ThreadsSMRSupport::_retired_since_reclaim_mem = 0; + // 'inline' functions first so the definitions are before first use: inline void ThreadsSMRSupport::add_deleted_thread_times(uint add_value) { @@ -693,6 +698,15 @@ void ThreadsList::dec_nested_handle_cnt() { AtomicAccess::dec(&_nested_handle_cnt); } +size_t ThreadsList::size_in_bytes() const { + size_t size = sizeof(*this); + if (_length != 0) { + // Add the size of the pointer array, and its null-terminator. + size += (size_t(_length) + 1) * sizeof(JavaThread *); + } + return size; +} + int ThreadsList::find_index_of_JavaThread(JavaThread *target) { if (target == nullptr) { return -1; @@ -909,7 +923,18 @@ bool ThreadsSMRSupport::delete_notify() { // The specified ThreadsList may not get deleted during this call if it // is still in-use (referenced by a hazard ptr). Other ThreadsLists // in the chain may get deleted by this call if they are no longer in-use. -void ThreadsSMRSupport::free_list(ThreadsList* threads) { +// A non-null thread_to_delete means that we were called from remove_thread() +// which should force an attempt to reclaim any retired ThreadsList. +void ThreadsSMRSupport::free_list(ThreadsList* threads, JavaThread* thread_to_delete) { + // Tests have shown that the latency sweet spot occurs when batching + // reclamation attempts for 16 retired ThreadsLists. A ThreadsList + // containing 32K threads occupies about 256K. Setting the memory + // threshold to 4M will thereby also allow a batch of 16 such lists, + // but may trigger an earlier reclamation attempt when individual + // ThreadsList instances are larger. + static const uint max_retire_cnt_before_reclaim = 16; + static const size_t max_retire_mem_before_reclaim = 4 * M; + assert_locked_or_safepoint(Threads_lock); if (is_bootstrap_list(threads)) { @@ -928,6 +953,26 @@ void ThreadsSMRSupport::free_list(ThreadsList* threads) { } } + // We can defer a reclamation attempt if free_list() was called when adding + // a thread (i.e., thread_to_delete == nullptr). This amortizes the cost of + // scanning the hazard pointers. When removing a thread, we attempt to + // reclaim any retired ThreadsList. We also use the same scan to determine + // if it's safe to delete the thread, thereby potentially avoiding a costly + // re-scan in wait_until_not_protected(). + if (thread_to_delete == nullptr) { + _retired_since_reclaim_cnt++; + _retired_since_reclaim_mem += threads->size_in_bytes(); + if ((_retired_since_reclaim_cnt < max_retire_cnt_before_reclaim) && + (_retired_since_reclaim_mem < max_retire_mem_before_reclaim)) { + return; + } + } + // Since the thresholds only control the intervals between reclamation + // attempts, we reset the bookkeeping here, even if some ThreadsList + // instances remain after the reclamation attempt. + _retired_since_reclaim_cnt = 0; + _retired_since_reclaim_mem = 0; + // Gather a hash table of the current hazard ptrs: ThreadScanHashtable *scan_table = new ThreadScanHashtable(); ScanHazardPtrGatherThreadsListClosure scan_cl(scan_table); @@ -941,6 +986,8 @@ void ThreadsSMRSupport::free_list(ThreadsList* threads) { ThreadsList* prev = nullptr; ThreadsList* next = nullptr; bool threads_is_freed = false; + bool thread_is_safe_to_delete = thread_to_delete != nullptr; + while (current != nullptr) { next = current->next_list(); if (!scan_table->has_entry((void*)current) && current->_nested_handle_cnt == 0) { @@ -960,6 +1007,14 @@ void ThreadsSMRSupport::free_list(ThreadsList* threads) { _to_delete_list_cnt--; } } else { + // This ThreadsList is referenced by a hazard ptr, or a nested + // handle. When a thread is removed, the 'threads' argument will + // be the old list and it will therefore contain thread_to_delete. + if (thread_is_safe_to_delete && (current == threads || current->includes(thread_to_delete))) { + // Since we found thread_to_delete in the current ThreadsList, it is + // still protected, and therefore not yet safe to delete. + thread_is_safe_to_delete = false; + } prev = current; } current = next; @@ -977,6 +1032,11 @@ void ThreadsSMRSupport::free_list(ThreadsList* threads) { #endif delete scan_table; + + if (thread_is_safe_to_delete) { + assert(thread_to_delete != nullptr, "must be"); + thread_to_delete->set_smr_delete_is_safe(); + } } // Return true if the specified JavaThread is protected by a hazard @@ -1049,7 +1109,7 @@ void ThreadsSMRSupport::remove_thread(JavaThread *thread) { log_debug(thread, smr)("tid=%zu: Threads::remove: new ThreadsList=" INTPTR_FORMAT, os::current_thread_id(), p2i(new_list)); ThreadsList *old_list = ThreadsSMRSupport::xchg_java_thread_list(new_list); - ThreadsSMRSupport::free_list(old_list); + ThreadsSMRSupport::free_list(old_list, thread); } // See note for clear_delete_notify(). @@ -1084,6 +1144,13 @@ void ThreadsSMRSupport::smr_delete(JavaThread *thread) { void ThreadsSMRSupport::wait_until_not_protected(JavaThread *thread) { assert(!Threads_lock->owned_by_self(), "sanity"); + if (thread->smr_delete_is_safe()) { + // ThreadsSMRSupport::free_list() has decided that it's safe to + // delete the thread, because it's no longer protected, and + // therefore we return early. + return; + } + bool has_logged_once = false; while (true) { @@ -1091,6 +1158,18 @@ void ThreadsSMRSupport::wait_until_not_protected(JavaThread *thread) { // Will not make a safepoint check because this JavaThread // is not on the current ThreadsList. MutexLocker ml(Threads_lock); + // ThreadsSMRSupport::remove_thread() has removed the thread + // from the current ThreadsList, but an older retired + // ThreadsList may still contain the thread. In that case, that + // ThreadsList will remain on the _to_delete_list as long as + // it's protected by a hazard pointer or a nested handle. Thus, + // if the _to_delete_list is empty, there is no ThreadsList + // protecting the thread, hence we return early. + if (_to_delete_list == nullptr) { + assert(!get_java_thread_list()->includes(thread), + "Thread should not be on the current ThreadsList"); + return; + } // Cannot use a MonitorLocker helper here because we have // to drop the Threads_lock first if we wait. ThreadsSMRSupport::delete_lock()->lock_without_safepoint_check(); diff --git a/src/hotspot/share/runtime/threadSMR.hpp b/src/hotspot/share/runtime/threadSMR.hpp index d2e716eaddfa..6b06de07b725 100644 --- a/src/hotspot/share/runtime/threadSMR.hpp +++ b/src/hotspot/share/runtime/threadSMR.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 @@ -110,12 +110,14 @@ class ThreadsSMRSupport : AllStatic { static ThreadsList* _to_delete_list; static uint _to_delete_list_cnt; static uint _to_delete_list_max; + static uint _retired_since_reclaim_cnt; + static size_t _retired_since_reclaim_mem; static void add_deleted_thread_times(uint add_value); static void add_tlh_times(uint add_value); static void clear_delete_notify(); static bool delete_notify(); - static void free_list(ThreadsList* threads); + static void free_list(ThreadsList* threads, JavaThread* thread_to_delete = nullptr); static void inc_deleted_thread_cnt(); static void inc_java_thread_list_alloc_cnt(); static void inc_tlh_cnt(); @@ -192,6 +194,8 @@ class ThreadsList : public CHeapObj { JavaThread *const *threads() const { return _threads; } + size_t size_in_bytes() const; + // Returns -1 if target is not found. int find_index_of_JavaThread(JavaThread* target); JavaThread* find_JavaThread_from_java_tid(jlong java_tid) const; diff --git a/src/java.base/share/classes/java/lang/Module.java b/src/java.base/share/classes/java/lang/Module.java index bd04345554b2..acc11ea6c6bc 100644 --- a/src/java.base/share/classes/java/lang/Module.java +++ b/src/java.base/share/classes/java/lang/Module.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -460,8 +460,24 @@ private static class ReflectionData { */ static final WeakPairMap, Boolean> uses = new WeakPairMap<>(); + + /** + * Return true if any of the maps are not empty. + */ + static boolean hasData() { + return !reads.values().isEmpty() + || !exports.values().isEmpty() + || !uses.values().isEmpty(); + } } + /** + * Returns true if ReflectionData has any readability, exports/opens or service + * uses that were added reflectively. + */ + static boolean hasReflectionData() { + return ReflectionData.hasData(); + } // -- readability -- @@ -493,9 +509,11 @@ public boolean canRead(Module other) { return true; // check if this module reads other - if (other.isNamed()) { - Set reads = this.reads; // volatile read - if (reads != null && reads.contains(other)) + Set reads = this.reads; + if (reads != null) { + if (reads.contains(other)) + return true; + if (!other.isNamed() && reads.contains(ALL_UNNAMED_MODULE)) return true; } @@ -592,8 +610,20 @@ private void implAddReads(Module other, boolean syncVM) { } } - // add reflective read - ReflectionData.reads.putIfAbsent(this, other, Boolean.TRUE); + if (VM.isModuleSystemInited()) { + // add reflective read + ReflectionData.reads.putIfAbsent(this, other, Boolean.TRUE); + } else { + // --add-reads + Set reads = this.reads; + if (reads == null) { + this.reads = Set.of(other); + } else { + Set newReads = new HashSet<>(reads); + newReads.add(other); + this.reads = newReads; + } + } } } @@ -1025,7 +1055,7 @@ public Module addOpens(String pn, Module other) { * @apiNote Used by Proxy and other dynamic modules. */ void implAddExports(String pn) { - implAddExportsOrOpens(pn, Module.EVERYONE_MODULE, false, true, true); + implAddExportsOrOpens(pn, Module.EVERYONE_MODULE, false, true); } /** @@ -1034,7 +1064,7 @@ void implAddExports(String pn) { * @apiNote Used by addExports, Instrumentation::redefineModule, and --add-exports */ void implAddExports(String pn, Module other) { - implAddExportsOrOpens(pn, other, false, VM.isBooted(), true); + implAddExportsOrOpens(pn, other, false, true); } /** @@ -1044,27 +1074,27 @@ void implAddExports(String pn, Module other) { * an executable JAR file has the "Add-Exports" attribute in its main manifest. */ void implAddExportsToAllUnnamed(String pn) { - implAddExportsOrOpens(pn, Module.ALL_UNNAMED_MODULE, false, false, true); + implAddExportsOrOpens(pn, Module.ALL_UNNAMED_MODULE, false, true); } /** - * Updates this export to export a package unconditionally without + * Updates this module to export a package unconditionally without * notifying the VM. * * @apiNote This method is for VM white-box testing. */ void implAddExportsNoSync(String pn) { - implAddExportsOrOpens(pn.replace('/', '.'), Module.EVERYONE_MODULE, false, true, false); + implAddExportsOrOpens(pn.replace('/', '.'), Module.EVERYONE_MODULE, false, false); } /** - * Updates a module to export a package to another module without + * Updates this module to export a package to another module without * notifying the VM. * * @apiNote This method is for VM white-box testing. */ void implAddExportsNoSync(String pn, Module other) { - implAddExportsOrOpens(pn.replace('/', '.'), other, false, true, false); + implAddExportsOrOpens(pn.replace('/', '.'), other, false, false); } /** @@ -1073,7 +1103,7 @@ void implAddExportsNoSync(String pn, Module other) { * @apiNote This method is for JDK tests only. */ void implAddOpens(String pn) { - implAddExportsOrOpens(pn, Module.EVERYONE_MODULE, true, true, true); + implAddExportsOrOpens(pn, Module.EVERYONE_MODULE, true, true); } /** @@ -1082,7 +1112,7 @@ void implAddOpens(String pn) { * @apiNote Used by addOpens, Instrumentation::redefineModule, and --add-opens */ void implAddOpens(String pn, Module other) { - implAddExportsOrOpens(pn, other, true, VM.isBooted(), true); + implAddExportsOrOpens(pn, other, true, true); } /** @@ -1092,21 +1122,19 @@ void implAddOpens(String pn, Module other) { * an executable JAR file has the "Add-Opens" attribute in its main manifest. */ void implAddOpensToAllUnnamed(String pn) { - implAddExportsOrOpens(pn, Module.ALL_UNNAMED_MODULE, true, false, true); + implAddExportsOrOpens(pn, Module.ALL_UNNAMED_MODULE, true, true); } /** - * Updates a module to export or open a module to another module. + * Updates this module to export or open a package to another module. * @param pn package name * @param other the module to export/open the package to * @param open true to open, false to export - * @param reflectively true if exported/opened reflectively * @param syncVM true to update the VM */ private void implAddExportsOrOpens(String pn, Module other, boolean open, - boolean reflectively, boolean syncVM) { Objects.requireNonNull(other); Objects.requireNonNull(pn); @@ -1136,7 +1164,10 @@ private void implAddExportsOrOpens(String pn, } } - if (reflectively) { + // use ReflectionData if module system already initialized and + // exporting/opening to a specific module (which may be a different layer) + if (VM.isModuleSystemInited() + && (other != EVERYONE_MODULE) && (other != ALL_UNNAMED_MODULE)) { // add package name to ReflectionData.exports if absent Map map = ReflectionData.exports .computeIfAbsent(this, other, @@ -1146,8 +1177,12 @@ private void implAddExportsOrOpens(String pn, } else { map.putIfAbsent(pn, Boolean.FALSE); } - } else { - // export/open packages during startup (--add-exports and --add-opens) + return; + } + + // --add-exports and --add-opens (during module system initialization) + // JAR Add-Exports and Add-Opens (after VM is fully initialized) + synchronized (this) { Map> packageToTargets = (open) ? openPackages : exportedPackages; if (packageToTargets != null) { // copy existing map @@ -1393,13 +1428,15 @@ static Map defineModules(Configuration cf, // update VM view addReads0(m, m2); } - m.reads = reads; // automatic modules read all unnamed modules if (descriptor.isAutomatic()) { - m.implAddReads(ALL_UNNAMED_MODULE, true); + addReads0(m, null); + reads.add(ALL_UNNAMED_MODULE); } + m.reads = reads; + // exports and opens, skipped for open and automatic if (!descriptor.isOpen() && !descriptor.isAutomatic()) { if (isBootLayer && descriptor.opens().isEmpty()) { diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index 59945dbdf8f7..8084f2ef3858 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -2108,6 +2108,9 @@ public boolean isReflectivelyExported(Module m, String pn, Module other) { public boolean isReflectivelyOpened(Module m, String pn, Module other) { return m.isReflectivelyOpened(pn, other); } + public boolean hasModuleReflectionData() { + return Module.hasReflectionData(); + } public void addEnableNativeAccess(Module m) { m.implAddEnableNativeAccess(); } diff --git a/src/java.base/share/classes/java/util/Currency.java b/src/java.base/share/classes/java/util/Currency.java index b254bae32a12..d446c20a1a50 100644 --- a/src/java.base/share/classes/java/util/Currency.java +++ b/src/java.base/share/classes/java/util/Currency.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,7 +38,6 @@ import java.time.format.DateTimeParseException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.function.Supplier; import java.util.regex.Pattern; import java.util.regex.Matcher; import java.util.spi.CurrencyNameProvider; @@ -321,6 +320,10 @@ private static Currency getInstance(String currencyCode, int defaultFractionDigi } char char1 = currencyCode.charAt(0); char char2 = currencyCode.charAt(1); + if (!isValidCountryFormat(char1, char2)) { + throw new IllegalArgumentException( + "The country code: \"%c%c\" is not a valid ISO 3166 code".formatted(char1, char2)); + } int tableEntry = getMainTableEntry(char1, char2); if ((tableEntry & COUNTRY_TYPE_MASK) == SIMPLE_CASE_COUNTRY_MASK && tableEntry != INVALID_COUNTRY_ENTRY @@ -394,14 +397,12 @@ public static Currency getInstance(Locale locale) { } String country = CalendarDataUtility.findRegionOverride(locale).getCountry(); - - if (country == null || !country.matches("^[a-zA-Z]{2}$")) { + char char1; + char char2; + if (country.length() != 2 || !isValidCountryFormat(char1 = country.charAt(0), char2 = country.charAt(1))) { throw new IllegalArgumentException( "The country of the input locale: \"%s\" is not a valid ISO 3166 country code".formatted(locale)); } - - char char1 = country.charAt(0); - char char2 = country.charAt(1); int tableEntry = getMainTableEntry(char1, char2); if ((tableEntry & COUNTRY_TYPE_MASK) == SIMPLE_CASE_COUNTRY_MASK && tableEntry != INVALID_COUNTRY_ENTRY) { @@ -693,15 +694,19 @@ private Object readResolve() { return getInstance(currencyCode); } + /** + * Returns whether the input is in the correct ISO 3166 country code format. + * That is, whether both char1 and char2 are between 'A' and 'Z' inclusive. + */ + private static boolean isValidCountryFormat(char char1, char char2) { + return char1 >= 'A' && char1 <= 'Z' && char2 >= 'A' && char2 <= 'Z'; + } + /** * Gets the main table entry for the country whose country code consists * of char1 and char2. */ private static int getMainTableEntry(char char1, char char2) { - if (char1 < 'A' || char1 > 'Z' || char2 < 'A' || char2 > 'Z') { - throw new IllegalArgumentException( - "The country code: \"%c%c\" is not a valid ISO 3166 code".formatted(char1, char2)); - } return mainTable[(char1 - 'A') * A_TO_Z + (char2 - 'A')]; } diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index 713c133ef2e2..7a643f55f5bc 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -250,6 +250,12 @@ public interface JavaLangAccess { */ boolean isReflectivelyOpened(Module module, String pn, Module other); + /** + * Returns true if there are readability, exports/uses, or service uses that were added + * reflectively. + */ + boolean hasModuleReflectionData(); + /** * Updates module m to allow access to restricted methods. */ diff --git a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java index f99090a04b96..3bda81b893c3 100644 --- a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java +++ b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java @@ -286,6 +286,7 @@ public int nullMarkerOffset(Field f) { * * A layout of 0 indicates this array is not flat. */ + @ForceInline public int arrayLayout(Object[] array) { if (array == null) { throw new NullPointerException(); @@ -1422,6 +1423,7 @@ public void notifyStrictStaticAccess(Class c, long staticFieldOffset, boolean * @see #getInt(Object, long) * @see #putInt(Object, long, int) */ + @ForceInline public long arrayBaseOffset(Class arrayClass) { if (arrayClass == null) { throw new NullPointerException(); @@ -1430,6 +1432,7 @@ public long arrayBaseOffset(Class arrayClass) { return arrayBaseOffset0(arrayClass); } + @ForceInline public long arrayInstanceBaseOffset(Object[] array) { if (array == null) { throw new NullPointerException(); @@ -1493,6 +1496,7 @@ public long arrayInstanceBaseOffset(Object[] array) { * @see #getInt(Object, long) * @see #putInt(Object, long, int) */ + @ForceInline public int arrayIndexScale(Class arrayClass) { if (arrayClass == null) { throw new NullPointerException(); @@ -1501,6 +1505,7 @@ public int arrayIndexScale(Class arrayClass) { return arrayIndexScale0(arrayClass); } + @ForceInline public int arrayInstanceIndexScale(Object[] array) { if (array == null) { throw new NullPointerException(); @@ -1514,6 +1519,7 @@ public int arrayInstanceIndexScale(Object[] array) { * Intended to be used by substitutability test in ValueObjectMethods only. * The format is subject to change. */ + @ForceInline public int[] getFieldMap(Class c) { if (c == null) { throw new NullPointerException(); @@ -1526,6 +1532,7 @@ public int[] getFieldMap(Class c) { * a possible flat layout that contains no oop. * Required for numerical CAS safety. */ + @ForceInline public boolean isFlatPayloadBinary(Class c) { int[] map = getFieldMap(c); int nbNonRef = map[0]; @@ -1820,11 +1827,17 @@ public final Object compareAndExchangeReferenceAcquire(Object o, long offset, return compareAndExchangeReference(o, offset, expected, x); } + @ForceInline public final Object compareAndExchangeReferenceAcquire(Object o, long offset, Class valueType, V expected, V x) { - return compareAndExchangeReference(o, offset, valueType, expected, x); + if (isValueObject(valueType, expected)) { + // Reusing a stronger operation for now, still compliant + return compareAndExchangeReference(o, offset, valueType, expected, x); + } else { + return compareAndExchangeReferenceAcquire(o, offset, expected, x); + } } @ForceInline @@ -1843,11 +1856,17 @@ public final Object compareAndExchangeReferenceRelease(Object o, long offset, return compareAndExchangeReference(o, offset, expected, x); } + @ForceInline public final Object compareAndExchangeReferenceRelease(Object o, long offset, Class valueType, V expected, V x) { - return compareAndExchangeReference(o, offset, valueType, expected, x); + if (isValueObject(valueType, expected)) { + // Reusing a stronger operation for now, still compliant + return compareAndExchangeReference(o, offset, valueType, expected, x); + } else { + return compareAndExchangeReferenceRelease(o, offset, expected, x); + } } @ForceInline @@ -1866,6 +1885,7 @@ public final boolean weakCompareAndSetReferencePlain(Object o, long offset, return compareAndSetReference(o, offset, expected, x); } + @ForceInline public final boolean weakCompareAndSetReferencePlain(Object o, long offset, Class valueType, V expected, @@ -1894,6 +1914,7 @@ public final boolean weakCompareAndSetReferenceAcquire(Object o, long offset, return compareAndSetReference(o, offset, expected, x); } + @ForceInline public final boolean weakCompareAndSetReferenceAcquire(Object o, long offset, Class valueType, V expected, @@ -1922,6 +1943,7 @@ public final boolean weakCompareAndSetReferenceRelease(Object o, long offset, return compareAndSetReference(o, offset, expected, x); } + @ForceInline public final boolean weakCompareAndSetReferenceRelease(Object o, long offset, Class valueType, V expected, @@ -1950,6 +1972,7 @@ public final boolean weakCompareAndSetReference(Object o, long offset, return compareAndSetReference(o, offset, expected, x); } + @ForceInline public final boolean weakCompareAndSetReference(Object o, long offset, Class valueType, V expected, @@ -3418,7 +3441,12 @@ public final Object getAndSetReferenceRelease(Object o, long offset, Object newV @ForceInline public final Object getAndSetReferenceRelease(Object o, long offset, Class valueType, Object newValue) { - return getAndSetReference(o, offset, valueType, newValue); + if (isValueObject(valueType, newValue)) { + // Reusing a stronger operation for now, still compliant + return getAndSetReference(o, offset, valueType, newValue); + } else { + return getAndSetReferenceRelease(o, offset, newValue); + } } @ForceInline @@ -3437,7 +3465,12 @@ public final Object getAndSetReferenceAcquire(Object o, long offset, Object newV @ForceInline public final Object getAndSetReferenceAcquire(Object o, long offset, Class valueType, Object newValue) { - return getAndSetReference(o, offset, valueType, newValue); + if (isValueObject(valueType, newValue)) { + // Reusing a stronger operation for now, still compliant + return getAndSetReference(o, offset, valueType, newValue); + } else { + return getAndSetReferenceAcquire(o, offset, newValue); + } } @ForceInline diff --git a/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java b/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java index 3d087a4c8c09..0cd3451574cf 100644 --- a/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java +++ b/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java @@ -466,6 +466,7 @@ private static ModuleLayer boot2() { && !haveUpgradeModulePath && allJrtOrModularJar(cf)) { assert !isPatched; + assert !JLA.hasModuleReflectionData(); // Archive module graph and maybe boot layer boolean hasSplitPackages = containsSplitPackages(cf); diff --git a/src/java.base/share/classes/sun/net/ftp/impl/FtpClient.java b/src/java.base/share/classes/sun/net/ftp/impl/FtpClient.java index 1951dbc925a1..6748c3d03a4c 100644 --- a/src/java.base/share/classes/sun/net/ftp/impl/FtpClient.java +++ b/src/java.base/share/classes/sun/net/ftp/impl/FtpClient.java @@ -529,7 +529,7 @@ private boolean issueCommand(String cmd) throws IOException, if (cmd.indexOf('\n') != -1 || cmd.indexOf('\r') != -1) { sun.net.ftp.FtpProtocolException ex = new sun.net.ftp.FtpProtocolException("Illegal FTP command"); - ex.initCause(new IllegalArgumentException("Illegal carriage return")); + ex.initCause(new IllegalArgumentException("Illegal carriage return or line feed")); throw ex; } sendServer(cmd + "\r\n"); diff --git a/src/java.base/share/classes/sun/net/smtp/SmtpClient.java b/src/java.base/share/classes/sun/net/smtp/SmtpClient.java index 73968ddf1926..c8807b645b3a 100644 --- a/src/java.base/share/classes/sun/net/smtp/SmtpClient.java +++ b/src/java.base/share/classes/sun/net/smtp/SmtpClient.java @@ -76,7 +76,7 @@ private void toCanonical(String s) throws IOException { public void to(String s) throws IOException { if (s.indexOf('\n') != -1 || s.indexOf('\r') != -1) { throw new IOException("Illegal SMTP command", - new IllegalArgumentException("Illegal carriage return")); + new IllegalArgumentException("Illegal carriage return or line feed")); } int st = 0; int limit = s.length(); @@ -122,7 +122,7 @@ else if (st == pos) public void from(String s) throws IOException { if (s.indexOf('\n') != -1 || s.indexOf('\r') != -1) { throw new IOException("Illegal SMTP command", - new IllegalArgumentException("Illegal carriage return")); + new IllegalArgumentException("Illegal carriage return or line feed")); } if (s.startsWith("<")) { issueCommand("mail from: " + s + "\r\n", 250); diff --git a/src/java.base/share/classes/sun/security/tools/keytool/CertAndKeyGen.java b/src/java.base/share/classes/sun/security/tools/keytool/CertAndKeyGen.java index 52fd73949dcb..9b5d19e5f3bc 100644 --- a/src/java.base/share/classes/sun/security/tools/keytool/CertAndKeyGen.java +++ b/src/java.base/share/classes/sun/security/tools/keytool/CertAndKeyGen.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,7 @@ import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; +import java.util.Locale; import java.util.TimeZone; import sun.security.pkcs10.PKCS10; @@ -148,17 +149,9 @@ public void generate(String name) { if (prng == null) { prng = new SecureRandom(); } - try { - keyGen.initialize(new NamedParameterSpec(name), prng); - } catch (InvalidAlgorithmParameterException e) { - if (keyType.equalsIgnoreCase("EC")) { - // EC has another NamedParameterSpec - keyGen.initialize(new ECGenParameterSpec(name), prng); - } else { - throw e; - } - } - + keyGen.initialize(keyType.toUpperCase(Locale.ROOT).equals("EC") + ? new ECGenParameterSpec(name) + : new NamedParameterSpec(name), prng); } catch (Exception e) { throw new IllegalArgumentException(e.getMessage(), e); } diff --git a/src/java.base/share/classes/sun/security/tools/keytool/Main.java b/src/java.base/share/classes/sun/security/tools/keytool/Main.java index 7f415da52709..e5535be8f966 100644 --- a/src/java.base/share/classes/sun/security/tools/keytool/Main.java +++ b/src/java.base/share/classes/sun/security/tools/keytool/Main.java @@ -1917,42 +1917,35 @@ private void doGenKeyPair(String alias, String dname, String keyAlgName, String signerAlias) throws Exception { - if (groupName != null) { - if (keysize != -1) { - throw new Exception(rb.getString("groupname.keysize.coexist")); + if (groupName != null && keysize != -1) { + throw new Exception(rb.getString("groupname.keysize.coexist")); + } else if (keysize != -1 || groupName != null) { + // Good. No need to choose default, but some warnings + if (keysize != -1 && "EC".equalsIgnoreCase(keyAlgName)) { + weakWarnings.add(String.format( + rb.getString("deprecate.keysize.for.ec"), + ecGroupNameForSize(keysize))); } } else { - if (keysize == -1) { - if ("EC".equalsIgnoreCase(keyAlgName)) { - keysize = SecurityProviderConstants.DEF_EC_KEY_SIZE; - } else if ("RSA".equalsIgnoreCase(keyAlgName)) { - keysize = SecurityProviderConstants.DEF_RSA_KEY_SIZE; - } else if ("RSASSA-PSS".equalsIgnoreCase(keyAlgName)) { - keysize = SecurityProviderConstants.DEF_RSASSA_PSS_KEY_SIZE; - } else if ("DSA".equalsIgnoreCase(keyAlgName)) { - keysize = SecurityProviderConstants.DEF_DSA_KEY_SIZE; - } else if ("EdDSA".equalsIgnoreCase(keyAlgName)) { - keysize = SecurityProviderConstants.DEF_ED_KEY_SIZE; - } else if ("Ed25519".equalsIgnoreCase(keyAlgName)) { - keysize = 255; - } else if ("Ed448".equalsIgnoreCase(keyAlgName)) { - keysize = 448; - } else if ("XDH".equalsIgnoreCase(keyAlgName)) { - keysize = SecurityProviderConstants.DEF_XEC_KEY_SIZE; - } else if ("X25519".equalsIgnoreCase(keyAlgName)) { - keysize = 255; - } else if ("X448".equalsIgnoreCase(keyAlgName)) { - keysize = 448; - } else if ("DH".equalsIgnoreCase(keyAlgName)) { - keysize = SecurityProviderConstants.DEF_DH_KEY_SIZE; - } - } else { - if ("EC".equalsIgnoreCase(keyAlgName)) { - weakWarnings.add(String.format( - rb.getString("deprecate.keysize.for.ec"), - ecGroupNameForSize(keysize))); - } - } + // Need to choose a default. Could be keysize or groupname + keysize = switch (keyAlgName.toUpperCase(Locale.ROOT)) { + case "RSA", "RSASSA-PSS", "DH" -> 3072; + case "DSA" -> 2048; + default -> -1; + }; + groupName = switch (keyAlgName.toUpperCase(Locale.ROOT)) { + case "EC" -> "secp384r1"; + case "EDDSA" -> "Ed25519"; + case "XDH" -> "X25519"; + case "ML-DSA" -> "ML-DSA-65"; + case "ML-KEM" -> "ML-KEM-768"; + default -> null; + }; + // After this, keysize might still be -1 and groupname might still + // be null. In some cases this is totally fine, for example, if + // keyAlgName is already "ML-KEM-768". Otherwise, we should add + // a case above. Without it, it will be up to the implementation + // to choose the default keysize or groupname. } if (alias == null) { @@ -2006,7 +1999,7 @@ private void doGenKeyPair(String alias, String dname, String keyAlgName, if (groupName != null) { keypair.generate(groupName); } else { - // This covers keysize both specified and unspecified + // keysize could be -1 keypair.generate(keysize); } diff --git a/src/java.base/share/classes/sun/security/util/math/intpoly/IntegerPolynomialP384.java b/src/java.base/share/classes/sun/security/util/math/intpoly/IntegerPolynomialP384.java new file mode 100644 index 000000000000..ced525e1b6d3 --- /dev/null +++ b/src/java.base/share/classes/sun/security/util/math/intpoly/IntegerPolynomialP384.java @@ -0,0 +1,870 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util.math.intpoly; + +import java.math.BigInteger; +public final class IntegerPolynomialP384 extends IntegerPolynomial { + private static final int BITS_PER_LIMB = 55; + private static final int NUM_LIMBS = 7; + private static final int MAX_ADDS = 2; + public static final BigInteger MODULUS = evaluateModulus(); + private static final long CARRY_ADD = 1L << (BITS_PER_LIMB - 1); + private static final long LIMB_MASK = -1L >>> (64 - BITS_PER_LIMB); + + public static final IntegerPolynomialP384 ONE = new IntegerPolynomialP384(); + + private IntegerPolynomialP384() { + super(BITS_PER_LIMB, NUM_LIMBS, MAX_ADDS, MODULUS); + } + + private static BigInteger evaluateModulus() { + BigInteger result = BigInteger.valueOf(2).pow(384); + + result = result.subtract(BigInteger.valueOf(1).shiftLeft(128)); + result = result.subtract(BigInteger.valueOf(1).shiftLeft(96)); + result = result.add(BigInteger.valueOf(1).shiftLeft(32)); + result = result.subtract(BigInteger.valueOf(1)); + + return result; + } + + /** + * Carry from a range of limb positions. + * Override for performance (unnesting). + * + * @param limbs [in|out] the limbs for carry operation. + * @param start [in] the starting position of carry. + * @param end [in] the ending position of carry. + */ + @Override + protected void carry(long[] limbs, int start, int end) { + long carry; + + for (int i = start; i < end; i++) { + carry = (limbs[i] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[i] -= (carry << BITS_PER_LIMB); + limbs[i + 1] += carry; + } + } + + /** + * Carry operation for all limb positions. + * Override for performance (unroll and unnesting). + * + * @param limbs [in|out] the limbs for carry operation. + */ + @Override + protected void carry(long[] limbs) { + long carry = (limbs[0] + CARRY_ADD) >> BITS_PER_LIMB; + + limbs[0] -= carry << BITS_PER_LIMB; + limbs[1] += carry; + + carry = (limbs[1] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[1] -= carry << BITS_PER_LIMB; + limbs[2] += carry; + + carry = (limbs[2] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[2] -= carry << BITS_PER_LIMB; + limbs[3] += carry; + + carry = (limbs[3] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[3] -= carry << BITS_PER_LIMB; + limbs[4] += carry; + + carry = (limbs[4] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[4] -= carry << BITS_PER_LIMB; + limbs[5] += carry; + + carry = (limbs[5] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[5] -= carry << BITS_PER_LIMB; + limbs[6] += carry; + } + + /** + * Reduces digit 'v' at limb position 'i' to a lower limb. + * + * @param limbs [in|out] the limbs to reduce in. + * @param v [in] the digit to reduce to the lower limb. + * @param i [in] the limbs to reduce from. + */ + protected void reduceIn(long[] limbs, long v, int i) { + limbs[i - 5] += (v << 19) & LIMB_MASK; + limbs[i - 4] += v >> 36; + limbs[i - 6] += (v << 42) & LIMB_MASK; + limbs[i - 5] += v >> 13; + limbs[i - 7] -= (v << 33) & LIMB_MASK; + limbs[i - 6] -= v >> 22; + limbs[i - 7] += (v << 1) & LIMB_MASK; + limbs[i - 6] += v >> 54; + } + + /** + * Carry from high order limb and reduce to the lower order limb. Assumed + * to be called two times to propagate the carries. + * + * @param limbs [in|out] the limbs to fully carry and reduce. + */ + protected void finalCarryReduceLast(long[] limbs) { + long carry = limbs[6] >> 54; + + limbs[6] -= carry << 54; + limbs[2] += (carry << 18) & LIMB_MASK; + limbs[3] += carry >> 37; + limbs[1] += (carry << 41) & LIMB_MASK; + limbs[2] += carry >> 14; + limbs[0] -= (carry << 32) & LIMB_MASK; + limbs[1] -= carry >> 23; + limbs[0] += carry; + } + + /** + * Reduce from the upper half of limbs to the lower half. + * + * @param r [out] the resulting limbs after reduction + * @param c0..c13 [in] the limbs to be reduced + */ + private void carryReduceUpper(long[] r, long c0, long c1, long c2, long c3, + long c4, long c5, long c6, long c7, long c8, long c9, long c10, + long c11, long c12, long c13) { + // reduce from position 13 + c8 += (c13 << 19) & LIMB_MASK; + c9 += c13 >> 36; + c7 += (c13 << 42) & LIMB_MASK; + c8 += c13 >> 13; + c6 -= (c13 << 33) & LIMB_MASK; + c7 -= c13 >> 22; + c6 += (c13 << 1) & LIMB_MASK; + c7 += c13 >> 54; + // reduce from position 12 + c7 += (c12 << 19) & LIMB_MASK; + c8 += c12 >> 36; + c6 += (c12 << 42) & LIMB_MASK; + c7 += c12 >> 13; + c5 -= (c12 << 33) & LIMB_MASK; + c6 -= c12 >> 22; + c5 += (c12 << 1) & LIMB_MASK; + c6 += c12 >> 54; + // reduce from position 11 + c6 += (c11 << 19) & LIMB_MASK; + c7 += c11 >> 36; + c5 += (c11 << 42) & LIMB_MASK; + c6 += c11 >> 13; + c4 -= (c11 << 33) & LIMB_MASK; + c5 -= c11 >> 22; + c4 += (c11 << 1) & LIMB_MASK; + c5 += c11 >> 54; + // reduce from position 10 + c5 += (c10 << 19) & LIMB_MASK; + c6 += c10 >> 36; + c4 += (c10 << 42) & LIMB_MASK; + c5 += c10 >> 13; + c3 -= (c10 << 33) & LIMB_MASK; + c4 -= c10 >> 22; + c3 += (c10 << 1) & LIMB_MASK; + c4 += c10 >> 54; + // reduce from position 9 + c4 += (c9 << 19) & LIMB_MASK; + c5 += c9 >> 36; + c3 += (c9 << 42) & LIMB_MASK; + c4 += c9 >> 13; + c2 -= (c9 << 33) & LIMB_MASK; + c3 -= c9 >> 22; + c2 += (c9 << 1) & LIMB_MASK; + c3 += c9 >> 54; + // reduce from position 8 + c3 += (c8 << 19) & LIMB_MASK; + c4 += c8 >> 36; + c2 += (c8 << 42) & LIMB_MASK; + c3 += c8 >> 13; + c1 -= (c8 << 33) & LIMB_MASK; + c2 -= c8 >> 22; + c1 += (c8 << 1) & LIMB_MASK; + c2 += c8 >> 54; + // reduce from position 7 + c2 += (c7 << 19) & LIMB_MASK; + c3 += c7 >> 36; + c1 += (c7 << 42) & LIMB_MASK; + c2 += c7 >> 13; + c0 -= (c7 << 33) & LIMB_MASK; + c1 -= c7 >> 22; + c0 += (c7 << 1) & LIMB_MASK; + c1 += c7 >> 54; + + // Reduce lower limbs + carryReduce(r, c0, c1, c2, c3, c4, c5, c6); + } + + /** + * Multiply two limbs using a high/low digit technique that allows for + * larger limb sizes. It is assumed that both limbs have already been + * reduced. + * + * @param a [in] the limb operand to multiply. + * @param b [in] the limb operand to multiply. + * @param r [out] the product of the limbs operands that is fully reduced. + */ + protected void mult(long[] a, long[] b, long[] r) { + // multiplication result digits for each column + long c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13; + + long aa0 = a[0]; + long aa1 = a[1]; + long aa2 = a[2]; + long aa3 = a[3]; + long aa4 = a[4]; + long aa5 = a[5]; + long aa6 = a[6]; + + long bb0 = b[0]; + long bb1 = b[1]; + long bb2 = b[2]; + long bb3 = b[3]; + long bb4 = b[4]; + long bb5 = b[5]; + long bb6 = b[6]; + + final long shift1 = 64 - BITS_PER_LIMB; + final long shift2 = BITS_PER_LIMB; + + // low digits from multiplication + long d0, d1, d2, d3, d4, d5, d6; + // high digits from multiplication + long dd0, dd1, dd2, dd3, dd4, dd5, dd6; + + // Row 0 - multiply by aa0 + d0 = aa0 * bb0; + dd0 = Math.multiplyHigh(aa0, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + d1 = aa0 * bb1; + dd1 = Math.multiplyHigh(aa0, bb1) << shift1 | (d1 >>> shift2); + d1 &= LIMB_MASK; + + d2 = aa0 * bb2; + dd2 = Math.multiplyHigh(aa0, bb2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa0 * bb3; + dd3 = Math.multiplyHigh(aa0, bb3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa0 * bb4; + dd4 = Math.multiplyHigh(aa0, bb4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + d5 = aa0 * bb5; + dd5 = Math.multiplyHigh(aa0, bb5) << shift1 | (d5 >>> shift2); + d5 &= LIMB_MASK; + + d6 = aa0 * bb6; + dd6 = Math.multiplyHigh(aa0, bb6) << shift1 | (d6 >>> shift2); + d6 &= LIMB_MASK; + + c0 = d0; + c1 = d1 + dd0; + c2 = d2 + dd1; + c3 = d3 + dd2; + c4 = d4 + dd3; + c5 = d5 + dd4; + c6 = d6 + dd5; + c7 = dd6; + + // Row 1 - multiply by aa1 + d0 = aa1 * bb0; + dd0 = Math.multiplyHigh(aa1, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + d1 = aa1 * bb1; + dd1 = Math.multiplyHigh(aa1, bb1) << shift1 | (d1 >>> shift2); + d1 &= LIMB_MASK; + + d2 = aa1 * bb2; + dd2 = Math.multiplyHigh(aa1, bb2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa1 * bb3; + dd3 = Math.multiplyHigh(aa1, bb3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa1 * bb4; + dd4 = Math.multiplyHigh(aa1, bb4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + d5 = aa1 * bb5; + dd5 = Math.multiplyHigh(aa1, bb5) << shift1 | (d5 >>> shift2); + d5 &= LIMB_MASK; + + d6 = aa1 * bb6; + dd6 = Math.multiplyHigh(aa1, bb6) << shift1 | (d6 >>> shift2); + d6 &= LIMB_MASK; + + c1 += d0; + c2 += d1 + dd0; + c3 += d2 + dd1; + c4 += d3 + dd2; + c5 += d4 + dd3; + c6 += d5 + dd4; + c7 += d6 + dd5; + c8 = dd6; + + // Row 2 - multiply by aa2 + d0 = aa2 * bb0; + dd0 = Math.multiplyHigh(aa2, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + d1 = aa2 * bb1; + dd1 = Math.multiplyHigh(aa2, bb1) << shift1 | (d1 >>> shift2); + d1 &= LIMB_MASK; + + d2 = aa2 * bb2; + dd2 = Math.multiplyHigh(aa2, bb2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa2 * bb3; + dd3 = Math.multiplyHigh(aa2, bb3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa2 * bb4; + dd4 = Math.multiplyHigh(aa2, bb4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + d5 = aa2 * bb5; + dd5 = Math.multiplyHigh(aa2, bb5) << shift1 | (d5 >>> shift2); + d5 &= LIMB_MASK; + + d6 = aa2 * bb6; + dd6 = Math.multiplyHigh(aa2, bb6) << shift1 | (d6 >>> shift2); + d6 &= LIMB_MASK; + + c2 += d0; + c3 += d1 + dd0; + c4 += d2 + dd1; + c5 += d3 + dd2; + c6 += d4 + dd3; + c7 += d5 + dd4; + c8 += d6 + dd5; + c9 = dd6; + + // Row 3 - multiply by aa3 + d0 = aa3 * bb0; + dd0 = Math.multiplyHigh(aa3, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + d1 = aa3 * bb1; + dd1 = Math.multiplyHigh(aa3, bb1) << shift1 | (d1 >>> shift2); + d1 &= LIMB_MASK; + + d2 = aa3 * bb2; + dd2 = Math.multiplyHigh(aa3, bb2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa3 * bb3; + dd3 = Math.multiplyHigh(aa3, bb3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa3 * bb4; + dd4 = Math.multiplyHigh(aa3, bb4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + d5 = aa3 * bb5; + dd5 = Math.multiplyHigh(aa3, bb5) << shift1 | (d5 >>> shift2); + d5 &= LIMB_MASK; + + d6 = aa3 * bb6; + dd6 = Math.multiplyHigh(aa3, bb6) << shift1 | (d6 >>> shift2); + d6 &= LIMB_MASK; + + c3 += d0; + c4 += d1 + dd0; + c5 += d2 + dd1; + c6 += d3 + dd2; + c7 += d4 + dd3; + c8 += d5 + dd4; + c9 += d6 + dd5; + c10 = dd6; + + // Row 4 - multiply by aa4 + d0 = aa4 * bb0; + dd0 = Math.multiplyHigh(aa4, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + d1 = aa4 * bb1; + dd1 = Math.multiplyHigh(aa4, bb1) << shift1 | (d1 >>> shift2); + d1 &= LIMB_MASK; + + d2 = aa4 * bb2; + dd2 = Math.multiplyHigh(aa4, bb2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa4 * bb3; + dd3 = Math.multiplyHigh(aa4, bb3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa4 * bb4; + dd4 = Math.multiplyHigh(aa4, bb4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + d5 = aa4 * bb5; + dd5 = Math.multiplyHigh(aa4, bb5) << shift1 | (d5 >>> shift2); + d5 &= LIMB_MASK; + + d6 = aa4 * bb6; + dd6 = Math.multiplyHigh(aa4, bb6) << shift1 | (d6 >>> shift2); + d6 &= LIMB_MASK; + + c4 += d0; + c5 += d1 + dd0; + c6 += d2 + dd1; + c7 += d3 + dd2; + c8 += d4 + dd3; + c9 += d5 + dd4; + c10 += d6 + dd5; + c11 = dd6; + + // Row 5 - multiply by aa5 + d0 = aa5 * bb0; + dd0 = Math.multiplyHigh(aa5, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + d1 = aa5 * bb1; + dd1 = Math.multiplyHigh(aa5, bb1) << shift1 | (d1 >>> shift2); + d1 &= LIMB_MASK; + + d2 = aa5 * bb2; + dd2 = Math.multiplyHigh(aa5, bb2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa5 * bb3; + dd3 = Math.multiplyHigh(aa5, bb3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa5 * bb4; + dd4 = Math.multiplyHigh(aa5, bb4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + d5 = aa5 * bb5; + dd5 = Math.multiplyHigh(aa5, bb5) << shift1 | (d5 >>> shift2); + d5 &= LIMB_MASK; + + d6 = aa5 * bb6; + dd6 = Math.multiplyHigh(aa5, bb6) << shift1 | (d6 >>> shift2); + d6 &= LIMB_MASK; + + c5 += d0; + c6 += d1 + dd0; + c7 += d2 + dd1; + c8 += d3 + dd2; + c9 += d4 + dd3; + c10 += d5 + dd4; + c11 += d6 + dd5; + c12 = dd6; + + // Row 6 - multiply by aa6 + d0 = aa6 * bb0; + dd0 = Math.multiplyHigh(aa6, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + d1 = aa6 * bb1; + dd1 = Math.multiplyHigh(aa6, bb1) << shift1 | (d1 >>> shift2); + d1 &= LIMB_MASK; + + d2 = aa6 * bb2; + dd2 = Math.multiplyHigh(aa6, bb2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa6 * bb3; + dd3 = Math.multiplyHigh(aa6, bb3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa6 * bb4; + dd4 = Math.multiplyHigh(aa6, bb4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + d5 = aa6 * bb5; + dd5 = Math.multiplyHigh(aa6, bb5) << shift1 | (d5 >>> shift2); + d5 &= LIMB_MASK; + + d6 = aa6 * bb6; + dd6 = Math.multiplyHigh(aa6, bb6) << shift1 | (d6 >>> shift2); + d6 &= LIMB_MASK; + + c6 += d0; + c7 += d1 + dd0; + c8 += d2 + dd1; + c9 += d3 + dd2; + c10 += d4 + dd3; + c11 += d5 + dd4; + c12 += d6 + dd5; + c13 = dd6; + + carryReduceUpper(r, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, + c12, c13); + } + + /** + * Carry in all lower positions and reduce high order limb. + * + * @param a [in|out] the limbs to carry and reduce. + */ + protected void reduce(long[] a) { + carryReduce(a, a[0], a[1], a[2], a[3], a[4], a[5], a[6]); + } + + /** + * Carry in all lower positions and reduce high order limb. + * + * @param r [out] the resulting limbs that are reduced. + * @param c0..c6 [in] the limbs to carry and reduce. + */ + protected void carryReduce(long[] r, long c0, long c1, long c2, long c3, + long c4, long c5, long c6) { + long carry, c7; + + // carry from position 5 + carry = (c5 + CARRY_ADD) >> BITS_PER_LIMB; + c5 -= carry << BITS_PER_LIMB; + c6 += carry; + // carry from position 6 + carry = (c6 + CARRY_ADD) >> BITS_PER_LIMB; + c6 -= carry << BITS_PER_LIMB; + c7 = carry; + // reduce from position 7 + c2 += (c7 << 19) & LIMB_MASK; + c3 += c7 >> 36; + c1 += (c7 << 42) & LIMB_MASK; + c2 += c7 >> 13; + c0 -= (c7 << 33) & LIMB_MASK; + c1 -= c7 >> 22; + c0 += (c7 << 1) & LIMB_MASK; + c1 += c7 >> 54; + // carry from position 0 + carry = (c0 + CARRY_ADD) >> BITS_PER_LIMB; + c0 -= carry << BITS_PER_LIMB; + c1 += carry; + // carry from position 1 + carry = (c1 + CARRY_ADD) >> BITS_PER_LIMB; + c1 -= carry << BITS_PER_LIMB; + c2 += carry; + // carry from position 2 + carry = (c2 + CARRY_ADD) >> BITS_PER_LIMB; + c2 -= carry << BITS_PER_LIMB; + c3 += carry; + // carry from position 3 + carry = (c3 + CARRY_ADD) >> BITS_PER_LIMB; + c3 -= carry << BITS_PER_LIMB; + c4 += carry; + // carry from position 4 + carry = (c4 + CARRY_ADD) >> BITS_PER_LIMB; + c4 -= carry << BITS_PER_LIMB; + c5 += carry; + // carry from position 5 + carry = (c5 + CARRY_ADD) >> BITS_PER_LIMB; + c5 -= carry << BITS_PER_LIMB; + c6 += carry; + + r[0] = c0; + r[1] = c1; + r[2] = c2; + r[3] = c3; + r[4] = c4; + r[5] = c5; + r[6] = c6; + } + + /** + * Multiply limbs by scalar value. + * Superclass assumes that limb primitive radix > (bits per limb * 2) + * + * @param a [in|out] the limbs to multiply a carry operation. 'a' is + * assumed to be reduced. + * @param b [in] the scalar value to be muliplied with the limbs. + */ + @Override + protected void multByInt(long[] a, long b) { + long aa0 = a[0]; + long aa1 = a[1]; + long aa2 = a[2]; + long aa3 = a[3]; + long aa4 = a[4]; + long aa5 = a[5]; + long aa6 = a[6]; + long bb0 = b; + final long shift1 = 64 - BITS_PER_LIMB; + final long shift2 = BITS_PER_LIMB; + long d0; // low digit from multiplication + long dd0; // high digit from multiplication + + // Row 0 - multiply by aa0 + d0 = aa0 * bb0; + dd0 = Math.multiplyHigh(aa0, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + a[0] = d0; + a[1] = dd0; + + // Row 1 - multiply by aa1 + d0 = aa1 * bb0; + dd0 = Math.multiplyHigh(aa1, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + a[1] += d0; + a[2] = dd0; + + // Row 2 - multiply by aa2 + d0 = aa2 * bb0; + dd0 = Math.multiplyHigh(aa2, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + a[2] += d0; + a[3] = dd0; + + // Row 3 - multiply by aa3 + d0 = aa3 * bb0; + dd0 = Math.multiplyHigh(aa3, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + a[3] += d0; + a[4] = dd0; + + // Row 4 - multiply by aa4 + d0 = aa4 * bb0; + dd0 = Math.multiplyHigh(aa4, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + a[4] += d0; + a[5] = dd0; + + // Row 5 - multiply by aa5 + d0 = aa5 * bb0; + dd0 = Math.multiplyHigh(aa5, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + a[5] += d0; + a[6] = dd0; + + // Row 6 - multiply by aa6 + d0 = aa6 * bb0; + dd0 = Math.multiplyHigh(aa6, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + a[6] += d0; + + // Reduce single high-order limb before reduce/carry the rest + reduceIn(a, dd0, 7); + reduce(a); + } + + /** + * Takes a single limb and squares it using a high/low digit technique that + * allows for larger limb sizes. It is assumed that the limb input has + * already been reduced. + * + * @param a [in] the limb operand to square. + * @param r [out] the resulting square of the limb which is fully reduced. + */ + protected void square(long[] a, long[] r) { + long aa0 = a[0]; + long aa1 = a[1]; + long aa2 = a[2]; + long aa3 = a[3]; + long aa4 = a[4]; + long aa5 = a[5]; + long aa6 = a[6]; + final long shift1 = 64 - BITS_PER_LIMB; + final long shift2 = BITS_PER_LIMB; + // low digits from multiplication + long d0, d1, d2, d3, d4, d5, d6; + // high digits from multiplication + long dd0, dd1, dd2, dd3, dd4, dd5, dd6; + // multiplication result digits for each column + long c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13; + + // Row 0 - multiply by aa0 + d0 = aa0 * aa0; + dd0 = Math.multiplyHigh(aa0, aa0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + d1 = aa0 * aa1; + dd1 = Math.multiplyHigh(aa0, aa1) << shift1 | (d1 >>> shift2); + d1 &= LIMB_MASK; + + d2 = aa0 * aa2; + dd2 = Math.multiplyHigh(aa0, aa2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa0 * aa3; + dd3 = Math.multiplyHigh(aa0, aa3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa0 * aa4; + dd4 = Math.multiplyHigh(aa0, aa4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + d5 = aa0 * aa5; + dd5 = Math.multiplyHigh(aa0, aa5) << shift1 | (d5 >>> shift2); + d5 &= LIMB_MASK; + + d6 = aa0 * aa6; + dd6 = Math.multiplyHigh(aa0, aa6) << shift1 | (d6 >>> shift2); + d6 &= LIMB_MASK; + + c0 = d0; + c1 = (d1 << 1) + dd0; + c2 = (d2 + dd1) << 1; + c3 = (d3 + dd2) << 1; + c4 = (d4 + dd3) << 1; + c5 = (d5 + dd4) << 1; + c6 = (d6 + dd5) << 1; + c7 = dd6 << 1; + + // Row 1 - multiply by aa1 + d1 = aa1 * aa1; + dd1 = Math.multiplyHigh(aa1, aa1) << shift1 | (d1 >>> shift2); + d1 &= LIMB_MASK; + + d2 = aa1 * aa2; + dd2 = Math.multiplyHigh(aa1, aa2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa1 * aa3; + dd3 = Math.multiplyHigh(aa1, aa3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa1 * aa4; + dd4 = Math.multiplyHigh(aa1, aa4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + d5 = aa1 * aa5; + dd5 = Math.multiplyHigh(aa1, aa5) << shift1 | (d5 >>> shift2); + d5 &= LIMB_MASK; + + d6 = aa1 * aa6; + dd6 = Math.multiplyHigh(aa1, aa6) << shift1 | (d6 >>> shift2); + d6 &= LIMB_MASK; + + c2 += d1; + c3 += (d2 << 1) + dd1; + c4 += (d3 + dd2) << 1; + c5 += (d4 + dd3) << 1; + c6 += (d5 + dd4) << 1; + c7 += (d6 + dd5) << 1; + c8 = dd6 << 1; + + // Row 2 - multiply by aa2 + d2 = aa2 * aa2; + dd2 = Math.multiplyHigh(aa2, aa2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa2 * aa3; + dd3 = Math.multiplyHigh(aa2, aa3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa2 * aa4; + dd4 = Math.multiplyHigh(aa2, aa4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + d5 = aa2 * aa5; + dd5 = Math.multiplyHigh(aa2, aa5) << shift1 | (d5 >>> shift2); + d5 &= LIMB_MASK; + + d6 = aa2 * aa6; + dd6 = Math.multiplyHigh(aa2, aa6) << shift1 | (d6 >>> shift2); + d6 &= LIMB_MASK; + + c4 += d2; + c5 += (d3 << 1) + dd2; + c6 += (d4 + dd3) << 1; + c7 += (d5 + dd4) << 1; + c8 += (d6 + dd5) << 1; + c9 = dd6 << 1; + + // Row 3 - multiply by aa3 + d3 = aa3 * aa3; + dd3 = Math.multiplyHigh(aa3, aa3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa3 * aa4; + dd4 = Math.multiplyHigh(aa3, aa4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + d5 = aa3 * aa5; + dd5 = Math.multiplyHigh(aa3, aa5) << shift1 | (d5 >>> shift2); + d5 &= LIMB_MASK; + + d6 = aa3 * aa6; + dd6 = Math.multiplyHigh(aa3, aa6) << shift1 | (d6 >>> shift2); + d6 &= LIMB_MASK; + + c6 += d3; + c7 += (d4 << 1) + dd3; + c8 += (d5 + dd4) << 1; + c9 += (d6 + dd5) << 1; + c10 = dd6 << 1; + + // Row 4 - multiply by aa4 + d4 = aa4 * aa4; + dd4 = Math.multiplyHigh(aa4, aa4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + d5 = aa4 * aa5; + dd5 = Math.multiplyHigh(aa4, aa5) << shift1 | (d5 >>> shift2); + d5 &= LIMB_MASK; + + d6 = aa4 * aa6; + dd6 = Math.multiplyHigh(aa4, aa6) << shift1 | (d6 >>> shift2); + d6 &= LIMB_MASK; + + c8 += d4; + c9 += (d5 << 1) + dd4; + c10 += (d6 + dd5) << 1; + c11 = dd6 << 1; + + // Row 5 - multiply by aa5 + d5 = aa5 * aa5; + dd5 = Math.multiplyHigh(aa5, aa5) << shift1 | (d5 >>> shift2); + d5 &= LIMB_MASK; + + d6 = aa5 * aa6; + dd6 = Math.multiplyHigh(aa5, aa6) << shift1 | (d6 >>> shift2); + d6 &= LIMB_MASK; + + c10 += d5; + c11 += (d6 << 1) + dd5; + c12 = dd6 << 1; + + // Row 6 - multiply by aa6 + d6 = aa6 * aa6; + dd6 = Math.multiplyHigh(aa6, aa6) << shift1 | (d6 >>> shift2); + d6 &= LIMB_MASK; + + c12 += d6; + c13 = dd6; + + carryReduceUpper(r, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, + c12, c13); + } +} diff --git a/src/java.base/share/data/cacerts/comodoaaaca b/src/java.base/share/data/cacerts/comodoaaaca deleted file mode 100644 index 76fc7fcf5f6a..000000000000 --- a/src/java.base/share/data/cacerts/comodoaaaca +++ /dev/null @@ -1,32 +0,0 @@ -Owner: CN=AAA Certificate Services, O=Comodo CA Limited, L=Salford, ST=Greater Manchester, C=GB -Issuer: CN=AAA Certificate Services, O=Comodo CA Limited, L=Salford, ST=Greater Manchester, C=GB -Serial number: 1 -Valid from: Thu Jan 01 00:00:00 GMT 2004 until: Sun Dec 31 23:59:59 GMT 2028 -Signature algorithm name: SHA1withRSA -Subject Public Key Algorithm: 2048-bit RSA key -Version: 3 ------BEGIN CERTIFICATE----- -MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb -MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow -GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj -YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL -MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE -BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM -GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua -BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe -3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 -YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR -rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm -ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU -oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF -MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v -QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t -b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF -AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q -GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz -Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 -G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi -l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 -smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== ------END CERTIFICATE----- diff --git a/src/java.base/share/data/cacerts/emsigneccrootcag3 b/src/java.base/share/data/cacerts/emsigneccrootcag3 deleted file mode 100644 index a286f81c2f45..000000000000 --- a/src/java.base/share/data/cacerts/emsigneccrootcag3 +++ /dev/null @@ -1,22 +0,0 @@ -Owner: CN=emSign ECC Root CA - G3, O=eMudhra Technologies Limited, OU=emSign PKI, C=IN -Issuer: CN=emSign ECC Root CA - G3, O=eMudhra Technologies Limited, OU=emSign PKI, C=IN -Serial number: 3cf607a968700eda8b84 -Valid from: Sun Feb 18 18:30:00 GMT 2018 until: Wed Feb 18 18:30:00 GMT 2043 -Signature algorithm name: SHA384withECDSA -Subject Public Key Algorithm: 384-bit EC (secp384r1) key -Version: 3 ------BEGIN CERTIFICATE----- -MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQG -EwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNo -bm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g -RzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4MTgzMDAwWjBrMQswCQYDVQQGEwJJ -TjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9s -b2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMw -djAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0 -WXTsuwYc58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xyS -fvalY8L1X44uT6EYGQIrMgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuB -zhccLikenEhjQjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggq -hkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+DCBeQyh+KTOgNG3qxrdWB -CUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7jHvrZQnD -+JbNR6iC8hZVdyR+EhCVBCyj ------END CERTIFICATE----- diff --git a/src/java.base/share/data/cacerts/emsignrootcag1 b/src/java.base/share/data/cacerts/emsignrootcag1 deleted file mode 100644 index 6b06f6689bc0..000000000000 --- a/src/java.base/share/data/cacerts/emsignrootcag1 +++ /dev/null @@ -1,29 +0,0 @@ -Owner: CN=emSign Root CA - G1, O=eMudhra Technologies Limited, OU=emSign PKI, C=IN -Issuer: CN=emSign Root CA - G1, O=eMudhra Technologies Limited, OU=emSign PKI, C=IN -Serial number: 31f5e4620c6c58edd6d8 -Valid from: Sun Feb 18 18:30:00 GMT 2018 until: Wed Feb 18 18:30:00 GMT 2043 -Signature algorithm name: SHA256withRSA -Subject Public Key Algorithm: 2048-bit RSA key -Version: 3 ------BEGIN CERTIFICATE----- -MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYD -VQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBU -ZWNobm9sb2dpZXMgTGltaXRlZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBH -MTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgxODMwMDBaMGcxCzAJBgNVBAYTAklO -MRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVkaHJhIFRlY2hub2xv -Z2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIBIjAN -BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQz -f2N4aLTNLnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO -8oG0x5ZOrRkVUkr+PHB1cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aq -d7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHWDV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhM -tTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ6DqS0hdW5TUaQBw+jSzt -Od9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrHhQIDAQAB -o0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQD -AgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31x -PaOfG1vR2vjTnGs2vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjM -wiI/aTvFthUvozXGaCocV685743QNcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6d -GNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q+Mri/Tm3R7nrft8EI6/6nAYH -6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeihU80Bv2noWgby -RQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx -iN66zB+Afko= ------END CERTIFICATE----- diff --git a/src/java.base/share/data/cacerts/emsignrootcag2 b/src/java.base/share/data/cacerts/emsignrootcag2 deleted file mode 100644 index e4e4ddda65a6..000000000000 --- a/src/java.base/share/data/cacerts/emsignrootcag2 +++ /dev/null @@ -1,39 +0,0 @@ -Owner: CN=emSign Root CA - G2, O=eMudhra Technologies Limited, OU=emSign PKI, C=IN -Issuer: CN=emSign Root CA - G2, O=eMudhra Technologies Limited, OU=emSign PKI, C=IN -Serial number: 864dbf0fe35ed77d8ed8 -Valid from: Sun Feb 18 18:30:00 GMT 2018 until: Wed Feb 18 18:30:00 GMT 2043 -Signature algorithm name: SHA384withRSA -Subject Public Key Algorithm: 4096-bit RSA key -Version: 3 ------BEGIN CERTIFICATE----- -MIIFlTCCA32gAwIBAgILAIZNvw/jXtd9jtgwDQYJKoZIhvcNAQEMBQAwZzELMAkG -A1UEBhMCSU4xEzARBgNVBAsTCmVtU2lnbiBQS0kxJTAjBgNVBAoTHGVNdWRocmEg -VGVjaG5vbG9naWVzIExpbWl0ZWQxHDAaBgNVBAMTE2VtU2lnbiBSb290IENBIC0g -RzIwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4MTgzMDAwWjBnMQswCQYDVQQGEwJJ -TjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9s -b2dpZXMgTGltaXRlZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBHMjCCAiIw -DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMNwGIWW2kHfHK+sXTNwxF07K+IV -ySTuyFM2r1v002wUfcdT+zs5OM5QbMYFFnedXQI6gCFLsjKrcaej48Zt37OyEb3i -aPs7CsP4kAyTwzKH9aZe6gXYHrJq40/ZVMNcQVI2PcIp40B/SAN2gUZ+ZaUtIOvV -jEx26/ebNaXRIsthlkOG/caB+QRwDw1tl7338Zlv0M2oTBUy4B3e7dGP5pgXH71M -jqHPCoNo+xv9f0NTBT+hUDa8h8wUtcGQq9CDeJTpjWcD2bP2AMdVG6oVpMAUeUzo -cCyglvtFdUMjggxBbw4qhau1HXPG8Ot9hwL7ZMi8tkTzrvUIxxb8G9LF/7kKeCE7 -tGZaVzDTnXuifl3msR4ErHsQ4P7lVu2AIjIAhrAXoedDidb7pMcf7TABdrYUT1Jo -G/AiK+J9jO6GTjeADD4LMDSBZhHMuBK/PJ/g0kGBt+/C1L+/HURzQhJkMlRnM6Rv -XoCtfKopSlns5trZmTi971Wjbn88QXP61lGpBCUPwCjs7rpOYvSUJtI+lcbF+37q -kIqOXYkVT3cupDSpw+H89kFtj5GKY+Xny4LxY+3IvDIRiyd6ky1DPj713DI0yqve -EpsIr3A0PdwuyUI7CS1jg0NnGFT6Xxyr0xB+VDt83FJYW8v16k2pbaQ4kVxA3aXd -X9dZYyVR1S59KM75AgMBAAGjQjBAMB0GA1UdDgQWBBTt7E1FYRgo57MjKBEcTaUn -DV7s9DAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B -AQwFAAOCAgEACFC/ilQg8KTCVBxFJW/sazomkS0kNYbEIZg4B3obqwsJ7SX98z8Z -gfzBpz0nYClwwJjWbFN1R2zY8pCEot6/dgmA8Vbq0GxhwPM5YN/SZquNyRIxO3cU -dlAcwf+vSezdVCf9wOzvSAF3q0a5ljvbdbNJNpfScQVp7UUd5sBsZk8jXO1KQ/go -/Vf/GDPnrIFmxpAIGE3sgnO8lAv9FzUaAeuv7HWe47xN9J7+bQzF93yHuIXACPTL -pQHhg2zMv5C7BAbuDHfbj1Cu294Z832yhSfBcziWGskOvl3es2EcHytbS9c9P+0z -Mpka7zGC1FHrvLb/FoduH86TeZt0QjZ6pcplNzoaxDnDvzTJ6CC2Eny+qH/APFCu -VUv5/wjwF+HPm8Pup2ARj9cEp92+0qcerfHacNq5hMeGZdbA/dzdUR/5z5zXdxAk -nl8mcfGb0eMNSTXQmmB/i4AecNnr72uYjzlaXUGYN7Nrb6XouG0pnh0/BBtWWp0U -ShIPpWEAqs7RJBj6+1ZUYXZ4ObrCw962DxhN2p19Hxw9LtuUUcLqqTPrFXYvwO4t -ouj7KJnAkaTUfXGdEaFVtFig1EA30WzJY2X1vAQ7hVnniCjgaXAGqjsU6sklNM9n -xDx5rFCCCEtj9Kh8UHjGK2QqgP5kwgttjOApQMaCoezMfK4KD7WpOXU= ------END CERTIFICATE----- diff --git a/src/java.base/share/man/keytool.md b/src/java.base/share/man/keytool.md index faa2ff563a17..b7b6b0a28bc2 100644 --- a/src/java.base/share/man/keytool.md +++ b/src/java.base/share/man/keytool.md @@ -1325,9 +1325,9 @@ which is equivalent to because `-keyalg` is a single-valued option and the `ec` value specified on the command line overrides the preconfigured options file. -## Examples of Option Values +## Defaults of Option Values -The following examples show the defaults for various option values: +These are the defaults for various option values: ``` -alias "mykey" @@ -1335,13 +1335,15 @@ The following examples show the defaults for various option values: -keysize 2048 (when using -genkeypair and -keyalg is "DSA") 3072 (when using -genkeypair and -keyalg is "RSA", "RSASSA-PSS", or "DH") - 384 (when using -genkeypair and -keyalg is "EC") 56 (when using -genseckey and -keyalg is "DES") 168 (when using -genseckey and -keyalg is "DESede") -groupname - ed25519 (when using -genkeypair and -keyalg is "EdDSA", key size is 255) - x25519 (when using -genkeypair and -keyalg is "XDH", key size is 255) + secp384r1 (when using -genkeypair and -keyalg is "EC") + ed25519 (when using -genkeypair and -keyalg is "EdDSA") + x25519 (when using -genkeypair and -keyalg is "XDH") + ML-DSA-65 (when using -genkeypair and -keyalg is "ML-DSA") + ML-KEM-768 (when using -genkeypair and -keyalg is "ML-KEM") -validity 90 @@ -1378,10 +1380,6 @@ RSASSA-PSS \< 624 RSASSA-PSS (with SHA-256, key size is too small for using SHA-384) \<= 7680 RSASSA-PSS (with SHA-384) \> 7680 RSASSA-PSS (with SHA-512) -EdDSA 255 Ed25519 - 448 Ed448 -Ed25519 255 Ed25519 -Ed448 448 Ed448 ------- -------- -------------- * The key size, measured in bits, corresponds to the size of the private key. @@ -1394,6 +1392,9 @@ algorithm as its hash and MGF1 algorithms. * If neither a default `-keysize` or `-groupname` is defined for an algorithm, the security provider will choose a default setting. +* Modern digital signature algorithms such as EdDSA and ML-DSA use the same +algorithm as the key. When using such a key, specifying `-sigalg` is unnecessary. + **Note:** To improve out of the box security, default keysize, groupname, and signature diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixConstants.java.template b/src/java.base/unix/classes/sun/nio/fs/UnixConstants.java.template index 7a9a22ac40db..0fbdd22c6d35 100644 --- a/src/java.base/unix/classes/sun/nio/fs/UnixConstants.java.template +++ b/src/java.base/unix/classes/sun/nio/fs/UnixConstants.java.template @@ -30,7 +30,7 @@ #include #include #include -#ifdef _ALLBSD_SOURCE +#ifdef __APPLE__ #include #include #endif @@ -152,7 +152,7 @@ class UnixConstants { static final int PREFIX_AT_REMOVEDIR = 00; #endif -#ifdef _ALLBSD_SOURCE +#ifdef __APPLE__ // flags used with clonefile static final int PREFIX_CLONE_NOFOLLOW = CLONE_NOFOLLOW; static final int PREFIX_CLONE_NOOWNERCOPY = CLONE_NOOWNERCOPY; @@ -170,7 +170,7 @@ class UnixConstants { static final int PREFIX_FSOPT_NOFOLLOW = 00; #endif -#ifdef __linux__ +#if defined(__linux__) || defined(_BSDONLY_SOURCE) // advice flags used with posix_fadvise(2) static final int PREFIX_POSIX_FADV_SEQUENTIAL = POSIX_FADV_SEQUENTIAL; #endif diff --git a/src/java.desktop/share/native/libsplashscreen/splashscreen_gif.c b/src/java.desktop/share/native/libsplashscreen/splashscreen_gif.c index 4f2cfca8dd09..4b75bf37d34e 100644 --- a/src/java.desktop/share/native/libsplashscreen/splashscreen_gif.c +++ b/src/java.desktop/share/native/libsplashscreen/splashscreen_gif.c @@ -281,7 +281,8 @@ SplashDecodeGif(Splash * splash, GifFileType * gif) if (colorMap && colorMap->Colors && - transparentColor < 0) { + transparentColor < 0 && + gif->SBackGroundColor < colorMap->ColorCount) { fillColor= MAKE_QUAD_GIF( colorMap->Colors[gif->SBackGroundColor], 0xff); } diff --git a/src/java.management/share/classes/javax/management/ImmutableDescriptor.java b/src/java.management/share/classes/javax/management/ImmutableDescriptor.java index 731dd619d268..2dd8f7f5e1d8 100644 --- a/src/java.management/share/classes/javax/management/ImmutableDescriptor.java +++ b/src/java.management/share/classes/javax/management/ImmutableDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -373,10 +373,12 @@ public boolean equals(Object o) { @Override public int hashCode() { - if (hashCode == -1) { - hashCode = Util.hashCode(names, values); + int hash = hashCode; + if (hash == -1) { + hash = Util.hashCode(names, values); + hashCode = hash; } - return hashCode; + return hash; } @Override diff --git a/src/java.management/share/classes/javax/management/MBeanInfo.java b/src/java.management/share/classes/javax/management/MBeanInfo.java index df54b4228e2b..68ba94bd9ae4 100644 --- a/src/java.management/share/classes/javax/management/MBeanInfo.java +++ b/src/java.management/share/classes/javax/management/MBeanInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, 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 @@ -511,16 +511,18 @@ public int hashCode() { We don't bother synchronizing, because, at worst, n different threads will compute the same hashCode at the same time. */ - if (hashCode != 0) - return hashCode; + int hash = hashCode; + if (hash != 0) + return hash; - hashCode = Objects.hash(getClassName(), getDescriptor()) + hash = Objects.hash(getClassName(), getDescriptor()) ^ Arrays.hashCode(fastGetAttributes()) ^ Arrays.hashCode(fastGetOperations()) ^ Arrays.hashCode(fastGetConstructors()) ^ Arrays.hashCode(fastGetNotifications()); - return hashCode; + hashCode = hash; + return hash; } /** diff --git a/src/java.management/share/classes/javax/management/remote/JMXServiceURL.java b/src/java.management/share/classes/javax/management/remote/JMXServiceURL.java index 44fdd75ec7b6..35b512719d0c 100644 --- a/src/java.management/share/classes/javax/management/remote/JMXServiceURL.java +++ b/src/java.management/share/classes/javax/management/remote/JMXServiceURL.java @@ -647,8 +647,9 @@ public String getURLPath() { public String toString() { /* We don't bother synchronizing the access to toString. At worst, n threads will independently compute and store the same value. */ - if (toString != null) - return toString; + String value = toString; + if (value != null) + return value; StringBuilder buf = new StringBuilder("service:jmx:"); buf.append(getProtocol()).append("://"); final String getHost = getHost(); @@ -660,8 +661,9 @@ public String toString() { if (getPort != 0) buf.append(':').append(getPort); buf.append(getURLPath()); - toString = buf.toString(); - return toString; + value = buf.toString(); + toString = value; + return value; } /** diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/common/SSLFlowDelegate.java b/src/java.net.http/share/classes/jdk/internal/net/http/common/SSLFlowDelegate.java index 2fa166028be4..79681455ebe1 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/common/SSLFlowDelegate.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/common/SSLFlowDelegate.java @@ -152,7 +152,7 @@ public SSLFlowDelegate(SSLEngine engine, this.exec = exec; this.handshakeState = new AtomicInteger(NOT_HANDSHAKING); this.readerCF = reader.completion(); - this.writerCF = reader.completion(); + this.writerCF = writer.completion(); readerCF.exceptionally(this::stopOnError); writerCF.exceptionally(this::stopOnError); this.stopCF = CompletableFuture.allOf(reader.completion(), writer.completion()) diff --git a/src/java.sql.rowset/share/classes/com/sun/rowset/internal/CachedRowSetWriter.java b/src/java.sql.rowset/share/classes/com/sun/rowset/internal/CachedRowSetWriter.java index 9f3f99d99034..339833ba464d 100644 --- a/src/java.sql.rowset/share/classes/com/sun/rowset/internal/CachedRowSetWriter.java +++ b/src/java.sql.rowset/share/classes/com/sun/rowset/internal/CachedRowSetWriter.java @@ -477,28 +477,6 @@ private boolean updateOriginalRow(CachedRowSet crs) try { updateWhere = buildWhereClause(updateWhere, origVals); - - /** - * The following block of code is for checking a particular type of - * query where in there is a where clause. Without this block, if a - * SQL statement is built the "where" clause will appear twice hence - * the DB errors out and a SQLException is thrown. This code also - * considers that the where clause is in the right place as the - * CachedRowSet object would already have been populated with this - * query before coming to this point. - **/ - - - String tempselectCmd = selectCmd.toLowerCase(); - - int idxWhere = tempselectCmd.indexOf("where"); - - if(idxWhere != -1) - { - String tempSelect = selectCmd.substring(0,idxWhere); - selectCmd = tempSelect; - } - pstmt = con.prepareStatement(selectCmd + updateWhere, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY); @@ -1125,24 +1103,6 @@ private void initSQLStatements(CachedRowSet caller) throws SQLException { updateCmd = "UPDATE " + buildTableName(dbmd, catalog, schema, table); - /** - * The following block of code is for checking a particular type of - * query where in there is a where clause. Without this block, if a - * SQL statement is built the "where" clause will appear twice hence - * the DB errors out and a SQLException is thrown. This code also - * considers that the where clause is in the right place as the - * CachedRowSet object would already have been populated with this - * query before coming to this point. - **/ - - String tempupdCmd = updateCmd.toLowerCase(); - - int idxupWhere = tempupdCmd.indexOf("where"); - - if(idxupWhere != -1) - { - updateCmd = updateCmd.substring(0,idxupWhere); - } updateCmd += "SET "; /* diff --git a/src/jdk.attach/aix/native/libattach/VirtualMachineImpl.c b/src/jdk.attach/aix/native/libattach/VirtualMachineImpl.c index 5d6252cbccd6..4a9574734a97 100644 --- a/src/jdk.attach/aix/native/libattach/VirtualMachineImpl.c +++ b/src/jdk.attach/aix/native/libattach/VirtualMachineImpl.c @@ -1,6 +1,6 @@ /* - * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2015, 2024 SAP SE. All rights reserved. + * Copyright (c) 2008, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -132,13 +132,14 @@ JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_checkPermissions memset(&sb, 0, sizeof(struct stat)); /* - * Check that the path is owned by the effective uid/gid of this - * process. Also check that group/other access is not allowed. + * Check that the path itself (not following symbolic links) is + * owned by the effective uid/gid of this process and that it is + * a socket. Also check that group/other access is not allowed. */ uid = geteuid(); gid = getegid(); - res = stat(p, &sb); + res = lstat(p, &sb); if (res != 0) { /* save errno */ res = errno; @@ -147,7 +148,11 @@ JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_checkPermissions if (res == 0) { char msg[100]; jboolean isError = JNI_FALSE; - if (sb.st_uid != uid && uid != ROOT_UID) { + if (!S_ISSOCK(sb.st_mode)) { + snprintf(msg, sizeof(msg), + "file is expected to be a socket: S_ISSOCK(sb.st_mode): %d", S_ISSOCK(sb.st_mode)); + isError = JNI_TRUE; + } else if (sb.st_uid != uid && uid != ROOT_UID) { snprintf(msg, sizeof(msg), "file should be owned by the current user (which is %d) but is owned by %d", uid, sb.st_uid); isError = JNI_TRUE; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java index c0852f26f902..c5d5cccd1085 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java @@ -2275,6 +2275,10 @@ private boolean shouldEmitOuterThis(ClassSymbol sym) { // Class is serializable return true; } + if (sym.isValueClass()) { + // Omitting enclosing instance field in value classes changes == + return true; + } return false; } @@ -3102,8 +3106,7 @@ public void visitConditional(JCConditional tree) { } //where private JCExpression convert(JCExpression tree, Type pt) { - if (tree.type == pt || tree.type.hasTag(BOT)) - return tree; + if (tree.type == pt) return tree; JCExpression result = make_at(tree.pos()).TypeCast(make.Type(pt), tree); result.type = (tree.type.constValue() != null) ? cfolder.coerce(tree.type, pt) : pt; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/UnicodeReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/UnicodeReader.java index c78694082c82..999de53393d5 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/UnicodeReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/UnicodeReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -242,10 +242,12 @@ private void nextCodePoint() { return; } - // Capture high surrogate and position. + // Capture high surrogate, position, and backslash state char hi = character; int savePosition = position; int saveWidth = width; + boolean saveWasBackslash = wasBackslash; + boolean saveWasUnicodeEscape = wasUnicodeEscape; // Get potential low surrogate. nextUnicodeInputCharacter(); @@ -263,6 +265,8 @@ private void nextCodePoint() { width = saveWidth; character = hi; codepoint = (int)hi; + wasBackslash = saveWasBackslash; + wasUnicodeEscape = saveWasUnicodeEscape; // Could potential report an error here (old code did not.) } } diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Tls12ExtendedMasterSecretGenerator.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Tls12ExtendedMasterSecretGenerator.java index 054baca520b7..bd5b800950e0 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Tls12ExtendedMasterSecretGenerator.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Tls12ExtendedMasterSecretGenerator.java @@ -35,7 +35,7 @@ import static sun.security.pkcs11.wrapper.PKCS11Constants.*; /** - * KeyGenerator for the TLS extended master secret. + * KeyGenerator for the TLS 1.2 extended master secret. * See RFC 7627 for further details. * * @since x.x @@ -45,7 +45,7 @@ public final class P11Tls12ExtendedMasterSecretGenerator extends KeyGeneratorSpi // See RFC 5246, section 8.1 Computing the Master Secret private static final int TLS_MASTER_SECRET_LEN = 48; - private static final String MSG = "TlsExtendedMasterSecretGenerator must be " + private static final String MSG = "Tls12ExtendedMasterSecretGenerator must be " + "initialized using a TlsMasterSecretParameterSpec"; private final Token token; @@ -129,7 +129,7 @@ protected void engineInit(int keysize, SecureRandom random) { protected SecretKey engineGenerateKey() { if (spec == null) { throw new IllegalStateException( - "TlsExtendedMasterSecretGenerator must be initialized"); + "Tls12ExtendedMasterSecretGenerator must be initialized"); } CK_MECHANISM ckMechanism = new CK_MECHANISM( diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java index 276f9e14a814..9dd3dd1942d5 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java @@ -1033,7 +1033,7 @@ private static void register(Descriptor d) { "sun.security.pkcs11.P11TlsMasterSecretGenerator", m(CKM_TLS12_MASTER_KEY_DERIVE, CKM_TLS12_MASTER_KEY_DERIVE_DH)); d(KG, "SunTls12ExtendedMasterSecret", - "sun.security.pkcs11.P11TlsExtendedMasterSecretGenerator", + "sun.security.pkcs11.P11Tls12ExtendedMasterSecretGenerator", m(CKM_TLS12_EXTENDED_MASTER_KEY_DERIVE, CKM_TLS12_EXTENDED_MASTER_KEY_DERIVE_DH)); d(KG, "SunTlsKeyMaterial", diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp index ab1a031bbe23..46f435184258 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp @@ -23,14 +23,18 @@ * */ +#include #include #include #include "dwarf.hpp" +#include "salibelf.h" #include "libproc_impl.h" DwarfParser::DwarfParser(lib_info *lib) : _lib(lib), _buf(NULL), + _dwarf_version(0), + _is_64bit_dwarf(false), _has_augmentation(false), _fde_ptr_encoding(0), _code_factor(0), @@ -74,18 +78,24 @@ uintptr_t DwarfParser::read_leb(bool sign) { } uint64_t DwarfParser::get_entry_length() { + _is_64bit_dwarf = false; uint64_t length = *(reinterpret_cast(_buf)); _buf += 4; if (length == 0xffffffff) { length = *(reinterpret_cast(_buf)); _buf += 8; + _is_64bit_dwarf = true; } return length; } -bool DwarfParser::process_cie(unsigned char *start_of_entry, uint32_t id) { +bool DwarfParser::process_cie(unsigned char *start_of_entry, uint64_t id) { unsigned char *orig_pos = _buf; - _buf = start_of_entry - id; + + // CIE pointer means the offset from FDE in .eh_frame. + // In .debug_frame, CIE pointer means the offset from start of .debug_frame . + _buf = _lib->frame.is_debug_frame ? _lib->frame.data + id + : start_of_entry - id; uint64_t length = get_entry_length(); if (length == 0L) { @@ -93,8 +103,13 @@ bool DwarfParser::process_cie(unsigned char *start_of_entry, uint32_t id) { } unsigned char *end = _buf + length; - _buf += 4; // Skip ID (This value of CIE would be always 0) - _buf++; // Skip version (assume to be "1") + // Skip ID + // Linux LSB Core defines 4byte unsigned value for CIE ID, + // thus need to check whether it is .debug_frame or not. + _buf += _is_64bit_dwarf && _lib->frame.is_debug_frame ? 8 : 4; + + _dwarf_version = *_buf; + _buf++; // Skip version char *augmentation_string = reinterpret_cast(_buf); bool has_ehdata = (strcmp("eh", augmentation_string) == 0); @@ -103,6 +118,21 @@ bool DwarfParser::process_cie(unsigned char *start_of_entry, uint32_t id) { _buf += sizeof(void *); // Skip EH data } + // address_size and segment_size are located in DWARF 4 or later, + // but they are not available in .eh_frame defined by Linux LSB Core Generic 5.0. + if (_lib->frame.is_debug_frame && _dwarf_version >= 4) { + if (*_buf != sizeof(void*)) { + print_warning("DWARF: address size in CIE does not match pointer size (%u).\n", *_buf); + return false; + } + _buf++; // Skip address size + if (*_buf != 0) { + print_warning("DWARF: segmented addressing is not supported.\n"); + return false; + } + _buf++; // Skip segment size + } + _code_factor = read_leb(false); _data_factor = static_cast(read_leb(true)); enum DWARF_Register initial_ra = static_cast(*_buf++); @@ -337,18 +367,46 @@ bool DwarfParser::process_dwarf(const uintptr_t pc) { // https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html _buf = _lib->frame.data; unsigned char *end = _lib->frame.data + _lib->frame.size; - while (_buf <= end) { + while (_buf < end) { uint64_t length = get_entry_length(); if (length == 0L) { + break; // it means "terminator" in .eh_frame, so go through to .debug_frame + } else if (_buf + length > end) { + // unexpected: DWARF is broken? + print_warning("DWARF: reached end of buffer even though DwarfParser did not find the pc (0x%lx).\n", pc); return false; } unsigned char *next_entry = _buf + length; unsigned char *start_of_entry = _buf; - uint32_t id = *(reinterpret_cast(_buf)); - _buf += 4; - if (id != 0) { // FDE - uintptr_t pc_begin = get_decoded_value(_fde_ptr_encoding) + _lib->base; - uintptr_t pc_end = pc_begin + get_pc_range(); + + // Linux LSB Core defines 4byte unsigned value for CIE ID, + // thus need to check whether it is .debug_frame or not. + uint64_t id; + bool is_fde; + if (_is_64bit_dwarf && _lib->frame.is_debug_frame) { + id = *(reinterpret_cast(_buf)); + _buf += 8; + is_fde = id != 0xffffffffffffffff; + } else { + id = *(reinterpret_cast(_buf)); + _buf += 4; + is_fde = (_lib->frame.is_debug_frame ? 0xffffffff : 0) != id; + } + + if (is_fde) { + uintptr_t begin_ofs = 0L; + uintptr_t inst_sz = 0L; + if (_lib->frame.is_debug_frame) { + begin_ofs = *(reinterpret_cast(_buf)); + _buf += sizeof(void*); + inst_sz = *(reinterpret_cast(_buf)); + _buf += sizeof(void*); + } else { + begin_ofs = get_decoded_value(_fde_ptr_encoding); + inst_sz = get_pc_range(); + } + uintptr_t pc_begin = begin_ofs + _lib->base; + uintptr_t pc_end = pc_begin + inst_sz; if ((pc >= pc_begin) && (pc < pc_end)) { // Process CIE @@ -370,5 +428,31 @@ bool DwarfParser::process_dwarf(const uintptr_t pc) { _buf = next_entry; } - return false; + bool result = false; + // try again with .debug_frame section if it hasn't been tried yet. + if (!_lib->frame.tried_debug_frame && _lib->fd != -1) { + // attempts to load .debug_frame from executables. + frame_info frame = {}; + if (!read_frame(".debug_frame", _lib->fd, &frame)) { + // attempts again to load .debug_frame from debuginfo if it could not be loaded from executables. + int debug_fd = open_debuginfo(_lib->name, _lib->fd); + if (debug_fd != -1) { + read_frame(".debug_frame", debug_fd, &frame); + close(debug_fd); + } + } + frame.tried_debug_frame = true; + _lib->frame.tried_debug_frame = true; + + // try process_dwarf() again with .debug_frame. + if (frame.data != NULL) { + if (_lib->frame.data != NULL) { + free(_lib->frame.data); + } + memcpy(&_lib->frame, &frame, sizeof(frame_info)); + result = process_dwarf(pc); + } + } + + return result; } diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.hpp b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.hpp index 8d5325b188e8..26c500601566 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.hpp +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.hpp @@ -61,8 +61,10 @@ struct DwarfState { */ class DwarfParser { private: - const lib_info *_lib; + lib_info *_lib; unsigned char *_buf; + unsigned char _dwarf_version; + unsigned char _is_64bit_dwarf; bool _has_augmentation; unsigned char _fde_ptr_encoding; unsigned int _code_factor; @@ -74,7 +76,7 @@ class DwarfParser { void init_state(struct DwarfState& st); uint64_t get_entry_length(); - bool process_cie(unsigned char *start_of_entry, uint32_t id); + bool process_cie(unsigned char *start_of_entry, uint64_t id); bool parse_dwarf_instructions(uintptr_t begin, uintptr_t pc, const unsigned char *end); uint32_t get_decoded_value(unsigned char enc); unsigned int get_pc_range(); diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c index 06bc4153aba3..e0ee95bb4951 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c @@ -136,6 +136,15 @@ static void destroy_lib_info(struct ps_prochandle* ph) { if (lib->symtab) { destroy_symtab(lib->symtab); } + if (lib->fd >= 0) { + if (ph->core == NULL) { + close(lib->fd); + } else if (lib->fd != ph->core->exec_fd) { + // Prevents double-close in core mode. + // exec_fd would be closed at close_files(). + close(lib->fd); + } + } free(lib->frame.data); free(lib); lib = next; @@ -156,9 +165,8 @@ static void destroy_thread_info(struct ps_prochandle* ph) { // ps_prochandle cleanup JNIEXPORT void JNICALL Prelease(struct ps_prochandle* ph) { - // do the "derived class" clean-up first - ph->ops->release(ph); destroy_lib_info(ph); + ph->ops->release(ph); destroy_thread_info(ph); free(ph); } @@ -222,7 +230,7 @@ static bool fill_addr_info(lib_info* lib) { return (lib->end != -1L) && (lib->exec_start != -1L) && (lib->exec_end != -1L); } -bool read_eh_frame(struct ps_prochandle* ph, lib_info* lib) { +bool read_frame(const char* section, int fd, frame_info* frame) { off_t current_pos = -1; ELF_EHDR ehdr; ELF_SHDR* shbuf = NULL; @@ -230,26 +238,27 @@ bool read_eh_frame(struct ps_prochandle* ph, lib_info* lib) { char* strtab = NULL; int cnt; - current_pos = lseek(lib->fd, (off_t)0L, SEEK_CUR); - lseek(lib->fd, (off_t)0L, SEEK_SET); + current_pos = lseek(fd, (off_t)0L, SEEK_CUR); + lseek(fd, (off_t)0L, SEEK_SET); - read_elf_header(lib->fd, &ehdr); - shbuf = read_section_header_table(lib->fd, &ehdr); - strtab = read_section_data(lib->fd, &ehdr, &shbuf[ehdr.e_shstrndx]); + read_elf_header(fd, &ehdr); + shbuf = read_section_header_table(fd, &ehdr); + strtab = read_section_data(fd, &ehdr, &shbuf[ehdr.e_shstrndx]); for (cnt = 0, sh = shbuf; cnt < ehdr.e_shnum; cnt++, sh++) { - if (strcmp(".eh_frame", sh->sh_name + strtab) == 0) { - lib->frame.v_addr = sh->sh_addr; - lib->frame.data = read_section_data(lib->fd, &ehdr, sh); - lib->frame.size = sh->sh_size; + if (strcmp(section, sh->sh_name + strtab) == 0) { + frame->v_addr = sh->sh_addr; + frame->data = read_section_data(fd, &ehdr, sh); + frame->size = sh->sh_size; + frame->is_debug_frame = strcmp(section, ".debug_frame") == 0; break; } } free(strtab); free(shbuf); - lseek(lib->fd, current_pos, SEEK_SET); - return lib->frame.data != NULL; + lseek(fd, current_pos, SEEK_SET); + return frame->data != NULL; } lib_info* add_lib_info_fd(struct ps_prochandle* ph, const char* libname, int fd, uintptr_t base) { @@ -293,7 +302,7 @@ lib_info* add_lib_info_fd(struct ps_prochandle* ph, const char* libname, int fd, } if (fill_addr_info(newlib)) { - if (!read_eh_frame(ph, newlib)) { + if (!read_frame(".eh_frame", newlib->fd, &newlib->frame)) { print_debug("Could not find .eh_frame section in %s\n", newlib->name); } } else { diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h index ee75fa07a5cd..3c7c7480aa75 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h @@ -39,6 +39,12 @@ typedef struct frame_info { uintptr_t v_addr; unsigned char* data; int size; + + // Following fields should be declared as int instead of bool + // because "bool" would be defined as int in C in libproc.h. + // It causes unexpected memory access. + int is_debug_frame; // true if this info comes from .debug_frame + int tried_debug_frame; // true if .debug_frame was tried to load. } frame_info; // list of shared objects @@ -131,6 +137,9 @@ void print_warning(const char* format,...); void print_error(const char* format,...); bool is_debug(); +// read frame information for unwinding from specified ELF section. +bool read_frame(const char* section, int fd, frame_info* frame); + // deletes a thread from the thread list void delete_thread_info(struct ps_prochandle* ph, thread_info* thr); diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c index 399a71e70425..65e474d712cb 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c @@ -408,11 +408,6 @@ static bool read_lib_info(struct ps_prochandle* ph) { #endif if ((lib = add_lib_info(ph, word[5], (uintptr_t)base)) == NULL) continue; // ignore, add_lib_info prints error - - // we don't need to keep the library open, symtab is already - // built. Only for core dump we need to keep the fd open. - close(lib->fd); - lib->fd = -1; } } fclose(fp); diff --git a/src/jdk.hotspot.agent/macosx/native/libsaproc/libproc_impl.c b/src/jdk.hotspot.agent/macosx/native/libsaproc/libproc_impl.c index fa56e620a0a6..8b2ebbe6ca60 100644 --- a/src/jdk.hotspot.agent/macosx/native/libsaproc/libproc_impl.c +++ b/src/jdk.hotspot.agent/macosx/native/libsaproc/libproc_impl.c @@ -193,6 +193,15 @@ void destroy_lib_info(struct ps_prochandle* ph) { if (lib->symtab) { destroy_symtab(lib->symtab); } + if (lib->fd >= 0) { + if (ph->core == NULL) { + close(lib->fd); + } else if (lib->fd != ph->core->exec_fd) { + // Prevents double-close in core mode. + // exec_fd would be closed at close_files(). + close(lib->fd); + } + } free(lib); lib = next; } @@ -209,9 +218,8 @@ void destroy_thread_info(struct ps_prochandle* ph) { // ps_prochandle cleanup void Prelease(struct ps_prochandle* ph) { - // do the "derived class" clean-up first - ph->ops->release(ph); destroy_lib_info(ph); + ph->ops->release(ph); destroy_thread_info(ph); free(ph); } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/StubQueue.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/StubQueue.java index a1fb2a1e73a6..fb5003e440c7 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/StubQueue.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/StubQueue.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 @@ -110,7 +110,7 @@ public Stub getFirst() { public Stub getNext(Stub s) { long i = getIndexOf(s) + getStubSize(s); - if (i == getBufferLimit()) { + if (i == getBufferLimit() && getQueueEnd() < getBufferLimit()) { i = 0; } return ((i == getQueueEnd()) ? null : getStubAt(i)); diff --git a/src/jdk.hotspot.agent/share/native/libsaproc/ps_core_common.c b/src/jdk.hotspot.agent/share/native/libsaproc/ps_core_common.c index 40757a346fe2..ec8837fe1506 100644 --- a/src/jdk.hotspot.agent/share/native/libsaproc/ps_core_common.c +++ b/src/jdk.hotspot.agent/share/native/libsaproc/ps_core_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -60,8 +60,6 @@ // close all file descriptors static void close_files(struct ps_prochandle* ph) { - lib_info* lib = NULL; - // close core file descriptor if (ph->core->core_fd >= 0) close(ph->core->core_fd); @@ -77,16 +75,6 @@ static void close_files(struct ps_prochandle* ph) { // close class share archive file if (ph->core->classes_jsa_fd >= 0) close(ph->core->classes_jsa_fd); - - // close all library file descriptors - lib = ph->libs; - while (lib) { - int fd = lib->fd; - if (fd >= 0 && fd != ph->core->exec_fd) { - close(fd); - } - lib = lib->next; - } } // clean all map_info stuff diff --git a/src/jdk.jartool/share/man/jarsigner.md b/src/jdk.jartool/share/man/jarsigner.md index b24382fdda5f..a02be0fbe179 100644 --- a/src/jdk.jartool/share/man/jarsigner.md +++ b/src/jdk.jartool/share/man/jarsigner.md @@ -269,10 +269,7 @@ For example, an 3072-bit RSASSA-PSS key will use RSASSA-PSS as the signature algorithm and SHA-384 as the hash and MGF1 algorithms. 2. Modern digital signature algorithms such as EdDSA and ML-DSA use the same -name for both the key and signature algorithms. Only the signature algorithm -with the same name can be used with a given key algorithm. The specific -signature parameter set (for example, Ed25519 or Ed448 for EdDSA) is the -same as that of the key. +algorithm as the key. When using such a key, specifying `-sigalg` is unnecessary. If a key algorithm is not listed in this table, the `.DSA` block file extension is always used. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/DocFilesHandler.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/DocFilesHandler.java index a61ccb21eb10..04a0309d1bef 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/DocFilesHandler.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/DocFilesHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -159,8 +159,7 @@ private void copyDirectory(DocFile srcdir, final DocPath dstDocPath, configuration.messages.warning("doclet.Copy_Overwrite_warning", srcfile.getPath(), dstdir.getPath()); } else { - var path = Utils.toLowerCase(srcfile.getPath()); - if (path.endsWith(".html") || path.endsWith(".md")) { + if (Utils.isHtmlFile(srcfile.getPath()) || Utils.isMarkdownFile(srcfile.getPath())) { handleDocFile(srcfile, dstDocPath); } else { configuration.messages.notice("doclet.Copying_File_0_To_Dir_1", diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java index 594c36c4af2a..49e882ca4fe4 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java @@ -549,9 +549,9 @@ public String getWindowTitle(String title) { */ public String getFileTitle(DocFileElement element) { var fileName = element.getFileObject().getName(); - if (fileName.endsWith(".html")) { + if (Utils.isHtmlFile(fileName)) { return getTextContent(utils.getPreamble(element), "title"); - } else if (fileName.endsWith(".md")) { + } else if (Utils.isMarkdownFile(fileName)) { var c = commentTagsToContent(element, utils.getBody(element), false); return getHeadingText(c); } else { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java index ce2872349228..4fec7881b5d9 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java @@ -1151,6 +1151,24 @@ public static String toLowerCase(String s) { return s.toLowerCase(Locale.US); } + /** + * {@return {@code true} if the {@code fileName} represents an HTML file} + * Note that the doc comment spec requires a lower-case extension. + * @param fileName the file name + */ + public static boolean isHtmlFile(String fileName) { + return fileName.endsWith(".html"); + } + + /** + * {@return {@code true} if the {@code fileName} represents a Markdown file} + * Note that the doc comment spec requires a lower-case extension. + * @param fileName the file name + */ + public static boolean isMarkdownFile(String fileName) { + return fileName.endsWith(".md"); + } + /** * Return true if the given Element is deprecated. * diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/ArrayTypeImpl.c b/src/jdk.jdwp.agent/share/native/libjdwp/ArrayTypeImpl.c index 0fc308350e46..9dd38d282470 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/ArrayTypeImpl.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/ArrayTypeImpl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,73 +30,96 @@ #include "signature.h" +static jdwpError +findComponentClass(JNIEnv *env, jobject arrayClassLoader, char *componentSignature, + jclass *classes, jint count, jclass *componentClassPtr) +{ + int i; + + *componentClassPtr = NULL; + for (i = 0; i < count; i++) { + char *signature = NULL; + jclass clazz = classes[i]; + jboolean match; + jvmtiError error; + + /* signature must match */ + error = classSignature(clazz, &signature, NULL); + if (error != JVMTI_ERROR_NONE) { + return map2jdwpError(error); + } + match = strcmp(signature, componentSignature) == 0; + jvmtiDeallocate(signature); + + /* if signature matches, get class loader to check if + * it matches + */ + if (match) { + jobject loader; + error = classLoader(clazz, &loader); + if (error != JVMTI_ERROR_NONE) { + return map2jdwpError(error); + } + match = isSameObject(env, loader, arrayClassLoader); + } + + if (match) { + *componentClassPtr = clazz; + return JDWP_ERROR(NONE); + } + } + return JDWP_ERROR(NONE); +} + /* - * Determine the component class by looking thru all classes for - * one that has the signature of the component and the same class loadeer - * as the array. See JVM spec 5.3.3: + * Determine the component class by first looking through the array's loader + * classes, then all classes if necessary, for one with the component signature + * and the same defining class loader as the array. See JVM spec 5.3.3: * If the component type is a reference type, C is marked as having * been defined by the defining class loader of the component type. */ static jdwpError getComponentClass(JNIEnv *env, jclass arrayClass, char *componentSignature, - jclass *componentClassPtr) + jclass *componentClassPtr) { jobject arrayClassLoader; jclass *classes; jint count; - jclass componentClass = NULL; jdwpError serror; jvmtiError error; - serror = JDWP_ERROR(NONE); - + *componentClassPtr = NULL; error = classLoader(arrayClass, &arrayClassLoader); if (error != JVMTI_ERROR_NONE) { return map2jdwpError(error); } - error = allLoadedClasses(&classes, &count); - if (error != JVMTI_ERROR_NONE) { - serror = map2jdwpError(error); - } else { - int i; - for (i = 0; (i < count) && (componentClass == NULL); i++) { - char *signature = NULL; - jclass clazz = classes[i]; - jboolean match; - jvmtiError error; - - /* signature must match */ - error = classSignature(clazz, &signature, NULL); - if (error != JVMTI_ERROR_NONE) { - serror = map2jdwpError(error); - break; - } - match = strcmp(signature, componentSignature) == 0; - jvmtiDeallocate(signature); - - /* if signature matches, get class loader to check if - * it matches - */ - if (match) { - jobject loader; - error = classLoader(clazz, &loader); - if (error != JVMTI_ERROR_NONE) { - return map2jdwpError(error); - } - match = isSameObject(env, loader, arrayClassLoader); - } - - if (match) { - componentClass = clazz; - } + /* + * Hidden classes and their arrays have '.' in their signatures and are + * not included in GetClassLoaderClasses. + */ + if (strchr(componentSignature, '.') == NULL) { + error = allClassLoaderClasses(arrayClassLoader, &classes, &count); + if (error != JVMTI_ERROR_NONE) { + return map2jdwpError(error); } + serror = findComponentClass(env, arrayClassLoader, componentSignature, + classes, count, componentClassPtr); jvmtiDeallocate(classes); + if (serror != JDWP_ERROR(NONE) || *componentClassPtr != NULL) { + return serror; + } + } - *componentClassPtr = componentClass; + error = allLoadedClasses(&classes, &count); + if (error != JVMTI_ERROR_NONE) { + return map2jdwpError(error); } + serror = findComponentClass(env, arrayClassLoader, componentSignature, + classes, count, componentClassPtr); + jvmtiDeallocate(classes); - if (serror == JDWP_ERROR(NONE) && componentClass == NULL) { + if (serror == JDWP_ERROR(NONE) && *componentClassPtr == NULL) { /* per JVM spec, component class is always loaded * before array class, so this should never occur. */ diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java index c2dbffd31a93..a61c7f6266c0 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java @@ -130,7 +130,6 @@ public final class JVM { * * @return a unique class identifier */ - @IntrinsicCandidate public static native long getClassId(Class clazz); /** diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixAppImageFragmentBuilder.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixAppImageFragmentBuilder.java index ac7af873739e..23b6112d8e29 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixAppImageFragmentBuilder.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixAppImageFragmentBuilder.java @@ -37,6 +37,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -44,7 +45,6 @@ import java.util.UUID; import java.util.function.Function; import java.util.function.Predicate; -import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.xml.stream.XMLOutputFactory; @@ -128,6 +128,8 @@ void initFromParams(BuildEnv env, WinMsiPackage pkg) { packageFile = null; } + upgradeCode = Objects.requireNonNull(pkg.upgradeCode()); + initFileAssociations(); } @@ -199,22 +201,6 @@ private void initAppImageLayouts(AppImageLayout appImageLayout, Path appImageRoo } } - private static UUID createNameUUID(String str) { - return UUID.nameUUIDFromBytes(str.getBytes(StandardCharsets.UTF_8)); - } - - private static UUID createNameUUID(Path path, String role) { - if (path.isAbsolute() || !ROOT_DIRS.contains(path.getName(0))) { - throw throwInvalidPathException(path); - } - // Paths are case insensitive on Windows - String keyPath = path.toString().toLowerCase(); - if (role != null) { - keyPath = role + "@" + keyPath; - } - return createNameUUID(keyPath); - } - /** * Value for Id attribute of various WiX elements. */ @@ -228,7 +214,7 @@ enum Id { RemoveFolder("rm"); Id() { - this.prefix = name().toLowerCase(); + this.prefix = name().toLowerCase(Locale.ROOT); } Id(String prefix) { @@ -255,14 +241,25 @@ String of(Path path) { private static String of(Path path, String prefix, String role) { Objects.requireNonNull(role); Objects.requireNonNull(prefix); - return String.format("%s%s", prefix, - createNameUUID(path, role).toString().replace("-", "")); + return String.format("%s%s", prefix, createId(path, role)); } static String of(Path path, String prefix) { return of(path, prefix, prefix); } + private static String createId(Path path, String role) { + if (path.isAbsolute() || !ROOT_DIRS.contains(path.getName(0))) { + throw throwInvalidPathException(path); + } + // Paths are case insensitive on Windows + String keyPath = path.toString().toLowerCase(Locale.ROOT); + if (role != null) { + keyPath = role + "@" + keyPath; + } + return UUID.nameUUIDFromBytes(keyPath.getBytes(StandardCharsets.UTF_8)).toString().replace("-", ""); + } + private final String prefix; } @@ -283,10 +280,6 @@ enum Component { this.id = Id.valueOf(name()); } - UUID guidOf(Path path) { - return createNameUUID(path, name()); - } - String idOf(Path path) { return id.of(path); } @@ -300,23 +293,24 @@ boolean isFile() { } static void startElement(WixToolsetType wixType, XMLStreamWriter xml, String componentId, - String componentGuid) throws XMLStreamException, IOException { + UUID componentGuid) throws XMLStreamException, IOException { + + Objects.requireNonNull(componentId); + Objects.requireNonNull(componentGuid); + xml.writeStartElement("Component"); switch (wixType) { case Wix3 -> { xml.writeAttribute("Win64", is64Bit() ? "yes" : "no"); - xml.writeAttribute("Guid", componentGuid); } case Wix4 -> { xml.writeAttribute("Bitness", is64Bit() ? "always64" : "always32"); - if (!componentGuid.equals("*")) { - xml.writeAttribute("Guid", componentGuid); - } } default -> { throw new IllegalArgumentException(); } } + xml.writeAttribute("Guid", String.format("{%s}", componentGuid)); xml.writeAttribute("Id", componentId); } @@ -369,9 +363,17 @@ private String addComponent(XMLStreamWriter xml, Path path, startDirectoryElement(xml, "DirectoryRef", directoryRefPath); + final var componentKey = new WixComponentKey().path(path).role(role.name()); + + final Optional regKeyPath = !systemWide || role.isRegistryKeyPath() ? + Optional.of(new RegistryKeyPath(directoryRefPath)) : Optional.empty(); + + regKeyPath.ifPresent(v -> { + v.configureComponentKey(componentKey); + }); + final String componentId = "c" + role.idOf(path); - Component.startElement(getWixType(), xml, componentId, String.format( - "{%s}", role.guidOf(path))); + Component.startElement(getWixType(), xml, componentId, finalizeComponentKey(componentKey)); if (role == Component.Shortcut) { String property = shortcutFolders.stream().filter(shortcutFolder -> { @@ -394,9 +396,8 @@ private String addComponent(XMLStreamWriter xml, Path path, } } - boolean isRegistryKeyPath = !systemWide || role.isRegistryKeyPath(); - if (isRegistryKeyPath) { - addRegistryKeyPath(xml, directoryRefPath); + if (regKeyPath.isPresent()) { + regKeyPath.orElseThrow().write(xml); if ((role.isFile() || (role == Component.CreateFolder && !systemWide)) && !SYSTEM_DIRS.contains(directoryRefPath)) { xml.writeStartElement("RemoveFolder"); @@ -415,7 +416,7 @@ private String addComponent(XMLStreamWriter xml, Path path, xml.writeAttribute("Id", role.idOf(path)); } - if (!isRegistryKeyPath) { + if (regKeyPath.isEmpty()) { xml.writeAttribute("KeyPath", "yes"); } @@ -821,31 +822,45 @@ public void copyFile(Path src, Path dst) throws IOException { } } - private void addRegistryKeyPath(XMLStreamWriter xml, Path path) throws - XMLStreamException, IOException { - addRegistryKeyPath(xml, path, () -> "ProductCode", () -> "[ProductCode]"); - } + private final class RegistryKeyPath { - private void addRegistryKeyPath(XMLStreamWriter xml, Path path, - Supplier nameAttr, Supplier valueAttr) throws - XMLStreamException, IOException { + RegistryKeyPath(Path path, String nameAttrValue, String valueAttrValue) { + this.path = Objects.requireNonNull(path); + this.nameAttrValue = Objects.requireNonNull(nameAttrValue); + this.valueAttrValue = Objects.requireNonNull(valueAttrValue); + } - String regRoot = USER_PROFILE_DIRS.stream().anyMatch(path::startsWith) - || !systemWide ? "HKCU" : "HKLM"; + RegistryKeyPath(Path path) { + this(path, "ProductCode", "[ProductCode]"); + } - xml.writeStartElement("RegistryKey"); - xml.writeAttribute("Root", regRoot); - xml.writeAttribute("Key", registryKeyPath); - if (!isWithWix36Features()) { - xml.writeAttribute("Action", "createAndRemoveOnUninstall"); - } - xml.writeStartElement("RegistryValue"); - xml.writeAttribute("Type", "string"); - xml.writeAttribute("KeyPath", "yes"); - xml.writeAttribute("Name", nameAttr.get()); - xml.writeAttribute("Value", valueAttr.get()); - xml.writeEndElement(); // - xml.writeEndElement(); // + String registryRoot() { + return USER_PROFILE_DIRS.stream().anyMatch(path::startsWith) || !systemWide ? "HKCU" : "HKLM"; + } + + WixComponentKey configureComponentKey(WixComponentKey key) { + return key.regkey(Path.of(registryRoot()).resolve(registryKeyPath).toString()); + } + + void write(XMLStreamWriter xml) throws XMLStreamException, IOException { + xml.writeStartElement("RegistryKey"); + xml.writeAttribute("Root", registryRoot()); + xml.writeAttribute("Key", registryKeyPath); + if (!isWithWix36Features()) { + xml.writeAttribute("Action", "createAndRemoveOnUninstall"); + } + xml.writeStartElement("RegistryValue"); + xml.writeAttribute("Type", "string"); + xml.writeAttribute("KeyPath", "yes"); + xml.writeAttribute("Name", nameAttrValue); + xml.writeAttribute("Value", valueAttrValue); + xml.writeEndElement(); // + xml.writeEndElement(); // + } + + private final Path path; + private final String nameAttrValue; + private final String valueAttrValue; } private String addDirectoryCleaner(XMLStreamWriter xml, Path path) throws @@ -859,6 +874,8 @@ private String addDirectoryCleaner(XMLStreamWriter xml, Path path) throws final String propertyId = baseId.toUpperCase(); final String componentId = ("c" + baseId); + final var regKeyPath = new RegistryKeyPath(INSTALLDIR, propertyId, toWixPath(path)); + xml.writeStartElement("Property"); xml.writeAttribute("Id", propertyId); xml.writeStartElement("RegistrySearch"); @@ -872,11 +889,15 @@ private String addDirectoryCleaner(XMLStreamWriter xml, Path path) throws xml.writeStartElement("DirectoryRef"); xml.writeAttribute("Id", INSTALLDIR.toString()); - Component.startElement(getWixType(), xml, componentId, "*"); + Component.startElement( + getWixType(), + xml, + componentId, + finalizeComponentKey( + regKeyPath.configureComponentKey( + new WixComponentKey().path(path).role("RemoveFolderRecursive")))); - addRegistryKeyPath(xml, INSTALLDIR, () -> propertyId, () -> { - return toWixPath(path); - }); + regKeyPath.write(xml); xml.writeStartElement(getWixNamespaces().get(WixNamespace.Util), "RemoveFolderEx"); @@ -893,6 +914,15 @@ private boolean isWithWix36Features() { return DottedVersion.compareComponents(getWixVersion(), DottedVersion.greedy("3.6")) >= 0; } + private UUID finalizeComponentKey(WixComponentKey key) { + final var path = Path.of(key.get(WixComponentKey.Attribute.PATH)); + if (path.isAbsolute() || !ROOT_DIRS.contains(path.getName(0))) { + throw throwInvalidPathException(path); + } + key.upgradeCode(upgradeCode); + return key.toGuid(); + } + // Does the following conversions: // INSTALLDIR -> [INSTALLDIR] // TARGETDIR/ProgramFiles64Folder/foo/bar -> [ProgramFiles64Folder]foo/bar @@ -988,6 +1018,8 @@ static Set getForPackage(WinMsiPackage pkg) { private PackageFile packageFile; + private UUID upgradeCode; + private static final Path TARGETDIR = Path.of("TARGETDIR"); private static final Path INSTALLDIR = Path.of("INSTALLDIR"); diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixComponentKey.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixComponentKey.java new file mode 100644 index 000000000000..590f89bbe45b --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixComponentKey.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Locale; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +record WixComponentKey(String[] attrs) { + + WixComponentKey { + Objects.requireNonNull(attrs); + if (attrs.length != MODEL.length) { + throw new IllegalArgumentException(); + } + + attrs = Arrays.copyOf(attrs, attrs.length); + } + + WixComponentKey() { + this(MODEL); + } + + enum Attribute { + UPGRADE_CODE, + PATH, + ROLE, + REGKEY, + } + + WixComponentKey set(Attribute attr, String value) { + attrs[attr.ordinal()] = value; + return this; + } + + String get(Attribute attr) { + return attrs[attr.ordinal()]; + } + + WixComponentKey upgradeCode(UUID v) { + return set(Attribute.UPGRADE_CODE, Objects.requireNonNull(v).toString()); + } + + WixComponentKey path(Path v) { + return set(Attribute.PATH, Objects.requireNonNull(v).toString()); + } + + WixComponentKey regkey(String v) { + return set(Attribute.REGKEY, Objects.requireNonNull(v)); + } + + WixComponentKey role(String v) { + return set(Attribute.ROLE, Objects.requireNonNull(v).toString()); + } + + UUID toGuid() { + var upgradeCode = get(Attribute.UPGRADE_CODE); + + if (upgradeCode == null || get(Attribute.PATH) == null) { + throw new IllegalStateException(); + } + + String effectiveUpgradeCode; + + try { + effectiveUpgradeCode = UUID.fromString(upgradeCode).toString(); + } catch (IllegalArgumentException ex) { + throw new IllegalStateException(ex); + } + + var key = Stream.of(Attribute.values()).flatMap(attr -> { + return Optional.ofNullable(get(attr)).map(attrValue -> { + switch (attr) { + // Windows paths and registry key/value names are not case sensitive. + case PATH, REGKEY -> attrValue = attrValue.toLowerCase(Locale.ROOT); + case UPGRADE_CODE -> attrValue = effectiveUpgradeCode; + default -> {} + } + return String.format("%s=%s", attr.name(), attrValue); + }).stream(); + }).collect(Collectors.joining(";")); + + return UUID.nameUUIDFromBytes(key.getBytes(StandardCharsets.UTF_8)); + } + + private final static String[] MODEL = new String[Attribute.values().length]; +} diff --git a/src/jdk.jshell/share/classes/jdk/jshell/Diag.java b/src/jdk.jshell/share/classes/jdk/jshell/Diag.java index e7e40c7b3a97..d5e7ae2833c7 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/Diag.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/Diag.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, 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 @@ -61,7 +61,7 @@ public abstract class Diag { * associated with this diagnostic that indicates the location of * the problem. In addition, the following must be true: * - *

{@code getStartPostion() <= getPosition()} + *

{@code getStartPosition() <= getPosition()} *

{@code getPosition() <= getEndPosition()} * * @return character offset from beginning of source; {@link diff --git a/src/jdk.jshell/share/classes/jdk/jshell/JShell.java b/src/jdk.jshell/share/classes/jdk/jshell/JShell.java index 535e4df02fb4..bede4c01e443 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/JShell.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/JShell.java @@ -249,7 +249,7 @@ public Builder err(PrintStream err) { } /** - * Sets the console for the running evalution. + * Sets the console for the running evaluation. *

* The default, if this is not set, is no console ({@code System.console()} * will return {@code null} while running a snippet). diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 5825040f56cb..87fe24a42343 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -123,14 +123,6 @@ serviceability/jvmti/vthread/GetSetLocalTest/GetSetLocalTest.java 8286836 generi serviceability/jvmti/vthread/CarrierThreadEventNotification/CarrierThreadEventNotification.java 8333681 generic-all serviceability/dcmd/gc/RunFinalizationTest.java 8227120 generic-all -serviceability/sa/ClhsdbCDSCore.java 8318754 macosx-aarch64 -serviceability/sa/ClhsdbFindPC.java#xcomp-core 8318754 macosx-aarch64 -serviceability/sa/ClhsdbFindPC.java#no-xcomp-core 8318754 macosx-aarch64 -serviceability/sa/ClhsdbPmap.java#core 8318754 macosx-aarch64 -serviceability/sa/ClhsdbPstack.java#core 8318754 macosx-aarch64 -serviceability/sa/TestJmapCore.java 8318754 macosx-aarch64 -serviceability/sa/TestJmapCoreMetaspace.java 8318754 macosx-aarch64 - serviceability/sa/ClhsdbThreadContext.java 8356704 windows-x64 serviceability/jvmti/stress/StackTrace/NotSuspended/GetStackTraceNotSuspendedStressTest.java 8315980 linux-all,windows-x64 @@ -201,8 +193,6 @@ vmTestbase/nsk/monitoring/ThreadMXBean/findMonitorDeadlockedThreads/find006/Test compiler/codegen/TestRedundantLea.java#Spill 8361089 generic-all -compiler/vectorapi/TestVectorReassociations.java 8388927 generic-all - serviceability/sa/TestJhsdbJstackMixedWithXComp.java#xcomp-disable-tiered-compilation 8386674 linux-aarch64 # The following test failure(s) DID NOT reproduce during Tier[1-8] testing diff --git a/test/hotspot/jtreg/compiler/c1/TestLargeArrayIndex.java b/test/hotspot/jtreg/compiler/c1/TestLargeArrayIndex.java new file mode 100644 index 000000000000..e524c07d0123 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c1/TestLargeArrayIndex.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2026, Alibaba Group Holding Limited. All rights reserved. + * 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 8396020 + * @summary C1 must handle constant array index whose byte offsets exceed the signed 32-bit range + * @requires vm.compiler1.enabled + * @run main/othervm -Xcomp -XX:TieredStopAtLevel=1 + * -XX:CompileCommand=compileonly,compiler.c1.TestLargeArrayIndex::test* + * compiler.c1.TestLargeArrayIndex + */ + +package compiler.c1; + +public class TestLargeArrayIndex { + static void testFloatNegative(float[] values) { + try { + values[Integer.MIN_VALUE] = -0.0f; + throw new RuntimeException("ArrayIndexOutOfBoundsException expected"); + } catch (ArrayIndexOutOfBoundsException expected) { + } + } + + static void testFloatPositive(float[] values) { + try { + values[Integer.MAX_VALUE] = 0.0f; + throw new RuntimeException("ArrayIndexOutOfBoundsException expected"); + } catch (ArrayIndexOutOfBoundsException expected) { + } + } + + static void testLongNegative(long[] values) { + try { + values[Integer.MIN_VALUE] = 0L; + throw new RuntimeException("ArrayIndexOutOfBoundsException expected"); + } catch (ArrayIndexOutOfBoundsException expected) { + } + } + + static void testBytePositive(byte[] values) { + try { + values[Integer.MAX_VALUE] = 0; + throw new RuntimeException("ArrayIndexOutOfBoundsException expected"); + } catch (ArrayIndexOutOfBoundsException expected) { + } + } + + public static void main(String[] args) { + float[] floats = new float[6]; + testFloatNegative(floats); + testFloatPositive(floats); + testLongNegative(new long[6]); + testBytePositive(new byte[6]); + } +} diff --git a/test/hotspot/jtreg/compiler/c1/TestUnloadedArrayKlassAfterOOME_C1.java b/test/hotspot/jtreg/compiler/c1/TestUnloadedArrayKlassAfterOOME_C1.java new file mode 100644 index 000000000000..86c0b2ded367 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c1/TestUnloadedArrayKlassAfterOOME_C1.java @@ -0,0 +1,111 @@ +/* + * 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 Checks C1 flat array code with unloaded array klass because of OOME + * @bug 8394660 + * @library /test/lib + * @requires vm.compiler1.enabled + * @requires vm.opt.AbortVMOnCompilationFailure != true + * @run driver ${test.main.class} + */ + +package compiler.c1; + +import java.util.ArrayList; +import java.util.List; + +import jdk.test.lib.Asserts; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class TestUnloadedArrayKlassAfterOOME_C1 { + + public static void main(String[] args) throws Exception { + List options = new ArrayList(); + options.add("-XX:+PrintCompilation"); + options.add("-Xcomp"); + options.add("-XX:TieredStopAtLevel=1"); + options.add("-Xmx12m"); + options.add("-XX:CompileCommand=compileonly," + getTestClass() + "::testNew"); + options.add("-XX:CompileCommand=compileonly," + getTestClass() + "::testArrayCopy"); + options.add(getTestClass()); + + OutputAnalyzer oa = ProcessTools.executeTestJava(options); + + oa.shouldHaveExitValue(0); + // The OOM condition cannot be reliably reproduced with all GCs, + // so no strong assertion, just some information printed in the log + if (oa.contains("compilation bailout: encountered unloaded ciobjarrayklass due to out of memory error")) { + IO.println("Compilation skipped because of OOM, good!"); + } else { + IO.println("No compilation bailout detected, test unconclusive"); + } + } + + // Test class that is invoked by the sub process + public static String getTestClass() { + return TestMain.class.getName(); + } + + public static class TestMain { + static class Value { + int value = 0; + } + + static Object[] keepAlive; + + static Object[] testNew() { + try { + return new Value[10]; + } catch (OutOfMemoryError expected) { + // Ignore + } + return null; + } + + static void testArrayCopy(Object[] array) { + try { + System.arraycopy(array, 0, array, 1, array.length - 2); + } catch (OutOfMemoryError expected) { + // Ignore + } + } + + public static void main(String[] args) { + Object[] array = new Object[10]; + Value value = new Value(); + try { + // Fill up the heap + while (true) { + keepAlive = new Object[] { keepAlive, new byte[1024] }; + } + } catch (OutOfMemoryError expected) { + // Ignore + } + testNew(); + testArrayCopy(array); + } + } +} diff --git a/test/hotspot/jtreg/compiler/c2/TestDeadPathManyDeadDataNodes.java b/test/hotspot/jtreg/compiler/c2/TestDeadPathManyDeadDataNodes.java new file mode 100644 index 000000000000..ad724c0dd00f --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestDeadPathManyDeadDataNodes.java @@ -0,0 +1,1301 @@ +/* + * Copyright (c) 2026 IBM Corporation. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8380166 + * @summary C2: crash in compiled code due to zero division because of widened CastII + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions + * -Xcomp -XX:CompileOnly=${test.main.class}::test1 + * -XX:CompileCommand=quiet + * -XX:CompileCommand=inline,${test.main.class}::inlined1 + * -XX:MaxRecursiveInlineLevel=1000 -XX:MaxInlineLevel=1000 + * -XX:-TieredCompilation -XX:+AlwaysIncrementalInline + * -XX:+DelayAfterInliningCutoff -XX:+IncrementalInlineForceCleanup + * -XX:NodeCountInliningCutoff=100000 -XX:+StressIGVN + * ${test.main.class} + * @run main ${test.main.class} + */ + +package compiler.c2; + +public class TestDeadPathManyDeadDataNodes { + private static int field; + private static boolean boolField2; + private static int arrayLengthField; + + public static void main(String[] args) { + Object o = new Object(); + try { + test1(false, 0); + } catch (NegativeArraySizeException nase) { + } + } + + private static int test1(boolean boolParam, int intParam) { + int length; + int res = 0; + length = -1; + for (int i = 0; i < 2; i++) { + if (boolParam) { + field = 42; + } + int[] array = new int[length]; + arrayLengthField = array.length; + while(true) { + Object o = new Object(); + int arrayLength = arrayLengthField; + arrayLengthField = 0; + switch (intParam) { + case 0: + if (boolField2) { + break; + } + field = 42; + continue; + case 1: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 2: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 3: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 4: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 5: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 6: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 7: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 8: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 9: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 10: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 11: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 12: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 13: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 14: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 15: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 16: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 17: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 18: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 19: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 20: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 21: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 22: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 23: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 24: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 25: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 26: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 27: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 28: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 29: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 30: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 31: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 32: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 33: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 34: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 35: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 36: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 37: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 38: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 39: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 40: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 41: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 42: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 43: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 44: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 45: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 46: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 47: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 48: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 49: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 50: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 51: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 52: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 53: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 54: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 55: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 56: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 57: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 58: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 59: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 60: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 61: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 62: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 63: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 64: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 65: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 66: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 67: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 68: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 69: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 70: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 71: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 72: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 73: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 74: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 75: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 76: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 77: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 78: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 79: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 80: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 81: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 82: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 83: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 84: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 85: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 86: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 87: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 88: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 89: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 90: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 91: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 92: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 93: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 94: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 95: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 96: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 97: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + case 98: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + continue; + case 99: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + continue; + default: + res += inlined1(boolParam, intParam/100, arrayLength, 92); + continue; + } + field = 42; + break; + } + length = lastInlined(); + } + return res; + } + + static int lastInlined() { + return -1; + } + + static int inlined1(boolean boolParam, int intParam, int arrayLength, int count) { + int res = 0; + switch (intParam) { + case 0: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 1: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 2: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 3: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 4: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 5: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 6: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 7: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 8: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 9: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 10: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 11: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 12: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 13: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 14: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 15: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 16: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 17: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 18: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 19: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 20: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 21: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 22: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 23: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 24: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 25: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 26: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 27: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 28: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 29: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 30: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 31: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 32: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 33: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 34: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 35: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 36: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 37: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 38: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 39: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 40: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 41: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 42: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 43: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 44: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 45: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 46: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 47: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 48: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 49: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 50: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 51: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 52: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 53: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 54: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 55: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 56: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 57: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 58: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 59: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 60: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 61: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 62: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 63: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 64: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 65: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 66: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 67: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 68: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 69: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 70: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 71: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 72: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 73: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 74: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 75: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 76: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 77: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 78: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 79: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 80: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 81: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 82: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 83: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 84: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 85: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 86: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 87: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 88: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 89: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 90: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 91: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 92: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 93: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 94: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 95: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 96: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 97: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + case 98: + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + case 99: + if (boolParam) { + res += arrayLength * 2; + } + field = 42; + return res; + default: + if (count == 0) { + if (boolParam) { + res += arrayLength * 1; + } + field = 42; + return res; + } else { + return inlined1(boolParam, intParam / 100, arrayLength, count-1); + } + } + } +} diff --git a/test/hotspot/jtreg/compiler/c2/TestSpilledUncommonTrapRequest.java b/test/hotspot/jtreg/compiler/c2/TestSpilledUncommonTrapRequest.java index 74ba57086668..1b5ced4354cf 100644 --- a/test/hotspot/jtreg/compiler/c2/TestSpilledUncommonTrapRequest.java +++ b/test/hotspot/jtreg/compiler/c2/TestSpilledUncommonTrapRequest.java @@ -35,6 +35,21 @@ * ${test.main.class} */ +/** + * @test + * @bug 8358889 + * @summary Test that a spilled uncommon trap request is handled properly. + * @requires vm.compiler2.enabled + * @requires os.arch == "aarch64" + * @library /test/lib + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:-TieredCompilation -Xcomp + * -XX:+UnlockExperimentalVMOptions -XX:+PreferCallBasedUncommonTraps + * -XX:StressSeed=403 -XX:+StressGCM + * -XX:CompileCommand=compileonly,${test.main.class}::test + * -XX:CompileCommand=dontinline,${test.main.class}::dontInline + * ${test.main.class} + */ + import jdk.test.lib.Asserts; public class TestSpilledUncommonTrapRequest { diff --git a/test/hotspot/jtreg/compiler/c2/igvn/TestMissingNestedAndIdealization.java b/test/hotspot/jtreg/compiler/c2/igvn/TestMissingNestedAndIdealization.java new file mode 100644 index 000000000000..72ea0f49b431 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/igvn/TestMissingNestedAndIdealization.java @@ -0,0 +1,69 @@ +/* + * 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.igvn; + +/* + * @test + * @bug 8395378 + * @summary Test notification for nested AndI/AndL optimization: (x & c1) & c2 -> x & (c1 & c2) + * @run main/othervm -Xbatch + * -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 + * -XX:CompileOnly=${test.main.class}::test* + * ${test.main.class} + * @run main ${test.main.class} + */ + +public class TestMissingNestedAndIdealization { + static int valueI = 0xffff_ffff; + static long valueL = 0xffff_ffff; + + public static void main(String[] args) { + for (int i = 0; i < 20_000; i++) { + testI(); + testL(); + } + } + + public static int testI() { + int v = 0xffff; + for (int i = 0; i < 200; i++) { + v &= valueI; + for (int j = 0; j < 10; j++) { + valueI = 0xff; + } + } + return v; + } + + public static long testL() { + long v = 0xffff; + for (int i = 0; i < 200; i++) { + v &= valueL; + for (int j = 0; j < 10; j++) { + valueL = 0xff; + } + } + return v; + } +} diff --git a/test/hotspot/jtreg/compiler/c2/igvn/TestMissingSubMulIdealization.java b/test/hotspot/jtreg/compiler/c2/igvn/TestMissingSubMulIdealization.java new file mode 100644 index 000000000000..bc497aba0949 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/igvn/TestMissingSubMulIdealization.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.c2.igvn; + +/* + * @test + * @bug 8394889 + * @summary C2 IGVN should idealize: (0 - x) * (0 - y) -> x * y + * @library /test/lib / + * @run main/othervm -Xbatch + * -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 + * -XX:CompileOnly=${test.main.class}::test* + * ${test.main.class} + * @run main ${test.main.class} + */ + +public class TestMissingSubMulIdealization { + static int xI; + static int yI; + static long xL; + static long yL; + static float xF; + static float yF; + static double xD; + static double yD; + + public static void main(String[] args) { + for (int i = 0; i < 20_000; i++) { + testI(); + testL(); + testF(); + testD(); + } + } + + public static void testI() { + yI = 0; + for (int i = 0; i < 1; i++) {} + int v = yI - xI; + yI = v * v; + } + + public static void testL() { + yL = 0; + for (int i = 0; i < 1; i++) {} + long v = yL - xL; + yL = v * v; + } + + public static void testF() { + yF = 0; + for (int i = 0; i < 1; i++) {} + float v = yF - xF; + yF = v * v; + } + + public static void testD() { + yD = 0; + for (int i = 0; i < 1; i++) {} + double v = yD - xD; + yD = v * v; + } +} diff --git a/test/hotspot/jtreg/compiler/c2/riscv64/TestVectorTestCMove.java b/test/hotspot/jtreg/compiler/c2/riscv64/TestVectorTestCMove.java index 028f93e20185..4c21e2dce2ec 100644 --- a/test/hotspot/jtreg/compiler/c2/riscv64/TestVectorTestCMove.java +++ b/test/hotspot/jtreg/compiler/c2/riscv64/TestVectorTestCMove.java @@ -23,7 +23,7 @@ /** * @test - * @bug 8395341 + * @bug 8395341 8391910 * @summary Conditional moves whose condition is a VectorTest are matched. * * A diamond controlled by VectorMask.anyTrue()/allTrue() is collapsed into a @@ -45,9 +45,11 @@ package compiler.c2.riscv64; import compiler.lib.ir_framework.CompLevel; +import compiler.lib.ir_framework.CompilePhase; import compiler.lib.ir_framework.IR; import compiler.lib.ir_framework.IRNode; import compiler.lib.ir_framework.Run; +import compiler.lib.ir_framework.Scenario; import compiler.lib.ir_framework.Test; import compiler.lib.ir_framework.TestFramework; @@ -65,9 +67,18 @@ public class TestVectorTestCMove { private static final long SOME = 1L; // only lane 0 set -> anyTrue private static final long NONE = 0L; // no lane set -> neither + private static final Object FIRST = new Object(); + private static final Object SECOND = new Object(); + + private static final Object[] ARRAY_SOURCE = {FIRST, SECOND}; + private static final Object[] ARRAY_RESULT = new Object[1]; + public static void main(String[] args) { TestFramework framework = new TestFramework(); framework.addFlags("--add-modules=jdk.incubator.vector"); + framework.addScenarios( + new Scenario(0, "-XX:+UseCompressedOops"), + new Scenario(1, "-XX:-UseCompressedOops")); framework.start(); } @@ -127,6 +138,42 @@ static double allTrueD(long bits, double a, double b) { return VectorMask.fromLong(SPECIES, bits).allTrue() ? a : b; } + @Test(compLevel = CompLevel.C2) + @IR(counts = {IRNode.RISCV_CMOVE_VTEST_ANYTRUE, ">0"}, + phase = CompilePhase.FINAL_CODE, + applyIf = {"UseCompressedOops", "false"}) + static Object anyTrueP(long bits, Object first, Object second) { + return VectorMask.fromLong(SPECIES, bits).anyTrue() ? first : second; + } + + @Test(compLevel = CompLevel.C2) + @IR(counts = {IRNode.RISCV_CMOVE_VTEST_ALLTRUE, ">0"}, + phase = CompilePhase.FINAL_CODE, + applyIf = {"UseCompressedOops", "false"}) + static Object allTrueP(long bits, Object first, Object second) { + return VectorMask.fromLong(SPECIES, bits).allTrue() ? first : second; + } + + @Test(compLevel = CompLevel.C2) + @IR(counts = {IRNode.RISCV_CMOVE_VTEST_ANYTRUE, ">0"}, + phase = CompilePhase.FINAL_CODE, + applyIf = {"UseCompressedOops", "true"}) + static void anyTrueN(long bits) { + Object first = ARRAY_SOURCE[0]; + Object second = ARRAY_SOURCE[1]; + ARRAY_RESULT[0] = VectorMask.fromLong(SPECIES, bits).anyTrue() ? first : second; + } + + @Test(compLevel = CompLevel.C2) + @IR(counts = {IRNode.RISCV_CMOVE_VTEST_ALLTRUE, ">0"}, + phase = CompilePhase.FINAL_CODE, + applyIf = {"UseCompressedOops", "true"}) + static void allTrueN(long bits) { + Object first = ARRAY_SOURCE[0]; + Object second = ARRAY_SOURCE[1]; + ARRAY_RESULT[0] = VectorMask.fromLong(SPECIES, bits).allTrue() ? first : second; + } + // ---------------- driver ---------------- // // Every test sees all three mask patterns on each invocation. Keeping the @@ -188,4 +235,38 @@ public void runAllTrueD() { Asserts.assertEQ(allTrueD(SOME, 7d, 9d), 9d); Asserts.assertEQ(allTrueD(NONE, 7d, 9d), 9d); } + + @Run(test = "anyTrueP") + public void runAnyTrueP() { + Asserts.assertSame(anyTrueP(ALL, FIRST, SECOND), FIRST); + Asserts.assertSame(anyTrueP(SOME, FIRST, SECOND), FIRST); + Asserts.assertSame(anyTrueP(NONE, FIRST, SECOND), SECOND); + } + + @Run(test = "allTrueP") + public void runAllTrueP() { + Asserts.assertSame(allTrueP(ALL, FIRST, SECOND), FIRST); + Asserts.assertSame(allTrueP(SOME, FIRST, SECOND), SECOND); + Asserts.assertSame(allTrueP(NONE, FIRST, SECOND), SECOND); + } + + @Run(test = "anyTrueN") + public void runAnyTrueN() { + anyTrueN(ALL); + Asserts.assertSame(ARRAY_RESULT[0], FIRST); + anyTrueN(SOME); + Asserts.assertSame(ARRAY_RESULT[0], FIRST); + anyTrueN(NONE); + Asserts.assertSame(ARRAY_RESULT[0], SECOND); + } + + @Run(test = "allTrueN") + public void runAllTrueN() { + allTrueN(ALL); + Asserts.assertSame(ARRAY_RESULT[0], FIRST); + allTrueN(SOME); + Asserts.assertSame(ARRAY_RESULT[0], SECOND); + allTrueN(NONE); + Asserts.assertSame(ARRAY_RESULT[0], SECOND); + } } diff --git a/test/hotspot/jtreg/compiler/c2/riscv64/TestZbsBitOperations.java b/test/hotspot/jtreg/compiler/c2/riscv64/TestZbsBitOperations.java new file mode 100644 index 000000000000..b5edefa7c108 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/riscv64/TestZbsBitOperations.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2026, Alibaba Group Holding Limited. All Rights Reserved. + * 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 8389236 + * @summary Test Zbs register and immediate single-bit operations on long values + * @library /test/lib / + * @requires os.arch == "riscv64" & vm.compiler2.enabled + * @requires vm.cpu.features ~= ".*zbs.*" + * @run driver compiler.c2.riscv64.TestZbsBitOperations + */ + +package compiler.c2.riscv64; + +import compiler.lib.ir_framework.CompLevel; +import compiler.lib.ir_framework.CompilePhase; +import compiler.lib.ir_framework.IR; +import compiler.lib.ir_framework.Run; +import compiler.lib.ir_framework.Test; +import compiler.lib.ir_framework.TestFramework; + +import jdk.test.lib.Asserts; + +public class TestZbsBitOperations { + private static final String BEXT = "andL_urshiftL_reg_1"; + private static final String BSET = "orL_reg_lshiftL_1"; + private static final String BINV = "xorL_reg_lshiftL_1"; + private static final String BCLR = "andL_reg_lshiftL_1_not"; + private static final String BSETI = "orL_reg_immL_SingleBit"; + private static final String BINVI = "xorL_reg_immL_SingleBit"; + private static final String BCLRI = "andL_reg_immL_SingleZero"; + + private static final long[] INPUTS = { + 0L, + -1L, + Long.MIN_VALUE, + Long.MAX_VALUE, + 0x55AA55AA55AA55AAL + }; + + public static void main(String[] args) { + TestFramework.runWithFlags("-XX:-TieredCompilation"); + } + + @Test(compLevel = CompLevel.C2) + @IR(counts = {BEXT, "1"}, phase = CompilePhase.FINAL_CODE) + static long testBext(long value, int shift) { + return (value >>> shift) & 1L; + } + + @Test(compLevel = CompLevel.C2) + @IR(counts = {BSET, "1"}, phase = CompilePhase.FINAL_CODE) + static long testBset(long value, int shift) { + return value | (1L << shift); + } + + @Test(compLevel = CompLevel.C2) + @IR(counts = {BINV, "1"}, phase = CompilePhase.FINAL_CODE) + static long testBinv(long value, int shift) { + return value ^ (1L << shift); + } + + @Test(compLevel = CompLevel.C2) + @IR(counts = {BCLR, "1"}, phase = CompilePhase.FINAL_CODE) + static long testBclr(long value, int shift) { + return value & ~(1L << shift); + } + + @Test(compLevel = CompLevel.C2) + @IR(counts = {BSETI, "4"}, phase = CompilePhase.FINAL_CODE) + static void testBseti(long[] values) { + values[0] |= 1L << 11; + values[1] |= 1L << 31; + values[2] |= 1L << 32; + values[3] |= 1L << 63; + } + + @Test(compLevel = CompLevel.C2) + @IR(counts = {BINVI, "4"}, phase = CompilePhase.FINAL_CODE) + static void testBinvi(long[] values) { + values[0] ^= 1L << 11; + values[1] ^= 1L << 31; + values[2] ^= 1L << 32; + values[3] ^= 1L << 63; + } + + @Test(compLevel = CompLevel.C2) + @IR(counts = {BCLRI, "4"}, phase = CompilePhase.FINAL_CODE) + static void testBclri(long[] values) { + values[0] &= ~(1L << 11); + values[1] &= ~(1L << 31); + values[2] &= ~(1L << 32); + values[3] &= ~(1L << 63); + } + + // Bit 10 is the largest bit mask encodable by ori/xori/andi. It must not + // select a Zbs immediate instruction, even when UseZbs is enabled. + @Test(compLevel = CompLevel.C2) + @IR(failOn = {BSETI, BINVI, BCLRI}, phase = CompilePhase.FINAL_CODE) + static void testSimm12(long[] values) { + values[0] |= 1L << 10; + values[1] ^= 1L << 10; + values[2] &= ~(1L << 10); + } + + @Run(test = {"testBseti", "testBinvi", "testBclri", "testSimm12"}) + static void runTests() { + for (long input : INPUTS) { + long[] values = {input, input, input, input}; + testBseti(values); + Asserts.assertEQ(values[0], input | (1L << 11)); + Asserts.assertEQ(values[1], input | (1L << 31)); + Asserts.assertEQ(values[2], input | (1L << 32)); + Asserts.assertEQ(values[3], input | (1L << 63)); + + values = new long[] {input, input, input, input}; + testBinvi(values); + Asserts.assertEQ(values[0], input ^ (1L << 11)); + Asserts.assertEQ(values[1], input ^ (1L << 31)); + Asserts.assertEQ(values[2], input ^ (1L << 32)); + Asserts.assertEQ(values[3], input ^ (1L << 63)); + + values = new long[] {input, input, input, input}; + testBclri(values); + Asserts.assertEQ(values[0], input & ~(1L << 11)); + Asserts.assertEQ(values[1], input & ~(1L << 31)); + Asserts.assertEQ(values[2], input & ~(1L << 32)); + Asserts.assertEQ(values[3], input & ~(1L << 63)); + + values = new long[] {input, input, input}; + testSimm12(values); + Asserts.assertEQ(values[0], input | (1L << 10)); + Asserts.assertEQ(values[1], input ^ (1L << 10)); + Asserts.assertEQ(values[2], input & ~(1L << 10)); + } + } + + @Run(test = {"testBext", "testBset", "testBinv", "testBclr"}) + static void runRegisterTests() { + int[] shifts = { + Integer.MIN_VALUE, -65, -64, -33, -32, -1, + 0, 1, 31, 32, 33, 63, 64, 65, Integer.MAX_VALUE + }; + for (long input : INPUTS) { + for (int shift : shifts) { + long mask = 1L << (shift & 63); + Asserts.assertEQ(testBext(input, shift), (input & mask) == 0 ? 0L : 1L); + Asserts.assertEQ(testBset(input, shift), input | mask); + Asserts.assertEQ(testBinv(input, shift), input ^ mask); + Asserts.assertEQ(testBclr(input, shift), input & ~mask); + } + } + } +} diff --git a/test/hotspot/jtreg/compiler/igvn/TestDeadLoopDependentPaths.java b/test/hotspot/jtreg/compiler/igvn/TestDeadLoopDependentPaths.java new file mode 100644 index 000000000000..81ef48cdee07 --- /dev/null +++ b/test/hotspot/jtreg/compiler/igvn/TestDeadLoopDependentPaths.java @@ -0,0 +1,55 @@ +/* + * 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 8388435 + * @run main/othervm -Xcomp -XX:CompileOnly=${test.main.class}::test1 ${test.main.class} + * @run main ${test.main.class} + */ + +package compiler.igvn; + + +public class TestDeadLoopDependentPaths { + private static int field; + private static boolean doThat = false; + + public static void main(String[] args) { + test1(true); + } + + private static void test1(boolean flag) { + if (doThat) { + if (flag) { + field = 42; + } else { + field = 42; + } + if (!doThat) { + while (true) { + } + } + } + } +} diff --git a/test/hotspot/jtreg/compiler/integerArithmetic/TestDivByZeroInLiveCFGPath.java b/test/hotspot/jtreg/compiler/integerArithmetic/TestDivByZeroInLiveCFGPath.java new file mode 100644 index 000000000000..d06f068952af --- /dev/null +++ b/test/hotspot/jtreg/compiler/integerArithmetic/TestDivByZeroInLiveCFGPath.java @@ -0,0 +1,64 @@ + +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8383815 + * @summary C2: assert(false) failed: malformed IfNode with 1 outputs + * @run main/othervm -XX:CompileCommand=compileonly,${test.main.class}::* -XX:-TieredCompilation -Xbatch -XX:PerMethodTrapLimit=0 ${test.main.class} + * @run main ${test.main.class} + */ + +package compiler.integerArithmetic; + +public class TestDivByZeroInLiveCFGPath { + static long lFld; + static int iArr[] = new int[400]; + + public static void main(String[] strArr) { + for (int i = 0; i < 10; i++) { + test(); + } + } + + static void test() { + int x; + for (int i = 9; i < 100; ++i) { + int j = 100; + while (--j > 0) { + iArr[1] = (int) lFld; + } + try { + iArr[1] = (5 / j); + x = (i / iArr[8]); + } catch (ArithmeticException a_e) { + } + } + + for (int i = 18; i < 50; i++) { + iArr[2] += lFld; + } + } +} + diff --git a/test/hotspot/jtreg/compiler/integerArithmetic/TestZeroDivModWidenedCastII.java b/test/hotspot/jtreg/compiler/integerArithmetic/TestZeroDivModWidenedCastII.java new file mode 100644 index 000000000000..a5bc8fc9287c --- /dev/null +++ b/test/hotspot/jtreg/compiler/integerArithmetic/TestZeroDivModWidenedCastII.java @@ -0,0 +1,1122 @@ +/* + * Copyright (c) 2026 IBM Corporation. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8380166 + * @summary C2: crash in compiled code due to zero division because of widened CastII + * + * @run main/othervm -XX:-TieredCompilation -XX:-UseOnStackReplacement -XX:-BackgroundCompilation + * ${test.main.class} + * @run main ${test.main.class} + * + */ + +package compiler.integerArithmetic; + +public class TestZeroDivModWidenedCastII { + private static int intField; + private static long longField; + private static volatile int volatileField; + + public static void main(String[] args) { + for (int i = 0; i < 20_000; i++) { + test1(0, 9, 1, true, false); + test1(0, 9, 1, false, false); + inlined1_2(9, 1, 1, true, 0); + inlined1_3(0, 0); + test2(0, 9, 1, true, false); + test2(0, 9, 1, false, false); + inlined2_2(9, 1, 1, true, 0); + inlined2_3(0, 0); + test3(0, 9, 1, true, false); + test3(0, 9, 1, false, false); + inlined3_2(9, 1, 1, true, 0); + inlined3_3(0, 0); + test4(0, 9, 1, true, false); + test4(0, 9, 1, false, false); + inlined4_2(9, 1, 1, true, 0); + inlined4_3(0, 0); + test5(0, 9, 1, true, false); + test5(0, 9, 1, false, false); + inlined5_2(9, 1, 1, true, 0); + inlined5_3(0, 0); + test6(0, 9, 1, true, false); + test6(0, 9, 1, false, false); + inlined6_2(9, 1, 1, true, 0); + inlined6_3(0, 0); + test7(0, 9, 1, true, false); + test7(0, 9, 1, false, false); + inlined7_2(9, 1, 1, true, 0); + inlined7_3(0, 0); + test8(0, 9, 1, true, false); + test8(0, 9, 1, false, false); + inlined8_2(9, 1, 1, true, 0); + inlined8_3(0, 0); + test9(0, 9, 1, true, false); + test9(0, 9, 1, false, false); + inlined9_2(9, 1, 1, true, 0); + inlined9_3(0, 0); + test10(0, 9, 1, false); + inlined10_2(9, 1, 1, true, 0); + inlined10_3(0, 0); + test11(0, 9, 1, false); + inlined11_2(9, 1, 1, true, 0); + inlined11_3(0, 0); + test12(0, 9, 1, false); + inlined12_2(9, 1, 1, true, 0); + inlined12_3(0, 0); + test13(0, 9, 1, false); + inlined13_2(9, 1, 1, true, 0); + inlined13_3(0, 0); + test14(0, 9, 1, false); + inlined14_2(9, 1, 1, true, 0); + inlined14_3(0, 0); + test15(0, 9, 1, false); + inlined15_2(9, 1, 1, true, 0); + inlined15_3(0, 0); + test16(0, 9, 1, false); + inlined16_2(9, 1, 1, true, 0); + inlined16_3(0, 0); + test17(0, 9, 1, false); + inlined17_2(9, 1, 1, true, 0); + inlined17_3(0, 0); + } + } + + private static void test1(int k, int j, int flag, boolean flag2, boolean flag3) { + int l = 0; + for (; l < 10; l++); + int m = inlined1_3(j, l); + + int i = inlined1(k, flag2); + j = Integer.min(j, 9); + int[] array = new int[10]; + if (flag == 0) { + throw new RuntimeException("never taken"); + } + if (flag2) { + inlined1_2(j, flag, i, flag3, m); + } else { + inlined1_2(j, flag, i, flag3, m); + } + } + + private static int inlined1_3(int j, int l) { + if (l == 10) { + j = 1; + } + return j; + } + + private static void inlined1_2(int j, int flag, int i, boolean flag3, int m) { + if (flag3) { + float[] newArray = new float[j + 1]; // j + 1 in [0..10] + // RC i 0", IRNode.STORE_VECTOR, "> 0", - IRNode.VECTOR_REINTERPRET, "> 0"}, + IRNode.VECTOR_REINTERPRET_I, IRNode.VECTOR_SIZE + "min(max_int, max_float)", "> 0"}, applyIfPlatform = {"64-bit", "true"}, applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) static Object[] test6(int[] a, float[] b) { @@ -380,7 +380,7 @@ static Object[] test6(int[] a, float[] b) { @Test @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.STORE_VECTOR, "> 0", - IRNode.VECTOR_REINTERPRET, "> 0"}, + IRNode.VECTOR_REINTERPRET_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", "> 0"}, applyIfPlatform = {"64-bit", "true"}, applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) static Object[] test7(int[] a, float[] b) { @@ -405,7 +405,7 @@ static Object[] test8(long[] a, double[] b) { @Test @IR(counts = {IRNode.LOAD_VECTOR_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", "> 0", IRNode.STORE_VECTOR, "> 0", - IRNode.VECTOR_REINTERPRET, "> 0"}, + IRNode.VECTOR_REINTERPRET_L, IRNode.VECTOR_SIZE + "min(max_long, max_double)", "> 0"}, applyIfPlatform = {"64-bit", "true"}, applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) static Object[] test9(long[] a, double[] b) { @@ -418,7 +418,7 @@ static Object[] test9(long[] a, double[] b) { @Test @IR(counts = {IRNode.LOAD_VECTOR_L, "> 0", IRNode.STORE_VECTOR, "> 0", - IRNode.VECTOR_REINTERPRET, "> 0"}, + IRNode.VECTOR_REINTERPRET_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", "> 0"}, applyIfPlatform = {"64-bit", "true"}, applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) static Object[] test10(long[] a, double[] b) { @@ -639,7 +639,7 @@ public Object[] testIntToLong(int[] ints, long[] res) { @Test @IR(counts = {IRNode.LOAD_VECTOR_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", "> 0", IRNode.STORE_VECTOR, "> 0", - IRNode.VECTOR_REINTERPRET, "> 0"}, + IRNode.VECTOR_REINTERPRET_I, IRNode.VECTOR_SIZE + "min(max_int, max_float)", "> 0"}, applyIf = {"AlignVector", "false"}, applyIfPlatform = {"64-bit", "true"}, applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) @@ -654,7 +654,7 @@ static Object[] testFloatToIntMemorySegment(MemorySegment a, float[] b) { @Test @IR(counts = {IRNode.LOAD_VECTOR_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", "> 0", IRNode.STORE_VECTOR, "> 0", - IRNode.VECTOR_REINTERPRET, "> 0"}, + IRNode.VECTOR_REINTERPRET_L, IRNode.VECTOR_SIZE + "min(max_long, max_double)", "> 0"}, applyIf = {"AlignVector", "false"}, applyIfPlatform = {"64-bit", "true"}, applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) @@ -669,7 +669,7 @@ static Object[] testDoubleToLongMemorySegment(MemorySegment a, double[] b) { @Test @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.STORE_VECTOR, "> 0", - IRNode.VECTOR_REINTERPRET, "> 0"}, + IRNode.VECTOR_REINTERPRET_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", "> 0"}, applyIf = {"AlignVector", "false"}, applyIfPlatform = {"64-bit", "true"}, applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) @@ -684,7 +684,7 @@ static Object[] testIntToFloatMemorySegment(MemorySegment a, float[] b) { @Test @IR(counts = {IRNode.LOAD_VECTOR_L, "> 0", IRNode.STORE_VECTOR, "> 0", - IRNode.VECTOR_REINTERPRET, "> 0"}, + IRNode.VECTOR_REINTERPRET_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", "> 0"}, applyIf = {"AlignVector", "false"}, applyIfPlatform = {"64-bit", "true"}, applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestReinterpretAndCast.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestReinterpretAndCast.java index 9c5140d2aaab..b89bc09a8c53 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestReinterpretAndCast.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestReinterpretAndCast.java @@ -120,7 +120,8 @@ public static Object[] setup1() { @IR(counts = {IRNode.LOAD_VECTOR_L, IRNode.VECTOR_SIZE + "min(max_int, max_float, max_double, max_long)", "> 0", IRNode.VECTOR_CAST_D2F, IRNode.VECTOR_SIZE + "min(max_int, max_float, max_double, max_long)", "> 0", IRNode.STORE_VECTOR, "> 0", - IRNode.VECTOR_REINTERPRET, "> 0"}, // We have both L2D and F2I + IRNode.VECTOR_REINTERPRET_I, IRNode.VECTOR_SIZE + "min(max_int, max_float, max_double, max_long)", "> 0", + IRNode.VECTOR_REINTERPRET_D, IRNode.VECTOR_SIZE + "min(max_int, max_float, max_double, max_long)", "> 0",}, applyIfPlatform = {"64-bit", "true"}, applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) public static void test1(long[] a, int[] b) { @@ -160,13 +161,13 @@ public static Object[] setup2() { @IR(counts = {IRNode.LOAD_VECTOR_I, IRNode.VECTOR_SIZE + "min(max_int, max_float, max_short)", "> 0", IRNode.VECTOR_CAST_F2HF, IRNode.VECTOR_SIZE + "min(max_int, max_float, max_short)", "> 0", IRNode.STORE_VECTOR, "> 0", - IRNode.VECTOR_REINTERPRET, "> 0"}, // We have at least I2F + IRNode.VECTOR_REINTERPRET_F, IRNode.VECTOR_SIZE + "min(max_int, max_float, max_short)", "> 0"}, applyIfPlatform = {"64-bit", "true"}, applyIfCPUFeatureAnd = {"avx", "true", "f16c", "true"}) @IR(counts = {IRNode.LOAD_VECTOR_I, IRNode.VECTOR_SIZE + "min(max_int, max_float, max_short)", "> 0", IRNode.VECTOR_CAST_F2HF, IRNode.VECTOR_SIZE + "min(max_int, max_float, max_short)", "> 0", IRNode.STORE_VECTOR, "> 0", - IRNode.VECTOR_REINTERPRET, "> 0"}, // We have at least I2F + IRNode.VECTOR_REINTERPRET_F, IRNode.VECTOR_SIZE + "min(max_int, max_float, max_short)", "> 0"}, applyIfPlatform = {"64-bit", "true"}, applyIfCPUFeatureAnd = {"asimd", "true", "fphp", "true", "asimdhp", "true"}) public static void test2(int[] a, short[] b) { @@ -206,14 +207,14 @@ public static Object[] setup3() { IRNode.VECTOR_CAST_HF2F, IRNode.VECTOR_SIZE + "min(max_float, max_short, max_long)", "> 0", IRNode.VECTOR_CAST_I2L, IRNode.VECTOR_SIZE + "min(max_float, max_short, max_long)", "> 0", IRNode.STORE_VECTOR, "> 0", - IRNode.VECTOR_REINTERPRET, "> 0"}, // We have at least F2I + IRNode.VECTOR_REINTERPRET_I, IRNode.VECTOR_SIZE + "min(max_float, max_short, max_long)", "> 0"}, applyIfPlatform = {"64-bit", "true"}, applyIfCPUFeatureAnd = {"avx", "true", "f16c", "true"}) @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_float, max_short, max_long)", "> 0", IRNode.VECTOR_CAST_HF2F, IRNode.VECTOR_SIZE + "min(max_float, max_short, max_long)", "> 0", IRNode.VECTOR_CAST_I2L, IRNode.VECTOR_SIZE + "min(max_float, max_short, max_long)", "> 0", IRNode.STORE_VECTOR, "> 0", - IRNode.VECTOR_REINTERPRET, "> 0"}, // We have at least F2I + IRNode.VECTOR_REINTERPRET_I, IRNode.VECTOR_SIZE + "min(max_float, max_short, max_long)", "> 0"}, applyIfPlatform = {"64-bit", "true"}, applyIfCPUFeatureAnd = {"asimd", "true", "fphp", "true", "asimdhp", "true"}) public static void test3(short[] a, long[] b) { diff --git a/test/hotspot/jtreg/compiler/profiling/spectrapredefineclass/Launcher.java b/test/hotspot/jtreg/compiler/profiling/spectrapredefineclass/Launcher.java index f525c14694bd..e3559aa4e50e 100644 --- a/test/hotspot/jtreg/compiler/profiling/spectrapredefineclass/Launcher.java +++ b/test/hotspot/jtreg/compiler/profiling/spectrapredefineclass/Launcher.java @@ -36,7 +36,7 @@ * @run driver compiler.profiling.spectrapredefineclass.Launcher * @run main/othervm/timeout=480 -XX:CompilationMode=high-only -XX:-BackgroundCompilation -XX:CompileThreshold=10000 * -XX:-UseOnStackReplacement -XX:TypeProfileLevel=222 - * -XX:ReservedCodeCacheSize=3M -Djdk.attach.allowAttachSelf + * -XX:ReservedCodeCacheSize=3M -XX:+EnableDynamicAgentLoading -Djdk.attach.allowAttachSelf * compiler.profiling.spectrapredefineclass.Agent */ diff --git a/test/hotspot/jtreg/compiler/profiling/spectrapredefineclass_classloaders/Launcher.java b/test/hotspot/jtreg/compiler/profiling/spectrapredefineclass_classloaders/Launcher.java index ee77e86e3ba4..33cd510fde51 100644 --- a/test/hotspot/jtreg/compiler/profiling/spectrapredefineclass_classloaders/Launcher.java +++ b/test/hotspot/jtreg/compiler/profiling/spectrapredefineclass_classloaders/Launcher.java @@ -39,7 +39,7 @@ * @run driver compiler.profiling.spectrapredefineclass_classloaders.Launcher * @run main/othervm/timeout=480 -XX:CompilationMode=high-only -XX:-BackgroundCompilation -XX:CompileThreshold=10000 * -XX:-UseOnStackReplacement -XX:TypeProfileLevel=222 - * -XX:ReservedCodeCacheSize=3M -Djdk.attach.allowAttachSelf + * -XX:ReservedCodeCacheSize=3M -XX:+EnableDynamicAgentLoading -Djdk.attach.allowAttachSelf * compiler.profiling.spectrapredefineclass_classloaders.Agent */ diff --git a/test/hotspot/jtreg/compiler/rangechecks/TestArrayAccessAboveRCAfterRCCastIIEliminated.java b/test/hotspot/jtreg/compiler/rangechecks/TestArrayAccessAboveRCAfterRCCastIIEliminated.java index b31c14257289..94b1f3b8eee3 100644 --- a/test/hotspot/jtreg/compiler/rangechecks/TestArrayAccessAboveRCAfterRCCastIIEliminated.java +++ b/test/hotspot/jtreg/compiler/rangechecks/TestArrayAccessAboveRCAfterRCCastIIEliminated.java @@ -27,17 +27,19 @@ * @summary [REDO] C2: crash in compiled code because of dependency on removed range check CastIIs * * @run main/othervm -XX:-TieredCompilation -XX:-UseOnStackReplacement -XX:-BackgroundCompilation - * -XX:CompileCommand=dontinline,TestArrayAccessAboveRCAfterRCCastIIEliminated::notInlined - * TestArrayAccessAboveRCAfterRCCastIIEliminated + * -XX:CompileCommand=dontinline,${test.main.class}::notInlined + * ${test.main.class} * @run main/othervm -XX:-TieredCompilation -XX:-UseOnStackReplacement -XX:-BackgroundCompilation - * -XX:CompileCommand=dontinline,TestArrayAccessAboveRCAfterRCCastIIEliminated::notInlined - * -XX:+UnlockDiagnosticVMOptions -XX:+StressGCM TestArrayAccessAboveRCAfterRCCastIIEliminated - * @run main TestArrayAccessAboveRCAfterRCCastIIEliminated - * @run main/othervm -XX:CompileCommand=dontinline,TestArrayAccessAboveRCAfterRCCastIIEliminated::notInlined - * TestArrayAccessAboveRCAfterRCCastIIEliminated + * -XX:CompileCommand=dontinline,${test.main.class}::notInlined + * -XX:+UnlockDiagnosticVMOptions -XX:+StressGCM ${test.main.class} + * @run main ${test.main.class} + * @run main/othervm -XX:CompileCommand=dontinline,${test.main.class}::notInlined + * ${test.main.class} * */ +package compiler.rangechecks; + public class TestArrayAccessAboveRCAfterRCCastIIEliminated { private static int intField; private static long longField; @@ -482,7 +484,7 @@ private static void test13(int i, int j, int flag, boolean flag2) { } } - // Range check cast type widen after loop opts causes control dependency to be lost + // Widened range check cast type after loop opts causes control dependency to be lost private static void test14(int i, int j, int flag, boolean flag2) { int l = 0; for (; l < 10; l++); @@ -490,12 +492,14 @@ private static void test14(int i, int j, int flag, boolean flag2) { int[] array = new int[10]; notInlined(array); if (flag == 0) { + throw new RuntimeException("never taken"); } if (flag2) { float[] newArray = new float[10]; newArray[i+j] = 42; // i+j in [0, 9] float[] otherArray = new float[i+j]; // i+j in [0, max] if (flag == 0) { + throw new RuntimeException("never taken"); } intField = array[otherArray.length]; } else { @@ -503,6 +507,7 @@ private static void test14(int i, int j, int flag, boolean flag2) { newArray[i+j] = 42; // i+j in [0, 9] float[] otherArray = new float[i+j]; // i+j in [0, max] if (flag == 0) { + throw new RuntimeException("never taken"); } intField = array[otherArray.length]; } @@ -523,12 +528,14 @@ private static void test15(int i, int j, int flag, boolean flag2) { int[] array = new int[10]; notInlined(array); if (flag == 0) { + throw new RuntimeException("never taken"); } if (flag2) { float[] newArray = new float[10]; newArray[i+j] = 42; // i+j in [0, 9] float[] otherArray = new float[i+j]; // i+j in [0, max] if (flag == 0) { + throw new RuntimeException("never taken"); } intField = array[otherArray.length]; } else { @@ -536,6 +543,7 @@ private static void test15(int i, int j, int flag, boolean flag2) { newArray[i+j] = 42; // i+j in [0, 9] float[] otherArray = new float[i+j]; // i+j in [0, max] if (flag == 0) { + throw new RuntimeException("never taken"); } intField = array[otherArray.length]; } diff --git a/test/hotspot/jtreg/compiler/rangechecks/TestUncommonTrapMerging.java b/test/hotspot/jtreg/compiler/rangechecks/TestUncommonTrapMerging.java index b6ae41e97d01..80e879c02d6a 100644 --- a/test/hotspot/jtreg/compiler/rangechecks/TestUncommonTrapMerging.java +++ b/test/hotspot/jtreg/compiler/rangechecks/TestUncommonTrapMerging.java @@ -33,6 +33,22 @@ * compiler.rangechecks.TestUncommonTrapMerging Test2 */ +/* + * @test + * @bug 8140574 + * @summary Verify proper re-execution of checks after merging of uncommon traps + * + * @requires os.arch == "aarch64" + * @requires vm.compiler2.enabled + * @run main/othervm -Xcomp -XX:-TieredCompilation + * -XX:CompileCommand=compileonly,compiler.rangechecks.TestUncommonTrapMerging::test* + * -XX:+UnlockExperimentalVMOptions -XX:+PreferCallBasedUncommonTraps + * compiler.rangechecks.TestUncommonTrapMerging Test1 + * @run main/othervm -XX:CompileCommand=compileonly,compiler.rangechecks.TestUncommonTrapMerging::test* + * -XX:+UnlockExperimentalVMOptions -XX:+PreferCallBasedUncommonTraps + * compiler.rangechecks.TestUncommonTrapMerging Test2 + */ + package compiler.rangechecks; public class TestUncommonTrapMerging { diff --git a/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsAppendUncommonTrap.java b/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsAppendUncommonTrap.java index 2b72796836f6..9f9bb0f159b5 100644 --- a/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsAppendUncommonTrap.java +++ b/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsAppendUncommonTrap.java @@ -33,6 +33,20 @@ * compiler.stringopts.TestStackedConcatsAppendUncommonTrap */ +/* + * @test + * @bug 8357105 + * @summary Test stacked string concatenations where the toString result + * of the first StringBuilder chain is wired into an uncommon trap + * located in the second one. + * @requires os.arch == "aarch64" + * @requires vm.compiler2.enabled + * @run main/othervm -XX:-TieredCompilation -Xbatch + * -XX:CompileOnly=compiler.stringopts.TestStackedConcatsAppendUncommonTrap::* + * -XX:+UnlockExperimentalVMOptions -XX:+PreferCallBasedUncommonTraps + * compiler.stringopts.TestStackedConcatsAppendUncommonTrap + */ + package compiler.stringopts; public class TestStackedConcatsAppendUncommonTrap { diff --git a/test/hotspot/jtreg/compiler/uncommontrap/Decompile.java b/test/hotspot/jtreg/compiler/uncommontrap/Decompile.java index 10e8185e05ca..c7d05f66dc02 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/Decompile.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/Decompile.java @@ -42,6 +42,29 @@ * compiler.uncommontrap.Decompile */ +/* + * @test + * @bug 8275908 + * @summary Quick test for the new WhiteBox methods of JDK-8275908 + * + * @requires vm.compiler2.enabled & vm.compMode != "Xcomp" + * @requires vm.opt.DeoptimizeALot != true + * @requires os.arch == "aarch64" + * + * @library /test/lib + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -Xbatch -XX:-UseOnStackReplacement -XX:-TieredCompilation + * -XX:+UnlockExperimentalVMOptions -XX:PerMethodTrapLimit=100 -XX:PerBytecodeTrapLimit=4 + * -XX:TypeProfileLevel=0 + * -XX:+IgnoreUnrecognizedVMOptions -XX:-AlwaysIncrementalInline -XX:-StressIncrementalInlining + * -XX:CompileCommand=compileonly,compiler.uncommontrap.Decompile::uncommonTrap + * -XX:CompileCommand=inline,compiler.uncommontrap.Decompile*::foo + * -XX:+PreferCallBasedUncommonTraps + * compiler.uncommontrap.Decompile + */ + package compiler.uncommontrap; import java.lang.reflect.Method; diff --git a/test/hotspot/jtreg/compiler/uncommontrap/DeoptReallocFailure.java b/test/hotspot/jtreg/compiler/uncommontrap/DeoptReallocFailure.java index 4bd7f89b59db..c3b9ea36f1a7 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/DeoptReallocFailure.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/DeoptReallocFailure.java @@ -35,6 +35,23 @@ * */ +/* + * @test + * @bug 8146416 + * @library /test/lib / + * + * @requires os.arch == "aarch64" + * @requires vm.compiler2.enabled + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI -Xbatch -Xmx100m + * -XX:CompileCommand=exclude,compiler.uncommontrap.DeoptReallocFailure::main + * -XX:+UnlockExperimentalVMOptions -XX:+PreferCallBasedUncommonTraps + * compiler.uncommontrap.DeoptReallocFailure + * + */ + package compiler.uncommontrap; import jdk.test.whitebox.WhiteBox; diff --git a/test/hotspot/jtreg/compiler/uncommontrap/FoldedIfNonDomMidIf.java b/test/hotspot/jtreg/compiler/uncommontrap/FoldedIfNonDomMidIf.java index 1e1e55b18117..a96500619664 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/FoldedIfNonDomMidIf.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/FoldedIfNonDomMidIf.java @@ -30,6 +30,20 @@ * */ +/** + * @test + * @bug 8201368 + * @summary IfNode::fold_compares() may lead to incorrect execution + * + * @requires os.arch == "aarch64" + * @requires vm.compiler2.enabled + * @run main/othervm -XX:-TieredCompilation -XX:-UseOnStackReplacement + * -XX:-BackgroundCompilation + * -XX:+UnlockExperimentalVMOptions -XX:+PreferCallBasedUncommonTraps + * FoldedIfNonDomMidIf + * + */ + public class FoldedIfNonDomMidIf { public static void main(String[] args) { for (int i = 0; i < 20_000; i++) { diff --git a/test/hotspot/jtreg/compiler/uncommontrap/Test8009761.java b/test/hotspot/jtreg/compiler/uncommontrap/Test8009761.java index 417a676b3fb9..4be9e09ec4a7 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/Test8009761.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/Test8009761.java @@ -37,6 +37,25 @@ * compiler.uncommontrap.Test8009761 */ +/* + * @test + * @bug 8009761 + * @summary Deoptimization on sparc doesn't set Llast_SP correctly in the interpreter frames it creates + * @library /test/lib / + * @modules java.base/jdk.internal.misc + * + * @requires os.arch == "aarch64" + * @requires vm.compiler2.enabled + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:-UseOnStackReplacement -XX:-BackgroundCompilation -Xss512K + * -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:CompileCommand=exclude,compiler.uncommontrap.Test8009761::m2 + * -XX:+PreferCallBasedUncommonTraps + * compiler.uncommontrap.Test8009761 + */ + package compiler.uncommontrap; import jdk.test.whitebox.WhiteBox; diff --git a/test/hotspot/jtreg/compiler/uncommontrap/TestDeoptDetailsLockRank.java b/test/hotspot/jtreg/compiler/uncommontrap/TestDeoptDetailsLockRank.java index 7ff00632d7f8..d44320c59af1 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/TestDeoptDetailsLockRank.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/TestDeoptDetailsLockRank.java @@ -21,13 +21,26 @@ * questions. */ - /** +/* + * @test + * @bug 8374862 + * @summary Regression test for -XX:+Verbose -XX:+WizardMode -Xlog:deoptimization=debug crash + * @requires vm.debug + * @run main/othervm -XX:-DisplayVMOutput + * -XX:+Verbose -XX:+WizardMode -Xlog:deoptimization=debug + * compiler.uncommontrap.TestDeoptDetailsLockRank + */ + +/* * @test * @bug 8374862 * @summary Regression test for -XX:+Verbose -XX:+WizardMode -Xlog:deoptimization=debug crash * @requires vm.debug + * @requires os.arch == "aarch64" + * @requires vm.compiler2.enabled * @run main/othervm -XX:-DisplayVMOutput * -XX:+Verbose -XX:+WizardMode -Xlog:deoptimization=debug + * -XX:+UnlockExperimentalVMOptions -XX:+PreferCallBasedUncommonTraps * compiler.uncommontrap.TestDeoptDetailsLockRank */ diff --git a/test/hotspot/jtreg/compiler/uncommontrap/TestDeoptOOM.java b/test/hotspot/jtreg/compiler/uncommontrap/TestDeoptOOM.java index 435dde2d8d53..18a2ee8d9852 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/TestDeoptOOM.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/TestDeoptOOM.java @@ -46,6 +46,37 @@ * compiler.uncommontrap.TestDeoptOOM */ +/* + * @test + * @bug 6898462 8198826 + * @summary failed reallocations of scalar replaced objects during deoptimization causes crash + * + * @requires os.arch == "aarch64" + * @requires vm.compiler2.enabled + * @run main/othervm/timeout=480 -XX:-BackgroundCompilation -Xmx128M -XX:+IgnoreUnrecognizedVMOptions -XX:+VerifyStack + * -XX:CompileCommand=exclude,compiler.uncommontrap.TestDeoptOOM::main + * -XX:CompileCommand=exclude,compiler.uncommontrap.TestDeoptOOM::m9_1 + * -XX:+UnlockExperimentalVMOptions -XX:+PreferCallBasedUncommonTraps + * compiler.uncommontrap.TestDeoptOOM + */ + +/* + * @test id=Z_PreferCallBasedUncommonTraps + * @bug 8273456 + * @summary Test that ttyLock is ranked above StackWatermark_lock + * @requires vm.gc.Z + * @requires os.arch == "aarch64" + * @requires vm.compiler2.enabled + * @run main/othervm/timeout=480 -XX:-BackgroundCompilation -Xmx128M -XX:+IgnoreUnrecognizedVMOptions -XX:+VerifyStack + * -XX:CompileCommand=exclude,compiler.uncommontrap.TestDeoptOOM::main + * -XX:CompileCommand=exclude,compiler.uncommontrap.TestDeoptOOM::m9_1 + * -XX:+UnlockDiagnosticVMOptions + * -XX:-DisplayVMOutput -XX:+UseZGC -XX:+LogCompilation + * -Xlog:deoptimization=debug -XX:+Verbose + * -XX:+UnlockExperimentalVMOptions -XX:+PreferCallBasedUncommonTraps + * compiler.uncommontrap.TestDeoptOOM + */ + package compiler.uncommontrap; public class TestDeoptOOM { diff --git a/test/hotspot/jtreg/compiler/uncommontrap/TestNullAssertAtCheckCast.java b/test/hotspot/jtreg/compiler/uncommontrap/TestNullAssertAtCheckCast.java index 7a80abbde4bd..032c1c91a37a 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/TestNullAssertAtCheckCast.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/TestNullAssertAtCheckCast.java @@ -37,6 +37,24 @@ * compiler.uncommontrap.TestNullAssertAtCheckCast */ +/* + * @test + * @bug 8257594 + * @summary Test that failing checkcast does not trigger repeated recompilation until cutoff is hit. + * @requires vm.compiler2.enabled + * @requires os.arch == "aarch64" + * @modules java.base/jdk.internal.misc + * @library /test/lib + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -Xbatch -XX:CompileCommand=dontinline,compiler.uncommontrap.TestNullAssertAtCheckCast::test* + * -XX:CompileCommand=inline,compiler.uncommontrap.TestNullAssertAtCheckCast::cast + * -XX:CompileCommand=inline,compiler.uncommontrap.TestNullAssertAtCheckCast::store + * -XX:+UnlockExperimentalVMOptions -XX:+PreferCallBasedUncommonTraps + * compiler.uncommontrap.TestNullAssertAtCheckCast + */ + package compiler.uncommontrap; import jdk.test.whitebox.WhiteBox; diff --git a/test/hotspot/jtreg/compiler/uncommontrap/TestNullCheckAntiDependence.java b/test/hotspot/jtreg/compiler/uncommontrap/TestNullCheckAntiDependence.java index d1b05c1967d5..7eef4ad58f08 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/TestNullCheckAntiDependence.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/TestNullCheckAntiDependence.java @@ -32,6 +32,18 @@ * compiler.uncommontrap.TestNullCheckAntiDependence */ +/** + * @test + * @bug 8261730 8265132 + * @summary Test that no anti-dependence violation is reported between a store + * used as an implicit null check and a load placed in the null block. + * @requires os.arch == "aarch64" + * @requires vm.compiler2.enabled + * @run main/othervm -XX:-BackgroundCompilation + * -XX:+UnlockExperimentalVMOptions -XX:+PreferCallBasedUncommonTraps + * compiler.uncommontrap.TestNullCheckAntiDependence + */ + public class TestNullCheckAntiDependence { private static class MyInteger { diff --git a/test/hotspot/jtreg/compiler/uncommontrap/TestPrintDiagnosticsWithoutProfileTraps.java b/test/hotspot/jtreg/compiler/uncommontrap/TestPrintDiagnosticsWithoutProfileTraps.java index 8d2db23c0147..6527878aa703 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/TestPrintDiagnosticsWithoutProfileTraps.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/TestPrintDiagnosticsWithoutProfileTraps.java @@ -21,16 +21,32 @@ * questions. */ - /** +/* + * @test + * @bug 8374807 + * @summary Regression test for -Xlog:deoptimization=debug -XX:-ProfileTraps + * -XX:-TieredCompilation -Xcomp crash + * @modules java.base/jdk.internal.misc + * @requires vm.debug + * @run main/othervm -XX:-DisplayVMOutput -Xlog:deoptimization=debug -XX:-ProfileTraps + * -XX:-TieredCompilation -Xcomp -Xbatch + * -XX:CompileCommand=compileonly,compiler.uncommontrap.TestPrintDiagnosticsWithoutProfileTraps::test + * compiler.uncommontrap.TestPrintDiagnosticsWithoutProfileTraps + */ + +/* * @test * @bug 8374807 * @summary Regression test for -Xlog:deoptimization=debug -XX:-ProfileTraps * -XX:-TieredCompilation -Xcomp crash * @modules java.base/jdk.internal.misc * @requires vm.debug + * @requires os.arch == "aarch64" + * @requires vm.compiler2.enabled * @run main/othervm -XX:-DisplayVMOutput -Xlog:deoptimization=debug -XX:-ProfileTraps * -XX:-TieredCompilation -Xcomp -Xbatch * -XX:CompileCommand=compileonly,compiler.uncommontrap.TestPrintDiagnosticsWithoutProfileTraps::test + * -XX:+UnlockExperimentalVMOptions -XX:+PreferCallBasedUncommonTraps * compiler.uncommontrap.TestPrintDiagnosticsWithoutProfileTraps */ diff --git a/test/hotspot/jtreg/compiler/uncommontrap/TestStackBangMonitorOwned.java b/test/hotspot/jtreg/compiler/uncommontrap/TestStackBangMonitorOwned.java index e1c45a7b2673..cbdd1df843cb 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/TestStackBangMonitorOwned.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/TestStackBangMonitorOwned.java @@ -32,6 +32,20 @@ * compiler.uncommontrap.TestStackBangMonitorOwned */ +/* + * @test + * @bug 8032410 + * @summary Stack overflow at deoptimization doesn't release owned monitors + * + * @requires os.arch == "aarch64" + * @requires vm.compiler2.enabled + * @run main/othervm -XX:-BackgroundCompilation -Xss512K -XX:-UseOnStackReplacement + * -XX:CompileCommand=dontinline,compiler.uncommontrap.TestStackBangMonitorOwned::m1 + * -XX:CompileCommand=exclude,compiler.uncommontrap.TestStackBangMonitorOwned::m2 + * -XX:+UnlockExperimentalVMOptions -XX:+PreferCallBasedUncommonTraps + * compiler.uncommontrap.TestStackBangMonitorOwned + */ + package compiler.uncommontrap; public class TestStackBangMonitorOwned { diff --git a/test/hotspot/jtreg/compiler/uncommontrap/TestStackBangRbp.java b/test/hotspot/jtreg/compiler/uncommontrap/TestStackBangRbp.java index c2c80ef565e8..c2df0fdc801f 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/TestStackBangRbp.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/TestStackBangRbp.java @@ -32,6 +32,20 @@ * compiler.uncommontrap.TestStackBangRbp */ +/* + * @test + * @bug 8028308 + * @summary rbp not restored when stack overflow is thrown from deopt/uncommon trap blobs + * + * @requires os.arch == "aarch64" + * @requires vm.compiler2.enabled + * @run main/othervm -XX:-BackgroundCompilation -Xss512K -XX:-UseOnStackReplacement + * -XX:CompileCommand=dontinline,compiler.uncommontrap.TestStackBangRbp::m1 + * -XX:CompileCommand=exclude,compiler.uncommontrap.TestStackBangRbp::m2 + * -XX:+UnlockExperimentalVMOptions -XX:+PreferCallBasedUncommonTraps + * compiler.uncommontrap.TestStackBangRbp + */ + package compiler.uncommontrap; public class TestStackBangRbp { diff --git a/test/hotspot/jtreg/compiler/uncommontrap/TestUnstableIfTrap.java b/test/hotspot/jtreg/compiler/uncommontrap/TestUnstableIfTrap.java index 1875230721da..d4739bb89af2 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/TestUnstableIfTrap.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/TestUnstableIfTrap.java @@ -64,6 +64,55 @@ * never_taken_fired.xml */ +/* + * @test + * @bug 8030976 8059226 + * @requires (vm.opt.StressUnstableIfTraps == null | !vm.opt.StressUnstableIfTraps) + * @library /test/lib / + * @library /testlibrary/asm + * @modules java.base/jdk.internal.misc + * java.compiler + * java.management + * jdk.internal.jvmstat/sun.jvmstat.monitor + * + * @requires os.arch == "aarch64" + * @requires vm.compiler2.enabled + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbatch -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+UnlockExperimentalVMOptions -XX:PerMethodTrapLimit=100 + * -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,UnstableIfExecutable.test + * -XX:LogFile=always_taken_not_fired.xml + * -XX:+PreferCallBasedUncommonTraps + * compiler.uncommontrap.TestUnstableIfTrap ALWAYS_TAKEN false + * @run main/othervm -Xbatch -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+UnlockExperimentalVMOptions -XX:PerMethodTrapLimit=100 + * -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,UnstableIfExecutable.test + * -XX:LogFile=always_taken_fired.xml + * -XX:+PreferCallBasedUncommonTraps + * compiler.uncommontrap.TestUnstableIfTrap ALWAYS_TAKEN true + * @run main/othervm -Xbatch -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+UnlockExperimentalVMOptions -XX:PerMethodTrapLimit=100 + * -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,UnstableIfExecutable.test + * -XX:LogFile=never_taken_not_fired.xml + * -XX:+PreferCallBasedUncommonTraps + * compiler.uncommontrap.TestUnstableIfTrap NEVER_TAKEN false + * @run main/othervm -Xbatch -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+UnlockExperimentalVMOptions -XX:PerMethodTrapLimit=100 + * -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,UnstableIfExecutable.test + * -XX:LogFile=never_taken_fired.xml + * -XX:+PreferCallBasedUncommonTraps + * compiler.uncommontrap.TestUnstableIfTrap NEVER_TAKEN true + * @run driver compiler.testlibrary.uncommontrap.Verifier always_taken_not_fired.xml + * always_taken_fired.xml + * never_taken_not_fired.xml + * never_taken_fired.xml + */ + package compiler.uncommontrap; import compiler.testlibrary.uncommontrap.Verifier; diff --git a/test/hotspot/jtreg/compiler/uncommontrap/TraceDeoptimizationNoRealloc.java b/test/hotspot/jtreg/compiler/uncommontrap/TraceDeoptimizationNoRealloc.java index ac3def06f89f..ce8c935fc04f 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/TraceDeoptimizationNoRealloc.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/TraceDeoptimizationNoRealloc.java @@ -32,6 +32,20 @@ * compiler.uncommontrap.TraceDeoptimizationNoRealloc */ +/* + * @test + * @bug 8067144 + * @summary -Xlog:deoptimization=debug tries to print realloc'ed objects even when there are none + * + * @requires os.arch == "aarch64" + * @requires vm.compiler2.enabled + * @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement + * -XX:+UnlockDiagnosticVMOptions -XX:-DisplayVMOutput + * -Xlog:deoptimization=debug + * -XX:+UnlockExperimentalVMOptions -XX:+PreferCallBasedUncommonTraps + * compiler.uncommontrap.TraceDeoptimizationNoRealloc + */ + package compiler.uncommontrap; public class TraceDeoptimizationNoRealloc { diff --git a/test/hotspot/jtreg/compiler/uncommontrap/UncommonTrapLeak.java b/test/hotspot/jtreg/compiler/uncommontrap/UncommonTrapLeak.java index 5593f5026144..a2d75181cec9 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/UncommonTrapLeak.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/UncommonTrapLeak.java @@ -33,6 +33,18 @@ * * @author Hui Shi */ + +/* + * @test + * @bug 8260473 + * @summary Handle leak might cause object not collected as expected + * + * @requires os.arch == "aarch64" + * @requires vm.compiler2.enabled + * @run main/othervm -XX:-Inline -XX:-TieredCompilation -XX:CompileCommand=compileonly,UncommonTrapLeak.foo + * -XX:+UnlockExperimentalVMOptions -XX:+PreferCallBasedUncommonTraps + * -XX:CompileThreshold=100 -XX:-BackgroundCompilation UncommonTrapLeak + */ public class UncommonTrapLeak { static WeakReference ref = null; static int val = 0; diff --git a/test/hotspot/jtreg/compiler/uncommontrap/UncommonTrapStackBang.java b/test/hotspot/jtreg/compiler/uncommontrap/UncommonTrapStackBang.java index 5a4916a97243..b943368658cd 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/UncommonTrapStackBang.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/UncommonTrapStackBang.java @@ -29,6 +29,18 @@ * @run main/othervm compiler.uncommontrap.UncommonTrapStackBang */ +/** + * @test + * @bug 8026775 + * @summary Uncommon trap blob did not bang all the stack shadow pages + * + * @requires os.arch == "aarch64" + * @requires vm.compiler2.enabled + * @run main/othervm + * -XX:+UnlockExperimentalVMOptions -XX:+PreferCallBasedUncommonTraps + * compiler.uncommontrap.UncommonTrapStackBang + */ + /* * Note: This test does not reproduce the problem with absolute * certainty. Empirically the bug reproduces on Windows some 80+% of diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrays.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrays.java index a5ea1ba863ed..5369756ac940 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrays.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrays.java @@ -2021,7 +2021,11 @@ public void test78_verifier() { @IR(applyIf = {"UseArrayFlattening", "false"}, failOn = {ALLOC, ALLOC_ARRAY, LOAD_UNKNOWN_VALUE}) public Object test79(Object[] array, int i) { - NonValueClass i1 = (NonValueClass)array[0]; + Object element = array[0]; + if (element == null) { + return null; + } + NonValueClass i1 = (NonValueClass)element; Object o = array[1]; return array[i]; } @@ -2030,6 +2034,7 @@ public Object test79(Object[] array, int i) { public void test79_verifier() { NonValueClass obj = new NonValueClass(rI); NonValueClass[] array = new NonValueClass[2]; + array[0] = obj; array[1] = obj; Object result = test79(array, 1); Asserts.assertEquals(result, obj); @@ -2042,7 +2047,11 @@ public void test79_verifier() { @IR(applyIf = {"UseArrayFlattening", "false"}, failOn = {ALLOC, ALLOC_ARRAY, LOAD_UNKNOWN_VALUE}) public Object test80(Object[] array, int i) { - NotFlattenable vt = (NotFlattenable)array[0]; + Object element = array[0]; + if (element == null) { + return null; + } + NotFlattenable vt = (NotFlattenable)element; Object o = array[1]; return array[i]; } @@ -2051,6 +2060,7 @@ public Object test80(Object[] array, int i) { public void test80_verifier() { NotFlattenable vt = new NotFlattenable(); NotFlattenable[] array = (NotFlattenable[])ValueClass.newNullRestrictedNonAtomicArray(NotFlattenable.class, 2, NotFlattenable.DEFAULT); + array[0] = vt; array[1] = vt; Object result = test80(array, 1); Asserts.assertEquals(result, vt); @@ -2124,7 +2134,11 @@ public void test82_verifier() { @IR(applyIf = {"UseArrayFlattening", "false"}, failOn = {ALLOC, ALLOC_ARRAY, LOAD_UNKNOWN_VALUE, STORE_UNKNOWN_VALUE, INLINE_ARRAY_NULL_GUARD}) public void test83(Object[] array, Object o) { - NonValueClass i = (NonValueClass)array[0]; + Object element = array[0]; + if (element == null) { + return; + } + NonValueClass i = (NonValueClass)element; array[1] = o; } @@ -2133,6 +2147,8 @@ public void test83_verifier() { NonValueClass obj = new NonValueClass(rI); NonValueClass[] array1 = new NonValueClass[2]; Object[] array2 = new Object[2]; + array1[0] = obj; + array2[0] = obj; test83(array1, obj); Asserts.assertEquals(array1[1], obj); test83(array2, null); diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestCCMismatchNativeMethods.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestCCMismatchNativeMethods.java new file mode 100644 index 000000000000..ecc366a3f4c5 --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestCCMismatchNativeMethods.java @@ -0,0 +1,103 @@ +/* + * 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. + */ + +package compiler.valhalla.valuetypes; + +/* + * @test + * @bug 8395610 + * @summary native method calls are not scalarized and cause mismatches + * @enablePreview + * @run main/othervm -XX:-TieredCompilation ${test.main.class} + * @run main ${test.main.class} + */ + + +public class TestCCMismatchNativeMethods { + static { + System.loadLibrary("TestCCMismatchNativeMethods"); + } + + value record V1(int x) { } + + static class A1 { + int m1(V1 v1, V1 v2) { + return v1.x; + } + } + + static class B1 extends A1 { + native int m1(V1 v1, V1 v2); + } + + static class C1 extends A1 { + } + + static class A2 { + int m1(V1 v1, V1 v2) { + return v1.x; + } + } + + static class B2 extends A2 { + } + + static class C2 extends A2 { + } + + static class D2 extends A2 { + native int m1(V1 v1, V1 v2); + } + + static int test1(A1 a, V1 v) { + return a.m1(v, v); + } + + static int test2(A2 a, V1 v) { + return a.m1(v, v); + } + + public static void main(String[] args) { + A1 a1 = new A1(); + B1 b1 = new B1(); + C1 c1 = new C1(); + A2 a2 = new A2(); + B2 b2 = new B2(); + C2 c2 = new C2(); + for (int i = 0; i < 20_000; i++) { + test1(a1, new V1(42)); + test1(b1, new V1(42)); + test1(c1, new V1(42)); + test2(a2, new V1(42)); + test2(b2, new V1(42)); + test2(c2, new V1(42)); + } + D2 d2 = new D2(); + for (int i = 0; i < 20_000; i++) { + test2(a2, new V1(42)); + test2(b2, new V1(42)); + test2(c2, new V1(42)); + test2(d2, new V1(42)); + } + } +} diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestDelayedInDifferentBlock.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestDelayedInDifferentBlock.java new file mode 100644 index 000000000000..54ddb94829f2 --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestDelayedInDifferentBlock.java @@ -0,0 +1,73 @@ +/* + * 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.valhalla.valuetypes; + +import jdk.internal.value.ValueClass; +import jdk.internal.vm.annotation.LooselyConsistentValue; +import jdk.internal.vm.annotation.NullRestricted; + +import jdk.test.lib.Asserts; + +/* + * @test + * @bug 8394860 + * @summary C1 must not delay flattened field and array accesses across basic block boundaries. + * @enablePreview + * @requires vm.compiler1.enabled + * @library /test/lib + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main ${test.main.class} + * @run main/othervm -Xcomp -XX:TieredStopAtLevel=1 + * -XX:CompileCommand=compileonly,${test.main.class}::test* + * ${test.main.class} + */ +public class TestDelayedInDifferentBlock { + static value class A { + @NullRestricted + Integer i = 42; + } + + @LooselyConsistentValue + static value class B { + @NullRestricted + A a = new A(); + } + + static final B[] ARRAY = (B[])ValueClass.newNullRestrictedNonAtomicArray(B.class, 1, new B()); + + static Integer testField(boolean b, B value) { + return (b ? value.a : value.a).i; + } + + static Integer testArray(boolean b) { + return (b ? ARRAY[0] : ARRAY[0]).a.i; + } + + public static void main(String[] args) { + Asserts.assertEQ(testField(false, new B()), 42); + Asserts.assertEQ(testArray(false), 42); + } +} + diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestFlatArrayInitValueStores.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestFlatArrayInitValueStores.java new file mode 100644 index 000000000000..cc8e0a30c937 --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestFlatArrayInitValueStores.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.valhalla.valuetypes; + +import jdk.internal.value.ValueClass; +import jdk.test.lib.Asserts; + +/* + * @test + * @bug 8395888 + * @summary Test initializing stores to flat arrays. + * @enablePreview + * @library /test/lib + * @modules java.base/jdk.internal.value + * @run main/othervm -Xbatch ${test.main.class} + */ +public class TestFlatArrayInitValueStores { + static value record MyValue(MyValue other, int i) { } + + static final MyValue INIT_VAL = new MyValue(null, 42); + + // Test that a zero initializing store is observed + static int testZeroInitStore(int i) { + // array is non-escaping and thus flat stores are expanded non-atomic + MyValue[] array = (MyValue[])ValueClass.newNullRestrictedAtomicArray(MyValue.class, 1, INIT_VAL); + array[0] = new MyValue(null, 0); // Zero store to i is incorrectly skipped + return array[0].i(); + } + + // Test that a non-zero initializing store is observed + static boolean testInitValueStore(int i) { + MyValue[] array = (MyValue[])ValueClass.newNullRestrictedAtomicArray(MyValue.class, 1, INIT_VAL); + array[0] = new MyValue(INIT_VAL, i + 1); // INIT_VAL store to 'other' is incorrectly skipped + return array[0].other() == INIT_VAL; + } + + public static void main(String[] args) { + for (int i = 0; i < 20_000; i++) { + Asserts.assertEQ(testZeroInitStore(i), 0); + Asserts.assertTrue(testInitValueStore(i)); + } + } +} diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestFlatArrayNullCheckcast.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestFlatArrayNullCheckcast.java new file mode 100644 index 000000000000..c454cb715123 --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestFlatArrayNullCheckcast.java @@ -0,0 +1,65 @@ +/* + * 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; + +import jdk.test.lib.Asserts; +import jdk.internal.value.ValueClass; + +/* + * @test + * @bug 8395897 + * @summary Test that a null checkcast does not change the array layout type information. + * @enablePreview + * @modules java.base/jdk.internal.value + * @requires vm.compiler2.enabled + * @library /test/lib + * @run main ${test.main.class} + * @run main/othervm -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:-UseArrayLoadStoreProfile + * -XX:CompileCommand=compileonly,${test.main.class}::test* + * ${test.main.class} + */ +public class TestFlatArrayNullCheckcast { + static Object testLoad(Object[] array, int index) { + String unused = (String)array[0]; + return array[index]; + } + + static void testStore(Object[] array, int index) { + String unused = (String)array[0]; + array[index] = 42; + } + + public static void main(String[] args) { + Integer[] array = new Integer[2]; + array[1] = 42; + for (int i = 0; i < 50_000; i++) { + Asserts.assertNull(testLoad(array, 0)); + Asserts.assertEquals(42, testLoad(array, 1)); + array[1] = 43; + testStore(array, 1); + Asserts.assertEquals(42, array[1]); + } + } +} + diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLongShiftInBarrier.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLongShiftInBarrier.java new file mode 100644 index 000000000000..f2ea169fc7d2 --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLongShiftInBarrier.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.c1; + +import jdk.internal.vm.annotation.NullRestricted; +import jdk.test.lib.Asserts; +import jdk.test.whitebox.WhiteBox; + +/* + * @test + * @bug 8395981 + * @summary Test C1's constant long shift in GC barrier. + * @enablePreview + * @requires vm.compiler1.enabled + * @modules java.base/jdk.internal.vm.annotation + * @library /test/lib /compiler/whitebox / + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * ${test.main.class} + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+VerifyAfterGC -Xbatch -XX:TieredStopAtLevel=1 + * -XX:CompileCommand=compileonly,${test.main.class}::store + * ${test.main.class} + */ +public class TestLongShiftInBarrier { + static final WhiteBox WB = WhiteBox.getWhiteBox(); + static value record TwoOops(Object x, Object y) { } + + @NullRestricted + TwoOops field; + + TestLongShiftInBarrier() { + field = new TwoOops(null, null); + super(); + } + + void store(TwoOops value) { + // The post-barrier of the long-sized store extracts oops 'x' and 'y' by shifting + field = value; + } + + public static void main(String[] args) { + TestLongShiftInBarrier holder = new TestLongShiftInBarrier(); + TwoOops value = new TwoOops(null, null); + for (int i = 0; i < 20_000; i++) { + holder.store(value); + } + // Move holder to old + WB.fullGC(); + + // Store a young 'value' into old holder to trigger the post-barrier + value = new TwoOops(null, new Object()); + holder.store(value); + + // Trigger collection of young which will move the object referenced + // by 'value.y' and should therefore update 'y' accordingly. If the + // post-barrier does not work as expected, 'y' will not be updated + // thus contain a stale reference that is dereferenced below. + WB.youngGC(); + + Asserts.assertEQ(holder.field, value); + } +} diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestReducePhiWithFlatAccess.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestReducePhiWithFlatAccess.java new file mode 100644 index 000000000000..8c1ab928b883 --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestReducePhiWithFlatAccess.java @@ -0,0 +1,56 @@ +/* + * 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.valhalla.valuetypes; + +/* + * @test + * @bug 8393467 + * @summary Test that temporary GraphKit map is killed after flat access expansion + * @enablePreview + * @run main/othervm -Xbatch ${test.main.class} + */ +public class TestReducePhiWithFlatAccess { + static class Test { + Integer i; + } + + static void sink(Object a, Object b) { } + + static void testLoad(boolean b) { + sink(b ? new Test() : null, new Test().i); + } + + static void testStore(boolean b) { + sink(b ? new Test() : null, new Test().i = 42); + } + + public static void main(String[] args) { + for (int i = 0; i < 10_000; i++) { + boolean b = (i & 1) == 0; + testLoad(b); + testStore(b); + } + } +} + diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestUnsafeAccessIntrinsicBailout.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestUnsafeAccessIntrinsicBailout.java new file mode 100644 index 000000000000..7d0cd93ac32d --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestUnsafeAccessIntrinsicBailout.java @@ -0,0 +1,50 @@ +/* + * 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.valhalla.valuetypes; + +import jdk.internal.misc.Unsafe; + +/* + * @test + * @bug 8396025 + * @summary Test control restoration when an Unsafe access intrinsic bails out + * @enablePreview + * @modules java.base/jdk.internal.misc + * @run main ${test.main.class} + * @run main/othervm -XX:-TieredCompilation -Xcomp + * -XX:CompileCommand=compileonly,${test.main.class}::test + * ${test.main.class} + */ +public class TestUnsafeAccessIntrinsicBailout { + static final Unsafe UNSAFE = Unsafe.getUnsafe(); + static final long OFFSET = UNSAFE.objectFieldOffset(Integer.class, "value"); + + static Object test(Integer value, boolean alwaysFalse) { + return alwaysFalse ? UNSAFE.getReference(value, OFFSET) : null; + } + + public static void main(String[] args) { + test(42, false); + } +} diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueTypeDeepRevisit.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueTypeDeepRevisit.java new file mode 100644 index 000000000000..84c7dca87201 --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueTypeDeepRevisit.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8394804 + * @summary Test case where IGVN deep revisit is required for ValueType + * @library /test/lib + * @enablePreview + * @run main ${test.main.class} + * @run main/othervm -Xbatch + * -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 + * -XX:CompileOnly=${test.main.class}::test + * ${test.main.class} + */ + +package compiler.valhalla.valuetypes; + +public class TestValueTypeDeepRevisit { + value record MyValue(Double d, int i) { } + + static MyValue source = new MyValue(1.0, 1); + + static void test(int limit) { + // ValueTypeNode::Ideal tries to see if there is already a heap representation + // for the new source via is_loaded. For this it needs to traverse deeply, + // through nested ValueType, object references, casts, DecodeN, to eventually + // find if the field load are at the desired offset and all from the same object. + source = new MyValue(source.d(), source.i()); + + // Empty loop, seems to make the missing optimization recognizable. + while (limit-- > 0) { } + } + + public static void main(String[] args) { + for (int i = 0; i < 20_000; i++) { + test(2); + } + } +} diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueTypeNodeNormalization.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueTypeNodeNormalization.java new file mode 100644 index 000000000000..3770bf06bf20 --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueTypeNodeNormalization.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 8393865 + * @summary [Valhalla] C2: assert(_gvn.type(stored_value_casted) == TypePtr::NULL_PTR) failed: Unexpected value + * @enablePreview + * @run main/othervm -XX:-TieredCompilation -XX:CompileCommand=compileonly,${test.main.class}::test* + -Xcomp ${test.main.class} + * @run main ${test.main.class} + */ + +package compiler.valhalla.valuetypes; + +public class TestValueTypeNodeNormalization { + static int[] array1 = new int[1]; + + static void test1() { + helper1(42,false); + } + + static int helper1(Object obj, boolean b) { + if (array1 != null && obj != null && array1.length > 0) { + return 0; + } + Integer val = (Integer) obj; + if (b) { + val = val; + } + return val; + } + + static Object[] array2 = new Long[1]; + + static void test2() { + helper2(0L); + } + + static void helper2(Object val) { + if (array2 != null && val != null && array2.length > 0) { + return; + } + array2[0] = val; + } + + public static void main(String[] args) { + test1(); + test2(); + } +} diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueUnitializedFieldsAtSafepoint.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueUnitializedFieldsAtSafepoint.java new file mode 100644 index 000000000000..52d2094dc9a0 --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueUnitializedFieldsAtSafepoint.java @@ -0,0 +1,121 @@ +/* + * 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.valhalla.valuetypes; + +import jdk.test.lib.Asserts; + +/* + * @test + * @bug 8394960 8395884 + * @summary Test scalarized null value returns with uninitialized fields + * @enablePreview + * @library /test/lib + * @run main ${test.main.class} + * @run main/othervm -Xcomp -XX:+IgnoreUnrecognizedVMOptions -XX:-PreloadClasses + * -XX:CompileCommand=exclude,${test.main.class}::getInterpreter* + * -XX:CompileCommand=exclude,${test.main.class}::getC1*,1000 + * -XX:CompileCommand=exclude,${test.main.class}::run* + * -XX:CompileCommand=dontinline,${test.main.class}::safepoint + * -XX:CompileOnly=${test.main.class}::* ${test.main.class} + */ +public class TestValueUnitializedFieldsAtSafepoint { + // Six oop fields to use all scalarized return registers on x64 + static value record V6(Object o0, Object o1, Object o2, + Object o3, Object o4, Object o5) { + } + + // Seven oop fields to use all scalarized return registers on AArch64 + static value record V7(Object o0, Object o1, Object o2, Object o3, + Object o4, Object o5, Object o6) { + } + + // This value is passed as argument to make sure the + // return register is initialized with garbage. + static final int INVALID_OOP = 0x12345678; + + static V6 getInterpreterX64() { + return null; + } + + static V7 getInterpreterAArch64() { + return null; + } + + static V6 getC1X64(int value) { + return null; + } + + static V7 getC1AArch64(int value) { + return null; + } + + static void safepoint() { + System.gc(); + } + + static int testInterpreterX64() { + V6 v = getInterpreterX64(); + safepoint(); + return v == null ? 42 : v.o0.hashCode(); + } + + static int testInterpreterAArch64() { + V7 v = getInterpreterAArch64(); + safepoint(); + return v == null ? 42 : v.o0.hashCode(); + } + + static int testC1X64() { + V6 v = getC1X64(INVALID_OOP); + safepoint(); + return v == null ? 42 : v.o5.hashCode(); + } + + static int testC1AArch64() { + V7 v = getC1AArch64(INVALID_OOP); + safepoint(); + return v == null ? 42 : v.o6.hashCode(); + } + + static void runX64() { + getC1X64(INVALID_OOP); + V6 forceLoad = new V6(null, null, null, null, null, null); + Asserts.assertEQ(testC1X64(), 42); // Make sure the call is resolved + Asserts.assertEQ(testC1X64(), 42); + Asserts.assertEQ(testInterpreterX64(), 42); + } + + static void runAArch64() { + getC1AArch64(INVALID_OOP); + V7 forceLoad = new V7(null, null, null, null, null, null, null); + Asserts.assertEQ(testC1AArch64(), 42); // Make sure the call is resolved + Asserts.assertEQ(testC1AArch64(), 42); + Asserts.assertEQ(testInterpreterAArch64(), 42); + } + + public static void main(String[] args) { + runX64(); + runAArch64(); + } +} diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/libTestCCMismatchNativeMethods.c b/test/hotspot/jtreg/compiler/valhalla/valuetypes/libTestCCMismatchNativeMethods.c new file mode 100644 index 000000000000..77c78b287e79 --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/libTestCCMismatchNativeMethods.c @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#include + +JNIEXPORT jint JNICALL +Java_compiler_valhalla_valuetypes_TestCCMismatchNativeMethods_00024B1_m1(JNIEnv* env, jobject receiver, jobject vt1, jobject vt2) { + jclass cls = (*env)->GetObjectClass(env, vt2); + jfieldID fid = (*env)->GetFieldID(env, cls, "x", "I"); + return (*env)->GetIntField(env, vt2, fid); +} + +JNIEXPORT jint JNICALL +Java_compiler_valhalla_valuetypes_TestCCMismatchNativeMethods_00024D2_m1(JNIEnv* env, jobject receiver, jobject vt1, jobject vt2) { + jclass cls = (*env)->GetObjectClass(env, vt2); + jfieldID fid = (*env)->GetFieldID(env, cls, "x", "I"); + return (*env)->GetIntField(env, vt2, fid); +} + diff --git a/test/hotspot/jtreg/compiler/vectorapi/reshape/tests/TestVectorDoubleExpandShrink.java b/test/hotspot/jtreg/compiler/vectorapi/reshape/tests/TestVectorDoubleExpandShrink.java index 2d6ca8222a47..4671bc5f1fba 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/reshape/tests/TestVectorDoubleExpandShrink.java +++ b/test/hotspot/jtreg/compiler/vectorapi/reshape/tests/TestVectorDoubleExpandShrink.java @@ -24,6 +24,7 @@ package compiler.vectorapi.reshape.tests; import compiler.lib.ir_framework.IR; +import compiler.lib.ir_framework.IRNode; import compiler.lib.ir_framework.Run; import compiler.lib.ir_framework.Test; import java.lang.foreign.MemorySegment; @@ -38,7 +39,7 @@ */ public class TestVectorDoubleExpandShrink { @Test - @IR(failOn = REINTERPRET_NODE) + @IR(failOn = IRNode.VECTOR_REINTERPRET_B) public static void testB64toB128(MemorySegment input, MemorySegment output) { vectorDoubleExpandShrink(BSPEC64, BSPEC128, input, output); } @@ -49,7 +50,7 @@ public static void runB64toB128() throws Throwable { } @Test - @IR(failOn = REINTERPRET_NODE) + @IR(failOn = IRNode.VECTOR_REINTERPRET_B) public static void testB64toB256(MemorySegment input, MemorySegment output) { vectorDoubleExpandShrink(BSPEC64, BSPEC256, input, output); } @@ -60,7 +61,7 @@ public static void runB64toB256() throws Throwable { } @Test - @IR(failOn = REINTERPRET_NODE) + @IR(failOn = IRNode.VECTOR_REINTERPRET_B) public static void testB64toB512(MemorySegment input, MemorySegment output) { vectorDoubleExpandShrink(BSPEC64, BSPEC512, input, output); } @@ -71,7 +72,9 @@ public static void runB64toB512() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "2"}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, + counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_8, "1", + IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_16, "1"}) public static void testB128toB64(MemorySegment input, MemorySegment output) { vectorDoubleExpandShrink(BSPEC128, BSPEC64, input, output); } @@ -82,7 +85,7 @@ public static void runB128toB64() throws Throwable { } @Test - @IR(failOn = REINTERPRET_NODE) + @IR(failOn = IRNode.VECTOR_REINTERPRET_B) public static void testB128toB256(MemorySegment input, MemorySegment output) { vectorDoubleExpandShrink(BSPEC128, BSPEC256, input, output); } @@ -93,7 +96,7 @@ public static void runB128toB256() throws Throwable { } @Test - @IR(failOn = REINTERPRET_NODE) + @IR(failOn = IRNode.VECTOR_REINTERPRET_B) public static void testB128toB512(MemorySegment input, MemorySegment output) { vectorDoubleExpandShrink(BSPEC128, BSPEC512, input, output); } @@ -104,7 +107,8 @@ public static void runB128toB512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "2"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_8, "1", + IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_32, "1"}) public static void testB256toB64(MemorySegment input, MemorySegment output) { vectorDoubleExpandShrink(BSPEC256, BSPEC64, input, output); } @@ -115,7 +119,8 @@ public static void runB256toB64() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "2"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_16, "1", + IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_32, "1"}) public static void testB256toB128(MemorySegment input, MemorySegment output) { vectorDoubleExpandShrink(BSPEC256, BSPEC128, input, output); } @@ -126,7 +131,7 @@ public static void runB256toB128() throws Throwable { } @Test - @IR(failOn = REINTERPRET_NODE) + @IR(failOn = IRNode.VECTOR_REINTERPRET_B) public static void testB256toB512(MemorySegment input, MemorySegment output) { vectorDoubleExpandShrink(BSPEC256, BSPEC512, input, output); } @@ -137,7 +142,8 @@ public static void runB256toB512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "2"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_8, "1", + IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_64, "1"}) public static void testB512toB64(MemorySegment input, MemorySegment output) { vectorDoubleExpandShrink(BSPEC512, BSPEC64, input, output); } @@ -148,7 +154,8 @@ public static void runB512toB64() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "2"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_16, "1", + IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_64, "1"}) public static void testB512toB128(MemorySegment input, MemorySegment output) { vectorDoubleExpandShrink(BSPEC512, BSPEC128, input, output); } @@ -159,7 +166,8 @@ public static void runB512toB128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "2"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_32, "1", + IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_64, "1"}) public static void testB512toB256(MemorySegment input, MemorySegment output) { vectorDoubleExpandShrink(BSPEC512, BSPEC256, input, output); } diff --git a/test/hotspot/jtreg/compiler/vectorapi/reshape/tests/TestVectorExpandShrink.java b/test/hotspot/jtreg/compiler/vectorapi/reshape/tests/TestVectorExpandShrink.java index 92755b96acbd..969d49b0229b 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/reshape/tests/TestVectorExpandShrink.java +++ b/test/hotspot/jtreg/compiler/vectorapi/reshape/tests/TestVectorExpandShrink.java @@ -24,6 +24,7 @@ package compiler.vectorapi.reshape.tests; import compiler.lib.ir_framework.IR; +import compiler.lib.ir_framework.IRNode; import compiler.lib.ir_framework.Run; import compiler.lib.ir_framework.Test; import java.lang.foreign.MemorySegment; @@ -38,7 +39,8 @@ */ public class TestVectorExpandShrink { @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, + counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_16, "1"}) public static void testB64toB128(MemorySegment input, MemorySegment output) { vectorExpandShrink(BSPEC64, BSPEC128, input, output); } @@ -49,7 +51,7 @@ public static void runB64toB128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_32, "1"}) public static void testB64toB256(MemorySegment input, MemorySegment output) { vectorExpandShrink(BSPEC64, BSPEC256, input, output); } @@ -60,7 +62,7 @@ public static void runB64toB256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_64, "1"}) public static void testB64toB512(MemorySegment input, MemorySegment output) { vectorExpandShrink(BSPEC64, BSPEC512, input, output); } @@ -71,7 +73,8 @@ public static void runB64toB512() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, + counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_8, "1"}) public static void testB128toB64(MemorySegment input, MemorySegment output) { vectorExpandShrink(BSPEC128, BSPEC64, input, output); } @@ -82,7 +85,7 @@ public static void runB128toB64() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_32, "1"}) public static void testB128toB256(MemorySegment input, MemorySegment output) { vectorExpandShrink(BSPEC128, BSPEC256, input, output); } @@ -93,7 +96,7 @@ public static void runB128toB256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_64, "1"}) public static void testB128toB512(MemorySegment input, MemorySegment output) { vectorExpandShrink(BSPEC128, BSPEC512, input, output); } @@ -104,7 +107,7 @@ public static void runB128toB512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_8, "1"}) public static void testB256toB64(MemorySegment input, MemorySegment output) { vectorExpandShrink(BSPEC256, BSPEC64, input, output); } @@ -115,7 +118,7 @@ public static void runB256toB64() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_16, "1"}) public static void testB256toB128(MemorySegment input, MemorySegment output) { vectorExpandShrink(BSPEC256, BSPEC128, input, output); } @@ -126,7 +129,7 @@ public static void runB256toB128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_64, "1"}) public static void testB256toB512(MemorySegment input, MemorySegment output) { vectorExpandShrink(BSPEC256, BSPEC512, input, output); } @@ -137,7 +140,7 @@ public static void runB256toB512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_8, "1"}) public static void testB512toB64(MemorySegment input, MemorySegment output) { vectorExpandShrink(BSPEC512, BSPEC64, input, output); } @@ -148,7 +151,7 @@ public static void runB512toB64() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_16, "1"}) public static void testB512toB128(MemorySegment input, MemorySegment output) { vectorExpandShrink(BSPEC512, BSPEC128, input, output); } @@ -159,7 +162,7 @@ public static void runB512toB128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_32, "1"}) public static void testB512toB256(MemorySegment input, MemorySegment output) { vectorExpandShrink(BSPEC512, BSPEC256, input, output); } diff --git a/test/hotspot/jtreg/compiler/vectorapi/reshape/tests/TestVectorRebracket.java b/test/hotspot/jtreg/compiler/vectorapi/reshape/tests/TestVectorRebracket.java index d3283e991a47..915eb83eee1c 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/reshape/tests/TestVectorRebracket.java +++ b/test/hotspot/jtreg/compiler/vectorapi/reshape/tests/TestVectorRebracket.java @@ -24,6 +24,7 @@ package compiler.vectorapi.reshape.tests; import compiler.lib.ir_framework.IR; +import compiler.lib.ir_framework.IRNode; import compiler.lib.ir_framework.Run; import compiler.lib.ir_framework.Test; @@ -41,7 +42,8 @@ */ public class TestVectorRebracket { @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, + counts = {IRNode.VECTOR_REINTERPRET_S, IRNode.VECTOR_SIZE_4, "1"}) public static void testB64toS64(byte[] input, short[] output) { vectorRebracket(BSPEC64, SSPEC64, input, output); } @@ -52,7 +54,8 @@ public static void runB64toS64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, + counts = {IRNode.VECTOR_REINTERPRET_I, IRNode.VECTOR_SIZE_2, "1"}) public static void testB64toI64(byte[] input, int[] output) { vectorRebracket(BSPEC64, ISPEC64, input, output); } @@ -63,7 +66,15 @@ public static void runB64toI64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + // Currently this test never runs because vectors of length 1 are not implemented. + // This test uses custom filtering which doesn't make it clear + // which tests are supposed to run in which environment. + // So to make it clear that this test never runs with the current implementation, + // we set the failOn condition to always fail because `*.` matches any IR node. + // JDK-8387061 plans to refactor tests like this so that filtering is based on `applyIf` rules. + // If vectors of length 1 are ever supported, this test will fail and the + // correct IR expectations will have to be defined and validated. + @IR(failOn = "*.") public static void testB64toL64(byte[] input, long[] output) { vectorRebracket(BSPEC64, LSPEC64, input, output); } @@ -74,7 +85,8 @@ public static void runB64toL64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, + counts = {IRNode.VECTOR_REINTERPRET_F, IRNode.VECTOR_SIZE_2,"1"}) public static void testB64toF64(byte[] input, float[] output) { vectorRebracket(BSPEC64, FSPEC64, input, output); } @@ -85,7 +97,15 @@ public static void runB64toF64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + // Currently this test never runs because vectors of length 1 are not implemented. + // This test uses custom filtering which doesn't make it clear + // which tests are supposed to run in which environment. + // So to make it clear that this test never runs with the current implementation, + // we set the failOn condition to always fail because `*.` matches any IR node. + // JDK-8387061 plans to refactor tests like this so that filtering is based on `applyIf` rules. + // If vectors of length 1 are ever supported, this test will fail and the + // correct IR expectations will have to be defined and validated. + @IR(failOn = "*.") public static void testB64toD64(byte[] input, double[] output) { vectorRebracket(BSPEC64, DSPEC64, input, output); } @@ -96,7 +116,8 @@ public static void runB64toD64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, + counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_8, "1"}) public static void testS64toB64(short[] input, byte[] output) { vectorRebracket(SSPEC64, BSPEC64, input, output); } @@ -107,7 +128,8 @@ public static void runS64toB64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, + counts = {IRNode.VECTOR_REINTERPRET_I, IRNode.VECTOR_SIZE_2, "1"}) public static void testS64toI64(short[] input, int[] output) { vectorRebracket(SSPEC64, ISPEC64, input, output); } @@ -118,7 +140,15 @@ public static void runS64toI64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + // Currently this test never runs because vectors of length 1 are not implemented. + // This test uses custom filtering which doesn't make it clear + // which tests are supposed to run in which environment. + // So to make it clear that this test never runs with the current implementation, + // we set the failOn condition to always fail because `*.` matches any IR node. + // JDK-8387061 plans to refactor tests like this so that filtering is based on `applyIf` rules. + // If vectors of length 1 are ever supported, this test will fail and the + // correct IR expectations will have to be defined and validated. + @IR(failOn = "*.") public static void testS64toL64(short[] input, long[] output) { vectorRebracket(SSPEC64, LSPEC64, input, output); } @@ -129,7 +159,8 @@ public static void runS64toL64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, + counts = {IRNode.VECTOR_REINTERPRET_F, IRNode.VECTOR_SIZE_2, "1"}) public static void testS64toF64(short[] input, float[] output) { vectorRebracket(SSPEC64, FSPEC64, input, output); } @@ -140,7 +171,15 @@ public static void runS64toF64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + // Currently this test never runs because vectors of length 1 are not implemented. + // This test uses custom filtering which doesn't make it clear + // which tests are supposed to run in which environment. + // So to make it clear that this test never runs with the current implementation, + // we set the failOn condition to always fail because `*.` matches any IR node. + // JDK-8387061 plans to refactor tests like this so that filtering is based on `applyIf` rules. + // If vectors of length 1 are ever supported, this test will fail and the + // correct IR expectations will have to be defined and validated. + @IR(failOn = "*.") public static void testS64toD64(short[] input, double[] output) { vectorRebracket(SSPEC64, DSPEC64, input, output); } @@ -151,7 +190,8 @@ public static void runS64toD64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, + counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_8, "1"}) public static void testI64toB64(int[] input, byte[] output) { vectorRebracket(ISPEC64, BSPEC64, input, output); } @@ -162,7 +202,8 @@ public static void runI64toB64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, + counts = {IRNode.VECTOR_REINTERPRET_S, IRNode.VECTOR_SIZE_4, "1"}) public static void testI64toS64(int[] input, short[] output) { vectorRebracket(ISPEC64, SSPEC64, input, output); } @@ -173,7 +214,15 @@ public static void runI64toS64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + // Currently this test never runs because vectors of length 1 are not implemented. + // This test uses custom filtering which doesn't make it clear + // which tests are supposed to run in which environment. + // So to make it clear that this test never runs with the current implementation, + // we set the failOn condition to always fail because `*.` matches any IR node. + // JDK-8387061 plans to refactor tests like this so that filtering is based on `applyIf` rules. + // If vectors of length 1 are ever supported, this test will fail and the + // correct IR expectations will have to be defined and validated. + @IR(failOn = "*.") public static void testI64toL64(int[] input, long[] output) { vectorRebracket(ISPEC64, LSPEC64, input, output); } @@ -184,7 +233,8 @@ public static void runI64toL64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, + counts = {IRNode.VECTOR_REINTERPRET_F, IRNode.VECTOR_SIZE_2, "1"}) public static void testI64toF64(int[] input, float[] output) { vectorRebracket(ISPEC64, FSPEC64, input, output); } @@ -195,7 +245,15 @@ public static void runI64toF64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + // Currently this test never runs because vectors of length 1 are not implemented. + // This test uses custom filtering which doesn't make it clear + // which tests are supposed to run in which environment. + // So to make it clear that this test never runs with the current implementation, + // we set the failOn condition to always fail because `*.` matches any IR node. + // JDK-8387061 plans to refactor tests like this so that filtering is based on `applyIf` rules. + // If vectors of length 1 are ever supported, this test will fail and the + // correct IR expectations will have to be defined and validated. + @IR(failOn = "*.") public static void testI64toD64(int[] input, double[] output) { vectorRebracket(ISPEC64, DSPEC64, input, output); } @@ -206,7 +264,15 @@ public static void runI64toD64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + // Currently this test never runs because vectors of length 1 are not implemented. + // This test uses custom filtering which doesn't make it clear + // which tests are supposed to run in which environment. + // So to make it clear that this test never runs with the current implementation, + // we set the failOn condition to always fail because `*.` matches any IR node. + // JDK-8387061 plans to refactor tests like this so that filtering is based on `applyIf` rules. + // If vectors of length 1 are ever supported, this test will fail and the + // correct IR expectations will have to be defined and validated. + @IR(failOn = "*.") public static void testL64toB64(long[] input, byte[] output) { vectorRebracket(LSPEC64, BSPEC64, input, output); } @@ -217,7 +283,15 @@ public static void runL64toB64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + // Currently this test never runs because vectors of length 1 are not implemented. + // This test uses custom filtering which doesn't make it clear + // which tests are supposed to run in which environment. + // So to make it clear that this test never runs with the current implementation, + // we set the failOn condition to always fail because `*.` matches any IR node. + // JDK-8387061 plans to refactor tests like this so that filtering is based on `applyIf` rules. + // If vectors of length 1 are ever supported, this test will fail and the + // correct IR expectations will have to be defined and validated. + @IR(failOn = "*.") public static void testL64toS64(long[] input, short[] output) { vectorRebracket(LSPEC64, SSPEC64, input, output); } @@ -228,7 +302,15 @@ public static void runL64toS64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + // Currently this test never runs because vectors of length 1 are not implemented. + // This test uses custom filtering which doesn't make it clear + // which tests are supposed to run in which environment. + // So to make it clear that this test never runs with the current implementation, + // we set the failOn condition to always fail because `*.` matches any IR node. + // JDK-8387061 plans to refactor tests like this so that filtering is based on `applyIf` rules. + // If vectors of length 1 are ever supported, this test will fail and the + // correct IR expectations will have to be defined and validated. + @IR(failOn = "*.") public static void testL64toI64(long[] input, int[] output) { vectorRebracket(LSPEC64, ISPEC64, input, output); } @@ -239,7 +321,15 @@ public static void runL64toI64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + // Currently this test never runs because vectors of length 1 are not implemented. + // This test uses custom filtering which doesn't make it clear + // which tests are supposed to run in which environment. + // So to make it clear that this test never runs with the current implementation, + // we set the failOn condition to always fail because `*.` matches any IR node. + // JDK-8387061 plans to refactor tests like this so that filtering is based on `applyIf` rules. + // If vectors of length 1 are ever supported, this test will fail and the + // correct IR expectations will have to be defined and validated. + @IR(failOn = "*.") public static void testL64toF64(long[] input, float[] output) { vectorRebracket(LSPEC64, FSPEC64, input, output); } @@ -250,7 +340,15 @@ public static void runL64toF64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + // Currently this test never runs because vectors of length 1 are not implemented. + // This test uses custom filtering which doesn't make it clear + // which tests are supposed to run in which environment. + // So to make it clear that this test never runs with the current implementation, + // we set the failOn condition to always fail because `*.` matches any IR node. + // JDK-8387061 plans to refactor tests like this so that filtering is based on `applyIf` rules. + // If vectors of length 1 are ever supported, this test will fail and the + // correct IR expectations will have to be defined and validated. + @IR(failOn = "*.") public static void testL64toD64(long[] input, double[] output) { vectorRebracket(LSPEC64, DSPEC64, input, output); } @@ -261,7 +359,8 @@ public static void runL64toD64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, + counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_8, "1"}) public static void testF64toB64(float[] input, byte[] output) { vectorRebracket(FSPEC64, BSPEC64, input, output); } @@ -272,7 +371,8 @@ public static void runF64toB64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, + counts = {IRNode.VECTOR_REINTERPRET_S, IRNode.VECTOR_SIZE_4, "1"}) public static void testF64toS64(float[] input, short[] output) { vectorRebracket(FSPEC64, SSPEC64, input, output); } @@ -283,7 +383,8 @@ public static void runF64toS64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, + counts = {IRNode.VECTOR_REINTERPRET_I, IRNode.VECTOR_SIZE_2, "1"}) public static void testF64toI64(float[] input, int[] output) { vectorRebracket(FSPEC64, ISPEC64, input, output); } @@ -294,7 +395,15 @@ public static void runF64toI64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + // Currently this test never runs because vectors of length 1 are not implemented. + // This test uses custom filtering which doesn't make it clear + // which tests are supposed to run in which environment. + // So to make it clear that this test never runs with the current implementation, + // we set the failOn condition to always fail because `*.` matches any IR node. + // JDK-8387061 plans to refactor tests like this so that filtering is based on `applyIf` rules. + // If vectors of length 1 are ever supported, this test will fail and the + // correct IR expectations will have to be defined and validated. + @IR(failOn = "*.") public static void testF64toL64(float[] input, long[] output) { vectorRebracket(FSPEC64, LSPEC64, input, output); } @@ -305,7 +414,15 @@ public static void runF64toL64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + // Currently this test never runs because vectors of length 1 are not implemented. + // This test uses custom filtering which doesn't make it clear + // which tests are supposed to run in which environment. + // So to make it clear that this test never runs with the current implementation, + // we set the failOn condition to always fail because `*.` matches any IR node. + // JDK-8387061 plans to refactor tests like this so that filtering is based on `applyIf` rules. + // If vectors of length 1 are ever supported, this test will fail and the + // correct IR expectations will have to be defined and validated. + @IR(failOn = "*.") public static void testF64toD64(float[] input, double[] output) { vectorRebracket(FSPEC64, DSPEC64, input, output); } @@ -316,7 +433,15 @@ public static void runF64toD64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + // Currently this test never runs because vectors of length 1 are not implemented. + // This test uses custom filtering which doesn't make it clear + // which tests are supposed to run in which environment. + // So to make it clear that this test never runs with the current implementation, + // we set the failOn condition to always fail because `*.` matches any IR node. + // JDK-8387061 plans to refactor tests like this so that filtering is based on `applyIf` rules. + // If vectors of length 1 are ever supported, this test will fail and the + // correct IR expectations will have to be defined and validated. + @IR(failOn = "*.") public static void testD64toB64(double[] input, byte[] output) { vectorRebracket(DSPEC64, BSPEC64, input, output); } @@ -327,7 +452,15 @@ public static void runD64toB64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + // Currently this test never runs because vectors of length 1 are not implemented. + // This test uses custom filtering which doesn't make it clear + // which tests are supposed to run in which environment. + // So to make it clear that this test never runs with the current implementation, + // we set the failOn condition to always fail because `*.` matches any IR node. + // JDK-8387061 plans to refactor tests like this so that filtering is based on `applyIf` rules. + // If vectors of length 1 are ever supported, this test will fail and the + // correct IR expectations will have to be defined and validated. + @IR(failOn = "*.") public static void testD64toS64(double[] input, short[] output) { vectorRebracket(DSPEC64, SSPEC64, input, output); } @@ -338,7 +471,15 @@ public static void runD64toS64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + // Currently this test never runs because vectors of length 1 are not implemented. + // This test uses custom filtering which doesn't make it clear + // which tests are supposed to run in which environment. + // So to make it clear that this test never runs with the current implementation, + // we set the failOn condition to always fail because `*.` matches any IR node. + // JDK-8387061 plans to refactor tests like this so that filtering is based on `applyIf` rules. + // If vectors of length 1 are ever supported, this test will fail and the + // correct IR expectations will have to be defined and validated. + @IR(failOn = "*.") public static void testD64toI64(double[] input, int[] output) { vectorRebracket(DSPEC64, ISPEC64, input, output); } @@ -349,7 +490,15 @@ public static void runD64toI64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + // Currently this test never runs because vectors of length 1 are not implemented. + // This test uses custom filtering which doesn't make it clear + // which tests are supposed to run in which environment. + // So to make it clear that this test never runs with the current implementation, + // we set the failOn condition to always fail because `*.` matches any IR node. + // JDK-8387061 plans to refactor tests like this so that filtering is based on `applyIf` rules. + // If vectors of length 1 are ever supported, this test will fail and the + // correct IR expectations will have to be defined and validated. + @IR(failOn = "*.") public static void testD64toL64(double[] input, long[] output) { vectorRebracket(DSPEC64, LSPEC64, input, output); } @@ -360,7 +509,15 @@ public static void runD64toL64() throws Throwable { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {REINTERPRET_NODE, "1"}) + // Currently this test never runs because vectors of length 1 are not implemented. + // This test uses custom filtering which doesn't make it clear + // which tests are supposed to run in which environment. + // So to make it clear that this test never runs with the current implementation, + // we set the failOn condition to always fail because `*.` matches any IR node. + // JDK-8387061 plans to refactor tests like this so that filtering is based on `applyIf` rules. + // If vectors of length 1 are ever supported, this test will fail and the + // correct IR expectations will have to be defined and validated. + @IR(failOn = "*.") public static void testD64toF64(double[] input, float[] output) { vectorRebracket(DSPEC64, FSPEC64, input, output); } @@ -371,7 +528,7 @@ public static void runD64toF64() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_S, IRNode.VECTOR_SIZE_8, "1"}) public static void testB128toS128(byte[] input, short[] output) { vectorRebracket(BSPEC128, SSPEC128, input, output); } @@ -382,7 +539,7 @@ public static void runB128toS128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_I, IRNode.VECTOR_SIZE_4, "1"}) public static void testB128toI128(byte[] input, int[] output) { vectorRebracket(BSPEC128, ISPEC128, input, output); } @@ -393,7 +550,7 @@ public static void runB128toI128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_L, IRNode.VECTOR_SIZE_2, "1"}) public static void testB128toL128(byte[] input, long[] output) { vectorRebracket(BSPEC128, LSPEC128, input, output); } @@ -404,7 +561,7 @@ public static void runB128toL128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_F, IRNode.VECTOR_SIZE_4, "1"}) public static void testB128toF128(byte[] input, float[] output) { vectorRebracket(BSPEC128, FSPEC128, input, output); } @@ -415,7 +572,7 @@ public static void runB128toF128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_D, IRNode.VECTOR_SIZE_2, "1"}) public static void testB128toD128(byte[] input, double[] output) { vectorRebracket(BSPEC128, DSPEC128, input, output); } @@ -426,7 +583,7 @@ public static void runB128toD128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_16, "1"}) public static void testS128toB128(short[] input, byte[] output) { vectorRebracket(SSPEC128, BSPEC128, input, output); } @@ -437,7 +594,7 @@ public static void runS128toB128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_I, IRNode.VECTOR_SIZE_4, "1"}) public static void testS128toI128(short[] input, int[] output) { vectorRebracket(SSPEC128, ISPEC128, input, output); } @@ -448,7 +605,7 @@ public static void runS128toI128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_L, IRNode.VECTOR_SIZE_2, "1"}) public static void testS128toL128(short[] input, long[] output) { vectorRebracket(SSPEC128, LSPEC128, input, output); } @@ -459,7 +616,7 @@ public static void runS128toL128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_F, IRNode.VECTOR_SIZE_4, "1"}) public static void testS128toF128(short[] input, float[] output) { vectorRebracket(SSPEC128, FSPEC128, input, output); } @@ -470,7 +627,7 @@ public static void runS128toF128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_D, IRNode.VECTOR_SIZE_2, "1"}) public static void testS128toD128(short[] input, double[] output) { vectorRebracket(SSPEC128, DSPEC128, input, output); } @@ -481,7 +638,7 @@ public static void runS128toD128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_16, "1"}) public static void testI128toB128(int[] input, byte[] output) { vectorRebracket(ISPEC128, BSPEC128, input, output); } @@ -492,7 +649,7 @@ public static void runI128toB128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_S, IRNode.VECTOR_SIZE_8, "1"}) public static void testI128toS128(int[] input, short[] output) { vectorRebracket(ISPEC128, SSPEC128, input, output); } @@ -503,7 +660,7 @@ public static void runI128toS128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_L, IRNode.VECTOR_SIZE_2, "1"}) public static void testI128toL128(int[] input, long[] output) { vectorRebracket(ISPEC128, LSPEC128, input, output); } @@ -514,7 +671,7 @@ public static void runI128toL128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_F, IRNode.VECTOR_SIZE_4, "1"}) public static void testI128toF128(int[] input, float[] output) { vectorRebracket(ISPEC128, FSPEC128, input, output); } @@ -525,7 +682,7 @@ public static void runI128toF128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_D, IRNode.VECTOR_SIZE_2, "1"}) public static void testI128toD128(int[] input, double[] output) { vectorRebracket(ISPEC128, DSPEC128, input, output); } @@ -536,7 +693,7 @@ public static void runI128toD128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_16, "1"}) public static void testL128toB128(long[] input, byte[] output) { vectorRebracket(LSPEC128, BSPEC128, input, output); } @@ -547,7 +704,7 @@ public static void runL128toB128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_S, IRNode.VECTOR_SIZE_8, "1"}) public static void testL128toS128(long[] input, short[] output) { vectorRebracket(LSPEC128, SSPEC128, input, output); } @@ -558,7 +715,7 @@ public static void runL128toS128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_I, IRNode.VECTOR_SIZE_4, "1"}) public static void testL128toI128(long[] input, int[] output) { vectorRebracket(LSPEC128, ISPEC128, input, output); } @@ -569,7 +726,7 @@ public static void runL128toI128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_F, IRNode.VECTOR_SIZE_4, "1"}) public static void testL128toF128(long[] input, float[] output) { vectorRebracket(LSPEC128, FSPEC128, input, output); } @@ -580,7 +737,7 @@ public static void runL128toF128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_D, IRNode.VECTOR_SIZE_2, "1"}) public static void testL128toD128(long[] input, double[] output) { vectorRebracket(LSPEC128, DSPEC128, input, output); } @@ -591,7 +748,7 @@ public static void runL128toD128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_16, "1"}) public static void testF128toB128(float[] input, byte[] output) { vectorRebracket(FSPEC128, BSPEC128, input, output); } @@ -602,7 +759,7 @@ public static void runF128toB128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_S, IRNode.VECTOR_SIZE_8, "1"}) public static void testF128toS128(float[] input, short[] output) { vectorRebracket(FSPEC128, SSPEC128, input, output); } @@ -613,7 +770,7 @@ public static void runF128toS128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_I, IRNode.VECTOR_SIZE_4, "1"}) public static void testF128toI128(float[] input, int[] output) { vectorRebracket(FSPEC128, ISPEC128, input, output); } @@ -624,7 +781,7 @@ public static void runF128toI128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_L, IRNode.VECTOR_SIZE_2, "1"}) public static void testF128toL128(float[] input, long[] output) { vectorRebracket(FSPEC128, LSPEC128, input, output); } @@ -635,7 +792,7 @@ public static void runF128toL128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_D, IRNode.VECTOR_SIZE_2, "1"}) public static void testF128toD128(float[] input, double[] output) { vectorRebracket(FSPEC128, DSPEC128, input, output); } @@ -646,7 +803,7 @@ public static void runF128toD128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_16, "1"}) public static void testD128toB128(double[] input, byte[] output) { vectorRebracket(DSPEC128, BSPEC128, input, output); } @@ -657,7 +814,7 @@ public static void runD128toB128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_S, IRNode.VECTOR_SIZE_8, "1"}) public static void testD128toS128(double[] input, short[] output) { vectorRebracket(DSPEC128, SSPEC128, input, output); } @@ -668,7 +825,7 @@ public static void runD128toS128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_I, IRNode.VECTOR_SIZE_4, "1"}) public static void testD128toI128(double[] input, int[] output) { vectorRebracket(DSPEC128, ISPEC128, input, output); } @@ -679,7 +836,7 @@ public static void runD128toI128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_L, IRNode.VECTOR_SIZE_2, "1"}) public static void testD128toL128(double[] input, long[] output) { vectorRebracket(DSPEC128, LSPEC128, input, output); } @@ -690,7 +847,7 @@ public static void runD128toL128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_F, IRNode.VECTOR_SIZE_4, "1"}) public static void testD128toF128(double[] input, float[] output) { vectorRebracket(DSPEC128, FSPEC128, input, output); } @@ -701,7 +858,7 @@ public static void runD128toF128() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_S, IRNode.VECTOR_SIZE_16, "1"}) public static void testB256toS256(byte[] input, short[] output) { vectorRebracket(BSPEC256, SSPEC256, input, output); } @@ -712,7 +869,7 @@ public static void runB256toS256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_I, IRNode.VECTOR_SIZE_8, "1"}) public static void testB256toI256(byte[] input, int[] output) { vectorRebracket(BSPEC256, ISPEC256, input, output); } @@ -723,7 +880,7 @@ public static void runB256toI256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_L, IRNode.VECTOR_SIZE_4, "1"}) public static void testB256toL256(byte[] input, long[] output) { vectorRebracket(BSPEC256, LSPEC256, input, output); } @@ -734,7 +891,7 @@ public static void runB256toL256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_F, IRNode.VECTOR_SIZE_8, "1"}) public static void testB256toF256(byte[] input, float[] output) { vectorRebracket(BSPEC256, FSPEC256, input, output); } @@ -745,7 +902,7 @@ public static void runB256toF256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_D, IRNode.VECTOR_SIZE_4, "1"}) public static void testB256toD256(byte[] input, double[] output) { vectorRebracket(BSPEC256, DSPEC256, input, output); } @@ -756,7 +913,7 @@ public static void runB256toD256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_32, "1"}) public static void testS256toB256(short[] input, byte[] output) { vectorRebracket(SSPEC256, BSPEC256, input, output); } @@ -767,7 +924,7 @@ public static void runS256toB256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_I, IRNode.VECTOR_SIZE_8, "1"}) public static void testS256toI256(short[] input, int[] output) { vectorRebracket(SSPEC256, ISPEC256, input, output); } @@ -778,7 +935,7 @@ public static void runS256toI256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_L, IRNode.VECTOR_SIZE_4, "1"}) public static void testS256toL256(short[] input, long[] output) { vectorRebracket(SSPEC256, LSPEC256, input, output); } @@ -789,7 +946,7 @@ public static void runS256toL256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_F, IRNode.VECTOR_SIZE_8, "1"}) public static void testS256toF256(short[] input, float[] output) { vectorRebracket(SSPEC256, FSPEC256, input, output); } @@ -800,7 +957,7 @@ public static void runS256toF256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_D, IRNode.VECTOR_SIZE_4, "1"}) public static void testS256toD256(short[] input, double[] output) { vectorRebracket(SSPEC256, DSPEC256, input, output); } @@ -811,7 +968,7 @@ public static void runS256toD256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_32, "1"}) public static void testI256toB256(int[] input, byte[] output) { vectorRebracket(ISPEC256, BSPEC256, input, output); } @@ -822,7 +979,7 @@ public static void runI256toB256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_S, IRNode.VECTOR_SIZE_16, "1"}) public static void testI256toS256(int[] input, short[] output) { vectorRebracket(ISPEC256, SSPEC256, input, output); } @@ -833,7 +990,7 @@ public static void runI256toS256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_L, IRNode.VECTOR_SIZE_4, "1"}) public static void testI256toL256(int[] input, long[] output) { vectorRebracket(ISPEC256, LSPEC256, input, output); } @@ -844,7 +1001,7 @@ public static void runI256toL256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_F, IRNode.VECTOR_SIZE_8, "1"}) public static void testI256toF256(int[] input, float[] output) { vectorRebracket(ISPEC256, FSPEC256, input, output); } @@ -855,7 +1012,7 @@ public static void runI256toF256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_D, IRNode.VECTOR_SIZE_4, "1"}) public static void testI256toD256(int[] input, double[] output) { vectorRebracket(ISPEC256, DSPEC256, input, output); } @@ -866,7 +1023,7 @@ public static void runI256toD256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_32, "1"}) public static void testL256toB256(long[] input, byte[] output) { vectorRebracket(LSPEC256, BSPEC256, input, output); } @@ -877,7 +1034,7 @@ public static void runL256toB256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_S, IRNode.VECTOR_SIZE_16, "1"}) public static void testL256toS256(long[] input, short[] output) { vectorRebracket(LSPEC256, SSPEC256, input, output); } @@ -888,7 +1045,7 @@ public static void runL256toS256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_I, IRNode.VECTOR_SIZE_8, "1"}) public static void testL256toI256(long[] input, int[] output) { vectorRebracket(LSPEC256, ISPEC256, input, output); } @@ -899,7 +1056,7 @@ public static void runL256toI256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_F, IRNode.VECTOR_SIZE_8, "1"}) public static void testL256toF256(long[] input, float[] output) { vectorRebracket(LSPEC256, FSPEC256, input, output); } @@ -910,7 +1067,7 @@ public static void runL256toF256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_D, IRNode.VECTOR_SIZE_4, "1"}) public static void testL256toD256(long[] input, double[] output) { vectorRebracket(LSPEC256, DSPEC256, input, output); } @@ -921,7 +1078,7 @@ public static void runL256toD256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_32, "1"}) public static void testF256toB256(float[] input, byte[] output) { vectorRebracket(FSPEC256, BSPEC256, input, output); } @@ -932,7 +1089,7 @@ public static void runF256toB256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_S, IRNode.VECTOR_SIZE_16, "1"}) public static void testF256toS256(float[] input, short[] output) { vectorRebracket(FSPEC256, SSPEC256, input, output); } @@ -943,7 +1100,7 @@ public static void runF256toS256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_I, IRNode.VECTOR_SIZE_8, "1"}) public static void testF256toI256(float[] input, int[] output) { vectorRebracket(FSPEC256, ISPEC256, input, output); } @@ -954,7 +1111,7 @@ public static void runF256toI256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_L, IRNode.VECTOR_SIZE_4, "1"}) public static void testF256toL256(float[] input, long[] output) { vectorRebracket(FSPEC256, LSPEC256, input, output); } @@ -965,7 +1122,7 @@ public static void runF256toL256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_D, IRNode.VECTOR_SIZE_4, "1"}) public static void testF256toD256(float[] input, double[] output) { vectorRebracket(FSPEC256, DSPEC256, input, output); } @@ -976,7 +1133,7 @@ public static void runF256toD256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, IRNode.VECTOR_SIZE_32, "1"}) public static void testD256toB256(double[] input, byte[] output) { vectorRebracket(DSPEC256, BSPEC256, input, output); } @@ -987,7 +1144,7 @@ public static void runD256toB256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_S, IRNode.VECTOR_SIZE_16, "1"}) public static void testD256toS256(double[] input, short[] output) { vectorRebracket(DSPEC256, SSPEC256, input, output); } @@ -998,7 +1155,7 @@ public static void runD256toS256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_I, IRNode.VECTOR_SIZE_8, "1"}) public static void testD256toI256(double[] input, int[] output) { vectorRebracket(DSPEC256, ISPEC256, input, output); } @@ -1009,7 +1166,7 @@ public static void runD256toI256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_L, IRNode.VECTOR_SIZE_4, "1"}) public static void testD256toL256(double[] input, long[] output) { vectorRebracket(DSPEC256, LSPEC256, input, output); } @@ -1020,7 +1177,7 @@ public static void runD256toL256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_F, IRNode.VECTOR_SIZE_8, "1"}) public static void testD256toF256(double[] input, float[] output) { vectorRebracket(DSPEC256, FSPEC256, input, output); } @@ -1031,7 +1188,7 @@ public static void runD256toF256() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_S, "1"}) public static void testB512toS512(byte[] input, short[] output) { vectorRebracket(BSPEC512, SSPEC512, input, output); } @@ -1042,7 +1199,7 @@ public static void runB512toS512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_I, "1"}) public static void testB512toI512(byte[] input, int[] output) { vectorRebracket(BSPEC512, ISPEC512, input, output); } @@ -1053,7 +1210,7 @@ public static void runB512toI512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_L, "1"}) public static void testB512toL512(byte[] input, long[] output) { vectorRebracket(BSPEC512, LSPEC512, input, output); } @@ -1064,7 +1221,7 @@ public static void runB512toL512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_F, "1"}) public static void testB512toF512(byte[] input, float[] output) { vectorRebracket(BSPEC512, FSPEC512, input, output); } @@ -1075,7 +1232,7 @@ public static void runB512toF512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_D, "1"}) public static void testB512toD512(byte[] input, double[] output) { vectorRebracket(BSPEC512, DSPEC512, input, output); } @@ -1086,7 +1243,7 @@ public static void runB512toD512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, "1"}) public static void testS512toB512(short[] input, byte[] output) { vectorRebracket(SSPEC512, BSPEC512, input, output); } @@ -1097,7 +1254,7 @@ public static void runS512toB512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_I, "1"}) public static void testS512toI512(short[] input, int[] output) { vectorRebracket(SSPEC512, ISPEC512, input, output); } @@ -1108,7 +1265,7 @@ public static void runS512toI512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_L, "1"}) public static void testS512toL512(short[] input, long[] output) { vectorRebracket(SSPEC512, LSPEC512, input, output); } @@ -1119,7 +1276,7 @@ public static void runS512toL512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_F, "1"}) public static void testS512toF512(short[] input, float[] output) { vectorRebracket(SSPEC512, FSPEC512, input, output); } @@ -1130,7 +1287,7 @@ public static void runS512toF512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_D, "1"}) public static void testS512toD512(short[] input, double[] output) { vectorRebracket(SSPEC512, DSPEC512, input, output); } @@ -1141,7 +1298,7 @@ public static void runS512toD512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, "1"}) public static void testI512toB512(int[] input, byte[] output) { vectorRebracket(ISPEC512, BSPEC512, input, output); } @@ -1152,7 +1309,7 @@ public static void runI512toB512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_S, "1"}) public static void testI512toS512(int[] input, short[] output) { vectorRebracket(ISPEC512, SSPEC512, input, output); } @@ -1163,7 +1320,7 @@ public static void runI512toS512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_L, "1"}) public static void testI512toL512(int[] input, long[] output) { vectorRebracket(ISPEC512, LSPEC512, input, output); } @@ -1174,7 +1331,7 @@ public static void runI512toL512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_F, "1"}) public static void testI512toF512(int[] input, float[] output) { vectorRebracket(ISPEC512, FSPEC512, input, output); } @@ -1185,7 +1342,7 @@ public static void runI512toF512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_D, "1"}) public static void testI512toD512(int[] input, double[] output) { vectorRebracket(ISPEC512, DSPEC512, input, output); } @@ -1196,7 +1353,7 @@ public static void runI512toD512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, "1"}) public static void testL512toB512(long[] input, byte[] output) { vectorRebracket(LSPEC512, BSPEC512, input, output); } @@ -1207,7 +1364,7 @@ public static void runL512toB512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_S, "1"}) public static void testL512toS512(long[] input, short[] output) { vectorRebracket(LSPEC512, SSPEC512, input, output); } @@ -1218,7 +1375,7 @@ public static void runL512toS512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_I, "1"}) public static void testL512toI512(long[] input, int[] output) { vectorRebracket(LSPEC512, ISPEC512, input, output); } @@ -1229,7 +1386,7 @@ public static void runL512toI512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_F, "1"}) public static void testL512toF512(long[] input, float[] output) { vectorRebracket(LSPEC512, FSPEC512, input, output); } @@ -1240,7 +1397,7 @@ public static void runL512toF512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_D, "1"}) public static void testL512toD512(long[] input, double[] output) { vectorRebracket(LSPEC512, DSPEC512, input, output); } @@ -1251,7 +1408,7 @@ public static void runL512toD512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, "1"}) public static void testF512toB512(float[] input, byte[] output) { vectorRebracket(FSPEC512, BSPEC512, input, output); } @@ -1262,7 +1419,7 @@ public static void runF512toB512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_S, "1"}) public static void testF512toS512(float[] input, short[] output) { vectorRebracket(FSPEC512, SSPEC512, input, output); } @@ -1273,7 +1430,7 @@ public static void runF512toS512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_I, "1"}) public static void testF512toI512(float[] input, int[] output) { vectorRebracket(FSPEC512, ISPEC512, input, output); } @@ -1284,7 +1441,7 @@ public static void runF512toI512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_L, "1"}) public static void testF512toL512(float[] input, long[] output) { vectorRebracket(FSPEC512, LSPEC512, input, output); } @@ -1295,7 +1452,7 @@ public static void runF512toL512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_D, "1"}) public static void testF512toD512(float[] input, double[] output) { vectorRebracket(FSPEC512, DSPEC512, input, output); } @@ -1306,7 +1463,7 @@ public static void runF512toD512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_B, "1"}) public static void testD512toB512(double[] input, byte[] output) { vectorRebracket(DSPEC512, BSPEC512, input, output); } @@ -1317,7 +1474,7 @@ public static void runD512toB512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_S, "1"}) public static void testD512toS512(double[] input, short[] output) { vectorRebracket(DSPEC512, SSPEC512, input, output); } @@ -1328,7 +1485,7 @@ public static void runD512toS512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_I, "1"}) public static void testD512toI512(double[] input, int[] output) { vectorRebracket(DSPEC512, ISPEC512, input, output); } @@ -1339,7 +1496,7 @@ public static void runD512toI512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_L, "1"}) public static void testD512toL512(double[] input, long[] output) { vectorRebracket(DSPEC512, LSPEC512, input, output); } @@ -1350,7 +1507,7 @@ public static void runD512toL512() throws Throwable { } @Test - @IR(counts = {REINTERPRET_NODE, "1"}) + @IR(counts = {IRNode.VECTOR_REINTERPRET_F, "1"}) public static void testD512toF512(double[] input, float[] output) { vectorRebracket(DSPEC512, FSPEC512, input, output); } diff --git a/test/hotspot/jtreg/compiler/vectorapi/reshape/utils/VectorReshapeHelper.java b/test/hotspot/jtreg/compiler/vectorapi/reshape/utils/VectorReshapeHelper.java index a9f2ed54d01f..f63e9701a4a9 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/reshape/utils/VectorReshapeHelper.java +++ b/test/hotspot/jtreg/compiler/vectorapi/reshape/utils/VectorReshapeHelper.java @@ -71,8 +71,6 @@ public class VectorReshapeHelper { public static final VectorSpecies FSPEC512 = FloatVector.SPECIES_512; public static final VectorSpecies DSPEC512 = DoubleVector.SPECIES_512; - public static final String REINTERPRET_NODE = IRNode.VECTOR_REINTERPRET; - public static void runMainHelper(Class testClass, Stream testMethods, String... flags) { var test = new TestFramework(testClass); test.setDefaultWarmup(1); diff --git a/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java b/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java index 9f5a90f2e03b..7ac44ca50cf1 100644 --- a/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java +++ b/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java @@ -29,7 +29,7 @@ /* * @test - * @bug 8350177 8362171 8369881 8342095 8380988 8394230 + * @bug 8350177 8362171 8369881 8342095 8380988 8394230 8394456 * @summary Ensure that truncation of subword vectors produces correct results * @library /test/lib / * @run driver compiler.vectorization.TestSubwordTruncation @@ -70,6 +70,16 @@ static Object[] setupCharArray() { return new Object[] { arr }; } + @Setup + static Object[] setupIntArray() { + int[] arr = new int[SIZE]; + for (int i = 0; i < SIZE; i++) { + arr[i] = G.next().intValue(); + } + + return new Object[] { arr }; + } + @Setup static Object[] setupLongArray() { long[] arr = new long[SIZE]; @@ -606,6 +616,62 @@ public Object[] testCountTrailingZerosL(long[] in) { return new Object[] { res }; } + @Test + @IR(counts = { IRNode.COMPRESS_BITS, ">0" }, applyIfPlatform = {"x64", "true"}, applyIf = { "UseParallelBitInstructions", "true" }) + @IR(counts = { IRNode.COMPRESS_BITS, ">0" }, applyIfCPUFeature = { "svebitperm", "true" }) + @Arguments(setup = "setupIntArray") + public Object[] testCompressBitsI(int[] in) { + byte[] res = new byte[SIZE]; + + for (int i = 0; i < SIZE; i++) { + res[i] = (byte) Integer.compress(in[i], in[i]); + } + + return new Object[] { res }; + } + + @Test + @IR(counts = { IRNode.EXPAND_BITS, ">0" }, applyIfPlatform = {"x64", "true"}, applyIf = { "UseParallelBitInstructions", "true" }) + @IR(counts = { IRNode.EXPAND_BITS, ">0" }, applyIfCPUFeature = { "svebitperm", "true" }) + @Arguments(setup = "setupIntArray") + public Object[] testExpandBitsI(int[] in) { + byte[] res = new byte[SIZE]; + + for (int i = 0; i < SIZE; i++) { + res[i] = (byte) Integer.expand(in[i], in[i]); + } + + return new Object[] { res }; + } + + @Test + @IR(counts = { IRNode.COMPRESS_BITS, ">0" }, applyIfPlatform = {"x64", "true"}, applyIf = { "UseParallelBitInstructions", "true" }) + @IR(counts = { IRNode.COMPRESS_BITS, ">0" }, applyIfCPUFeature = { "svebitperm", "true" }) + @Arguments(setup = "setupLongArray") + public Object[] testCompressBitsL(long[] in) { + byte[] res = new byte[SIZE]; + + for (int i = 0; i < SIZE; i++) { + res[i] = (byte) Long.compress(in[i], in[i]); + } + + return new Object[] { res }; + } + + @Test + @IR(counts = { IRNode.EXPAND_BITS, ">0" }, applyIfPlatform = {"x64", "true"}, applyIf = { "UseParallelBitInstructions", "true" }) + @IR(counts = { IRNode.EXPAND_BITS, ">0" }, applyIfCPUFeature = { "svebitperm", "true" }) + @Arguments(setup = "setupLongArray") + public Object[] testExpandBitsL(long[] in) { + byte[] res = new byte[SIZE]; + + for (int i = 0; i < SIZE; i++) { + res[i] = (byte) Long.expand(in[i], in[i]); + } + + return new Object[] { res }; + } + public static void main(String[] args) { TestFramework.run(); } diff --git a/test/hotspot/jtreg/gc/g1/TestEagerReclaimHumongousRegions.java b/test/hotspot/jtreg/gc/g1/TestEagerReclaimHumongousRegions.java index f4843f948576..808140711784 100644 --- a/test/hotspot/jtreg/gc/g1/TestEagerReclaimHumongousRegions.java +++ b/test/hotspot/jtreg/gc/g1/TestEagerReclaimHumongousRegions.java @@ -25,7 +25,7 @@ /* * @test TestEagerReclaimHumongousRegions - * @bug 8051973 + * @bug 8051973 8394667 * @summary Test to make sure that eager reclaim of humongous objects correctly works. * @requires vm.gc.G1 * @requires vm.debug @@ -231,12 +231,12 @@ public static void main(String[] args) throws Exception { } class TestEagerReclaimHumongousRegionsClearMarkBitsRunner { - value class RefValue { + static value class RefValue { Object o; RefValue() { o = null; } } - value class IntValue { + static value class IntValue { int i; IntValue() { i = 0; } } diff --git a/test/hotspot/jtreg/gc/g1/TestHumongousFullGCLeak.java b/test/hotspot/jtreg/gc/g1/TestHumongousFullGCLeak.java new file mode 100644 index 000000000000..a8cff1ffa41d --- /dev/null +++ b/test/hotspot/jtreg/gc/g1/TestHumongousFullGCLeak.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package gc.g1; + +/* + * @test TestHumongousFullGCLeak + * @bug 8394730 + * @summary Compaction of humongous objects during Full GC should not leak card set memory. + * @requires vm.gc.G1 + * @requires vm.flagless + * @library /test/lib / + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -Xmx64M -Xms64M -Xmn6M + * -XX:MaxTenuringThreshold=0 -XX:+UseG1GC -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=summary + * -XX:G1HeapRegionSize=1M gc.g1.TestHumongousFullGCLeak + */ + +import jdk.test.lib.Asserts; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.dcmd.JMXExecutor; +import jdk.test.whitebox.WhiteBox; + +import java.lang.ref.Reference; + +public class TestHumongousFullGCLeak { + private static final int M = 1024 * 1024; + + static int nmt() throws Exception { + OutputAnalyzer output = new JMXExecutor().execute("VM.native_memory summary scale=1", true); + + String stdout = output.getStdout(); + int i = stdout.indexOf("GCCardSet"); + if (i < 0) { + throw new Exception("GCCardSet category not found."); + } + int j = stdout.indexOf("#", i); + // Example: + // GCCardSet (reserved=8720, committed=8720) (malloc=8720 tag=GCCardSet #99) + String gcCardSetLine = stdout.substring(i, stdout.indexOf(')', j) + 1).replaceAll("\\s+", " ").trim(); + System.out.println(gcCardSetLine); + int mallocStart = gcCardSetLine.indexOf("malloc=") + 7; + return Integer.parseInt(gcCardSetLine.substring(mallocStart, gcCardSetLine.indexOf(" ", mallocStart))); + } + + public static void main(String[] args) throws Exception { + WhiteBox wb = WhiteBox.getWhiteBox(); + + long regionSize = wb.g1RegionSize(); + // We want to create partially overlapping moves of humongous objects. We do that by allocating + // regular objects that die in old gen, allowing the humongous object to move if we cause a + // maximally compacting full gc. + int mallocAtStart = nmt(); + boolean hadOverlappingMove = false; + for (int j = 0; j < 20; j++) { + Object live; + Object humongous; + // Take the 2nd round as reference value, the first is not warmed up. + if (j == 2) { + mallocAtStart = nmt(); + } + wb.fullGC(); + // Something that gets promoted into old gen that is later reclaimed during the next round again. + live = new Object(); + // Cause a young gc. That "live" object will be promoted due to MaxTenuringThreshold=0 + wb.youngGC(); + // Make sure that that object survives. + Reference.reachabilityFence(live); + // Allocate our humongous object that is moving. + humongous = new int[7 * M / 4]; + long allocationAddress = wb.getObjectAddress(humongous); + // Cause a maximal compaction gc. + try { + Object big = new byte[64 * M]; + } catch (OutOfMemoryError e) { + } + long displacement = allocationAddress - wb.getObjectAddress(humongous); + long objectSize = wb.getObjectSize(humongous); + long objectFootprint = ((objectSize + regionSize - 1) / regionSize) * regionSize; + + // Synchronize checking for overlap with starting the memory probe at iteration 2. + if (j >= 2) { + hadOverlappingMove |= displacement > 0 && displacement < objectFootprint; + } + System.out.println("Malloc: " + nmt()); + } + int mallocAtEnd = nmt(); + Asserts.assertTrue(hadOverlappingMove, "No overlapping move"); + Asserts.assertLessThan((double)mallocAtEnd, mallocAtStart * 1.2); + } +} diff --git a/test/hotspot/jtreg/gc/metaspace/TestMetaspaceFirstGC.java b/test/hotspot/jtreg/gc/metaspace/TestMetaspaceFirstGC.java index d8ba25ca9f54..ee4caebd5d9d 100644 --- a/test/hotspot/jtreg/gc/metaspace/TestMetaspaceFirstGC.java +++ b/test/hotspot/jtreg/gc/metaspace/TestMetaspaceFirstGC.java @@ -94,8 +94,8 @@ private static boolean fromLoadOneClass(RecordedEvent event) { // kept alive so no collection can unload them before the threshold is reached private static final List loaders = new ArrayList<>(); - // Counted down from the JFR stream when a metadata allocation failure inside loadOneClass - // arrives, whether it was the first request is decided afterwards from the recording. + // Counted down from the JFR stream at the first metadata allocation failure from any thread, + // the recording decides afterwards if it came from loadOneClass and was the first request. private static final CountDownLatch metadataGC = new CountDownLatch(1); public interface Dummy {} @@ -117,13 +117,11 @@ public static void main(String[] args) throws Exception { try (RecordingStream rs = new RecordingStream()) { // a concurrent collector may fold the requested GC into a cycle that is already // running and never emit the cause, the failed allocation inside loadOneClass is the - // candidate, whether a GC was really asked for is checked afterwards + // candidate, whether a GC was really asked for is checked afterwards. + // A failure outside loadOneClass also stops loading; the recording determines + // whether it preceded the candidate and made the measurement unusable. rs.enable(EventNames.MetaspaceAllocationFailure).withStackTrace(); - rs.onEvent(EventNames.MetaspaceAllocationFailure, event -> { - if (fromLoadOneClass(event)) { - metadataGC.countDown(); - } - }); + rs.onEvent(EventNames.MetaspaceAllocationFailure, event -> metadataGC.countDown()); rs.startAsync(); // load what the measured window touches before it opens, the recording is checked // afterwards for any other allocation failure inside it @@ -210,6 +208,10 @@ public static void main(String[] args) throws Exception { } earlierFailures++; } + if (request == null && (startupFailures > 0 || earlierFailures > 0)) { + throw new SkippedException(startupFailures + " allocation failures before loading started and " + + earlierFailures + " other allocation failures, none inside loadOneClass, the first metadata GC request can't be measured"); + } Asserts.assertNotNull(request, "no metaspace allocation failure inside loadOneClass"); Instant nextLoad = null; for (RecordedEvent event : events) { @@ -304,7 +306,7 @@ private static void loadClassesUntilGC(int maxIterations) throws InterruptedExce System.out.println("Metadata allocation failure seen after " + maxIterations + " load attempts, metaspace used=" + getMetaspaceUsed()); return; } - throw new RuntimeException("No metadata allocation failure inside loadOneClass after " + maxIterations + " load attempts"); + throw new RuntimeException("No metadata allocation failure after " + maxIterations + " load attempts"); } private static void loadOneClass() { diff --git a/test/hotspot/jtreg/gc/shenandoah/TestFullGCHeapDump.java b/test/hotspot/jtreg/gc/shenandoah/TestFullGCHeapDump.java new file mode 100644 index 000000000000..7eb9bc963a93 --- /dev/null +++ b/test/hotspot/jtreg/gc/shenandoah/TestFullGCHeapDump.java @@ -0,0 +1,135 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +/* + * @test id=satb-before + * @bug 8394986 + * @summary Shenandoah performs a heap dump before a full GC + * @requires vm.gc.Shenandoah + * @requires vm.flagless + * @library /test/lib + * @run main/othervm -XX:+UseShenandoahGC -XX:ShenandoahGCMode=satb + * -XX:-ExplicitGCInvokesConcurrent -XX:+HeapDumpBeforeFullGC + * -XX:HeapDumpPath=dump -Xms64m -Xmx64m TestFullGCHeapDump 1 + */ + +/* + * @test id=satb-after + * @bug 8394986 + * @summary Shenandoah performs a heap dump after a full GC + * @requires vm.gc.Shenandoah + * @requires vm.flagless + * @library /test/lib + * @run main/othervm -XX:+UseShenandoahGC -XX:ShenandoahGCMode=satb + * -XX:-ExplicitGCInvokesConcurrent -XX:+HeapDumpAfterFullGC + * -XX:HeapDumpPath=dump -Xms64m -Xmx64m TestFullGCHeapDump 1 + */ + +/* + * @test id=satb-both + * @bug 8394986 + * @summary Shenandoah performs heap dumps before and after a full GC + * @requires vm.gc.Shenandoah + * @requires vm.flagless + * @library /test/lib + * @run main/othervm -XX:+UseShenandoahGC -XX:ShenandoahGCMode=satb + * -XX:-ExplicitGCInvokesConcurrent -XX:+HeapDumpBeforeFullGC + * -XX:+HeapDumpAfterFullGC -XX:HeapDumpPath=dump + * -Xms64m -Xmx64m TestFullGCHeapDump 2 + */ + +/* + * @test id=generational-before + * @bug 8394986 + * @summary Generational Shenandoah performs a heap dump before a full GC + * @requires vm.gc.Shenandoah + * @requires vm.flagless + * @library /test/lib + * @run main/othervm -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:-ExplicitGCInvokesConcurrent -XX:+HeapDumpBeforeFullGC + * -XX:HeapDumpPath=dump -Xms64m -Xmx64m TestFullGCHeapDump 1 + */ + +/* + * @test id=generational-after + * @bug 8394986 + * @summary Generational Shenandoah performs a heap dump after a full GC + * @requires vm.gc.Shenandoah + * @requires vm.flagless + * @library /test/lib + * @run main/othervm -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:-ExplicitGCInvokesConcurrent -XX:+HeapDumpAfterFullGC + * -XX:HeapDumpPath=dump -Xms64m -Xmx64m TestFullGCHeapDump 1 + */ + +/* + * @test id=generational-both + * @bug 8394986 + * @summary Generational Shenandoah performs heap dumps before and after a full GC + * @requires vm.gc.Shenandoah + * @requires vm.flagless + * @library /test/lib + * @run main/othervm -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:-ExplicitGCInvokesConcurrent -XX:+HeapDumpBeforeFullGC + * -XX:+HeapDumpAfterFullGC -XX:HeapDumpPath=dump + * -Xms64m -Xmx64m TestFullGCHeapDump 2 + */ + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import jdk.test.lib.Asserts; +import jdk.test.lib.hprof.HprofParser; + +public class TestFullGCHeapDump { + public static void main(String[] args) throws Exception { + final int expectedFiles = Integer.parseInt(args[0]); + final String heapDumpPrefix = "java_pid"; + File heapDumpDirectory = new File("dump"); + boolean directoryCreated = heapDumpDirectory.mkdir(); + Asserts.assertTrue(directoryCreated, "Directory couldn't be created"); + + // Create the heap dump file(s). + System.gc(); + + File[] heapDumpDirectoryFiles = heapDumpDirectory.listFiles(); + Asserts.assertNotNull(heapDumpDirectoryFiles, "Directory couldn't be listed"); + + List heapDumpFiles = new ArrayList<>(); + for (File file : heapDumpDirectoryFiles) { + if (!file.isDirectory() && file.getName().startsWith(heapDumpPrefix)) { + heapDumpFiles.add(file); + } + } + + Asserts.assertEquals(expectedFiles, heapDumpFiles.size(), "Heap dump files: " + heapDumpFiles); + + for (File file : heapDumpFiles) { + HprofParser.parseAndVerify(file); + file.delete(); + } + heapDumpDirectory.delete(); + } +} diff --git a/test/hotspot/jtreg/gc/shenandoah/generational/TestGenerationalReferenceProcessing.java b/test/hotspot/jtreg/gc/shenandoah/generational/TestGenerationalReferenceProcessing.java new file mode 100644 index 000000000000..d459110093aa --- /dev/null +++ b/test/hotspot/jtreg/gc/shenandoah/generational/TestGenerationalReferenceProcessing.java @@ -0,0 +1,312 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +/* + * @test id=young + * @requires vm.gc.Shenandoah + * @summary Confirm that young referents are cleared. + * @bug 8373203 + * @library /test/lib / + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. + * -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:ShenandoahGenerationalMinTenuringAge=1 -XX:ShenandoahGenerationalMaxTenuringAge=1 + * -Xmx128M -Xms128M -ea + * gc.shenandoah.generational.TestGenerationalReferenceProcessing young + */ + +/* + * @test id=old + * @requires vm.gc.Shenandoah + * @summary Confirm that old referents are cleared. + * @bug 8373203 + * @library /test/lib / + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. + * -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:ShenandoahGenerationalMinTenuringAge=1 -XX:ShenandoahGenerationalMaxTenuringAge=1 + * -Xmx128M -Xms128M -ea + * gc.shenandoah.generational.TestGenerationalReferenceProcessing old + */ + +/* + * @test id=young-no-compressed-oops + * @requires vm.gc.Shenandoah + * @summary Confirm that young referents are cleared (when compressed oops are disabled). + * @bug 8373203 + * @library /test/lib / + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. + * -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+UnlockExperimentalVMOptions -XX:-UseCompressedOops + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:ShenandoahGenerationalMinTenuringAge=1 -XX:ShenandoahGenerationalMaxTenuringAge=1 + * -Xmx128M -Xms128M -ea + * gc.shenandoah.generational.TestGenerationalReferenceProcessing young + */ + +/* + * @test id=old-no-compressed-oops + * @requires vm.gc.Shenandoah + * @summary Confirm that old referents are cleared (when compressed oops are disabled). + * @bug 8373203 + * @library /test/lib / + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. + * -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+UnlockExperimentalVMOptions -XX:-UseCompressedOops + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:ShenandoahGenerationalMinTenuringAge=1 -XX:ShenandoahGenerationalMaxTenuringAge=1 + * -Xmx128M -Xms128M -ea + * gc.shenandoah.generational.TestGenerationalReferenceProcessing old + */ +package gc.shenandoah.generational; + +import java.lang.ref.WeakReference; +import java.lang.ref.ReferenceQueue; +import java.util.Collection; +import java.util.Set; +import java.util.ArrayList; +import java.util.List; +import java.util.HashSet; +import java.util.function.Supplier; + +import jdk.test.whitebox.WhiteBox; + +public class TestGenerationalReferenceProcessing { + static final int OLD = 0; + static final int YOUNG = 1; + + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + private static class LeakedObject {} + + private static final int REGION_SIZE = WB.shenandoahRegionSize(); + private static final int REGION_COUNT = WB.shenandoahRegionCount(); + private static final int OBJECT_SIZE = (int)WB.getObjectSize(new LeakedObject()); + + // We don't want to fill too much of the heap, or the heuristics will trigger GCs instead of our test + private static final int REGIONS_TO_FILL = REGION_COUNT / 12; + private static final int OBJECTS_PER_REGION = REGION_SIZE / OBJECT_SIZE / 2; + private static final int OBJECT_COUNT = OBJECTS_PER_REGION * REGIONS_TO_FILL; + + private static final Collection> WEAK_REFS = new HashSet<>(OBJECT_COUNT); + private static final ArrayList REFERENTS = new ArrayList<>(OBJECT_COUNT); + private static final ReferenceQueue REF_QUEUE = new ReferenceQueue<>(); + + private static final int MINIMUM_CROSS_GENERATIONAL_REFERENCE_COUNT = 50; + + static class ReferenceClassifier { + private final Object[][] references; + + ReferenceClassifier() { + references = new Object[][]{ + {new HashSet>(), new HashSet>()}, + {new HashSet>(), new HashSet>()} + }; + } + + void classify() { + clear(); + + for (var weakRef : TestGenerationalReferenceProcessing.WEAK_REFS) { + var referent = weakRef.get(); + if (referent != null) { + int row = WB.isObjectInOldGen(weakRef) ? OLD : YOUNG; + int column = WB.isObjectInOldGen(referent) ? OLD : YOUNG; + getReferences(row, column).add(weakRef); + } + } + } + + private void clear() { + getReferences(OLD, OLD).clear(); + getReferences(OLD, YOUNG).clear(); + getReferences(YOUNG, OLD).clear(); + getReferences(YOUNG, YOUNG).clear(); + } + + Set> getReferences(int reference, int referent) { + assert reference == OLD || reference == YOUNG; + assert referent == OLD || referent == YOUNG; + return (Set>)references[reference][referent]; + } + + @Override + public String toString() { + return String.format("OO: %d, OY: %d, YO: %d, YY: %d", + getReferences(OLD, OLD).size(), getReferences(OLD, YOUNG).size(), + getReferences(YOUNG, OLD).size(), getReferences(YOUNG, YOUNG).size()); + } + } + + public static void usage() { + System.out.println("Call with generation to test: young|old"); + throw new IllegalArgumentException("Missing required argument: young|old"); + } + + public static void main(String[] args) throws Exception { + if (args.length != 1) { + usage(); + } + + if ("young".equals(args[0])) { + testCollectCrossGenerationalReferents(OLD, YOUNG); + } else if ("old".equals(args[0])) { + testCollectCrossGenerationalReferents(OLD, OLD); + } else { + usage(); + } + } + + private static String name(int generation) { + return generation == OLD ? "old" : "young"; + } + + private static void testCollectCrossGenerationalReferents(int referenceGen, int referentGen) { + ReferenceClassifier classifier = new ReferenceClassifier(); + + useMemoryUntil(() -> { + classifier.classify(); + return classifier.getReferences(referenceGen, referentGen).size() > MINIMUM_CROSS_GENERATIONAL_REFERENCE_COUNT; + }); + + assert !classifier.getReferences(referenceGen, referentGen).isEmpty() : "Conditions for test not met: " + classifier; + + System.out.println("Before clearing all referents: " + classifier); + drainReferenceQueueAndClearReferents(); + + if (referentGen == YOUNG) { + Set> youngToYoung = classifier.getReferences(YOUNG, YOUNG); + Set> oldToYoung = classifier.getReferences(OLD, YOUNG); + + WB.youngGC(); + + assertReferencesCleared(YOUNG, YOUNG, youngToYoung); + assertReferencesCleared(OLD, YOUNG, oldToYoung); + } else { + Set> oldToOld = classifier.getReferences(OLD, OLD); + + WB.shenandoahOldMarkGC(); + + assertReferencesCleared(OLD, OLD, oldToOld); + } + } + + private static void assertReferencesCleared(int referenceGen, int referentGen, Set> references) { + final int MaxReferences = 10; + int referencesShown = 0; + boolean allClear = true; + for (var reference : references) { + if (reference.get() != null) { + if (referencesShown >= MaxReferences) { + break; + } + + printReference(referenceGen, referentGen, reference); + ++referencesShown; + allClear = false; + } + } + + if (!allClear) { + throw new AssertionError(name(referenceGen) + " to " + name(referentGen) + " referents should have been cleared"); + } + } + + private static void printReference(int referenceGen, int referentGen, WeakReference reference) { + // Addresses here could be relocated and may not match logs from old gen collection + System.out.printf("reference: 0x%x (%d) in %s refers to 0x%x (%d) in %s\n", + WB.getObjectAddress(reference), reference.hashCode(), name(referenceGen), + WB.getObjectAddress(reference.get()), reference.get().hashCode(), name(referentGen)); + } + + private static void drainReferenceQueueAndClearReferents() { + // Drain the reference queue of any incidental weak references from outside the test + while (REF_QUEUE.poll() != null); + + // Make all our referents unreachable now + REFERENTS.clear(); + } + + private static void useMemoryUntil(Supplier exitCondition) { + // This is not an exact science here. We want to create weak references + // with referents in a different region. We also don't want to allocate + // everything up front, or else they will all end up in old together, and + // we won't get a good mix of cross generational pointers. + // + // We reuse the same arraylist to avoid having it be resized in the old + // generation. In this case, the replaced backing array is unreachable, + // but it may point to referents in the young generation. Without running + // an old collection, the remembered set scan must assume the old objects + // on these dirty cards are still alive and so it will strongly mark them. + // This form of nepotism would defeat the test scenario. + var referents = new ArrayList(OBJECTS_PER_REGION); + for (int i = 0; i < REGIONS_TO_FILL; i += 4) { + allocateReferents(OBJECTS_PER_REGION, referents); + allocateReferences(referents); + REFERENTS.addAll(referents); + referents.clear(); + + // Some objects will be promoted, some will not. + WB.youngGC(); + if (exitCondition.get()) { + break; + } + } + } + + private static void allocateReferents(int objects, ArrayList referents) { + for (int i = 0; i < objects; ++i) { + var leakedObject = new LeakedObject(); + referents.add(leakedObject); + byte[] garbage = new byte[OBJECT_SIZE]; + garbage[0] = (byte) 1; + } + } + + private static void allocateReferences(List referents) { + // Fill up regions that are equal parts garbage and references + // We want to create cross region references to increase the chances + // of cross generational references. + for (var referent : referents) { + var ref = new WeakReference<>(referent, REF_QUEUE); + WEAK_REFS.add(ref); + byte[] garbage = new byte[OBJECT_SIZE]; + garbage[0] = (byte) 1; + } + } +} diff --git a/test/hotspot/jtreg/gc/shenandoah/generational/TestOldRefsOnDirtyCardAreCleared.java b/test/hotspot/jtreg/gc/shenandoah/generational/TestOldRefsOnDirtyCardAreCleared.java new file mode 100644 index 000000000000..34fe97d28300 --- /dev/null +++ b/test/hotspot/jtreg/gc/shenandoah/generational/TestOldRefsOnDirtyCardAreCleared.java @@ -0,0 +1,135 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +/* + * @test id=generational + * @bug 8393426 + * @requires vm.gc.Shenandoah + * @summary The remembered set scan must not inadvertently keep old referents on dirty cards alive. + * @library /test/lib / + * @build jdk.test.lib.helpers.ClassFileInstaller 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:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:+UnlockExperimentalVMOptions + * -XX:-DisableExplicitGC -XX:+ExplicitGCInvokesConcurrent -XX:ShenandoahGenerationalMinPIPUsage=0 + * gc.shenandoah.generational.TestOldRefsOnDirtyCardAreCleared + */ + +package gc.shenandoah.generational; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; + +import jdk.test.whitebox.WhiteBox; + +public final class TestOldRefsOnDirtyCardAreCleared { + // Use a subclass so that we can reliably dirty the card for this weak reference + static final class WeakReferenceWithYoungPointer extends WeakReference { + public Object youngPointer; + public WeakReferenceWithYoungPointer(Object object) { + super(object); + } + } + + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + private static final List WEAK_REFS = new ArrayList<>(); + private static final List STRONG = new ArrayList<>(); + + // These will be intentionally strongly reachable to be sure the reference is NOT cleared + private static final Object RETAIN_REFERENT = new Object(); + private static final WeakReferenceWithYoungPointer RETAIN_REF = new WeakReferenceWithYoungPointer(RETAIN_REFERENT); + + // Put a safety net on the number of System.gcs that will be used for tenuring. + private static final int MAX_FULL_GCS = 5; + + // Multiple references helps the test avoid the false positive scenario where + // the `youngPointer` ends up on a different card. + private static final int REF_COUNT = 8; + + private static boolean allAreInOld(List objects) { + for (Object obj : objects) { + if (!WB.isObjectInOldGen(obj)) { + return false; + } + } + return true; + } + + private static boolean testConditionsMet() { + return allAreInOld(WEAK_REFS) && allAreInOld(STRONG) + && WB.isObjectInOldGen(RETAIN_REFERENT) + && WB.isObjectInOldGen(RETAIN_REF); + } + + public static void main(String[] args) throws Exception { + // Step 1. Make references with strongly reachable referents. + for (int i = 0; i < REF_COUNT; ++i) { + Object obj = new Object(); + WeakReferenceWithYoungPointer wr = new WeakReferenceWithYoungPointer(obj); + WEAK_REFS.add(wr); + STRONG.add(obj); + } + + // Step 2. Run global GCs until all the references and their referents are promoted + int tries = 0; + while (!testConditionsMet()) { + if (tries >= MAX_FULL_GCS) { + throw new RuntimeException("Test condition unmet: weak refs and referents not promoted"); + } + WB.fullGC(); + ++tries; + } + + // Step 3. Drop the strong references to the referents + STRONG.clear(); + + // Step 4. Dirty the cards for all the weak references + for (WeakReferenceWithYoungPointer ref : WEAK_REFS) { + ref.youngPointer = new Object(); + } + RETAIN_REF.youngPointer = new Object(); + + // Step 5. Run an old GC cycle. This should clear out the weak referents + WB.shenandoahOldMarkGC(); + + // Step 6. Fail if any references still have referents (should be zero) + for (int i = 0, n = WEAK_REFS.size(); i < n; ++i) { + WeakReferenceWithYoungPointer wr = WEAK_REFS.get(i); + Object referent = wr.get(); + if (referent != null) { + throw new RuntimeException("Uncleared weak ref = " + wr + + ", referent = " + referent + ", at index = " + i); + } + } + + // Step 7. Verify that our strongly reachable referent was not cleared + if (RETAIN_REF.get() == null) { + throw new RuntimeException("Strongly reachable referent was cleared: " + RETAIN_REF); + } + } +} diff --git a/test/hotspot/jtreg/runtime/Metaspace/DefineClass.java b/test/hotspot/jtreg/runtime/Metaspace/DefineClass.java index 9c7d1b5532a4..6e90c86801b8 100644 --- a/test/hotspot/jtreg/runtime/Metaspace/DefineClass.java +++ b/test/hotspot/jtreg/runtime/Metaspace/DefineClass.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -41,9 +41,9 @@ * -XX:-AllowParallelDefineClass * test.DefineClass defineClassParallel * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * -Djdk.attach.allowAttachSelf test.DefineClass redefineClass + * -XX:+EnableDynamicAgentLoading -Djdk.attach.allowAttachSelf test.DefineClass redefineClass * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * -Djdk.attach.allowAttachSelf test.DefineClass redefineClassWithError + * -XX:+EnableDynamicAgentLoading -Djdk.attach.allowAttachSelf test.DefineClass redefineClassWithError * @author volker.simonis@gmail.com */ diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AutomaticModuleTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AutomaticModuleTest.java new file mode 100644 index 000000000000..d763ff4c9c3b --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AutomaticModuleTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +/* + * @test + * @summary Automatic modules can read all unnamed modules, and vice versa, even if AOT cache is used. + * @bug 8393235 + * @modules java.logging + * @requires vm.cds.supports.aot.class.linking + * @library /test/lib + * @run driver AutomaticModuleTest AOT + */ + +import java.nio.file.Path; +import java.nio.file.Paths; +import jdk.test.lib.cds.CDSModulePackager; +import jdk.test.lib.cds.SimpleCDSAppTester; +import jdk.test.lib.process.OutputAnalyzer; + +public class AutomaticModuleTest { + static final Path SRC = Paths.get(System.getProperty("test.src")).resolve("modules"); + + public static void main(String... args) throws Exception { + CDSModulePackager modulePackager = new CDSModulePackager(SRC); + String modulePath = modulePackager.getOutputDir().toString(); + modulePackager.createAutomaticModularJar("com.auto"); + modulePackager.createModularJar("com.test"); + + test(modulePath); + } + + static void test(String modulePath) throws Exception { + SimpleCDSAppTester tester = SimpleCDSAppTester.of("AutomaticModuleTest") + .modulepath(modulePath) + .addVmArgs("--add-modules", "java.logging,com.test") + .addVmArgs("--add-reads", "com.test=java.logging") + .addVmArgs("--add-reads", "com.test=com.auto") + .appCommandLine("-m", "com.auto/com.auto.ComAuto") + .setTrainingChecker(out -> check(out)) + .setProductionChecker(out -> check(out)) + .runAOTWorkflow(); + } + + static void check(OutputAnalyzer out) { + out.shouldContain("OUT: com.auto.isNamed() = true"); + out.shouldContain("OUT: com.auto.getName() = com.auto"); + out.shouldContain("OUT: com.auto.canRead(java.logging) = true"); + out.shouldContain("OUT: com.test.canRead(java.logging) = true"); + out.shouldContain("OUT: unnamed.canRead(java.logging) = true"); + out.shouldContain("OUT: com.auto.canRead(unnamed) = true"); + out.shouldContain("OUT: unnamed.canRead(com.auto) = true"); + out.shouldContain("OUT: com.auto.canRead(com.test) = true"); + out.shouldContain("OUT: com.test.canRead(com.auto) = true"); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules/com.auto/com/auto/ComAuto.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules/com.auto/com/auto/ComAuto.java new file mode 100644 index 000000000000..7f2c7df63c0a --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules/com.auto/com/auto/ComAuto.java @@ -0,0 +1,48 @@ +/* + * 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 com.auto; + +public class ComAuto { + public static void main(String args[]) { + Module unnamed = ComAuto.class.getClassLoader().getUnnamedModule(); + Module com_auto = ComAuto.class.getModule(); + Module com_test = ModuleLayer.boot() + .findModule("com.test") + .orElseThrow(() -> new IllegalStateException("Run with --add-modules com.test")); + Module java_logging = ModuleLayer.boot() + .findModule("java.logging") + .orElseThrow(() -> new IllegalStateException("Run with --add-modules java.logging")); + + System.out.println("OUT: com.auto.isNamed() = " + com_auto.isNamed()); + System.out.println("OUT: com.auto.getName() = " + com_auto.getName()); + System.out.println("OUT: com.test.canRead(java.logging) = " + com_test.canRead(java_logging)); // must be true + System.out.println("OUT: com.auto.canRead(java.logging) = " + com_auto.canRead(java_logging)); // must be true + System.out.println("OUT: unnamed.canRead(java.logging) = " + unnamed.canRead(java_logging)); // must be true + System.out.println("OUT: unnamed.canRead(com.auto) = " + unnamed.canRead(com_auto)); // must be true + System.out.println("OUT: com.auto.canRead(com.test) = " + com_auto.canRead(com_test)); // must be true + System.out.println("OUT: com.auto.canRead(unnamed) = " + com_auto.canRead(unnamed)); // must be true + System.out.println("OUT: com.test.canRead(com.auto) = " + com_test.canRead(com_auto)); // must be true + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jvmti/InstrumentationTest.java b/test/hotspot/jtreg/runtime/cds/appcds/jvmti/InstrumentationTest.java index b57ba2633066..52c1b2c18ec2 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/jvmti/InstrumentationTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/jvmti/InstrumentationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -115,6 +115,7 @@ public static void runTest(boolean attachAgent) throws Throwable { .setXShareMode("off") .addSuffix(bootCP, "-cp", appJar, + "-XX:+EnableDynamicAgentLoading", "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", "-Xshare:off", @@ -152,6 +153,7 @@ public static void runTest(boolean attachAgent) throws Throwable { t = doAttach(attachAgent, flagFile, agentJar); out = TestCommon.execAuto("-cp", appJar, bootCP, + "-XX:+EnableDynamicAgentLoading", "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", agentCmdArg, diff --git a/test/hotspot/jtreg/runtime/jni/critical/SuspendInCritical.java b/test/hotspot/jtreg/runtime/jni/critical/SuspendInCritical.java index 47ece0f1857e..71f24a067d3a 100644 --- a/test/hotspot/jtreg/runtime/jni/critical/SuspendInCritical.java +++ b/test/hotspot/jtreg/runtime/jni/critical/SuspendInCritical.java @@ -60,6 +60,11 @@ public class SuspendInCritical { */ public static void main(String[] args) throws Throwable { int mode = Integer.parseInt(args[0]); + // This assert is just to load the Asserts class before the + // main thread starts anything interesting. This avoids classloading + // during a critical part of the test where the main thread must + // not itself try to use a JNI critical region. + Asserts.assertTrue(mode >= 1 && mode <= 3); final byte[] bytes = (mode == 1 || mode == 3) ? new byte[16] : null; final String str = (mode == 2 || mode == 3) ? "A String" : null; diff --git a/test/hotspot/jtreg/runtime/vthread/RedefineClass.java b/test/hotspot/jtreg/runtime/vthread/RedefineClass.java index b17bb55701bc..e4a0c64b3aea 100644 --- a/test/hotspot/jtreg/runtime/vthread/RedefineClass.java +++ b/test/hotspot/jtreg/runtime/vthread/RedefineClass.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,7 +26,7 @@ * @test * @summary Regression test based on runtime/Metaspace/DefineClass.java * @compile RedefineClass.java - * @run main/othervm -Djdk.attach.allowAttachSelf RedefineClass + * @run main/othervm -XX:+EnableDynamicAgentLoading -Djdk.attach.allowAttachSelf RedefineClass */ import java.io.ByteArrayOutputStream; diff --git a/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachIncorrectLibrary.java b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachIncorrectLibrary.java index bc83dece0f24..a8a23714349f 100644 --- a/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachIncorrectLibrary.java +++ b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachIncorrectLibrary.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 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 @@ -27,7 +27,7 @@ * @test * @bug 8165736 * @library /test/lib - * @run testng AttachIncorrectLibrary + * @run testng/othervm -XX:+EnableDynamicAgentLoading AttachIncorrectLibrary */ public class AttachIncorrectLibrary extends AttachFailedTestBase { @Override diff --git a/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachNoEntry.java b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachNoEntry.java index 3920bb3691ec..ed3f0ee5e004 100644 --- a/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachNoEntry.java +++ b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachNoEntry.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 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 @@ -27,7 +27,7 @@ * @test * @bug 8165736 * @library /test/lib - * @run testng AttachNoEntry + * @run testng/othervm -XX:+EnableDynamicAgentLoading AttachNoEntry */ public class AttachNoEntry extends AttachFailedTestBase { @Override diff --git a/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachReturnError.java b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachReturnError.java index 42f3a45086d8..b33dc8a943a2 100644 --- a/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachReturnError.java +++ b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachReturnError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,7 @@ * @comment muslc dlclose is a no-op, see 8285921 * @requires !vm.musl * @library /test/lib - * @run testng AttachReturnError + * @run testng/othervm -XX:+EnableDynamicAgentLoading AttachReturnError */ public class AttachReturnError extends AttachFailedTestBase { @Override diff --git a/test/hotspot/jtreg/serviceability/dcmd/jvmti/LoadAgentDcmdTest.java b/test/hotspot/jtreg/serviceability/dcmd/jvmti/LoadAgentDcmdTest.java index 39068a47e95c..549d7567f52b 100644 --- a/test/hotspot/jtreg/serviceability/dcmd/jvmti/LoadAgentDcmdTest.java +++ b/test/hotspot/jtreg/serviceability/dcmd/jvmti/LoadAgentDcmdTest.java @@ -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 @@ -49,7 +49,7 @@ * jdk.internal.jvmstat/sun.jvmstat.monitor * @build SimpleJvmtiAgent * @run driver jdk.test.lib.helpers.ClassFileInstaller SimpleJvmtiAgent - * @run testng/othervm LoadAgentDcmdTest + * @run testng/othervm -XX:+EnableDynamicAgentLoading LoadAgentDcmdTest */ public class LoadAgentDcmdTest { diff --git a/test/hotspot/jtreg/serviceability/jvmti/ForceEarlyReturn/ForceEarlyReturnStrictInitFields.java b/test/hotspot/jtreg/serviceability/jvmti/ForceEarlyReturn/ForceEarlyReturnStrictInitFields.java index de0a1d9d28fe..843f66d0d60b 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/ForceEarlyReturn/ForceEarlyReturnStrictInitFields.java +++ b/test/hotspot/jtreg/serviceability/jvmti/ForceEarlyReturn/ForceEarlyReturnStrictInitFields.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8387648 + * @bug 8387648 8394667 * @summary Test ForceEarlyReturnVoid when the target thread's top frame is the class * initializer, constructor or method of a class with strictly-initialized fields. * @enablePreview @@ -388,7 +388,7 @@ void testAfterSuper() throws Exception { */ @Nested class ValueClassConstructor2 extends ForceEarlyReturnTest { - value class TestClass { + static value class TestClass { TestClass(ForceEarlyReturnTest test, int where) { // before super if (where == 1) { diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/ToggleNotifyJvmtiTest/ToggleNotifyJvmtiTest.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/ToggleNotifyJvmtiTest/ToggleNotifyJvmtiTest.java index 8332b06cadbc..43bba88151ea 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/ToggleNotifyJvmtiTest/ToggleNotifyJvmtiTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/ToggleNotifyJvmtiTest/ToggleNotifyJvmtiTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,7 +31,7 @@ * * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/native -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. -agentlib:ToggleNotifyJvmtiTest ToggleNotifyJvmtiTest - * @run main/othervm/native -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. -Djdk.attach.allowAttachSelf=true ToggleNotifyJvmtiTest attach + * @run main/othervm/native -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. -XX:+EnableDynamicAgentLoading -Djdk.attach.allowAttachSelf=true ToggleNotifyJvmtiTest attach */ import com.sun.tools.attach.VirtualMachine; diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/VirtualStackTraceTest/VirtualStackTraceTest.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/VirtualStackTraceTest/VirtualStackTraceTest.java index a7aeed106e55..b3a813ec68e9 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/VirtualStackTraceTest/VirtualStackTraceTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/VirtualStackTraceTest/VirtualStackTraceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, 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 @@ -25,14 +25,14 @@ * @test id=default * @summary Verifies JVMTI GetStackTrace does not truncate virtual thread stack trace with agent attach * @requires vm.jvmti - * @run main/othervm/native -Djdk.attach.allowAttachSelf=true VirtualStackTraceTest + * @run main/othervm/native -XX:+EnableDynamicAgentLoading -Djdk.attach.allowAttachSelf=true VirtualStackTraceTest */ /* * @test id=no-vmcontinuations * @requires vm.continuations * @requires vm.jvmti - * @run main/othervm/native -Djdk.attach.allowAttachSelf=true -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations VirtualStackTraceTest + * @run main/othervm/native -XX:+EnableDynamicAgentLoading -Djdk.attach.allowAttachSelf=true -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations VirtualStackTraceTest */ import com.sun.tools.attach.VirtualMachine; diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/VirtualThreadStartTest/VirtualThreadStartTest.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/VirtualThreadStartTest/VirtualThreadStartTest.java index e571501eea6e..1aa5b9fa2c3f 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/VirtualThreadStartTest/VirtualThreadStartTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/VirtualThreadStartTest/VirtualThreadStartTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,8 +27,8 @@ * @requires vm.jvmti * @run main/othervm/native -agentlib:VirtualThreadStartTest VirtualThreadStartTest * @run main/othervm/native -agentlib:VirtualThreadStartTest=can_support_virtual_threads VirtualThreadStartTest - * @run main/othervm/native -Djdk.attach.allowAttachSelf=true VirtualThreadStartTest attach - * @run main/othervm/native -Djdk.attach.allowAttachSelf=true VirtualThreadStartTest attach can_support_virtual_threads + * @run main/othervm/native -XX:+EnableDynamicAgentLoading -Djdk.attach.allowAttachSelf=true VirtualThreadStartTest attach + * @run main/othervm/native -XX:+EnableDynamicAgentLoading -Djdk.attach.allowAttachSelf=true VirtualThreadStartTest attach can_support_virtual_threads /* * @test id=no-vmcontinuations @@ -36,8 +36,8 @@ * @requires vm.jvmti * @run main/othervm/native -agentlib:VirtualThreadStartTest -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations VirtualThreadStartTest * @run main/othervm/native -agentlib:VirtualThreadStartTest=can_support_virtual_threads -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations VirtualThreadStartTest - * @run main/othervm/native -Djdk.attach.allowAttachSelf=true -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations VirtualThreadStartTest attach - * @run main/othervm/native -Djdk.attach.allowAttachSelf=true -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations VirtualThreadStartTest attach can_support_virtual_threads + * @run main/othervm/native -XX:+EnableDynamicAgentLoading -Djdk.attach.allowAttachSelf=true -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations VirtualThreadStartTest attach + * @run main/othervm/native -XX:+EnableDynamicAgentLoading -Djdk.attach.allowAttachSelf=true -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations VirtualThreadStartTest attach can_support_virtual_threads */ import com.sun.tools.attach.VirtualMachine; diff --git a/test/hotspot/jtreg/serviceability/sa/ClhsdbPstack.java b/test/hotspot/jtreg/serviceability/sa/ClhsdbPstack.java index 1d64f9687b4b..798372a8fecf 100644 --- a/test/hotspot/jtreg/serviceability/sa/ClhsdbPstack.java +++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbPstack.java @@ -28,11 +28,12 @@ import jdk.test.lib.apps.LingeredApp; import jdk.test.lib.util.CoreUtils; import jdk.test.lib.Platform; +import jdk.test.lib.SA.SATestUtils; import jtreg.SkippedException; /** * @test id=process - * @bug 8190198 + * @bug 8190198 8390621 * @summary Test clhsdb pstack command on a live process * @requires vm.hasSA * @requires vm.gc != "Z" @@ -43,7 +44,7 @@ /** * @test id=core - * @bug 8190198 + * @bug 8190198 8390621 * @summary Test clhsdb pstack command on a core file * @requires vm.hasSA * @requires !vm.ubsan @@ -57,7 +58,14 @@ public class ClhsdbPstack { public static void main(String[] args) throws Exception { if (Platform.isMusl()) { - throw new SkippedException("This test does not work on musl libc."); + if (!Platform.isX64()) { + throw new SkippedException("This platform is not supported."); + } + + var libc = SATestUtils.getLibCPath(); + if (SATestUtils.getDebugInfo(libc) == null) { + throw new SkippedException("libc debug info is not available."); + } } boolean withCore = Boolean.parseBoolean(args[0]); diff --git a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixed.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixed.java index a5750edf360f..a09e80e0ec5b 100644 --- a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixed.java +++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixed.java @@ -38,7 +38,7 @@ /** * @test * @key randomness - * @bug 8208091 8374469 8377710 + * @bug 8208091 8374469 8377710 8390621 * @requires vm.hasSA * @requires vm.gc != "Z" * @requires (os.family == "linux" | os.family == "windows") @@ -177,7 +177,14 @@ private static void runJstackMixedInLoop(LingeredApp app) throws Exception { public static void main(String... args) throws Exception { if (Platform.isMusl()) { - throw new SkippedException("This test does not work on musl libc."); + if (!Platform.isX64()) { + throw new SkippedException("This platform is not supported."); + } + + var libc = SATestUtils.getLibCPath(); + if (SATestUtils.getDebugInfo(libc) == null) { + throw new SkippedException("libc debug info is not available."); + } } SATestUtils.skipIfCannotAttach(); // throws SkippedException if attach not expected to work. diff --git a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedCore.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedCore.java index 99232ed98131..88d7a8686eec 100644 --- a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedCore.java +++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedCore.java @@ -37,7 +37,7 @@ /** * @test - * @bug 8374482 8376264 8376284 8377395 + * @bug 8374482 8376264 8376284 8377395 8390621 * @requires vm.hasSA * @requires vm.gc != "Z" * @requires os.family == "linux" @@ -72,13 +72,17 @@ private static void runJstackMixed(String coreFileName) throws Exception { } public static void main(String... args) throws Throwable { - if (Platform.isMusl()) { - throw new SkippedException("This test does not work on musl libc."); - } - // Check whether the symbol of signal trampoline is available. var libc = SATestUtils.getLibCPath(); + if (Platform.isMusl()) { + if (!Platform.isX64()) { + throw new SkippedException("This platform is not supported."); + } else if (SATestUtils.getDebugInfo(libc) == null) { + throw new SkippedException("libc debug info is not available."); + } + } + // SA distinguishes the frame is signal trampoline if the function // is named "__restore_rt". // SA cannot unwind problematic frame from it if the symbol not found. diff --git a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java index 70a9e67e4060..28fb46e97b86 100644 --- a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java +++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java @@ -37,7 +37,7 @@ /** * @test id=xcomp - * @bug 8370176 + * @bug 8370176 8390621 * @requires vm.hasSA * @requires vm.gc != "Z" * @requires os.family == "linux" @@ -48,7 +48,7 @@ /** * @test id=xcomp-preserve-frame-pointer - * @bug 8370176 + * @bug 8370176 8390621 * @requires vm.hasSA * @requires vm.gc != "Z" * @requires os.family == "linux" @@ -59,7 +59,7 @@ /** * @test id=xcomp-disable-tiered-compilation - * @bug 8370176 + * @bug 8370176 8390621 * @requires vm.hasSA * @requires vm.gc != "Z" * @requires os.family == "linux" @@ -115,7 +115,14 @@ private static void runJstack(LingeredApp app) throws Exception { public static void main(String... args) throws Exception { if (Platform.isMusl()) { - throw new SkippedException("This test does not work on musl libc."); + if (!Platform.isX64()) { + throw new SkippedException("This platform is not supported."); + } + + var libc = SATestUtils.getLibCPath(); + if (SATestUtils.getDebugInfo(libc) == null) { + throw new SkippedException("libc debug info is not available."); + } } SATestUtils.skipIfCannotAttach(); // throws SkippedException if attach not expected to work. diff --git a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackPrintVMLocks.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackPrintVMLocks.java index e0774917f807..0d43a4db1966 100644 --- a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackPrintVMLocks.java +++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackPrintVMLocks.java @@ -45,7 +45,14 @@ public class TestJhsdbJstackPrintVMLocks { final static int MAX_ATTEMPTS = 5; public static void main(String[] args) throws Exception { if (Platform.isMusl()) { - throw new SkippedException("This test does not work on musl libc."); + if (!Platform.isX64()) { + throw new SkippedException("This platform is not supported."); + } + + var libc = SATestUtils.getLibCPath(); + if (SATestUtils.getDebugInfo(libc) == null) { + throw new SkippedException("libc debug info is not available."); + } } SATestUtils.skipIfCannotAttach(); // throws SkippedException if attach not expected to work. diff --git a/test/hotspot/jtreg/vmTestbase/vm/mlvm/meth/stress/jdi/breakpointInCompiledCode/Test.java b/test/hotspot/jtreg/vmTestbase/vm/mlvm/meth/stress/jdi/breakpointInCompiledCode/Test.java index 78cdb534c11b..3ee73e9a1d33 100644 --- a/test/hotspot/jtreg/vmTestbase/vm/mlvm/meth/stress/jdi/breakpointInCompiledCode/Test.java +++ b/test/hotspot/jtreg/vmTestbase/vm/mlvm/meth/stress/jdi/breakpointInCompiledCode/Test.java @@ -43,7 +43,7 @@ * @build vm.mlvm.meth.stress.jdi.breakpointInCompiledCode.Test * @run driver vm.mlvm.share.IndifiedClassesBuilder * - * @run main/othervm + * @run main/othervm/timeout=240 * vm.mlvm.meth.stress.jdi.breakpointInCompiledCode.Test * -verbose * -arch=${os.family}-${os.simpleArch} diff --git a/test/jdk/ProblemList-Virtual.txt b/test/jdk/ProblemList-Virtual.txt index 573407a6858f..2e13b15c6ac5 100644 --- a/test/jdk/ProblemList-Virtual.txt +++ b/test/jdk/ProblemList-Virtual.txt @@ -34,6 +34,9 @@ java/lang/ScopedValue/StressStackOverflow.java#default 8309646 generic-all java/lang/ScopedValue/StressStackOverflow.java#no-TieredCompilation 8309646 generic-all java/lang/ScopedValue/StressStackOverflow.java#TieredStopAtLevel1 8309646 generic-all +java/net/URLPermission/OpenURL.java 8387805 windows-x64 +tools/launcher/MiscTests.java 8394341 generic-all + ############################################################################# # Preview project specific failures go here at the end of the file. diff --git a/test/jdk/ProblemList-enable-preview.txt b/test/jdk/ProblemList-enable-preview.txt index 84aa30f843d8..0b7da07baed9 100644 --- a/test/jdk/ProblemList-enable-preview.txt +++ b/test/jdk/ProblemList-enable-preview.txt @@ -35,4 +35,5 @@ # Value Objects failures start here: -sun/tools/jhsdb/HeapDumpTestWithActiveProcess.java 8394812 generic-all \ No newline at end of file +jdk/jfr/event/oldobject/TestEmergencyDumpAtOOM.java 8385942 generic-all +sun/tools/jhsdb/HeapDumpTestWithActiveProcess.java 8394812 generic-all diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 30509fa70330..4358bd94b3c7 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -463,9 +463,6 @@ sun/security/smartcardio/TestTransmit.java 8039280 generic- sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java 8316183 linux-ppc64le -sun/security/pkcs11/Provider/CheckRegistration.java 8394846 generic-all -sun/security/pkcs11/tls/TestExtendedMasterSecretHandshake.java 8394846 generic-all - ############################################################################ # jdk_sound diff --git a/test/jdk/com/sun/jdi/NewInstanceTest.java b/test/jdk/com/sun/jdi/NewInstanceTest.java index 42069f17ef58..d5f9f04d6573 100644 --- a/test/jdk/com/sun/jdi/NewInstanceTest.java +++ b/test/jdk/com/sun/jdi/NewInstanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2015, 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 @@ -23,7 +23,7 @@ /** * @test - * @bug 4419450 + * @bug 4419450 8343851 * @summary Test newInstance() for arrays - currently covers * only reference type arrays (see bug #4450091). * @author Robert Field @@ -36,6 +36,9 @@ import com.sun.jdi.event.*; import com.sun.jdi.request.*; +import java.io.InputStream; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Array; import java.util.*; /********** target program **********/ @@ -52,6 +55,17 @@ class NewInstanceTarg { static ClsFoo[] clsArray = new ClsFoo[10]; static IfcFoo[] ifc0Array = new IfcFoo[10]; static IfcFoo[] ifcArray = {aFoo, aFoo}; + static int[][] multiIntArray = new int[0][]; + static Object hiddenArray; + + static { + try (InputStream in = ClsFoo.class.getResourceAsStream("ClsFoo.class")) { + byte[] bytes = in.readAllBytes(); + hiddenArray = Array.newInstance(MethodHandles.lookup().defineHiddenClass(bytes, true).lookupClass(), 0); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } public static void main(String[] args){ System.out.println("Howdy!"); @@ -77,10 +91,15 @@ public static void main(String[] args) throws Exception { void makeArray(String fieldName) throws Exception { println("Making array for field: " + fieldName); Field arrayField = targetClass.fieldByName(fieldName); - ArrayType arrayType = (ArrayType)arrayField.type(); + ArrayReference original = (ArrayReference)targetClass.getValue(arrayField); + ArrayType arrayType = (ArrayType)original.referenceType(); println("Type for " + fieldName + " is " + arrayType); ArrayReference arrayReference = arrayType.newInstance(20); - println("Passed subtest: " + fieldName); + if (!arrayReference.referenceType().equals(arrayType) || arrayReference.length() != 20) { + failure("Unexpected array type or length for " + fieldName); + } else { + println("Passed subtest: " + fieldName); + } } /********** test core **********/ @@ -97,6 +116,8 @@ protected void runTests() throws Exception { makeArray("clsArray"); makeArray("ifc0Array"); makeArray("ifcArray"); + makeArray("hiddenArray"); + makeArray("multiIntArray"); /* * resume the target until end diff --git a/test/jdk/com/sun/tools/attach/modules/Driver.java b/test/jdk/com/sun/tools/attach/modules/Driver.java index 12be9003a9b8..383b1a365979 100644 --- a/test/jdk/com/sun/tools/attach/modules/Driver.java +++ b/test/jdk/com/sun/tools/attach/modules/Driver.java @@ -25,10 +25,10 @@ * @test * @modules jdk.attach * @build m/* Agent - * @run main/othervm -Djdk.attach.allowAttachSelf m/p.Main jmx javaagent - * @run main/othervm -Djdk.attach.allowAttachSelf m/p.Main javaagent jmx - * @run main/othervm --limit-modules=jdk.attach -Djdk.attach.allowAttachSelf m/p.Main jmx javaagent - * @run main/othervm --limit-modules=jdk.attach -Djdk.attach.allowAttachSelf m/p.Main javaagent jmx + * @run main/othervm -XX:+EnableDynamicAgentLoading -Djdk.attach.allowAttachSelf m/p.Main jmx javaagent + * @run main/othervm -XX:+EnableDynamicAgentLoading -Djdk.attach.allowAttachSelf m/p.Main javaagent jmx + * @run main/othervm --limit-modules=jdk.attach -XX:+EnableDynamicAgentLoading -Djdk.attach.allowAttachSelf m/p.Main jmx javaagent + * @run main/othervm --limit-modules=jdk.attach -XX:+EnableDynamicAgentLoading -Djdk.attach.allowAttachSelf m/p.Main javaagent jmx * @summary Basic test to ensure that a JMX agent or a tool agent can be loaded/started in * a modular application. */ diff --git a/test/jdk/java/awt/SplashScreen/SplashGifBackgroundColorTest.java b/test/jdk/java/awt/SplashScreen/SplashGifBackgroundColorTest.java new file mode 100644 index 000000000000..4c5f44bfd407 --- /dev/null +++ b/test/jdk/java/awt/SplashScreen/SplashGifBackgroundColorTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you have any questions. + */ + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.TimeUnit; + +import javax.imageio.ImageIO; + +/* + * @test + * @bug 8393264 + * @key headful + * @summary The splash screen GIF decoder must not read past the color table + * when the background color index is not covered by the global + * color table + * @run main SplashGifBackgroundColorTest + */ + +public class SplashGifBackgroundColorTest { + + private static final byte[] GIF = { + 'G', 'I', 'F', '8', '9', 'a', + 0x01, 0x00, 0x01, 0x00, (byte) 0x80, (byte) 0xFF, 0x00, + 0x00, 0x00, 0x00, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + 0x21, (byte) 0xF9, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x02, 0x02, 0x44, 0x01, + 0x00, 0x3B + }; + + private static final int BACKGROUND_COLOR = 11; + + public static void main(String[] args) throws Exception { + var image = ImageIO.read(new ByteArrayInputStream(GIF)); + if (image == null || image.getWidth() != 1 || image.getHeight() != 1) { + throw new RuntimeException("crafted GIF is not decodable"); + } + + byte[] validGif = GIF.clone(); + validGif[BACKGROUND_COLOR] = 1; + + Path dir = Files.createTempDirectory("splash-gif-background"); + try { + Path java = Path.of(System.getProperty("java.home"), "bin", "java"); + Path badGif = dir.resolve("background-color-oob.gif"); + Files.write(badGif, GIF); + runSplash(java, badGif); + Path goodGif = dir.resolve("background-color-valid.gif"); + Files.write(goodGif, validGif); + runSplash(java, goodGif); + System.out.println("OK: splash screen decoded GIFs with an out-of-range and a valid background color index"); + } finally { + delete(dir); + } + } + + + private static void runSplash(Path java, Path gif) throws Exception { + Path log = gif.resolveSibling(gif.getFileName() + ".log"); + + Process p = new ProcessBuilder(java.toString(), + "-splash:" + gif, "-version") + .redirectErrorStream(true) + .redirectOutput(log.toFile()) + .start(); + + if (!p.waitFor(60, TimeUnit.SECONDS)) { + p.destroyForcibly().waitFor(); + throw new RuntimeException("child VM timed out: " + gif); + } + + if (p.exitValue() != 0) { + throw new RuntimeException("child VM failed, exit " + + p.exitValue() + ":\n" + Files.readString(log)); + } + } + + private static void delete(Path path) throws IOException { + if (!Files.exists(path)) { + return; + } + try (var stream = Files.list(path)) { + for (var entry : stream.toList()) { + Files.deleteIfExists(entry); + } + } + Files.deleteIfExists(path); + } +} diff --git a/test/jdk/java/lang/LazyConstant/ThrowablesClassUnloading.java b/test/jdk/java/lang/LazyConstant/ThrowablesClassUnloading.java index 460f1f3808b3..4b51c2d07481 100644 --- a/test/jdk/java/lang/LazyConstant/ThrowablesClassUnloading.java +++ b/test/jdk/java/lang/LazyConstant/ThrowablesClassUnloading.java @@ -28,6 +28,8 @@ * @library /test/lib * @build jdk.test.whitebox.WhiteBox * @requires vm.opt.final.ClassUnloading + * @requires vm.compMode != "Xcomp" + * @requires vm.opt.CompileThresholdScaling == null & vm.opt.CompileThreshold == null * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm * -Xbootclasspath/a:. diff --git a/test/jdk/java/lang/ModuleTests/TEST.properties b/test/jdk/java/lang/ModuleTests/TEST.properties deleted file mode 100644 index 908b451f8ea6..000000000000 --- a/test/jdk/java/lang/ModuleTests/TEST.properties +++ /dev/null @@ -1,2 +0,0 @@ -# disabled till JDK-8219408 is fixed -allowSmartActionArgs=false diff --git a/test/jdk/java/lang/ModuleTests/reflectionData/TestNotUsedDuringStartup.java b/test/jdk/java/lang/ModuleTests/reflectionData/TestNotUsedDuringStartup.java new file mode 100644 index 000000000000..46bb3655fd48 --- /dev/null +++ b/test/jdk/java/lang/ModuleTests/reflectionData/TestNotUsedDuringStartup.java @@ -0,0 +1,148 @@ +/* + * 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 8393235 + * @summary Tests command line options and deployments that augment the readability and + * export/uses graphs. The test checks that these additions are not recorded as + * reflective updates. + * @modules java.xml + * @library /test/lib + * @build java.base/java.lang.ModuleHelper + * @build p.TestEmpty jdk.test.lib.util.JarUtils + * @run junit ${test.main.class} + */ + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.util.JarUtils; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class TestNotUsedDuringStartup { + private static final String TEST_CLASSES = System.getProperty("test.classes"); + private static final String MAIN_CLASS = "p.TestEmpty"; + + /** + * Test --add-exports to export a package to a module or to all unnamed modules. + */ + @Test + void testAddExports() throws Exception { + run("--add-exports", "java.base/jdk.internal.misc=java.xml", MAIN_CLASS); + run("--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED", MAIN_CLASS); + } + + /** + * Test --add-opens to open a package to a module or all unnamed modules. + */ + @Test + void tesAddOpens() throws Exception { + run("--add-opens", "java.base/jdk.internal.misc=java.xml", MAIN_CLASS); + run("--add-opens", "java.base/jdk.internal.misc=ALL-UNNAMED", MAIN_CLASS); + } + + /** + * Test executable JAR with Add-Exports attribute to export a package to all unnamed modules. + */ + @Test + void testExecutableJarWithAddExports() throws Exception { + String jarFile = createJar( + "Main-Class", MAIN_CLASS, + "Add-Exports", "java.base/jdk.internal.misc" + ); + run("-jar", jarFile); + } + + /** + * Test executable JAR with Add-Opens attribute to open a package to all unnamed modules. + */ + @Test + void testExecutableJarWithAddOpens() throws Exception { + String jarFile = createJar( + "Main-Class", MAIN_CLASS, + "Add-Opens", "java.base/jdk.internal.misc" + ); + run("-jar", jarFile); + } + + /** + * Test automatic module. + */ + @Test + void testAutomaticModule() throws Exception { + String jarFile = createJar("Automatic-Module-Name", "app"); + run("--module-path", jarFile, "--module", "app/" + MAIN_CLASS); + } + + /** + * Test --add-reads to have java.base read a module or all unnamed modules. + */ + @Test + void testAddReads() throws Exception { + run("--add-reads", "java.base=java.xml", MAIN_CLASS); + run("--add-reads", "java.base=ALL-UNNAMED", MAIN_CLASS); + } + + /** + * Creates executable JAR named helper.jar with ??* classes. + */ + private String createJar(String... args) throws Exception { + Path jarFile = Path.of("app.jar"); + var man = new Manifest(); + Attributes attrs = man.getMainAttributes(); + attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0"); + int i = 0; + while (i < args.length) { + var name = new Attributes.Name(args[i++]); + String value = args[i++]; + attrs.put(name, value); + } + Path dir = Path.of(TEST_CLASSES); + try (Stream stream = Files.find(dir, Integer.MAX_VALUE, (p, a) -> a.isRegularFile())) { + Path[] files = stream.map(p -> dir.relativize(p)) + .filter(p -> p.startsWith("p")) + .toArray(Path[]::new); + JarUtils.createJarFile(jarFile, man, dir, files); + } + return jarFile.toString(); + } + + void run(String... opts) throws Exception { + Path patches = Path.of(TEST_CLASSES).resolve("patches").resolve("java.base"); + Stream s1 = Stream.of("--patch-module", "java.base=" + patches); + Stream s2 = Stream.of(opts); + String[] allOpts = Stream.concat(s1, s2).toArray(String[]::new); + ProcessTools.executeTestJava(allOpts) + .outputTo(System.err) + .errorTo(System.err) + .shouldHaveExitValue(0); + } +} diff --git a/test/jdk/java/lang/ModuleTests/reflectionData/java.base/java/lang/ModuleHelper.java b/test/jdk/java/lang/ModuleTests/reflectionData/java.base/java/lang/ModuleHelper.java new file mode 100644 index 000000000000..1a1237bbd914 --- /dev/null +++ b/test/jdk/java/lang/ModuleTests/reflectionData/java.base/java/lang/ModuleHelper.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 java.lang; + +/** + * Helper class to patch to java.base and provide access to java.lang.Module internals. + */ +public class ModuleHelper { + ModuleHelper() { } + + /** + * Returns true if there are readability, exports/uses, or service uses that were added + * reflectively. + */ + public static boolean hasReflectionData() { + return Module.hasReflectionData(); + } +} diff --git a/test/jdk/java/lang/ModuleTests/reflectionData/p/TestEmpty.java b/test/jdk/java/lang/ModuleTests/reflectionData/p/TestEmpty.java new file mode 100644 index 000000000000..9b010cd0cdb7 --- /dev/null +++ b/test/jdk/java/lang/ModuleTests/reflectionData/p/TestEmpty.java @@ -0,0 +1,35 @@ +/* + * 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 p; + +/** + * Tests if any readability, exports/uses, or service uses have been added reflectively. + */ +public class TestEmpty { + public static void main(String[] args) throws Exception { + if (ModuleHelper.hasReflectionData()) { + throw new RuntimeException("Module has reflective data"); + } + } +} diff --git a/test/jdk/java/lang/instrument/TraceUsageTest.java b/test/jdk/java/lang/instrument/TraceUsageTest.java index ee3b83fb7753..a30de668b92a 100644 --- a/test/jdk/java/lang/instrument/TraceUsageTest.java +++ b/test/jdk/java/lang/instrument/TraceUsageTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -88,6 +88,7 @@ void testPremain() throws Exception { @Test void testAgentmain() throws Exception { OutputAnalyzer outputAnalyzer = execute( + "-XX:+EnableDynamicAgentLoading", "-Djdk.attach.allowAttachSelf=true", "-Djdk.instrument.traceUsage=true", "TraceUsageTest", diff --git a/test/jdk/java/lang/reflect/valhalla/ValueClassesReflectionTest.java b/test/jdk/java/lang/reflect/valhalla/ValueClassesReflectionTest.java index 296549236f40..39ba75a70664 100644 --- a/test/jdk/java/lang/reflect/valhalla/ValueClassesReflectionTest.java +++ b/test/jdk/java/lang/reflect/valhalla/ValueClassesReflectionTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8334334 + * @bug 8334334 8394667 * @summary reflection test for value classes * @enablePreview * @compile ValueClassesReflectionTest.java @@ -41,8 +41,6 @@ @Test public class ValueClassesReflectionTest { - final static int numberOfFields = 2; - value class ValueClass { private int i = 0; private String s = ""; @@ -55,19 +53,21 @@ value record ValueRecord(int i, String s) {} class Inner {} + record ClassNoOfFieldsPair(Class cls, int numberOfFields) {} + @DataProvider(name = "valueClasses") public Object[][] valueClassesData() { return List.of( - ValueClass.class, - AValueClass.class, - ValueRecord.class + new ClassNoOfFieldsPair(ValueClass.class, 3), + new ClassNoOfFieldsPair(AValueClass.class, 3), + new ClassNoOfFieldsPair(ValueRecord.class, 2) ).stream().map(c -> new Object[] {c}).toArray(Object[][]::new); } @Test(dataProvider = "valueClasses") - public void testValueClasses(Class cls) { - assertTrue(cls.isValue()); - Set accessFlagSet = cls.accessFlags(); + public void testValueClasses(ClassNoOfFieldsPair pair) { + assertTrue(pair.cls.isValue()); + Set accessFlagSet = pair.cls.accessFlags(); assertTrue(!accessFlagSet.contains(AccessFlag.IDENTITY)); } @@ -95,10 +95,10 @@ public void testNotValueClasses(Class cls) { } @Test(dataProvider = "valueClasses") - public void testValueClassReflection(Class valueClass) throws ReflectiveOperationException { - assertTrue(valueClass.isValue()); - Field[] fields = valueClass.getDeclaredFields(); - assertTrue(fields.length == numberOfFields); + public void testValueClassReflection(ClassNoOfFieldsPair pair) throws ReflectiveOperationException { + assertTrue(pair.cls.isValue()); + Field[] fields = pair.cls.getDeclaredFields(); + assertTrue(fields.length == pair.numberOfFields); for (Field field : fields) { int mod = field.getModifiers(); assertTrue((mod & Modifier.STRICT) != 0); diff --git a/test/jdk/javax/sql/test/rowset/cachedrowset/CachedRowSetUpdateTest.java b/test/jdk/javax/sql/test/rowset/cachedrowset/CachedRowSetUpdateTest.java new file mode 100644 index 000000000000..fd7016ef7766 --- /dev/null +++ b/test/jdk/javax/sql/test/rowset/cachedrowset/CachedRowSetUpdateTest.java @@ -0,0 +1,176 @@ +/* + * 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 test.rowset.cachedrowset; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import javax.sql.rowset.CachedRowSet; +import javax.sql.rowset.RowSetMetaDataImpl; +import javax.sql.rowset.RowSetProvider; + +import org.junit.jupiter.api.Test; +import util.StubConnection; +import util.StubPreparedStatement; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +// This test uses stub classes to simulate the bug without an actual +// connection to a database. +public class CachedRowSetUpdateTest { + + private final List commands = new ArrayList<>(); + private boolean firstQuery = true; + + /* + * Address a bug where the occurrence of "where" in catalog, schema or a table name + * could cause an incorrect SQL statement to be executed. + */ + @Test + void tableNameWithWhereTest() throws SQLException { + try (var conn = new TestStubConnection(); + var rows = createRowSet()) { + + // Define the table name that includes "where" + rows.setTableName("BARWHEREBAZ"); + + // Before the fix in 8394143, these SQL statements would have trimmed the + // portions after "WHERE" in the table name, so "BAZ" would be missing. + rows.acceptChanges(conn); + // First update + assertTrue(commands.contains("SELECT N, M FROM BARWHEREBAZ WHERE N = ? AND M IS NULL "), commands.toString()); + assertTrue(commands.contains("UPDATE BARWHEREBAZ SET N = ? , M = ? WHERE N = ? AND M IS NULL "), commands.toString()); + // Second update + assertTrue(commands.contains("SELECT N, M FROM BARWHEREBAZ WHERE N IS NULL AND M = ? "), commands.toString()); + assertTrue(commands.contains("UPDATE BARWHEREBAZ SET N = ? , M = ? WHERE N IS NULL AND M = ? "), commands.toString()); + } + } + + // 2 rows and 2 cols: M and N. N is updated in the first row and M in the second row. + private static CachedRowSet createRowSet() throws SQLException { + var rows = RowSetProvider.newFactory().createCachedRowSet(); + rows.setMetaData(createMetaData()); + // Apply UPDATE to subsequent cols + for (var col : List.of("N", "M")) { + // Add the data + rows.moveToInsertRow(); + rows.updateInt(col, -1); + rows.updateNull(col.equals("N") ? "M" : "N"); + rows.insertRow(); + rows.moveToCurrentRow(); + + // Update the data + rows.next(); + rows.setOriginalRow(); + rows.updateInt(col, 0); + rows.updateRow(); + } + return rows; + } + + // Matches `createRowSet` + private static RowSetMetaDataImpl createMetaData() throws SQLException { + var metadata = new RowSetMetaDataImpl(); + metadata.setColumnCount(2); + metadata.setColumnType(1, Types.INTEGER); + metadata.setColumnName(1, "N"); + metadata.setColumnType(2, Types.INTEGER); + metadata.setColumnName(2, "M"); + return metadata; + } + + // All the stub classes implement the minimal methods required to exercise + // the bug path for `writeData` and `initSQLStatements`. + private class TestStubConnection extends StubConnection { + + @Override + public Map> getTypeMap() throws SQLException { + return Map.of(); + } + + @Override + public PreparedStatement prepareStatement(String sql) { + commands.add(sql); + return new TestStubPreparedStatement(this); + } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) { + commands.add(sql); + return new TestStubPreparedStatement(this); + } + + @Override + public void commit() {} + } + + private class TestStubPreparedStatement extends StubPreparedStatement { + + public TestStubPreparedStatement(StubConnection con) { + super(con); + } + + // Needs to return an actual result that matches the update queries in the test. + // Fixed ordering to match the query order that the test naturally encounters. + @Override + public ResultSet executeQuery() throws SQLException { + String first; + String second; + if (firstQuery) { + firstQuery = false; + first = "N"; + second = "M"; + } else { + second = "N"; + first = "M"; + } + var rows = RowSetProvider.newFactory().createCachedRowSet(); + rows.setMetaData(createMetaData()); + rows.moveToInsertRow(); + rows.updateInt(first, -1); + rows.updateNull(second); + rows.insertRow(); + rows.moveToCurrentRow(); + rows.beforeFirst(); + return rows; + } + + @Override + public void setNull(int parameterIndex, int sqlType) {} + + @Override + public void setObject(int parameterIndex, Object x) {} + + @Override + public int executeUpdate() { + return 1; + } + + @Override + public void close() {} + } +} diff --git a/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSamplingStop.java b/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSamplingStop.java new file mode 100644 index 000000000000..a367c218baac --- /dev/null +++ b/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSamplingStop.java @@ -0,0 +1,89 @@ +/* + * 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 jdk.jfr.event.profiling; + +import java.util.Random; +import java.util.zip.Deflater; + +import jdk.jfr.Recording; + +import static jdk.test.lib.jfr.EventNames.CPUTimeSample; + +/* + * @test + * @bug 8392187 + * @summary Repeatedly disenroll CPU-time sampler while threads are executing native code + * @requires vm.hasJFR & vm.flagless & os.family == "linux" + * @library /test/lib + * @run main/othervm/timeout=600 jdk.jfr.event.profiling.TestCPUTimeSamplingStop + */ +public class TestCPUTimeSamplingStop { + + private static final int TEST_ITERATIONS = 100; + private static final int LOOP_ITERATIONS = 100; + private static final int DELAY_MILLIS = 2; + + public static void main(String[] args) throws Exception { + int threads = Math.max(Runtime.getRuntime().availableProcessors() - 1, 2); + for (int i = 0; i < threads; i++) { + Thread t = new Thread(TestCPUTimeSamplingStop::nativeBurner, "Native CPU burner " + i); + t.setDaemon(true); + t.start(); + } + + for (int i = 0; i < TEST_ITERATIONS; i++) { + runTest(); + } + } + + private static void runTest() throws InterruptedException { + try (Recording r = new Recording()) { + r.start(); + for (int i = 0; i < LOOP_ITERATIONS; i++) { + r.disable(CPUTimeSample); + r.enable(CPUTimeSample).with("throttle", "1ms"); + Thread.sleep(DELAY_MILLIS); + } + r.stop(); + } + } + + // Burn CPU cycles inside a long native call (compression of a large array) + private static void nativeBurner() { + byte[] input = new byte[1024 * 1024]; + byte[] output = new byte[input.length * 9 / 8]; + new Random().nextBytes(input); + + try (Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION)) { + while (true) { + deflater.reset(); + deflater.setInput(input); + deflater.finish(); + while (!deflater.finished()) { + deflater.deflate(output); + } + } + } + } +} diff --git a/test/jdk/jdk/jfr/event/runtime/TestAgentEvent.java b/test/jdk/jdk/jfr/event/runtime/TestAgentEvent.java index 6e168153947e..473cb9e28ba6 100644 --- a/test/jdk/jdk/jfr/event/runtime/TestAgentEvent.java +++ b/test/jdk/jdk/jfr/event/runtime/TestAgentEvent.java @@ -61,7 +61,7 @@ * jdk.jfr.event.runtime.TestAgentEvent * testJavaStatic * - * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -Djdk.attach.allowAttachSelf=true + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+EnableDynamicAgentLoading -Djdk.attach.allowAttachSelf=true * jdk.jfr.event.runtime.TestAgentEvent * testJavaDynamic * diff --git a/test/jdk/jdk/jfr/javaagent/TestLoadedAgent.java b/test/jdk/jdk/jfr/javaagent/TestLoadedAgent.java index 92b88336b3e0..cc361252a80b 100644 --- a/test/jdk/jdk/jfr/javaagent/TestLoadedAgent.java +++ b/test/jdk/jdk/jfr/javaagent/TestLoadedAgent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,7 @@ * @run driver jdk.test.lib.util.JavaAgentBuilder * jdk.jfr.javaagent.EventEmitterAgent EventEmitterAgent.jar * - * @run main/othervm -Djdk.attach.allowAttachSelf=true jdk.jfr.javaagent.TestLoadedAgent + * @run main/othervm -XX:+EnableDynamicAgentLoading -Djdk.attach.allowAttachSelf=true jdk.jfr.javaagent.TestLoadedAgent */ package jdk.jfr.javaagent; diff --git a/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/CAInterop.java b/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/CAInterop.java index 65a534f12860..d34003ed5009 100644 --- a/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/CAInterop.java +++ b/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/CAInterop.java @@ -415,28 +415,6 @@ * @run main/othervm/manual -Djava.security.debug=certpath CAInterop teliarootcav2 CRL */ -/* - * @test id=emsignrootcag1 - * @bug 8319187 - * @summary Interoperability tests with eMudhra Root CA G1 - * @library /test/lib - * @build jtreg.SkippedException ValidatePathWithURL CAInterop - * @run main/othervm/manual -Djava.security.debug=certpath,ocsp CAInterop emsignrootcag1 OCSP - * @run main/othervm/manual -Djava.security.debug=certpath,ocsp -Dcom.sun.security.ocsp.useget=false CAInterop emsignrootcag1 OCSP - * @run main/othervm/manual -Djava.security.debug=certpath CAInterop emsignrootcag1 CRL - */ - -/* - * @test id=emsigneccrootcag3 - * @bug 8319187 - * @summary Interoperability tests with eMudhra ECC Root CA G3 - * @library /test/lib - * @build jtreg.SkippedException ValidatePathWithURL CAInterop - * @run main/othervm/manual -Djava.security.debug=certpath,ocsp CAInterop emsigneccrootcag3 OCSP - * @run main/othervm/manual -Djava.security.debug=certpath,ocsp -Dcom.sun.security.ocsp.useget=false CAInterop emsigneccrootcag3 OCSP - * @run main/othervm/manual -Djava.security.debug=certpath CAInterop emsigneccrootcag3 CRL - */ - /* * @test id=certainlyrootr1 * @bug 8321408 @@ -727,13 +705,6 @@ private CATestURLs getTestURLs(String alias) { new CATestURLs("https://juolukka.cover.telia.fi:10600", "https://juolukka.cover.telia.fi:10601"); - case "emsignrootcag1" -> - new CATestURLs("https://testovg1.emsign.com/RootOVG1.html", - "https://testovg1r.emsign.com/RootOVG1MR.html"); - case "emsigneccrootcag3" -> - new CATestURLs("https://testovg3.emsign.com/RootOVG3.html", - "https://testovg3r.emsign.com/RootOVG3MR.html"); - case "certainlyrootr1" -> new CATestURLs("https://valid.root-r1.certainly.com", "https://revoked.root-r1.certainly.com"); diff --git a/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/EmSignRootG2CA.java b/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/EmSignRootG2CA.java deleted file mode 100644 index 60c2066e1345..000000000000 --- a/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/EmSignRootG2CA.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * 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 8319187 8385585 - * @summary Interoperability tests with eMudhra emSign Root CA G2 CS root - * @build ValidatePathWithParams - * @run main/othervm/manual -Djava.security.debug=certpath EmSignRootG2CA OCSP - * @run main/othervm/manual -Djava.security.debug=certpath EmSignRootG2CA CRL - */ - -public class EmSignRootG2CA { - - // Owner: CN=emSign CS CA - G2, O=eMudhra Technologies Limited, OU=emSign PKI, C=IN - // Issuer: CN=emSign Root CA - G2, O=eMudhra Technologies Limited, OU=emSign PKI, C=IN - // Serial number: c084e666596139a1fa9b - // Valid from: Sun Feb 18 10:30:00 PST 2018 until: Fri Feb 18 10:30:00 PST 2033 - private static final String INT = "-----BEGIN CERTIFICATE-----\n" + - "MIIGeDCCBGCgAwIBAgILAMCE5mZZYTmh+pswDQYJKoZIhvcNAQEMBQAwZzELMAkG\n" + - "A1UEBhMCSU4xEzARBgNVBAsTCmVtU2lnbiBQS0kxJTAjBgNVBAoTHGVNdWRocmEg\n" + - "VGVjaG5vbG9naWVzIExpbWl0ZWQxHDAaBgNVBAMTE2VtU2lnbiBSb290IENBIC0g\n" + - "RzIwHhcNMTgwMjE4MTgzMDAwWhcNMzMwMjE4MTgzMDAwWjBlMQswCQYDVQQGEwJJ\n" + - "TjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9s\n" + - "b2dpZXMgTGltaXRlZDEaMBgGA1UEAxMRZW1TaWduIENTIENBIC0gRzIwggIiMA0G\n" + - "CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDYYkv6Q9an5RylOJ6rkTAHT0cAwfYg\n" + - "ZsFKk/Hz/4VwWYsmzf+Z7M8i3CK3mnUcqgw0AIzrVLUwxiKAaL0qca+SbXwOk/7p\n" + - "Y/zwwLdg0OhHVGeeU3OTvkbsBpiLS08i7ids9FGrte6m1kqk+QSOY2F5AESxA4+F\n" + - "AKXGtzIImQd15m67C88AzzFsvszAAxSvVTqs4hb8BcRnUCzlAp7gMJSwwrrgTiEv\n" + - "6Ap6cFVT+n1oj6370sd5KBiRelLoqZtQx4njoNJkJlM30ftPNMGnqPLCloQ6koP/\n" + - "dAdpmwWB+F0/5d5UVmVPC3R/F8w7aX3fdSC8+M2E/ZXPVIYkEquLT7K2yXhRl3hn\n" + - "xwG6qqGp6TjvKvhiyac8qieu9YNG1R+PVFqejOFMohV2g0Z5MfwaruhUCNwHHeZs\n" + - "Dv/MVYMiHcV+5qU+MMzcKngb3RCmq0jzCb+MESomEMiAieCC15W7YC/LpgDHO0jY\n" + - "vV4AdLquUHfsOnhT2KD7mEg2PnL7JOwoQSFtuJYmM/coh+Y6CIoV3x+aV1bO7FDF\n" + - "ap33u36lE639oQj0tTqW3n1WcyNxhD0lwGlYIAjG8XnhRjtl6/MVVrGuyPWpB4TH\n" + - "u8CgNT0roENuq13RnHbBz2rLnndenHiMbxCyElGJBpZfXiF1H25KHUzvyzxt++L+\n" + - "hSfprX9BSXLpGQIDAQABo4IBJTCCASEwHwYDVR0jBBgwFoAU7exNRWEYKOezIygR\n" + - "HE2lJw1e7PQwHQYDVR0OBBYEFBWGyrZ0lhdIWDSCLM3S4XWer0S3MA4GA1UdDwEB\n" + - "/wQEAwIBBjATBgNVHSUEDDAKBggrBgEFBQcDAzA9BgNVHSAENjA0MDIGBFUdIAAw\n" + - "KjAoBggrBgEFBQcCARYcaHR0cDovL3JlcG9zaXRvcnkuZW1zaWduLmNvbTASBgNV\n" + - "HRMBAf8ECDAGAQH/AgEAMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAYYWaHR0\n" + - "cDovL29jc3AuZW1zaWduLmNvbTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js\n" + - "LmVtc2lnbi5jb20/Um9vdENBRzIuY3JsMA0GCSqGSIb3DQEBDAUAA4ICAQCDkogs\n" + - "d5Tv1zwsQdk15btzYK/oI1tEwvN6IpIM9rSqIrje8XnXKjHHmbHX6emHIR31bxuK\n" + - "7mY77XjrJMWp+71udC/DgDy4tfZTXIzEekI0XQfcui1UPC08Ysl0taQKTANwsAOV\n" + - "VSi7boSGqLet0qSmeKVyQ5/blbwx1NhjyLTyi66rVYf7fYdPV55X5TKUJdKDgiRI\n" + - "BomNVRcrrnHZtS8+t9CXxSXR35VAu2ube44Tl+dQHIWz9XwLxtYFwIPSEdqPpoAu\n" + - "5XEVo7evwMHQoY/MQj6Ywbw6tYh6bHu6C/qrp4oSyYXbz2ZWlHkz1oEXvefi7a9Z\n" + - "6mKnnaY3UYHq5AI+k6ojazVFbSTenb/TO/Z247gdhG7Wssshd6pgyqcTEa+FZz+F\n" + - "5ZZdoiIl8UJsTCPPg0xP9Ab0WE3BjCCqTPt+Czbd3cgBxiBS7KTQs/DnQRFuPCjC\n" + - "khbDtHsCN4aUoLM9OOw94/ZcoU0G5cg9mSvONBxUv9W7SIpJreXXMPXixcBKULoJ\n" + - "focui3s0yzGqTA9tSzQ4nmA9aXBCAAxrABlY/hk10ImeBa1SPjocRb/vuCaGp74T\n" + - "n8oADP42XudDnp8wlOKWxFJulhNi960Rev+5vZOPF/LGfS78GI6yzBjR49VJGhOP\n" + - "EJK8NSNmK3FNblQfOyFM7VE0uOGHOUwpMGVM2A==\n" + - "-----END CERTIFICATE-----"; - - // Owner: CN=eMudhra Technologies Limited, O=eMudhra Technologies Limited, - // L=Bangalore, ST=Karnataka, C=IN - // Issuer: CN=emSign CS CA - G2, O=eMudhra Technologies Limited, - // OU=emSign PKI, C=IN - // Serial number: e66c2d81f1cf97f024897e285626ad - // Valid from: Fri Sep 04 00:59:47 PDT 2026 until: Sat Sep 04 00:59:47 - // PDT 2027 - private static final String VALID = "-----BEGIN CERTIFICATE-----\n" + - "MIIG6zCCBNOgAwIBAgIQAOZsLYHxz5fwJIl+KFYmrTANBgkqhkiG9w0BAQwFADBl\n" + - "MQswCQYDVQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11\n" + - "ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEaMBgGA1UEAxMRZW1TaWduIENTIENB\n" + - "IC0gRzIwHhcNMjYwOTA0MDc1OTQ3WhcNMjcwOTA0MDc1OTQ3WjCBgzELMAkGA1UE\n" + - "BhMCSU4xEjAQBgNVBAgTCUthcm5hdGFrYTESMBAGA1UEBxMJQmFuZ2Fsb3JlMSUw\n" + - "IwYDVQQKDBxlTXVkaHJhIFRlY2hub2xvZ2llcyBMaW1pdGVkMSUwIwYDVQQDExxl\n" + - "TXVkaHJhIFRlY2hub2xvZ2llcyBMaW1pdGVkMIICIjANBgkqhkiG9w0BAQEFAAOC\n" + - "Ag8AMIICCgKCAgEA5Mh6duhdJ4xfZw3Iwnvy7KIauGVXCpX/EOS3ueDOKPCQ8XG+\n" + - "pWHP+bs1OioHxWwRAo0AVV/hYr9c+nAyAq0Y0RsZGeBDqZUVK0oECYdWvssPP/0o\n" + - "ZUcmiDvDI3NDYKXE8XqPrxd6uHZ0mFkJn+GQHUrTx7gOtNCscijmkeMip9jyLq8B\n" + - "m8c9B6TzkSXD6Q2tB76cRuHGLL7FHDzBut6GZElJicsoeI/AQtLfakz02Taea8Ue\n" + - "ILyLCnpq5jgij2zqk6ozMDd8lpHYvJCwZ7tFHfuxQBcWy9R2qUAeXBUX+Iymq+Me\n" + - "gpv8m3Ov1382p+iP6wpxp/LsDlr+Cg3uil19aFG2GO7WIxiIyDbSY3t6VjhZWFWP\n" + - "YbemMXhiKCMWZmX8bKVb8wJYjxUaBDpCoIq8+gj/XglUjq7vpXN8oARLW69z1fJi\n" + - "Yt0QAlEDLMcM0Rh3wmEr/sqtu7mkBU7Lp1nnqNx4bPXDf69SalbTnM3ukN7QYpgz\n" + - "d/cxzACOQioTwoCDFSHuOBhLuyo81xho9UKzpMR50r7FcePmiLiAtkgl7fZ02U1C\n" + - "DincqL+bLtpUxGcSmxNtI3NF+OqpcG+DUjUFDge7k+WgbFxvzahcxH5i5YdzIRf0\n" + - "y3NbodvHIYmVPcH0vMv1ntywwP1jy9VuAgT24cj+Es5rmPYy8xogrZa95kkCAwEA\n" + - "AaOCAXYwggFyMB8GA1UdIwQYMBaAFBWGyrZ0lhdIWDSCLM3S4XWer0S3MB0GA1Ud\n" + - "DgQWBBQy1M8ziJmPioriHY7mJB7+beUk5zAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0l\n" + - "BAwwCgYIKwYBBQUHAwMwNwYDVR0fBDAwLjAsoCqgKIYmaHR0cDovL2NybC5lbXNp\n" + - "Z24uY29tP2VtU2lnbkNTQ0FHMi5jcmwwTwYDVR0gBEgwRjA6BgwrBgEEAYOOIQEA\n" + - "AQIwKjAoBggrBgEFBQcCARYcaHR0cDovL3JlcG9zaXRvcnkuZW1zaWduLmNvbTAI\n" + - "BgZngQwBBAEwcwYIKwYBBQUHAQEEZzBlMCIGCCsGAQUFBzABhhZodHRwOi8vb2Nz\n" + - "cC5lbVNpZ24uY29tMD8GCCsGAQUFBzAChjNodHRwOi8vcmVwb3NpdG9yeS5lbXNp\n" + - "Z24uY29tL2NlcnRzL2VtU2lnbkNTQ0FHMi5jcnQwDAYDVR0TAQH/BAIwADANBgkq\n" + - "hkiG9w0BAQwFAAOCAgEAL9uz2lWq/Dx1P9jot/DUvDS3u7wKf3qw5XH191kQ1B+S\n" + - "m9/uhkEptH4lanpXoB/QorvTSkSvu2D2LxQOjHbXdYzLJqDO4NNbFw3S7H5jRVWT\n" + - "dRBrb/2VREX5LJ2NBVgxETCpOMyXKgpmefcvVYxZ1jKa80H6Jqd7YzPpFhwsHglX\n" + - "/YQX+jYUWjdxkWSo48jlAbi81vUj5snn+AbrFnq8RRy1r22Z5k3zSFNGz2W9iWzS\n" + - "UjpSQoaz7e4/L+2flScEydmmv1F2MH9boP5XIZmNXKp6sTr55jZQmQiWysiERPJ2\n" + - "0bHhM9Q8qr+Sp8NWVZs5tIYnWJSyBasOctkVvl2ADdrIofR9cmWaP1/EdB5Hl1cx\n" + - "Aqo3FKfXK5ZNRHt7B7v11PoBiwzYqk45L03AABNmuZcNC3HkG5emfBrlY0Ud6QuK\n" + - "/8Oi4+G01H/6DFCqMqEFR/AFQtzPDnlaEL3XvoZyJcIJARP14XTyXXlUJt5fI0yq\n" + - "E6F0KsJCeCRcGKZcjLMWyN+5mPsNdT9EdQOpYJ8dXLtB5r2uN0xsjcOPpFTAIwRt\n" + - "fUW5MUFiZZvWqqadhI1EhKhWDTyTn8vJGWNiqXKbbfedck/k/B5Kc75A5nfuBB/u\n" + - "awVsnhMuAU7yVUOH37dGv4b4tKRnkW/L+owhEOfbWbiRrtmQ4z/u5+84yxddNiY=\n" + - "-----END CERTIFICATE-----"; - - // Owner: CN=eMudhra Technologies Limited, O=eMudhra Technologies Limited, - // L=Bangalore, ST=Karnataka, C=IN - // Issuer: CN=emSign CS CA - G2, O=eMudhra Technologies Limited, - // OU=emSign PKI, C=IN - // Serial number: 399515f0e9db1ec53b695df8a4216 - // Valid from: Sat Sep 05 10:12:31 PDT 2026 until: Sun Sep 05 10:12:31 - // PDT 2027 - private static final String REVOKED = "-----BEGIN CERTIFICATE-----\n" + - "MIIG6jCCBNKgAwIBAgIPA5lRXw6dsexTtpXfikIWMA0GCSqGSIb3DQEBDAUAMGUx\n" + - "CzAJBgNVBAYTAklOMRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVk\n" + - "aHJhIFRlY2hub2xvZ2llcyBMaW1pdGVkMRowGAYDVQQDExFlbVNpZ24gQ1MgQ0Eg\n" + - "LSBHMjAeFw0yNjA5MDUxNzEyMzFaFw0yNzA5MDUxNzEyMzFaMIGDMQswCQYDVQQG\n" + - "EwJJTjESMBAGA1UECBMJS2FybmF0YWthMRIwEAYDVQQHEwlCYW5nYWxvcmUxJTAj\n" + - "BgNVBAoMHGVNdWRocmEgVGVjaG5vbG9naWVzIExpbWl0ZWQxJTAjBgNVBAMTHGVN\n" + - "dWRocmEgVGVjaG5vbG9naWVzIExpbWl0ZWQwggIiMA0GCSqGSIb3DQEBAQUAA4IC\n" + - "DwAwggIKAoICAQDamKtV3CD/F8V6OpeyMvQZakL/CLsYZVSlSHN34u2nxtpG5RIt\n" + - "buEMQfZl1lsLQlV39TgoCfrf7kY2S9xLZcMapUGMwT0HoP+rRnyVJpfhgq/ULlX/\n" + - "L2KgGxJyUP6XpVcdrlyrHOAvErFhdhdg9L4/tTwOoarHUZCNBB1Wx3Rl1sgCMVbl\n" + - "UcbzgJZulYhMJ+deEnHwJTPLO/GTFHek7hGy7bYoD2jhwUgG6Yjae5rZWHnLaKRg\n" + - "zACpgQl2sUAtJ7l8Zh0Ig14BTzM0OTfQutPaB3JYr81wZj4awhk39MDf4ob6kcEm\n" + - "8KqqYqlARhhFe8XOCZ5IrmQEzie2am0EZICk6g2nBvIcMUgPN9yFOGQdUaSSQe5+\n" + - "lA6skGA/ZADVCBf9E1j/tqABdcFdYiPvpInlRxXCK/xxbspupQhffyamqUtj7PT+\n" + - "tIdAaruiFV/PIx6MvmNpBhzyPt01QJlAy5GU//5/EnQ7HW/np9jjHr0QdZFbnis+\n" + - "O+VloqNSP7UW9eWo1mMwQWLGr/PQHNERGGsPb6gofxUJdKgp4kbg9rqfBkLdYLBV\n" + - "jVYAKIpOlP7b93eta5qr0DOImBLor5fvZOs7bljltp68S1mmVxMsvezmSJIcvLvG\n" + - "R0G4YWDvobhcCVmEk1nV580+/3OZEMbBk3R4oOB2MUv4eiCYrlpWI0xmjQIDAQAB\n" + - "o4IBdjCCAXIwHwYDVR0jBBgwFoAUFYbKtnSWF0hYNIIszdLhdZ6vRLcwHQYDVR0O\n" + - "BBYEFMQn0uMfp84zx4vz2EYeBnzyyh9fMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUE\n" + - "DDAKBggrBgEFBQcDAzA3BgNVHR8EMDAuMCygKqAohiZodHRwOi8vY3JsLmVtc2ln\n" + - "bi5jb20/ZW1TaWduQ1NDQUcyLmNybDBPBgNVHSAESDBGMDoGDCsGAQQBg44hAQAB\n" + - "AjAqMCgGCCsGAQUFBwIBFhxodHRwOi8vcmVwb3NpdG9yeS5lbXNpZ24uY29tMAgG\n" + - "BmeBDAEEATBzBggrBgEFBQcBAQRnMGUwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3Nw\n" + - "LmVtU2lnbi5jb20wPwYIKwYBBQUHMAKGM2h0dHA6Ly9yZXBvc2l0b3J5LmVtc2ln\n" + - "bi5jb20vY2VydHMvZW1TaWduQ1NDQUcyLmNydDAMBgNVHRMBAf8EAjAAMA0GCSqG\n" + - "SIb3DQEBDAUAA4ICAQBy0UYUXy/AwPgRmUWu1B0xeq9hU2vFwoFmGbOkd2sJqOOi\n" + - "KdIbpjvIdLtPchIasENu+oc55ENgmbD4u5WDxGspBjL18sbE6jmzmGY1dsbCgF17\n" + - "Pi9KvlX3LRtD8qTD2oJ5y/YJ7fMKW4j+h4ssNrmA2NboXhgX3e+idQeZ2ItAgV0k\n" + - "s3STGRkoOKdsuwpeD/+69nXVIIYusVSgOuYXZb/ViWcL3cMq75cZdR/WYixxU9ZV\n" + - "Z55NQKikOGVTuKBpDv9BEa5yqAK32RcajUl7R7bT7AuY+LnTRHtX0P4sCkohj1kP\n" + - "9JGoFWN1NIJeCou5GSRmuWLYpnMfi9oDvMPt2e7GUqKkNF+OXTxWfq5A0xtkLWGN\n" + - "3qEr9xD34ZnFP5h0c6sE8hzee65erhqSo6LqlQMGINP2gL+fUG+smdX0JQYbX9hP\n" + - "raKmp53+aHwolzn0R3t9otgZ6yVdAZLScNLXqDd55/gr/TDrfu4XzRTHK9RCrMzT\n" + - "trvbKKtqTuwEp1vQBoced60CI1ZjG41tipF1irhU+pyHk/Kt9YiExp6QZGoBznTY\n" + - "9Rvw5No0XoVQHLC56JbsGUot8bVVpGNXzF8PtWBmskqttsw1XTgSUOsnoopfb+Eb\n" + - "fpPUdqTDs47FRowITPbj4cgYzYRKWEc7xh8fozZ18lN8yAP6hA2OfDN3j4HrCA==\n" + - "-----END CERTIFICATE-----"; - - public static void main(String[] args) throws Exception { - - ValidatePathWithParams pathValidator = new ValidatePathWithParams(null); - - if (args.length >= 1 && "CRL".equalsIgnoreCase(args[0])) { - pathValidator.enableCRLCheck(); - } else { - // OCSP check by default - pathValidator.enableOCSPCheck(); - } - - // Validate valid - pathValidator.validate(new String[]{VALID, INT}, - ValidatePathWithParams.Status.GOOD, null, System.out); - - // Validate Revoked - pathValidator.validate(new String[]{REVOKED, INT}, - ValidatePathWithParams.Status.REVOKED, - "Sat Sep 05 10:32:30 PDT 2026", System.out); - } -} diff --git a/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java b/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java index 17d098006d93..2cae63aaa374 100644 --- a/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java +++ b/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java @@ -29,7 +29,7 @@ * 8243559 8225072 8258630 8259312 8256421 8225081 8225082 8225083 8245654 * 8305975 8304760 8307134 8295894 8314960 8317373 8317374 8318759 8319187 * 8321408 8316138 8341057 8303770 8350498 8359170 8361212 8372351 8387123 - * 8386371 8392771 + * 8386371 8392771 8395690 * @summary Check root CA entries in cacerts file */ import java.io.ByteArrayInputStream; @@ -49,13 +49,13 @@ public class VerifyCACerts { // The numbers of certs now. // SapMachine 2021-09-23: Additional certificate for SAP - private static final int COUNT = 112; + private static final int COUNT = 108; // SHA-256 of cacerts, can be generated with // shasum -a 256 cacerts | sed -e 's/../&:/g' | tr '[:lower:]' '[:upper:]' | cut -c1-95 // SapMachine 2021-09-23: Additional certificate for SAP private static final String CHECKSUM - = "E3:3C:F7:4C:16:02:30:58:29:57:83:B3:04:89:47:B6:4D:BA:F4:6E:E4:14:98:C4:58:6D:8F:55:27:F9:6A:A2"; + = "0F:BA:3F:6E:A4:03:73:53:98:EF:29:3B:16:E8:8D:79:DF:7D:1D:FC:E6:D7:77:D1:5C:E6:B6:06:55:6B:6C:A8"; // Hex formatter to upper case with ":" delimiter private static final HexFormat HEX = HexFormat.ofDelimiter(":").withUpperCase(); @@ -80,8 +80,6 @@ public class VerifyCACerts { "C0:A6:F4:DC:63:A2:4B:FD:CF:54:EF:2A:6A:08:2A:0A:72:DE:35:80:3E:2F:F5:FF:52:7A:E5:D8:72:06:DF:D5"); put("comodorsaca [jdk]", "52:F0:E1:C4:E5:8E:C6:29:29:1B:60:31:7F:07:46:71:B8:5D:7E:A8:0D:5B:07:27:34:63:53:4B:32:B4:02:34"); - put("comodoaaaca [jdk]", - "D7:A7:A0:FB:5D:7E:27:31:D7:71:E9:48:4E:BC:DE:F7:1D:5F:0C:3E:0A:29:48:78:2B:C8:3E:E0:EA:69:9E:F4"); put("comodoeccca [jdk]", "17:93:92:7A:06:14:54:97:89:AD:CE:2F:8F:34:F7:F0:B6:6D:0F:3A:E3:A3:B8:4D:21:EC:15:DB:BA:4F:AD:C7"); put("usertrustrsaca [jdk]", @@ -255,12 +253,6 @@ public class VerifyCACerts { "D4:8D:3D:23:EE:DB:50:A4:59:E5:51:97:60:1C:27:77:4B:9D:7B:18:C9:4D:5A:05:95:11:A1:02:50:B9:31:68"); put("teliarootcav2 [jdk]", "24:2B:69:74:2F:CB:1E:5B:2A:BF:98:89:8B:94:57:21:87:54:4E:5B:4D:99:11:78:65:73:62:1F:6A:74:B8:2C"); - put("emsignrootcag1 [jdk]", - "40:F6:AF:03:46:A9:9A:A1:CD:1D:55:5A:4E:9C:CE:62:C7:F9:63:46:03:EE:40:66:15:83:3D:C8:C8:D0:03:67"); - put("emsigneccrootcag3 [jdk]", - "86:A1:EC:BA:08:9C:4A:8D:3B:BE:27:34:C6:12:BA:34:1D:81:3E:04:3C:F9:E8:A8:62:CD:5C:57:A3:6B:BE:6B"); - put("emsignrootcag2 [jdk]", - "1A:A0:C2:70:9E:83:1B:D6:E3:B5:12:9A:00:BA:41:F7:EE:EF:02:08:72:F1:E6:50:4B:F0:F6:C3:F2:4F:3A:F3"); put("certainlyrootr1 [jdk]", "77:B8:2C:D8:64:4C:43:05:F7:AC:C5:CB:15:6B:45:67:50:04:03:3D:51:C6:0C:62:02:A8:E0:C3:34:67:D3:A0"); put("certainlyroote1 [jdk]", diff --git a/test/jdk/sun/security/pkcs11/tls/TestExtendedMasterSecretHandshake.java b/test/jdk/sun/security/pkcs11/tls/TestExtendedMasterSecretHandshake.java index bedac5341ab6..3486e0d36f74 100644 --- a/test/jdk/sun/security/pkcs11/tls/TestExtendedMasterSecretHandshake.java +++ b/test/jdk/sun/security/pkcs11/tls/TestExtendedMasterSecretHandshake.java @@ -30,13 +30,15 @@ * * @run main/othervm * -Djdk.tls.useExtendedMasterSecret=true + * -Djdk.tls.allowLegacyMasterSecret=false + * -Djavax.net.debug=ssl,handshake * TestExtendedMasterSecretHandshake true * * @run main/othervm * -Djdk.tls.useExtendedMasterSecret=false + * -Djavax.net.debug=ssl,handshake * TestExtendedMasterSecretHandshake false */ - import jdk.test.lib.security.SSLSocketTest; import jtreg.SkippedException; import sun.security.internal.spec.TlsMasterSecretParameterSpec; @@ -77,18 +79,22 @@ public void main(Provider p) throws Exception { verifyDirectEMSGeneration(p); } - System.setProperty("javax.net.debug", "ssl,handshake"); - String log = runHandshake(); if (!log.contains("TLS handshake and application data exchange succeeded")) { throw new RuntimeException("Handshake failed"); } + int consumed = countOccurrences(log, + "Consumed extension: extended_master_secret"); + int ignored = countOccurrences(log, + "Ignore unavailable extension: extended_master_secret"); + System.out.println("consumed = " + consumed + ", ignored = " + ignored); + if (useExtendedMasterSecret) { - if (countOccurrences(log, "Consumed extension: extended_master_secret") != 2 || - countOccurrences(log, "Ignore unavailable extension: extended_master_secret") != 0) { - throw new RuntimeException("EMS extension not negotiated on both sides"); + if (consumed != 2 || ignored != 0) { + throw new RuntimeException("EMS log check failed: consumed=" + + consumed + ", ignored=" + ignored + "\n" + log); } if (!log.contains("TLS handshake and application data exchange succeeded")) { @@ -97,8 +103,7 @@ public void main(Provider p) throws Exception { System.out.println("Verified EMS derivation through PKCS11 enabled"); } else { - if (countOccurrences(log, "Consumed extension: extended_master_secret") != 0 || - countOccurrences(log, "Ignore unavailable extension: extended_master_secret") < 2) { + if (consumed != 0 || ignored < 2) { throw new RuntimeException("EMS extension negotiated although jdk.tls.useExtendedMasterSecret=false"); } @@ -238,4 +243,4 @@ public void run() throws Exception { System.out.println("TLS handshake and application data exchange succeeded"); } } -} \ No newline at end of file +} diff --git a/test/jdk/sun/security/tools/keytool/DefaultGroupName.java b/test/jdk/sun/security/tools/keytool/DefaultGroupName.java new file mode 100644 index 000000000000..f4bcb3188135 --- /dev/null +++ b/test/jdk/sun/security/tools/keytool/DefaultGroupName.java @@ -0,0 +1,297 @@ +/* + * 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.Asserts; +import sun.security.tools.keytool.Main; +import sun.security.util.KeyUtil; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidParameterException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyPairGeneratorSpi; +import java.security.KeyStore; +import java.security.Provider; +import java.security.ProviderException; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Security; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.NamedParameterSpec; + +/** + * @test + * @bug 8391472 + * @summary Default group name should be hardcoded by keytool instead of + * determined by providers or a system property + * @library /test/lib + * @modules java.base/sun.security.tools.keytool + * java.base/sun.security.util + * @run main/othervm + * -Djdk.security.defaultKeySize=RSA:2048,DSA:1024,EC:256,EdDSA:448,XDH:448 + * DefaultGroupName + */ + +public class DefaultGroupName { + + private static final String COMMON + = "-keystore ks -storepass changeit -keypass changeit -debug"; + private static final char[] PASS = "changeit".toCharArray(); + + // A public observable status string set inside our private + // provider so we can check what has been done recently. + private static String status = ""; + private static int count = 0; + + public static void main(String[] args) throws Exception { + + Security.insertProviderAt(new ProviderImpl(), 1); + Files.deleteIfExists(Path.of("ks")); + + // default + test("RSA", 3072); + test("DSA", 2048); + test("EC", "secp384r1"); + test("EdDSA", "Ed25519"); + test("XDH", "X25519"); + test("ML-DSA", "ML-DSA-65"); + test("ML-KEM", "ML-KEM-768"); + + // Specified + test("RSA", 2048, "-keysize 2048"); + test("DSA", 1024, "-keysize 1024"); + test("EC", "secp256r1", "-groupname secp256r1"); + test("EdDSA", "Ed448", "-groupname ed448"); + test("EdDSA", "Ed25519", "-groupname ed25519"); + test("XDH", "X448", "-groupname X448"); + test("ML-KEM", "ML-KEM-512", "-groupname ML-KEM-512"); + test("ML-DSA", "ML-DSA-44", "-groupname ML-DSA-44"); + + // concrete algorithm names + test("Ed448", "Ed448"); + test("X448", "X448"); + test("ML-KEM-1024", "ML-KEM-1024"); + test("ML-DSA-87", "ML-DSA-87"); + } + + private static void test(String alg, String expected, String... extras) throws Exception { + + String alias = "a" + count++; + String cmd = COMMON + " -genkeypair -alias " + alias + + " -dname CN=" + alias + " -keyalg " + alg; + if (alg.startsWith("X") || alg.startsWith("ML-KEM")) { + cmd += " -signer a0"; // the one for EC + } + for (String extra : extras) { + cmd += " " + extra; + } + + status = ""; + Main.main(cmd.split(" ")); + Asserts.assertEquals(alg, status); // ensure our provider is used + + KeyStore ks = KeyStore.getInstance(new File("ks"), PASS); + PublicKey key = ks.getCertificate(alias).getPublicKey(); + AlgorithmParameterSpec params = key.getParams(); + if (params instanceof NamedParameterSpec nps) { + Asserts.assertEquals(expected, nps.getName()); + } else if (params instanceof ECParameterSpec){ + Asserts.assertTrue(params.toString().contains(expected), params.toString()); + } else { + throw new RuntimeException("Unknown type: " + params.getClass()); + } + } + + private static void test(String alg, int expected, String... extras) throws Exception { + + String alias = "a" + count++; + String cmd = COMMON + " -genkeypair -alias " + alias + + " -dname CN=" + alias + " -keyalg " + alg; + for (String extra : extras) { + cmd += " " + extra; + } + + status = ""; + Main.main(cmd.split(" ")); + Asserts.assertEquals(alg, status); // ensure our provider is used + + KeyStore ks = KeyStore.getInstance(new File("ks"), PASS); + PublicKey key = ks.getCertificate(alias).getPublicKey(); + int keySize = KeyUtil.getKeySize(key); + Asserts.assertEquals(expected, keySize); + } + + public static class ProviderImpl extends Provider { + public ProviderImpl() { + super("P8391472", "1", "cool"); + put("KeyPairGenerator.RSA", KPG.RSA.class.getName()); + put("KeyPairGenerator.DSA", KPG.DSA.class.getName()); + put("KeyPairGenerator.ML-DSA", KPG.MLDSA.class.getName()); + put("KeyPairGenerator.ML-KEM", KPG.MLKEM.class.getName()); + put("KeyPairGenerator.EC", KPG.EC.class.getName()); + put("KeyPairGenerator.EdDSA", KPG.EDDSA.class.getName()); + put("KeyPairGenerator.XDH", KPG.XDH.class.getName()); + put("KeyPairGenerator.Ed448", KPG.ED448.class.getName()); + put("KeyPairGenerator.X448", KPG.X448.class.getName()); + put("KeyPairGenerator.ML-KEM-1024", KPG.MLKEM1024.class.getName()); + put("KeyPairGenerator.ML-DSA-87", KPG.MLDSA87.class.getName()); + } + } + + public static class KPG extends KeyPairGeneratorSpi { + + // None of these KeyPairGeneratorSpis uses the same default + // as claimed in keytool + public static class RSA extends KPG { + public RSA() { + super("RSA", "SunRsaSign", 2048); + } + } + + public static class DSA extends KPG { + public DSA() { + super("DSA", "SUN", 1024); + } + } + + public static class MLDSA extends KPG { + public MLDSA() { + super("ML-DSA", "SUN", NamedParameterSpec.ML_DSA_44, true); + } + } + + public static class MLKEM extends KPG { + public MLKEM() { + super("ML-KEM", "SunJCE", NamedParameterSpec.ML_KEM_512, true); + } + } + + public static class EC extends KPG { + public EC() { + super("EC", "SunEC", new ECGenParameterSpec("secp256r1"), true); + } + } + + public static class EDDSA extends KPG { + public EDDSA() { + super("EdDSA", "SunEC", NamedParameterSpec.ED448, true); + } + } + + public static class XDH extends KPG { + public XDH() { + super("XDH", "SunEC", NamedParameterSpec.X448, true); + } + } + + public static class X448 extends KPG { + public X448() { + super("X448", "SunEC", NamedParameterSpec.X448, false); + } + } + + public static class ED448 extends KPG { + public ED448() { + super("Ed448", "SunEC", NamedParameterSpec.ED448, false); + } + } + + public static class MLKEM1024 extends KPG { + public MLKEM1024() { + super("ML-KEM-1024", "SunJCE", NamedParameterSpec.ML_KEM_1024, false); + } + } + + public static class MLDSA87 extends KPG { + public MLDSA87() { + super("ML-DSA-87", "SUN", NamedParameterSpec.ML_DSA_87, false); + } + } + + private int keySize = 0; + private AlgorithmParameterSpec spec = null; + + private final String algorithm; + private final String realProvider; + private final boolean isDynamic; + + public KPG(String algorithm, String realProvider, + AlgorithmParameterSpec defaultSpec, boolean isDynamic) { + this.algorithm = algorithm; + this.realProvider = realProvider; + this.spec = defaultSpec; + this.isDynamic = isDynamic; + } + + public KPG(String algorithm, String realProvider, int defaultkeySize) { + this.algorithm = algorithm; + this.realProvider = realProvider; + this.keySize = defaultkeySize; + this.isDynamic = true; + } + + @Override + public void initialize(AlgorithmParameterSpec params, + SecureRandom random) throws InvalidAlgorithmParameterException { + if (isDynamic) { + spec = params; + } else { + throw new InvalidAlgorithmParameterException(); + } + } + + @Override + public void initialize(int size, SecureRandom random) { + if (isDynamic) { + keySize = size; + } else { + throw new InvalidParameterException(); + } + } + + @Override + public KeyPair generateKeyPair() { + try { + KeyPairGenerator gen = KeyPairGenerator.getInstance(algorithm, realProvider); + if (keySize > 0 && spec != null) { + throw new IllegalStateException(); + } else if (keySize > 0) { + gen.initialize(keySize); + } else { + gen.initialize(spec); + } + KeyPair kp = gen.generateKeyPair(); + status = algorithm; + return kp; + } catch (Exception e) { + status = e.getMessage(); + throw new ProviderException(e); + } + } + } +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MsiDatabase.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MsiDatabase.java index 6c92e820f2ed..d6fd813443c2 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MsiDatabase.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MsiDatabase.java @@ -39,6 +39,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.UUID; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -101,6 +102,7 @@ String tableName() { static final Set FIND_PROPERTY_REQUIRED_TABLES = Set.of(PROPERTY); static final Set
LIST_SHORTCUTS_REQUIRED_TABLES = Set.of(COMPONENT, DIRECTORY, FILE, SHORTCUT); static final Set
UI_ALTERATIONS_REQUIRED_TABLES = Set.of(CONTROL_EVENT, INSTALL_UI_SEQUENCE); + static final Set
LIST_COMPONENT_IDS_REQUIRED_TABLES = Set.of(COMPONENT); } @@ -134,6 +136,14 @@ Collection listShortcuts() { }).toList(); } + Collection listComponentIds() { + return rows(Table.COMPONENT).map(row -> { + var str = row.apply("ComponentId"); + // Strip leading and trailing brackets + return UUID.fromString(str.substring(1, str.length() - 1)); + }).toList(); + } + UIAlterations uiAlterations() { var includeActions = Set.of("WelcomeEulaDlg", "WelcomeDlg"); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java index 2dc2351be3c5..3686e3326252 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java @@ -45,6 +45,7 @@ import java.util.Properties; import java.util.ResourceBundle; import java.util.Set; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; import jdk.jpackage.internal.model.DottedVersion; @@ -314,6 +315,11 @@ public static MsiDatabase.UIAlterations getUIAlterations(JPackageCommand cmd) { return MsiDatabaseCache.INSTANCE.uiAlterations(cmd.outputBundle()); } + public static Collection getMsiComponentIds(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.WIN_MSI); + return MsiDatabaseCache.INSTANCE.getMsiComponentIds(cmd.outputBundle()); + } + static Collection getMsiShortcuts(JPackageCommand cmd) { cmd.verifyIsOfType(PackageType.WIN_MSI); return MsiDatabaseCache.INSTANCE.listShortcuts(cmd.outputBundle()); @@ -614,6 +620,10 @@ MsiDatabase.UIAlterations uiAlterations(Path msiPath) { return ensureTables(msiPath, MsiDatabase.Table.UI_ALTERATIONS_REQUIRED_TABLES).uiAlterations(); } + Collection getMsiComponentIds(Path msiPath) { + return ensureTables(msiPath, MsiDatabase.Table.LIST_COMPONENT_IDS_REQUIRED_TABLES).listComponentIds(); + } + MsiDatabase ensureTables(Path msiPath, Set tableNames) { Objects.requireNonNull(msiPath); try { diff --git a/test/jdk/tools/jpackage/junit/windows/jdk.jpackage/jdk/jpackage/internal/WixComponentKeyTest.java b/test/jdk/tools/jpackage/junit/windows/jdk.jpackage/jdk/jpackage/internal/WixComponentKeyTest.java new file mode 100644 index 000000000000..3ed7cad72025 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/windows/jdk.jpackage/jdk/jpackage/internal/WixComponentKeyTest.java @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.internal; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.TreeMap; +import java.util.UUID; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.NullSource; + +class WixComponentKeyTest { + + @Test + void test_default_ctor() { + + var key = new WixComponentKey(); + + for (var attr : WixComponentKey.Attribute.values()) { + assertEquals(null, key.get(attr)); + assertEquals(null, key.attrs()[attr.ordinal()]); + } + } + + @Test + void test_copy() { + + var key = new WixComponentKey(); + + var copy = new WixComponentKey(key.attrs()); + + key.role("foo"); + + assertEquals("foo", key.get(WixComponentKey.Attribute.ROLE)); + assertEquals(null, copy.get(WixComponentKey.Attribute.ROLE)); + assertNotSame(key.attrs(), copy.attrs()); + } + + @Test + void test_ctor_invalid() { + + final var validArraySize = WixComponentKey.Attribute.values().length; + + for (int arraySize : List.of(validArraySize - 1, validArraySize + 1)) { + assertThrowsExactly(IllegalArgumentException.class, () -> { + new WixComponentKey(new String[arraySize]); + }); + } + } + + @ParameterizedTest + @NullSource + @CsvSource({ + "foo", + "c:\\bAr", + "c:/bAr", + "''", + }) + void test_path(String v) { + testAttr(WixComponentKey.Attribute.PATH, v, WixComponentKey::path, Path::of); + } + + @Test + void test_path_keep_path_separators() { + + Function key = path -> { + var result = new WixComponentKey(); + result.upgradeCode(UUID.fromString("9c8d7332-41a6-42d9-8e3d-56f41e8d7568")); + result.attrs()[WixComponentKey.Attribute.PATH.ordinal()] = path; + return result; + }; + + assertNotEquals(key.apply("c:\\foo").toGuid(), key.apply("c:/foo").toGuid()); + } + + @ParameterizedTest + @NullSource + @CsvSource({ + "foo", + "c:\\bAr", + "c:/bAr", + "''", + "*", + }) + void test_role(String v) { + testAttr(WixComponentKey.Attribute.ROLE, v, WixComponentKey::role, Function.identity()); + } + + @ParameterizedTest + @NullSource + @CsvSource({ + "foo", + "c:\\bAr", + "c:/bAr", + "''", + "*", + }) + void test_regkey(String v) { + testAttr(WixComponentKey.Attribute.REGKEY, v, WixComponentKey::regkey, Function.identity()); + } + + @ParameterizedTest + @NullSource + @CsvSource({ + "9c8d7332-41a6-42d9-8e3d-56f41e8d7568", + }) + void test_upgradeCode(String v) { + testAttr(WixComponentKey.Attribute.UPGRADE_CODE, v, WixComponentKey::upgradeCode, UUID::fromString); + } + + @ParameterizedTest + @EnumSource(WixComponentKey.Attribute.class) + void test_case_sensitive(WixComponentKey.Attribute attr) { + + var a = new WixComponentKey().path(Path.of("")).upgradeCode(UUID.fromString("9c8d7332-41a6-42d9-8e3d-56f41e8d7568")); + var b = new WixComponentKey(a.attrs()); + + switch (attr) { + case PATH, ROLE, REGKEY -> { + a.set(attr, "Foo"); + b.set(attr, "foo"); + } + case UPGRADE_CODE -> { + a.set(attr, "aaaaaaaa-1234-1234-bbbb-1234567890ab"); + b.set(attr, a.get(attr).toUpperCase(Locale.ROOT)); + } + } + + switch (attr) { + case PATH, REGKEY, UPGRADE_CODE -> { + assertEquals(a.toGuid(), b.toGuid()); + } + default -> { + assertNotEquals(a.toGuid(), b.toGuid()); + } + } + } + + @ParameterizedTest + @MethodSource + void test_toGuid(ToGuidTestSpec testCase) { + testCase.test(); + } + + record ToGuidTestSpec(Map input, Optional expected) { + + void test() { + + var key = new WixComponentKey(); + + for (var e : input.entrySet()) { + key.attrs()[e.getKey().ordinal()] = e.getValue(); + } + + expected.ifPresentOrElse(v -> { + assertEquals(v, key.toGuid()); + }, () -> { + assertThrowsExactly(IllegalStateException.class, key::toGuid); + }); + } + + @Override + public String toString() { + + var sb = new StringBuilder(); + + sb.append(input.toString()); + expected.ifPresent(v -> { + sb.append('=').append(v); + }); + + return sb.toString(); + } + + static final class Builder { + + Builder path(String v) { + return set(WixComponentKey.Attribute.PATH, v); + } + + Builder upgradeCode(String v) { + return set(WixComponentKey.Attribute.UPGRADE_CODE, v); + } + + Builder upgradeCode() { + return upgradeCode(UUID.fromString("9c8d7332-41a6-42d9-8e3d-56f41e8d7568").toString()); + } + + Builder regkey(String v) { + return set(WixComponentKey.Attribute.REGKEY, v); + } + + Builder role(String v) { + return set(WixComponentKey.Attribute.ROLE, v); + } + + ToGuidTestSpec create() { + + var keyStr = new StringBuilder(); + + Map input = new TreeMap<>(); + for (var attr : WixComponentKey.Attribute.values()) { + var attrValue = key.get(attr); + if (attrValue == null) { + continue; + } + + input.put(attr, attrValue); + + if (!keyStr.isEmpty()) { + keyStr.append(';'); + } + keyStr.append(attr.name()).append('='); + + switch (attr) { + case PATH, REGKEY -> keyStr.append(attrValue.toLowerCase(Locale.ROOT)); + default -> keyStr.append(attrValue); + } + } + + Optional guid = Optional.empty(); + + if (input.containsKey(WixComponentKey.Attribute.PATH) && input.containsKey(WixComponentKey.Attribute.UPGRADE_CODE)) { + do { + var upgradeCode = input.get(WixComponentKey.Attribute.UPGRADE_CODE); + try { + UUID.fromString(upgradeCode); + } catch (IllegalArgumentException _) { + break; + } + guid = Optional.of(UUID.nameUUIDFromBytes(keyStr.toString().getBytes(StandardCharsets.UTF_8))); + } while (false); + } + + return new ToGuidTestSpec(input, guid); + } + + private Builder set(WixComponentKey.Attribute attr, String v) { + key.set(attr, v); + return this; + } + + private final WixComponentKey key = new WixComponentKey(); + } + } + + private static Stream test_toGuid() { + + Supplier builder = ToGuidTestSpec.Builder::new; + + Collection testCases = new ArrayList<>(); + + testCases.addAll(List.of( + builder.get().path("C:/foo").upgradeCode(), + builder.get().path("C:/foo").upgradeCode("blah-blah-blah"), + builder.get().path("C:/foo").upgradeCode().regkey("*").role("role"), + builder.get().path("C:/foo").role("bar"), + builder.get().upgradeCode().role("bar") + )); + + return testCases.stream().map(ToGuidTestSpec.Builder::create); + } + + private static void testAttr( + WixComponentKey.Attribute attr, + String attrValue, + BiFunction attrSetter, + Function conv) { + + var key = new WixComponentKey(); + + Optional typedAttrValue = Optional.ofNullable(attrValue).map(conv); + + typedAttrValue.ifPresentOrElse(v -> { + assertSame(key, attrSetter.apply(key, v)); + }, () -> { + assertThrowsExactly(NullPointerException.class, () -> { + attrSetter.apply(key, null); + }); + }); + + for (var curAttr : WixComponentKey.Attribute.values()) { + if (curAttr == attr) { + assertEquals(typedAttrValue.map(Object::toString).orElse(null), key.get(curAttr)); + } else { + assertEquals(null, key.get(curAttr)); + } + } + } +} diff --git a/test/jdk/tools/jpackage/junit/windows/junit.java b/test/jdk/tools/jpackage/junit/windows/junit.java index c046589a3632..bc4a0137875e 100644 --- a/test/jdk/tools/jpackage/junit/windows/junit.java +++ b/test/jdk/tools/jpackage/junit/windows/junit.java @@ -65,3 +65,11 @@ * jdk/jpackage/internal/RtfConverterTest.java * @run junit jdk.jpackage/jdk.jpackage.internal.RtfConverterTest */ + +/* @test + * @summary WixComponentKeyTest unit tests + * @requires (os.family == "windows") + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/WixComponentKeyTest.java + * @run junit jdk.jpackage/jdk.jpackage.internal.WixComponentKeyTest + */ diff --git a/test/jdk/tools/jpackage/windows/WinComponentIDsTest.java b/test/jdk/tools/jpackage/windows/WinComponentIDsTest.java new file mode 100644 index 000000000000..723f6165558c --- /dev/null +++ b/test/jdk/tools/jpackage/windows/WinComponentIDsTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import jdk.jpackage.test.Comm; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.RunnablePackageTest.Action; +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.WindowsHelper; +import jdk.jpackage.test.Annotations.ParameterSupplier; + +/** + * Test that MSI component IDs for the same path differ between packages with + * different upgrade codes, but match between packages with the same upgrade code. + */ + +/* + * @test + * @bug 8394606 + * @summary Test case for JDK-8394606 + * @library /test/jdk/tools/jpackage/helpers + * @build jdk.jpackage.test.* + * @build WinComponentIDsTest + * @requires (os.family == "windows") + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=WinComponentIDsTest + */ +public class WinComponentIDsTest { + + @Test + @ParameterSupplier + public void test(Optional upgradeCodeA, Optional upgradeCodeB) { + new PackageTest() + .forTypes(PackageType.WIN_MSI) + .configureHelloApp() + .addInitializer(JPackageCommand::setFakeRuntime) + .mutate(test -> { + upgradeCodeA.ifPresent(v -> { + test.addInitializer(cmd -> { + cmd.setArgumentValue("--win-upgrade-uuid", v.toString()); + }); + }); + }) + .addBundleVerifier(cmdA -> { + var cmdB = new JPackageCommand(); + cmdB.clearArguments().addArguments(cmdA.getAllArguments()); + upgradeCodeB.ifPresentOrElse(v -> { + cmdB.setArgumentValue("--win-upgrade-uuid", v.toString()); + }, () -> { + cmdB.removeArgumentWithValue("--win-upgrade-uuid"); + }); + + cmdB.setArgumentValue("--dest", TKit.createTempDirectory("output-b")).execute(); + + final var comm = Comm.compare(msiComponentIds(cmdA), msiComponentIds(cmdB)); + if (upgradeCodeA.equals(upgradeCodeB)) { + TKit.assertTrue(comm.uniqueEmpty(), "Check component IDs are the same in both MSI-s"); + } else { + TKit.assertEquals(0, comm.common().size(), "Check component IDs are unique accross MSI-s"); + } + + }).run(Action.CREATE); + } + + public static Collection test() { + return Stream.of( + List.of(uuid(null), uuid(null)), + List.of(uuid("9c8d7332-41a6-42d9-8e3d-56f41e8d7568"), uuid("9c8d7332-41a6-42d9-8e3d-56f41e8d7568")), + List.of(uuid("9c8d7332-41a6-42d9-8e3d-56f41e8d7568"), uuid(null)), + List.of(uuid("9c8d7332-41a6-42d9-8e3d-56f41e8d7568"), uuid("f37c8b4d-2c8b-4fb0-9f45-0f9b6f0c7b2e")) + ).map(List::toArray).toList(); + } + + private static Optional uuid(String v) { + return Optional.ofNullable(v).map(UUID::fromString); + } + + private static Set msiComponentIds(JPackageCommand cmd) { + // Use Collectors.toMap() to validate the uniqueness of component IDs. + return WindowsHelper.getMsiComponentIds(cmd).stream().collect(Collectors.toMap(x -> x, x -> x)).keySet(); + } +} diff --git a/test/langtools/jdk/javadoc/doclet/testDocFiles/TestDocFiles.java b/test/langtools/jdk/javadoc/doclet/testDocFiles/TestDocFiles.java index 4c5e27cb86bb..7dcdb70b6cc3 100644 --- a/test/langtools/jdk/javadoc/doclet/testDocFiles/TestDocFiles.java +++ b/test/langtools/jdk/javadoc/doclet/testDocFiles/TestDocFiles.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8008949 8234051 8245696 + * @bug 8008949 8234051 8245696 8234051 * @summary doclet crashes if HTML files in module doc-files directories * @library /tools/lib ../../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool @@ -175,4 +175,36 @@ public void testBadFiles(Path base) throws IOException { } } } + + @Test + public void testUppercaseExtension(Path base) throws IOException { + Path src = base.resolve("src"); + + tb.writeJavaFiles(src, + "package p; public class C { }"); + Path df = src.resolve("p").resolve("doc-files"); + + // Files with upper-case extension should not be recognized as HTML or Markdown doc files. + tb.writeFile(df.resolve("README.MD"), """ + README + ====== + This is *not* recognized as Markdown file by javadoc. + """); + tb.writeFile(df.resolve("INDEX.HTML"), """ + This is not recognized as HTML doc file by {@code javadoc}. + """); + + javadoc("-d", base.resolve("out").toString(), + "--source-path", src.toString(), + "p"); + checkExit(Exit.OK); + + // Files should be copied without changes. + checkFiles(false, "p/doc-files/README.html"); + checkOutput("p/doc-files/README.MD", true, "README", "======", + "This is *not* recognized as Markdown file by javadoc."); + checkOutput("p/doc-files/INDEX.HTML", true, + "This is not recognized as HTML doc file by {@code javadoc}."); + } + } diff --git a/test/langtools/tools/javac/conditional/8394468/T8394468.java b/test/langtools/tools/javac/conditional/8394468/T8394468.java new file mode 100644 index 000000000000..967e2447aed2 --- /dev/null +++ b/test/langtools/tools/javac/conditional/8394468/T8394468.java @@ -0,0 +1,42 @@ +/* + * 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 8394468 + * @summary javac crash on constant conditional with null operand + * @compile T8394468.java + * @run main T8394468 + */ + +public class T8394468 { + public static void main(String[] args) { + try { + int dummy = (true ? null : "abc").length(); + // if we got here, then there must be a problem + throw new RuntimeException("NullPointerException expected"); + } catch (NullPointerException e) { + // expected + } + } +} diff --git a/test/langtools/tools/javac/unicode/UnicodeSurrogateBackslash.java b/test/langtools/tools/javac/unicode/UnicodeSurrogateBackslash.java new file mode 100644 index 000000000000..fac37828023b --- /dev/null +++ b/test/langtools/tools/javac/unicode/UnicodeSurrogateBackslash.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8394548 + * @summary Incomplete Unicode escape surrogate pair causes incorrect backslash handling + * @compile UnicodeSurrogateBackslash.java + * @run main UnicodeSurrogateBackslash + */ + +import java.util.Arrays; + +public class UnicodeSurrogateBackslash { + + record TestCase(String string, int value1, int value2) { + void check() { + final String expected = String.format("%c\\u%04x", (char)value1, value2); + if (!string.equals(expected)) + throw new AssertionError(String.format("%s: expected \"%s\"", this, expected)); + } + + @Override + public String toString() { + return String.format("TestCase[string=\"%s\",value1=0x%04x,value2=0x%04x]", string, value1, value2); + } + } + + // All of these Strings should compile as: Escaped Unicode character, backslash character, then "uXXXX" (for some X's). + // The value of the first escaped Unicode character should have no effect on how the rest of the string is parsed. + public static final TestCase[] TEST_CASES = new TestCase[] { + + // These are the test cases from the original bug report + new TestCase("\u1234\\u1234", 0x1234, 0x1234), + new TestCase("\ud800\\u1234", 0xd800, 0x1234), + + // Some other test cases + new TestCase("\u0000\\u0000", 0x0000, 0x0000), + new TestCase("\ud7ff\\u0000", 0xd7ff, 0x0000), + new TestCase("\uaaaa\\ud7ff", 0xaaaa, 0xd7ff), + new TestCase("\ud8ff\\ud8ff", 0xd8ff, 0xd8ff), + new TestCase("\udbff\\u1234", 0xdbff, 0x1234), + new TestCase("\udc01\\u1234", 0xdc01, 0x1234), + new TestCase("\ud888\\udccc", 0xd888, 0xdccc), + new TestCase("\ue123\\u1234", 0xe123, 0x1234), + new TestCase("\ueeee\\ueabc", 0xeeee, 0xeabc) + }; + + public static void main(String[] args) { + Arrays.asList(TEST_CASES).forEach(TestCase::check); + } +} diff --git a/test/langtools/tools/javac/valhalla/value-objects/ValueClassEnclosingInstance.java b/test/langtools/tools/javac/valhalla/value-objects/ValueClassEnclosingInstance.java new file mode 100644 index 000000000000..8c3558848392 --- /dev/null +++ b/test/langtools/tools/javac/valhalla/value-objects/ValueClassEnclosingInstance.java @@ -0,0 +1,57 @@ +/* + * @test + * @bug 8394667 + * @summary Omitting enclosing instance fields in value classes can provoke undesirable effects + * @enablePreview + */ + +public class ValueClassEnclosingInstance { + value class MemberValue { + int value; + + MemberValue(int value) { + this.value = value; + super(); + } + } + + Object newLocalValue(int value) { + value class LocalValue { + int value; + + LocalValue(int value) { + this.value = value; + super(); + } + } + return new LocalValue(value); + } + + public static void main(String[] args) { + var firstOuter = new ValueClassEnclosingInstance(); + var secondOuter = new ValueClassEnclosingInstance(); + + checkEquality("member class, different enclosing instances", + firstOuter.new MemberValue(-1), secondOuter.new MemberValue(-1), false); + checkEquality("member class, same enclosing instance and field values", + firstOuter.new MemberValue(-1), firstOuter.new MemberValue(-1), true); + checkEquality("member class, different field values", + firstOuter.new MemberValue(-1), firstOuter.new MemberValue(0), false); + + checkEquality("local class, different enclosing instances", + firstOuter.newLocalValue(-1), secondOuter.newLocalValue(-1), false); + checkEquality("local class, same enclosing instance and field values", + firstOuter.newLocalValue(-1), firstOuter.newLocalValue(-1), true); + checkEquality("local class, different field values", + firstOuter.newLocalValue(-1), firstOuter.newLocalValue(0), false); + } + + static void checkEquality(String description, Object first, Object second, boolean expected) { + if ((first == second) != expected) { + throw new AssertionError(description + ": expected == to return " + expected); + } + if (first.equals(second) != expected) { + throw new AssertionError(description + ": expected equals to return " + expected); + } + } +} diff --git a/test/langtools/tools/javap/value_classes/ValueClassesJavapTest.java b/test/langtools/tools/javap/value_classes/ValueClassesJavapTest.java index 54a90430573c..d71db213b66e 100644 --- a/test/langtools/tools/javap/value_classes/ValueClassesJavapTest.java +++ b/test/langtools/tools/javap/value_classes/ValueClassesJavapTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8335770 8375740 + * @bug 8335770 8375740 8394667 * @summary improve javap code coverage for value classes * @enablePreview * @library /tools/lib @@ -55,11 +55,13 @@ void m(ValueClass param) {} private static final List expectedValueClassOutput = List.of( "Compiled from \"ValueClassesJavapTest.java\"", "final value class ValueClassesJavapTest$ValueClass {", + " final ValueClassesJavapTest this$0;", " ValueClassesJavapTest$ValueClass(ValueClassesJavapTest);", "}"); private static final List expectedAbstractValueClassOutput = List.of( "Compiled from \"ValueClassesJavapTest.java\"", "abstract value class ValueClassesJavapTest$AbstractValueClass {", + " final ValueClassesJavapTest this$0;", " ValueClassesJavapTest$AbstractValueClass(ValueClassesJavapTest);", "}"); private static final List expectedLoadableDescriptorOutput = List.of( diff --git a/test/lib/jdk/test/lib/cds/CDSJarUtils.java b/test/lib/jdk/test/lib/cds/CDSJarUtils.java index a7ae2b460e51..5fd304dca617 100644 --- a/test/lib/jdk/test/lib/cds/CDSJarUtils.java +++ b/test/lib/jdk/test/lib/cds/CDSJarUtils.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,11 +31,16 @@ import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.util.FileUtils; +import java.io.ByteArrayInputStream; import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.jar.Attributes; +import java.util.jar.Manifest; import java.util.spi.ToolProvider; public class CDSJarUtils { @@ -159,11 +164,14 @@ private static void addClassArgs(ArrayList args, String jarclassDir, */ public static class JarOptions { private String [] options; + private String manifestFile; + private final Attributes extraManifestAttributes = new Attributes(); private JarOptions() {} public static JarOptions of(String... options) { JarOptions jo = new JarOptions(); jo.options = options; + jo.manifestFile = null; return jo; } @@ -173,12 +181,50 @@ public JarOptions setMainClass(String mainClass) { } return this; } - public JarOptions setManifest(String manifest) { - if (manifest != null) { - options = StringArrayUtils.concat(options, "--manifest=" + manifest); - } + public JarOptions setManifest(String manifestFile) { + this.manifestFile = manifestFile; + return this; + } + + public JarOptions setAutomaticModule(String moduleName) { + extraManifestAttributes.putValue("Automatic-Module-Name", moduleName); return this; } + + private String mergeManifestHelper() throws IOException { + Manifest manifest = new Manifest(); + if (manifestFile != null) { + String content = Files.readString(Path.of(manifestFile)) + "\n"; + manifest.read(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))); + } + Attributes attributes = manifest.getMainAttributes(); + attributes.putIfAbsent(Attributes.Name.MANIFEST_VERSION, "1.0"); + attributes.putAll(extraManifestAttributes); + + Path tempPath = Files.createTempFile("jar", ".manifest"); + try (var out = Files.newOutputStream(tempPath)) { + manifest.write(out); + } + return tempPath.toAbsolutePath().toString(); + } + + private String mergeManifest() { + try { + return mergeManifestHelper(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public String [] getOptions() { + if (!extraManifestAttributes.isEmpty()) { + return StringArrayUtils.concat(options, "--manifest=" + mergeManifest()); + } else if (manifestFile != null) { + return StringArrayUtils.concat(options, "--manifest=" + manifestFile); + } else { + return options; + } + } } public static void buildFromDirectory(String jarPath, @@ -193,7 +239,7 @@ public static void buildFromDirectory(String jarPath, argList.add("--create"); argList.add("--file=" + jarPath); if (jarOptions != null) { - for (String s : jarOptions.options) { + for (String s : jarOptions.getOptions()) { argList.add(s); } } diff --git a/test/lib/jdk/test/lib/cds/CDSModulePackager.java b/test/lib/jdk/test/lib/cds/CDSModulePackager.java index c825ea9ec18c..7efe3c87b8b3 100644 --- a/test/lib/jdk/test/lib/cds/CDSModulePackager.java +++ b/test/lib/jdk/test/lib/cds/CDSModulePackager.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 @@ -161,4 +161,44 @@ public Path createModularJarWithMainClass(String moduleName, String mainClass, J return jarFile; } + + public Path createAutomaticModularJar(String moduleName) throws Exception { + return createAutomaticModularJarWithMainClass(moduleName, null, (JarOptions)null); + } + + public Path createAutomaticModularJar(String moduleName, String... javacOptions) throws Exception { + return createAutomaticModularJarWithMainClass(moduleName, null, (JarOptions)null, javacOptions); + } + + + public Path createAutomaticModularJarWithMainClass(String moduleName, String mainClass, String... javacOptions) throws Exception { + return createAutomaticModularJarWithMainClass(moduleName, mainClass, (JarOptions)null, javacOptions); + } + + // Compile all files under ${this.srcRoot}/${moduleName}. + public Path createAutomaticModularJarWithMainClass(String moduleName, + String mainClass, + JarOptions jarOptions, + String... javacOptions) throws Exception { + Path src = srcRoot.resolve(moduleName); + + if (javacOptions == null) { + javacOptions = new String[] {}; + } + + if (jarOptions == null) { + jarOptions = JarOptions.of(); + } + + if (mainClass != null) { + jarOptions.setMainClass(mainClass); + } + + jarOptions.setAutomaticModule(moduleName); + + Path jarFile = outputDir.resolve(moduleName + ".jar"); + CDSJarUtils.buildFromSourceDirectory(jarFile.toString(), src.toString(), jarOptions, javacOptions); + + return jarFile; + } } diff --git a/test/lib/jdk/test/lib/security/OpensslArtifactFetcher.java b/test/lib/jdk/test/lib/security/OpensslArtifactFetcher.java index f66ad542e473..e89626d86f67 100644 --- a/test/lib/jdk/test/lib/security/OpensslArtifactFetcher.java +++ b/test/lib/jdk/test/lib/security/OpensslArtifactFetcher.java @@ -24,7 +24,9 @@ package jdk.test.lib.security; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; +import java.util.List; import jdk.test.lib.Platform; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.artifacts.Artifact; @@ -130,12 +132,50 @@ private static String fetchOpenssl(Class clazz) { // retrieve the provider directory path from /bin/openssl public static Path getProviderPath(String opensslPath) { - Path openSslRootPath = Path.of(opensslPath).getParent().getParent(); - String libDir = "lib"; - if (Platform.isX64() && (Platform.isLinux() || Platform.isWindows())) { - libDir = "lib64"; + Path modules = null; + + // Prefer the modules dir next to the binary: the downloaded bundle is + // relocatable, so "info -modulesdir" reports a stale build-time path. + Path bin = Path.of(opensslPath).getParent(); + if (bin != null && bin.getParent() != null) { + Path root = bin.getParent(); + for (String lib : List.of("lib", "lib64")) { + Path candidate = root.resolve(lib).resolve("ossl-modules"); + if (Files.isDirectory(candidate)) { + modules = candidate; + break; + } + } + } + + // System installs (e.g. distro multiarch) don't keep modules next to + // the binary; fall back to "openssl info -modulesdir". + if (modules == null) { + try { + String dir = ProcessTools.executeCommand(opensslPath, "info", "-modulesdir") + .shouldHaveExitValue(0) + .getStdout().trim(); + modules = Path.of(dir); + } catch (Exception e) { + throw new SkippedException("Could not determine OpenSSL modules directory", e); + } } - return openSslRootPath.resolve(libDir, "ossl-modules"); + + // This test uses -legacy (RC4/SHA1 PBE). Skip if the legacy provider + // module is missing. Check the module file rather than + // "openssl list -providers", which only lists loaded providers. + if (!hasLegacyProvider(modules)) { + throw new SkippedException("OpenSSL legacy provider not available in " + modules); + } + + return modules; + } + + // Provider module file name differs per platform. + private static boolean hasLegacyProvider(Path modulesDir) { + return Files.isRegularFile(modulesDir.resolve("legacy.so")) + || Files.isRegularFile(modulesDir.resolve("legacy.dylib")) + || Files.isRegularFile(modulesDir.resolve("legacy.dll")); } public static String getTestOpensslBundleVersion() { diff --git a/test/micro/org/openjdk/bench/java/lang/ThreadStartWithIdleThreads.java b/test/micro/org/openjdk/bench/java/lang/ThreadStartWithIdleThreads.java new file mode 100644 index 000000000000..9faa9b8ffe33 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/ThreadStartWithIdleThreads.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.lang; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.LockSupport; + +// This benchmark is used as an easy reproducer of JDK-8307970, aka +// "Thread.start is slow with large number of threads". Use the +// startOnly parameter to measure only the thread start, or to measure +// the full create, start and join cycle. + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Benchmark) +@Threads(1) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(value = 3) +public class ThreadStartWithIdleThreads { + + @Param({"0", "1000", "10000", "30000"}) + private int idleThreads; + + @Param({"true", "false"}) + private boolean startOnly; + + private volatile boolean done; + private Thread[] ts; + private Thread startOnlyThread; + + @Setup + public void setup() throws InterruptedException { + ts = new Thread[idleThreads]; + var started = new CountDownLatch(idleThreads); + done = false; + for (int i = 0; i < idleThreads; i++) { + ts[i] = new Thread(() -> { + started.countDown(); + while (!done) { + LockSupport.park(); + } + }); + ts[i].start(); + } + started.await(); + } + + @Setup(Level.Invocation) + public void setupLevel() throws InterruptedException { + if (startOnly) { + startOnlyThread = new Thread(); + } + } + + @TearDown + public final void cleanup() throws InterruptedException { + done = true; + if (ts != null) { + for (Thread t : ts) { + if (t != null) { + LockSupport.unpark(t); + } + } + for (Thread t : ts) { + if (t != null) { + t.join(); + } + } + ts = null; + } + } + + @TearDown(Level.Invocation) + public final void cleanupLevel() throws InterruptedException { + if (startOnly) { + startOnlyThread.join(); + } + } + + @Benchmark + public void threadStartWithIdleThreads() throws Exception { + if (startOnly) { + startOnlyThread.start(); + } else { + Thread t = new Thread(); + t.start(); + t.join(); + } + } +}