diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 48ed893f2a6f..80220366bf4e 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -3339,28 +3339,28 @@ encode %{ // aarch64_enc_cmpxchg_acq is that we use load-acquire in the // CompareAndSwap sequence to serve as a barrier on acquiring a // lock. - enc_class aarch64_enc_cmpxchg_acq(memory mem, iRegLNoSp oldval, iRegLNoSp newval) %{ + enc_class aarch64_enc_cmpxchg_acq(memory mem, iRegL oldval, iRegL newval) %{ guarantee($mem$$index == -1 && $mem$$disp == 0, "impossible encoding"); __ cmpxchg($mem$$base$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, /*acquire*/ true, /*release*/ true, /*weak*/ false, noreg); %} - enc_class aarch64_enc_cmpxchgw_acq(memory mem, iRegINoSp oldval, iRegINoSp newval) %{ + enc_class aarch64_enc_cmpxchgw_acq(memory mem, iRegI oldval, iRegI newval) %{ guarantee($mem$$index == -1 && $mem$$disp == 0, "impossible encoding"); __ cmpxchg($mem$$base$$Register, $oldval$$Register, $newval$$Register, Assembler::word, /*acquire*/ true, /*release*/ true, /*weak*/ false, noreg); %} - enc_class aarch64_enc_cmpxchgs_acq(memory mem, iRegINoSp oldval, iRegINoSp newval) %{ + enc_class aarch64_enc_cmpxchgs_acq(memory mem, iRegI oldval, iRegI newval) %{ guarantee($mem$$index == -1 && $mem$$disp == 0, "impossible encoding"); __ cmpxchg($mem$$base$$Register, $oldval$$Register, $newval$$Register, Assembler::halfword, /*acquire*/ true, /*release*/ true, /*weak*/ false, noreg); %} - enc_class aarch64_enc_cmpxchgb_acq(memory mem, iRegINoSp oldval, iRegINoSp newval) %{ + enc_class aarch64_enc_cmpxchgb_acq(memory mem, iRegI oldval, iRegI newval) %{ guarantee($mem$$index == -1 && $mem$$disp == 0, "impossible encoding"); __ cmpxchg($mem$$base$$Register, $oldval$$Register, $newval$$Register, Assembler::byte, /*acquire*/ true, /*release*/ true, @@ -3368,7 +3368,7 @@ encode %{ %} // auxiliary used for CompareAndSwapX to set result register - enc_class aarch64_enc_cset_eq(iRegINoSp res) %{ + enc_class aarch64_enc_cset_eq(iRegI res) %{ Register res_reg = as_Register($res$$reg); __ cset(res_reg, Assembler::EQ); %} @@ -8267,7 +8267,7 @@ instruct castVVMask(pRegGov dst) // XXX No flag versions for CompareAndSwap{I,L,P,N} because matcher // can't match them -instruct compareAndSwapB(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoSp newval, rFlagsReg cr) %{ +instruct compareAndSwapB(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval, rFlagsReg cr) %{ match(Set res (CompareAndSwapB mem (Binary oldval newval))); ins_cost(2 * VOLATILE_REF_COST); @@ -8285,7 +8285,7 @@ instruct compareAndSwapB(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoS ins_pipe(pipe_slow); %} -instruct compareAndSwapS(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoSp newval, rFlagsReg cr) %{ +instruct compareAndSwapS(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval, rFlagsReg cr) %{ match(Set res (CompareAndSwapS mem (Binary oldval newval))); ins_cost(2 * VOLATILE_REF_COST); @@ -8303,7 +8303,7 @@ instruct compareAndSwapS(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoS ins_pipe(pipe_slow); %} -instruct compareAndSwapI(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoSp newval, rFlagsReg cr) %{ +instruct compareAndSwapI(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval, rFlagsReg cr) %{ match(Set res (CompareAndSwapI mem (Binary oldval newval))); ins_cost(2 * VOLATILE_REF_COST); @@ -8321,7 +8321,7 @@ instruct compareAndSwapI(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoS ins_pipe(pipe_slow); %} -instruct compareAndSwapL(iRegINoSp res, indirect mem, iRegLNoSp oldval, iRegLNoSp newval, rFlagsReg cr) %{ +instruct compareAndSwapL(iRegINoSp res, indirect mem, iRegL oldval, iRegL newval, rFlagsReg cr) %{ match(Set res (CompareAndSwapL mem (Binary oldval newval))); ins_cost(2 * VOLATILE_REF_COST); @@ -8358,7 +8358,7 @@ instruct compareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval ins_pipe(pipe_slow); %} -instruct compareAndSwapN(iRegINoSp res, indirect mem, iRegNNoSp oldval, iRegNNoSp newval, rFlagsReg cr) %{ +instruct compareAndSwapN(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval, rFlagsReg cr) %{ match(Set res (CompareAndSwapN mem (Binary oldval newval))); predicate(n->as_LoadStore()->barrier_data() == 0); @@ -8379,7 +8379,7 @@ instruct compareAndSwapN(iRegINoSp res, indirect mem, iRegNNoSp oldval, iRegNNoS // alternative CompareAndSwapX when we are eliding barriers -instruct compareAndSwapBAcq(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoSp newval, rFlagsReg cr) %{ +instruct compareAndSwapBAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval, rFlagsReg cr) %{ predicate(needs_acquiring_load_exclusive(n)); match(Set res (CompareAndSwapB mem (Binary oldval newval))); @@ -8398,7 +8398,7 @@ instruct compareAndSwapBAcq(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegI ins_pipe(pipe_slow); %} -instruct compareAndSwapSAcq(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoSp newval, rFlagsReg cr) %{ +instruct compareAndSwapSAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval, rFlagsReg cr) %{ predicate(needs_acquiring_load_exclusive(n)); match(Set res (CompareAndSwapS mem (Binary oldval newval))); @@ -8417,7 +8417,7 @@ instruct compareAndSwapSAcq(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegI ins_pipe(pipe_slow); %} -instruct compareAndSwapIAcq(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoSp newval, rFlagsReg cr) %{ +instruct compareAndSwapIAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval, rFlagsReg cr) %{ predicate(needs_acquiring_load_exclusive(n)); match(Set res (CompareAndSwapI mem (Binary oldval newval))); @@ -8436,7 +8436,7 @@ instruct compareAndSwapIAcq(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegI ins_pipe(pipe_slow); %} -instruct compareAndSwapLAcq(iRegINoSp res, indirect mem, iRegLNoSp oldval, iRegLNoSp newval, rFlagsReg cr) %{ +instruct compareAndSwapLAcq(iRegINoSp res, indirect mem, iRegL oldval, iRegL newval, rFlagsReg cr) %{ predicate(needs_acquiring_load_exclusive(n)); match(Set res (CompareAndSwapL mem (Binary oldval newval))); @@ -8474,7 +8474,7 @@ instruct compareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP new ins_pipe(pipe_slow); %} -instruct compareAndSwapNAcq(iRegINoSp res, indirect mem, iRegNNoSp oldval, iRegNNoSp newval, rFlagsReg cr) %{ +instruct compareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval, rFlagsReg cr) %{ predicate(needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() == 0); match(Set res (CompareAndSwapN mem (Binary oldval newval))); diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp index f4fd8ad0dca5..5c8ca166ecb6 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp @@ -56,6 +56,8 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, Dec __ cbz(count, done); // Is GC active? + assert(!saved_regs.contains(rscratch1), "Sanity: about to clobber rscratch1"); + assert(!saved_regs.contains(rscratch2), "Sanity: about to clobber rscratch2"); Address gc_state(rthread, in_bytes(ShenandoahThreadLocalData::gc_state_offset())); __ ldrb(rscratch1, gc_state); if (ShenandoahSATBBarrier && dest_uninitialized) { @@ -66,13 +68,26 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, Dec __ br(Assembler::EQ, done); } - __ push(saved_regs, sp); + __ push_call_clobbered_registers(); + // If arguments are not in proper places, shuffle them. + // Doing this via the stack is the most straight-forward way to avoid + // accidentally smashing any register. + if (c_rarg0 != src || c_rarg1 != dst || c_rarg2 != count) { + __ push(RegSet::of(src), sp); + __ push(RegSet::of(dst), sp); + __ push(RegSet::of(count), sp); + __ pop(RegSet::of(c_rarg2), sp); + __ pop(RegSet::of(c_rarg1), sp); + __ pop(RegSet::of(c_rarg0), sp); + } + address target = nullptr; if (UseCompressedOops) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_narrow_oop), src, dst, count); + target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_narrow_oop); } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_oop), src, dst, count); + target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_oop); } - __ pop(saved_regs, sp); + __ call_VM_leaf(target, 3); + __ pop_call_clobbered_registers(); __ bind(done); } } @@ -90,15 +105,8 @@ void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm, Register pre_val, Register thread, Register tmp1, - Register tmp2, - bool tosca_live, - bool expand_call) { + Register tmp2) { assert(ShenandoahSATBBarrier, "Should be checked by caller"); - - // If expand_call is true then we expand the call_VM_leaf macro - // directly to skip generating the check by - // InterpreterMacroAssembler::call_VM_leaf_base that checks _last_sp. - assert(thread == rthread, "must be"); Label done; @@ -117,7 +125,12 @@ void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm, // Do we need to load the previous value? if (obj != noreg) { - __ load_heap_oop(pre_val, Address(obj, 0), noreg, noreg, AS_RAW); + if (UseCompressedOops) { + __ ldrw(pre_val, Address(obj, 0)); + __ decode_heap_oop(pre_val); + } else { + __ ldr(pre_val, Address(obj, 0)); + } } // Is the previous value null? @@ -141,33 +154,17 @@ void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm, __ b(done); __ bind(runtime); - // save the live input values - RegSet saved = RegSet::of(pre_val); - if (tosca_live) saved += RegSet::of(r0); - if (obj != noreg) saved += RegSet::of(obj); - __ push(saved, sp); - - // Calling the runtime using the regular call_VM_leaf mechanism generates - // code (generated by InterpreterMacroAssember::call_VM_leaf_base) - // that checks that the *(rfp+frame::interpreter_frame_last_sp) == nullptr. - // - // If we care generating the pre-barrier without a frame (e.g. in the - // intrinsified Reference.get() routine) then rfp might be pointing to - // the caller frame and so this check will most likely fail at runtime. - // - // Expanding the call directly bypasses the generation of the check. - // So when we do not have have a full interpreter frame on the stack - // expand_call should be passed true. - - if (expand_call) { - assert(pre_val != c_rarg1, "smashed arg"); - __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), pre_val); - } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), pre_val); + // Slow-path call + __ enter(/* strip_ret_addr = */ true); + __ push_call_clobbered_registers(); + if (c_rarg0 != pre_val) { + __ mov(c_rarg0, pre_val); } - - __ pop(saved, sp); + // Calling with super_call_VM_leaf with c_rarg0 bypasses interpreter checks and avoids any moves. + __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), c_rarg0); + __ pop_call_clobbered_registers(); + __ leave(); __ bind(done); } @@ -231,7 +228,6 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, bool is_narrow = UseCompressedOops && !is_native; Label heap_stable, not_cset; - __ enter(/*strip_ret_addr*/true); Address gc_state(rthread, in_bytes(ShenandoahThreadLocalData::gc_state_offset())); __ ldrb(rscratch2, gc_state); @@ -266,29 +262,33 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, __ tbz(rscratch2, 0, not_cset); } + // Slow-path call + __ enter(/* strip_ret_addr = */ true); __ push_call_clobbered_registers(); + address target = nullptr; if (is_strong) { if (is_narrow) { - __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow)); + target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow); } else { - __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong)); + target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong); } } else if (is_weak) { if (is_narrow) { - __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow)); + target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow); } else { - __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak)); + target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak); } } else { assert(is_phantom, "only remaining strength"); assert(!is_narrow, "phantom access cannot be narrow"); - // AOT saved adapters need relocation for this call. - __ lea(lr, RuntimeAddress(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom))); + target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom); } - __ blr(lr); + // Calling with super_call_VM_leaf with c_rarg0/1 bypasses interpreter checks and avoids any moves. + __ super_call_VM_leaf(target, c_rarg0, c_rarg1); __ mov(rscratch1, r0); __ pop_call_clobbered_registers(); __ mov(r0, rscratch1); + __ leave(); __ bind(not_cset); @@ -296,7 +296,6 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, __ pop(to_save, sp); __ bind(heap_stable); - __ leave(); } // @@ -346,18 +345,12 @@ void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet d // 3: apply keep-alive barrier if needed if (ShenandoahBarrierSet::need_keep_alive_barrier(decorators, type)) { - __ enter(/*strip_ret_addr*/true); - __ push_call_clobbered_registers(); satb_barrier(masm /* masm */, noreg /* obj */, dst /* pre_val */, rthread /* thread */, tmp1 /* tmp1 */, - tmp2 /* tmp2 */, - true /* tosca_live */, - true /* expand_call */); - __ pop_call_clobbered_registers(); - __ leave(); + tmp2 /* tmp2 */); } } @@ -399,8 +392,6 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet __ lea(tmp3, dst); } - bool storing_non_null = (val != noreg); - // 2: pre-barrier: SATB needs the previous value if (ShenandoahBarrierSet::need_satb_barrier(decorators, type)) { satb_barrier(masm, @@ -408,15 +399,14 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet tmp2 /* pre_val */, rthread /* thread */, tmp1 /* tmp */, - rscratch1 /* tmp2 */, - storing_non_null /* tosca_live */, - false /* expand_call */); + rscratch1 /* tmp2 */); } // Store! BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), val, noreg, noreg, noreg); // 3: post-barrier: card barrier needs store address + bool storing_non_null = (val != noreg); if (ShenandoahBarrierSet::need_card_barrier(decorators, type) && storing_non_null) { card_barrier(masm, tmp3); } diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp index ae607d5c63c9..461e74e2336d 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp @@ -45,9 +45,7 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { Register pre_val, Register thread, Register tmp1, - Register tmp2, - bool tosca_live, - bool expand_call); + Register tmp2); void card_barrier(MacroAssembler* masm, Register obj); diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp index 3b5ecd60cabe..ccd5a6065349 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp @@ -59,6 +59,8 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, Dec Address gc_state(xthread, in_bytes(ShenandoahThreadLocalData::gc_state_offset())); assert_different_registers(src, dst, count, t0); + assert(!saved_regs.contains(t0), "Sanity: about to clobber t0"); + __ lbu(t0, gc_state); if (ShenandoahSATBBarrier && dest_uninitialized) { __ test_bit(t0, t0, ShenandoahHeap::HAS_FORWARDED_BITPOS); @@ -68,14 +70,26 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, Dec __ beqz(t0, done); } - __ push_reg(saved_regs, sp); + __ push_call_clobbered_registers(); + // If arguments are not in proper places, shuffle them. + // Doing this via the stack is the most straight-forward way to avoid + // accidentally smashing any register. + if (c_rarg0 != src || c_rarg1 != dst || c_rarg2 != count) { + __ push_reg(RegSet::of(src), sp); + __ push_reg(RegSet::of(dst), sp); + __ push_reg(RegSet::of(count), sp); + __ pop_reg(RegSet::of(c_rarg2), sp); + __ pop_reg(RegSet::of(c_rarg1), sp); + __ pop_reg(RegSet::of(c_rarg0), sp); + } + address target = nullptr; if (UseCompressedOops) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_narrow_oop), - src, dst, count); + target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_narrow_oop); } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_oop), src, dst, count); + target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_oop); } - __ pop_reg(saved_regs, sp); + __ call_VM_leaf(target, 3); + __ pop_call_clobbered_registers(); __ bind(done); } } @@ -93,14 +107,8 @@ void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm, Register pre_val, Register thread, Register tmp1, - Register tmp2, - bool tosca_live, - bool expand_call) { + Register tmp2) { assert(ShenandoahSATBBarrier, "Should be checked by caller"); - - // If expand_call is true then we expand the call_VM_leaf macro - // directly to skip generating the check by - // InterpreterMacroAssembler::call_VM_leaf_base that checks _last_sp. assert(thread == xthread, "must be"); Label done; @@ -120,7 +128,12 @@ void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm, // Do we need to load the previous value? if (obj != noreg) { - __ load_heap_oop(pre_val, Address(obj, 0), noreg, noreg, AS_RAW); + if (UseCompressedOops) { + __ lwu(pre_val, Address(obj, 0)); + __ decode_heap_oop(pre_val); + } else { + __ ld(pre_val, Address(obj, 0)); + } } // Is the previous value null? @@ -141,33 +154,17 @@ void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm, __ sd(pre_val, Address(tmp1, 0)); __ j(done); + // Slow-path call. __ bind(runtime); - // save the live input values - RegSet saved = RegSet::of(pre_val); - if (tosca_live) saved += RegSet::of(x10); - if (obj != noreg) saved += RegSet::of(obj); - - __ push_reg(saved, sp); - - // Calling the runtime using the regular call_VM_leaf mechanism generates - // code (generated by InterpreterMacroAssember::call_VM_leaf_base) - // that checks that the *(rfp+frame::interpreter_frame_last_sp) is null. - // - // If we care generating the pre-barrier without a frame (e.g. in the - // intrinsified Reference.get() routine) then ebp might be pointing to - // the caller frame and so this check will most likely fail at runtime. - // - // Expanding the call directly bypasses the generation of the check. - // So when we do not have have a full interpreter frame on the stack - // expand_call should be passed true. - if (expand_call) { - assert(pre_val != c_rarg1, "smashed arg"); - __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), pre_val); - } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), pre_val); + __ enter(); + __ push_call_clobbered_registers(); + if (c_rarg0 != pre_val) { + __ mv(c_rarg0, pre_val); } - - __ pop_reg(saved, sp); + // Calling with super_call_VM_leaf with c_rarg0 bypasses interpreter checks and avoids any moves. + __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), c_rarg0); + __ pop_call_clobbered_registers(); + __ leave(); __ bind(done); } @@ -234,7 +231,6 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, bool is_narrow = UseCompressedOops && !is_native; Label heap_stable, not_cset; - __ enter(); Address gc_state(xthread, in_bytes(ShenandoahThreadLocalData::gc_state_offset())); __ lbu(t1, gc_state); @@ -274,6 +270,8 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, __ beqz(t0, not_cset); } + // Slow-path call + __ enter(); __ push_call_clobbered_registers(); address target = nullptr; if (is_strong) { @@ -293,16 +291,18 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, assert(!is_narrow, "phantom access cannot be narrow"); target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom); } - __ rt_call(target); + // Calling with super_call_VM_leaf with c_rarg0/1 bypasses interpreter checks and avoids any moves. + __ super_call_VM_leaf(target, c_rarg0, c_rarg1); __ mv(t0, x10); __ pop_call_clobbered_registers(); __ mv(x10, t0); + __ leave(); + __ bind(not_cset); __ mv(result_dst, x10); __ pop_reg(saved_regs, sp); __ bind(heap_stable); - __ leave(); } // @@ -364,18 +364,12 @@ void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm, // 3: apply keep-alive barrier if needed if (ShenandoahBarrierSet::need_keep_alive_barrier(decorators, type)) { - __ enter(); - __ push_call_clobbered_registers(); satb_barrier(masm /* masm */, noreg /* obj */, dst /* pre_val */, xthread /* thread */, tmp1 /* tmp1 */, - tmp2 /* tmp2 */, - true /* tosca_live */, - true /* expand_call */); - __ pop_call_clobbered_registers(); - __ leave(); + tmp2 /* tmp2 */); } } @@ -418,8 +412,6 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet __ la(tmp3, dst); } - bool storing_non_null = (val != noreg); - // 2: pre-barrier: SATB needs the previous value if (ShenandoahBarrierSet::need_satb_barrier(decorators, type)) { satb_barrier(masm, @@ -427,15 +419,14 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet tmp2 /* pre_val */, xthread /* thread */, tmp1 /* tmp */, - t0 /* tmp2 */, - storing_non_null /* tosca_live */, - false /* expand_call */); + t0 /* tmp2 */); } // Store! BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), val, noreg, noreg, noreg); // 3: post-barrier: card barrier needs store address + bool storing_non_null = (val != noreg); if (ShenandoahBarrierSet::need_card_barrier(decorators, type) && storing_non_null) { card_barrier(masm, tmp3); } diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp index 8aef89723ea6..a1b4e8634111 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp @@ -46,9 +46,7 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { Register pre_val, Register thread, Register tmp1, - Register tmp2, - bool tosca_live, - bool expand_call); + Register tmp2); void card_barrier(MacroAssembler* masm, Register obj); diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp index 97829a10a3b5..a45255b012c2 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp @@ -44,47 +44,6 @@ #define __ masm-> -static void save_machine_state(MacroAssembler* masm, bool handle_gpr, bool handle_fp) { - if (handle_gpr) { - __ push_IU_state(); - } - - if (handle_fp) { - // Some paths can be reached from the c2i adapter with live fp arguments in registers. - assert(Argument::n_float_register_parameters_j == 8, "8 fp registers to save at java call"); - - const int xmm_size = wordSize * 2; - __ subptr(rsp, xmm_size * 8); - __ movdbl(Address(rsp, xmm_size * 0), xmm0); - __ movdbl(Address(rsp, xmm_size * 1), xmm1); - __ movdbl(Address(rsp, xmm_size * 2), xmm2); - __ movdbl(Address(rsp, xmm_size * 3), xmm3); - __ movdbl(Address(rsp, xmm_size * 4), xmm4); - __ movdbl(Address(rsp, xmm_size * 5), xmm5); - __ movdbl(Address(rsp, xmm_size * 6), xmm6); - __ movdbl(Address(rsp, xmm_size * 7), xmm7); - } -} - -static void restore_machine_state(MacroAssembler* masm, bool handle_gpr, bool handle_fp) { - if (handle_fp) { - const int xmm_size = wordSize * 2; - __ movdbl(xmm0, Address(rsp, xmm_size * 0)); - __ movdbl(xmm1, Address(rsp, xmm_size * 1)); - __ movdbl(xmm2, Address(rsp, xmm_size * 2)); - __ movdbl(xmm3, Address(rsp, xmm_size * 3)); - __ movdbl(xmm4, Address(rsp, xmm_size * 4)); - __ movdbl(xmm5, Address(rsp, xmm_size * 5)); - __ movdbl(xmm6, Address(rsp, xmm_size * 6)); - __ movdbl(xmm7, Address(rsp, xmm_size * 7)); - __ addptr(rsp, xmm_size * 8); - } - - if (handle_gpr) { - __ pop_IU_state(); - } -} - void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Register src, Register dst, Register count) { @@ -129,20 +88,27 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, Dec __ testb(gc_state, flags); __ jcc(Assembler::zero, L_done); - save_machine_state(masm, /* handle_gpr = */ true, /* handle_fp = */ false); - - assert(src == rdi, "expected"); - assert(dst == rsi, "expected"); - assert(count == rdx, "expected"); + __ push_call_clobbered_registers(/* save_fpu = */ false); + // If arguments are not in proper places, shuffle them. + // Doing this via the stack is the most straight-forward way to avoid + // accidentally smashing any register. + if (c_rarg0 != src || c_rarg1 != dst || c_rarg2 != count) { + __ push(src); + __ push(dst); + __ push(count); + __ pop(c_rarg2); + __ pop(c_rarg1); + __ pop(c_rarg0); + } + address target = nullptr; if (UseCompressedOops) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_narrow_oop), - src, dst, count); + target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_narrow_oop); } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_oop), - src, dst, count); + target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_oop); } + __ call_VM_leaf(target, 3); - restore_machine_state(masm, /* handle_gpr = */ true, /* handle_fp = */ false); + __ pop_call_clobbered_registers(/* restore_fpu = */ false); __ bind(L_done); } @@ -177,15 +143,8 @@ void ShenandoahBarrierSetAssembler::arraycopy_epilogue(MacroAssembler* masm, Dec void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm, Register obj, Register pre_val, - Register tmp, - bool tosca_live, - bool expand_call) { + Register tmp) { assert(ShenandoahSATBBarrier, "Should be checked by caller"); - - // If expand_call is true then we expand the call_VM_leaf macro - // directly to skip generating the check by - // InterpreterMacroAssembler::call_VM_leaf_base that checks _last_sp. - const Register thread = r15_thread; Label done; @@ -207,7 +166,12 @@ void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm, // Do we need to load the previous value? if (obj != noreg) { - __ load_heap_oop(pre_val, Address(obj, 0), noreg, AS_RAW); + if (UseCompressedOops) { + __ movl(pre_val, Address(obj, 0)); + __ decode_heap_oop(pre_val); + } else { + __ movq(pre_val, Address(obj, 0)); + } } // Is the previous value null? @@ -231,54 +195,22 @@ void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm, __ jmp(done); __ bind(runtime); - // save the live input values - if(tosca_live) __ push(rax); - - if (obj != noreg && obj != rax) - __ push(obj); - - if (pre_val != rax) - __ push(pre_val); - // Calling the runtime using the regular call_VM_leaf mechanism generates - // code (generated by InterpreterMacroAssember::call_VM_leaf_base) - // that checks that the *(ebp+frame::interpreter_frame_last_sp) == nullptr. - // - // If we care generating the pre-barrier without a frame (e.g. in the - // intrinsified Reference.get() routine) then ebp might be pointing to - // the caller frame and so this check will most likely fail at runtime. - // - // Expanding the call directly bypasses the generation of the check. - // So when we do not have have a full interpreter frame on the stack - // expand_call should be passed true. + // Slow-path call. + // Some paths can be reached from the c2i adapter with live fp arguments in registers. + __ enter(); + __ push_call_clobbered_registers(/* save_fpu = */ true); - // We move pre_val into c_rarg0 early, in order to avoid smashing it, should - // pre_val be c_rarg1 (where the call prologue would copy thread argument). - // Note: this should not accidentally smash thread, because thread is always r15. assert(thread != c_rarg0, "smashed arg"); if (c_rarg0 != pre_val) { __ mov(c_rarg0, pre_val); } - if (expand_call) { - assert(pre_val != c_rarg1, "smashed arg"); - if (c_rarg1 != thread) { - __ mov(c_rarg1, thread); - } - // Already moved pre_val into c_rarg0 above - __ MacroAssembler::call_VM_leaf_base(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), 1); - } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), c_rarg0); - } + // Calling with super_call_VM_leaf with c_rarg0 bypasses interpreter checks and avoids any moves. + __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), c_rarg0); - // save the live input values - if (pre_val != rax) - __ pop(pre_val); - - if (obj != noreg && obj != rax) - __ pop(obj); - - if(tosca_live) __ pop(rax); + __ pop_call_clobbered_registers(/* restore_fpu = */ true); + __ leave(); __ bind(done); } @@ -339,111 +271,51 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, __ jcc(Assembler::zero, not_cset); } - save_machine_state(masm, /* handle_gpr = */ false, /* handle_fp = */ true); - - // The rest is saved with the optimized path - - uint num_saved_regs = 4 + (dst != rax ? 1 : 0) + 4 + (UseAPX ? 16 : 0); - __ subptr(rsp, num_saved_regs * wordSize); - uint slot = num_saved_regs; + // Slow-path call. + // Save registers that can be clobbered by call. + // Some paths can be reached from the c2i adapter with live fp arguments in registers. + __ enter(); if (dst != rax) { - __ movptr(Address(rsp, (--slot) * wordSize), rax); - } - __ movptr(Address(rsp, (--slot) * wordSize), rcx); - __ movptr(Address(rsp, (--slot) * wordSize), rdx); - __ movptr(Address(rsp, (--slot) * wordSize), rdi); - __ movptr(Address(rsp, (--slot) * wordSize), rsi); - __ movptr(Address(rsp, (--slot) * wordSize), r8); - __ movptr(Address(rsp, (--slot) * wordSize), r9); - __ movptr(Address(rsp, (--slot) * wordSize), r10); - __ movptr(Address(rsp, (--slot) * wordSize), r11); - // Save APX extended registers r16–r31 if enabled - if (UseAPX) { - __ movptr(Address(rsp, (--slot) * wordSize), r16); - __ movptr(Address(rsp, (--slot) * wordSize), r17); - __ movptr(Address(rsp, (--slot) * wordSize), r18); - __ movptr(Address(rsp, (--slot) * wordSize), r19); - __ movptr(Address(rsp, (--slot) * wordSize), r20); - __ movptr(Address(rsp, (--slot) * wordSize), r21); - __ movptr(Address(rsp, (--slot) * wordSize), r22); - __ movptr(Address(rsp, (--slot) * wordSize), r23); - __ movptr(Address(rsp, (--slot) * wordSize), r24); - __ movptr(Address(rsp, (--slot) * wordSize), r25); - __ movptr(Address(rsp, (--slot) * wordSize), r26); - __ movptr(Address(rsp, (--slot) * wordSize), r27); - __ movptr(Address(rsp, (--slot) * wordSize), r28); - __ movptr(Address(rsp, (--slot) * wordSize), r29); - __ movptr(Address(rsp, (--slot) * wordSize), r30); - __ movptr(Address(rsp, (--slot) * wordSize), r31); - } - // r12-r15 are callee saved in all calling conventions - assert(slot == 0, "must use all slots"); + __ push(rax); + } + __ push_call_clobbered_registers_except(rax, /* save_fpu = */ true); // Shuffle registers such that dst is in c_rarg0 and addr in c_rarg1. - Register arg0 = c_rarg0, arg1 = c_rarg1; - if (dst == arg1) { - __ lea(arg0, src); - __ xchgptr(arg1, arg0); + if (dst == c_rarg1) { + __ lea(c_rarg0, src); + __ xchgptr(c_rarg1, c_rarg0); } else { - __ lea(arg1, src); - __ movptr(arg0, dst); + __ lea(c_rarg1, src); + __ movptr(c_rarg0, dst); } + address target = nullptr; if (is_strong) { if (is_narrow) { - __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow), arg0, arg1); + target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow); } else { - __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong), arg0, arg1); + target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong); } } else if (is_weak) { if (is_narrow) { - __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow), arg0, arg1); + target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow); } else { - __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak), arg0, arg1); + target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak); } } else { assert(is_phantom, "only remaining strength"); assert(!is_narrow, "phantom access cannot be narrow"); - __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom), arg0, arg1); - } - - // Restore APX extended registers r31–r16 if previously saved - if (UseAPX) { - __ movptr(r31, Address(rsp, (slot++) * wordSize)); - __ movptr(r30, Address(rsp, (slot++) * wordSize)); - __ movptr(r29, Address(rsp, (slot++) * wordSize)); - __ movptr(r28, Address(rsp, (slot++) * wordSize)); - __ movptr(r27, Address(rsp, (slot++) * wordSize)); - __ movptr(r26, Address(rsp, (slot++) * wordSize)); - __ movptr(r25, Address(rsp, (slot++) * wordSize)); - __ movptr(r24, Address(rsp, (slot++) * wordSize)); - __ movptr(r23, Address(rsp, (slot++) * wordSize)); - __ movptr(r22, Address(rsp, (slot++) * wordSize)); - __ movptr(r21, Address(rsp, (slot++) * wordSize)); - __ movptr(r20, Address(rsp, (slot++) * wordSize)); - __ movptr(r19, Address(rsp, (slot++) * wordSize)); - __ movptr(r18, Address(rsp, (slot++) * wordSize)); - __ movptr(r17, Address(rsp, (slot++) * wordSize)); - __ movptr(r16, Address(rsp, (slot++) * wordSize)); - } - __ movptr(r11, Address(rsp, (slot++) * wordSize)); - __ movptr(r10, Address(rsp, (slot++) * wordSize)); - __ movptr(r9, Address(rsp, (slot++) * wordSize)); - __ movptr(r8, Address(rsp, (slot++) * wordSize)); - __ movptr(rsi, Address(rsp, (slot++) * wordSize)); - __ movptr(rdi, Address(rsp, (slot++) * wordSize)); - __ movptr(rdx, Address(rsp, (slot++) * wordSize)); - __ movptr(rcx, Address(rsp, (slot++) * wordSize)); + target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom); + } + // Calling with super_call_VM_leaf with c_rarg0/1 bypasses interpreter checks and avoids any moves. + __ super_call_VM_leaf(target, c_rarg0, c_rarg1); + __ pop_call_clobbered_registers_except(rax, /* restore_fpu = */ true); if (dst != rax) { __ movptr(dst, rax); - __ movptr(rax, Address(rsp, (slot++) * wordSize)); + __ pop(rax); } - - assert(slot == num_saved_regs, "must use all slots"); - __ addptr(rsp, num_saved_regs * wordSize); - - restore_machine_state(masm, /* handle_gpr = */ false, /* handle_fp = */ true); + __ leave(); __ bind(not_cset); @@ -518,19 +390,10 @@ void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet d // 3: apply keep-alive barrier if needed if (ShenandoahBarrierSet::need_keep_alive_barrier(decorators, type)) { - save_machine_state(masm, /* handle_gpr = */ true, /* handle_fp = */ true); - - assert_different_registers(dst, tmp1, r15_thread); - // Generate the SATB pre-barrier code to log the value of - // the referent field in an SATB buffer. satb_barrier(masm /* masm */, noreg /* obj */, dst /* pre_val */, - tmp1 /* tmp */, - true /* tosca_live */, - true /* expand_call */); - - restore_machine_state(masm, /* handle_gpr = */ true, /* handle_fp = */ true); + tmp1 /* tmp */); } } @@ -581,22 +444,19 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet __ lea(tmp1, dst); } - bool storing_non_null = (val != noreg); - // 2: pre-barrier: SATB needs the previous value if (ShenandoahBarrierSet::need_satb_barrier(decorators, type)) { satb_barrier(masm, tmp1 /* obj */, tmp2 /* pre_val */, - tmp3 /* tmp */, - storing_non_null /* tosca_live */, - false /* expand_call */); + tmp3 /* tmp */); } // Store! BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp1, 0), val, noreg, noreg, noreg); // 3: post-barrier: card barrier needs store address + bool storing_non_null = (val != noreg); if (ShenandoahBarrierSet::need_card_barrier(decorators, type) && storing_non_null) { card_barrier(masm, tmp1); } diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp index b5cc5c8d8345..69bdaeeb6131 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp @@ -44,9 +44,7 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { void satb_barrier(MacroAssembler* masm, Register obj, Register pre_val, - Register tmp, - bool tosca_live, - bool expand_call); + Register tmp); void card_barrier(MacroAssembler* masm, Register obj); diff --git a/src/hotspot/cpu/x86/methodHandles_x86.hpp b/src/hotspot/cpu/x86/methodHandles_x86.hpp index 6ba9b5f6a4fa..b158720ca875 100644 --- a/src/hotspot/cpu/x86/methodHandles_x86.hpp +++ b/src/hotspot/cpu/x86/methodHandles_x86.hpp @@ -27,7 +27,7 @@ // Adapters enum /* platform_dependent_constants */ { - adapter_code_size = 4000 DEBUG_ONLY(+ 6000) + adapter_code_size = 6000 DEBUG_ONLY(+ 6000) }; // Additional helper methods for MethodHandles code generation: diff --git a/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp b/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp index 0c223ee3128b..a51b5032de1d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp @@ -181,9 +181,6 @@ class ShenandoahCleanUpdateWeakOopsClosure : public OopClosure { }; class ShenandoahNMethodAndDisarmClosure : public NMethodToOopClosure { -private: - BarrierSetNMethod* const _bs; - public: inline ShenandoahNMethodAndDisarmClosure(OopClosure* cl); inline void do_nmethod(nmethod* nm); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp index 725e4e6e3e9f..a839e4de4c70 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp @@ -209,15 +209,13 @@ void ShenandoahCleanUpdateWeakOopsClosure::do_oo } ShenandoahNMethodAndDisarmClosure::ShenandoahNMethodAndDisarmClosure(OopClosure* cl) : - NMethodToOopClosure(cl, true /* fix_relocations */), - _bs(BarrierSet::barrier_set()->barrier_set_nmethod()) { -} + NMethodToOopClosure(cl, true /* fix_relocations */) {} void ShenandoahNMethodAndDisarmClosure::do_nmethod(nmethod* nm) { assert(nm != nullptr, "Sanity"); assert(!ShenandoahNMethod::gc_data(nm)->is_unregistered(), "Should not be here"); NMethodToOopClosure::do_nmethod(nm); - _bs->disarm(nm); + ShenandoahNMethod::disarm_nmethod(nm); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp index 49d2df0cc936..424b486f3288 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp @@ -40,20 +40,6 @@ ShenandoahNMethodTable* ShenandoahCodeRoots::_nmethod_table; int ShenandoahCodeRoots::_disarmed_value = 1; -bool ShenandoahCodeRoots::use_nmethod_barriers_for_mark() { - // Continuations need nmethod barriers for scanning stack chunk nmethods. - if (Continuations::enabled()) return true; - - // Concurrent class unloading needs nmethod barriers. - // When a nmethod is about to be executed, we need to make sure that all its - // metadata are marked. The alternative is to remark thread roots at final mark - // pause, which would cause latency issues. - if (ShenandoahHeap::heap()->unload_classes()) return true; - - // Otherwise, we can go without nmethod barriers. - return false; -} - void ShenandoahCodeRoots::initialize() { _nmethod_table = new ShenandoahNMethodTable(); } @@ -68,27 +54,14 @@ void ShenandoahCodeRoots::unregister_nmethod(nmethod* nm) { _nmethod_table->unregister_nmethod(nm); } -void ShenandoahCodeRoots::arm_nmethods_for_mark() { - if (use_nmethod_barriers_for_mark()) { - BarrierSet::barrier_set()->barrier_set_nmethod()->arm_all_nmethods(); - } -} - -void ShenandoahCodeRoots::arm_nmethods_for_evac() { +void ShenandoahCodeRoots::arm_nmethods() { BarrierSet::barrier_set()->barrier_set_nmethod()->arm_all_nmethods(); } class ShenandoahDisarmNMethodClosure : public NMethodClosure { -private: - BarrierSetNMethod* const _bs; - public: - ShenandoahDisarmNMethodClosure() : - _bs(BarrierSet::barrier_set()->barrier_set_nmethod()) { - } - virtual void do_nmethod(nmethod* nm) { - _bs->disarm(nm); + ShenandoahNMethod::disarm_nmethod(nm); } }; @@ -111,10 +84,8 @@ class ShenandoahDisarmNMethodsTask : public WorkerTask { }; void ShenandoahCodeRoots::disarm_nmethods() { - if (use_nmethod_barriers_for_mark()) { - ShenandoahDisarmNMethodsTask task; - ShenandoahHeap::heap()->workers()->run_task(&task); - } + ShenandoahDisarmNMethodsTask task; + ShenandoahHeap::heap()->workers()->run_task(&task); } class ShenandoahNMethodUnlinkClosure : public NMethodClosure { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.hpp b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.hpp index d29c446f2102..d395b4516f4f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.hpp @@ -67,14 +67,11 @@ class ShenandoahCodeRoots : public AllStatic { // Concurrent nmethod unloading support static void unlink(WorkerThreads* workers, bool unloading_occurred); static void purge(); - static void arm_nmethods_for_mark(); - static void arm_nmethods_for_evac(); + static void arm_nmethods(); static void disarm_nmethods(); static int disarmed_value() { return _disarmed_value; } static int* disarmed_value_address() { return &_disarmed_value; } - static bool use_nmethod_barriers_for_mark(); - private: static ShenandoahNMethodTable* _nmethod_table; static int _disarmed_value; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index b207b8d2ea6e..3885a3f4c1d6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -739,9 +739,8 @@ void ShenandoahConcurrentGC::op_init_mark() { // Make above changes visible to worker threads OrderAccess::fence(); - // Arm nmethods for concurrent mark - ShenandoahCodeRoots::arm_nmethods_for_mark(); - + // Arm nmethods/stack for concurrent processing + ShenandoahCodeRoots::arm_nmethods(); ShenandoahStackWatermark::change_epoch_id(); if (ShenandoahPacing) { heap->pacer()->setup_for_mark(); @@ -803,7 +802,7 @@ void ShenandoahConcurrentGC::op_final_mark() { heap->set_has_forwarded_objects(true); // Arm nmethods/stack for concurrent processing - ShenandoahCodeRoots::arm_nmethods_for_evac(); + ShenandoahCodeRoots::arm_nmethods(); ShenandoahStackWatermark::change_epoch_id(); if (ShenandoahPacing) { @@ -1033,14 +1032,10 @@ void ShenandoahConcurrentGC::op_class_unloading() { class ShenandoahEvacUpdateCodeCacheClosure : public NMethodClosure { private: - BarrierSetNMethod* const _bs; ShenandoahEvacuateUpdateMetadataClosure _cl; public: - ShenandoahEvacUpdateCodeCacheClosure() : - _bs(BarrierSet::barrier_set()->barrier_set_nmethod()), - _cl() { - } + ShenandoahEvacUpdateCodeCacheClosure() : _cl() {} void do_nmethod(nmethod* n) { ShenandoahNMethod* data = ShenandoahNMethod::gc_data(n); @@ -1048,8 +1043,8 @@ class ShenandoahEvacUpdateCodeCacheClosure : public NMethodClosure { // Setup EvacOOM scope below reentrant lock to avoid deadlock with // nmethod_entry_barrier ShenandoahEvacOOMScope oom; - data->oops_do(&_cl, true/*fix relocation*/); - _bs->disarm(n); + data->oops_do(&_cl, /* fix_relocations = */ true); + ShenandoahNMethod::disarm_nmethod(n); } }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp index 8693046297d5..a513c4453561 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp @@ -204,9 +204,6 @@ ShenandoahRootAdjuster::ShenandoahRootAdjuster(uint n_workers, ShenandoahPhaseTi void ShenandoahRootAdjuster::roots_do(uint worker_id, OopClosure* oops) { NMethodToOopClosure code_blob_cl(oops, NMethodToOopClosure::FixRelocations); ShenandoahNMethodAndDisarmClosure nmethods_and_disarm_Cl(oops); - NMethodToOopClosure* adjust_code_closure = ShenandoahCodeRoots::use_nmethod_barriers_for_mark() ? - static_cast(&nmethods_and_disarm_Cl) : - static_cast(&code_blob_cl); CLDToOopClosure adjust_cld_closure(oops, ClassLoaderData::_claim_strong); // Process light-weight/limited parallel roots then @@ -215,7 +212,7 @@ void ShenandoahRootAdjuster::roots_do(uint worker_id, OopClosure* oops) { _cld_roots.cld_do(&adjust_cld_closure, worker_id); // Process heavy-weight/fully parallel roots the last - _code_roots.nmethods_do(adjust_code_closure, worker_id); + _code_roots.nmethods_do(&nmethods_and_disarm_Cl, worker_id); _thread_roots.oops_do(oops, nullptr, worker_id); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.inline.hpp index fa3fa90b2f50..178b1dea3aa4 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.inline.hpp @@ -172,10 +172,6 @@ template void ShenandoahRootUpdater::roots_do(uint worker_id, IsAlive* is_alive, KeepAlive* keep_alive) { NMethodToOopClosure update_nmethods(keep_alive, NMethodToOopClosure::FixRelocations); ShenandoahNMethodAndDisarmClosure nmethods_and_disarm_Cl(keep_alive); - NMethodToOopClosure* codes_cl = ShenandoahCodeRoots::use_nmethod_barriers_for_mark() ? - static_cast(&nmethods_and_disarm_Cl) : - static_cast(&update_nmethods); - CLDToOopClosure clds(keep_alive, ClassLoaderData::_claim_strong); // Process light-weight/limited parallel roots then @@ -184,7 +180,7 @@ void ShenandoahRootUpdater::roots_do(uint worker_id, IsAlive* is_alive, KeepAliv _cld_roots.cld_do(&clds, worker_id); // Process heavy-weight/fully parallel roots the last - _code_roots.nmethods_do(codes_cl, worker_id); + _code_roots.nmethods_do(&nmethods_and_disarm_Cl, worker_id); _thread_roots.oops_do(keep_alive, nullptr, worker_id); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp index c2bfea664fdc..365165546dfc 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp @@ -70,7 +70,7 @@ void ShenandoahSTWMark::mark() { // Arm all nmethods. Even though this is STW mark, some marking code // piggybacks on nmethod barriers for special instances. - ShenandoahCodeRoots::arm_nmethods_for_mark(); + ShenandoahCodeRoots::arm_nmethods(); // Weak reference processing assert(ShenandoahHeap::heap()->gc_generation() == _generation, "Marking unexpected generation"); diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index 5ecc038954dd..50f5276c8516 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -991,18 +991,22 @@ bool Node::has_out_with(int opcode1, int opcode2, int opcode3, int opcode4) { //---------------------------uncast_helper------------------------------------- Node* Node::uncast_helper(const Node* p, bool keep_deps) { #ifdef ASSERT + // If we end up traversing more nodes than we actually have, + // it is definitely an infinite loop. + uint max_depth = Compile::current()->unique(); uint depth_count = 0; const Node* orig_p = p; #endif while (true) { #ifdef ASSERT - if (depth_count >= K) { + if (depth_count++ >= max_depth) { orig_p->dump(4); - if (p != orig_p) + if (p != orig_p) { p->dump(1); + } + fatal("infinite loop in Node::uncast_helper"); } - assert(depth_count++ < K, "infinite loop in Node::uncast_helper"); #endif if (p == nullptr || p->req() != 2) { break; @@ -1216,6 +1220,9 @@ bool Node::has_special_unique_user() const { } else if ((is_IfFalse() || is_IfTrue()) && n->is_If()) { // See IfNode::fold_compares return true; + } else if (n->Opcode() == Op_XorV || n->Opcode() == Op_XorVMask) { + // Condition for XorVMask(VectorMaskCmp(x,y,cond), MaskAll(true)) ==> VectorMaskCmp(x,y,ncond) + return true; } else { return false; } diff --git a/src/hotspot/share/opto/subnode.hpp b/src/hotspot/share/opto/subnode.hpp index 57a501ecbc37..5acf31b45c4b 100644 --- a/src/hotspot/share/opto/subnode.hpp +++ b/src/hotspot/share/opto/subnode.hpp @@ -328,7 +328,9 @@ struct BoolTest { // a simple char array where each element is the ASCII version of a 'mask' // enum from above. mask commute( ) const { return mask("032147658"[_test]-'0'); } - mask negate( ) const { return mask(_test^4); } + mask negate( ) const { return negate_mask(_test); } + // Return the negative mask for the given mask, for both signed and unsigned comparison. + static mask negate_mask(mask btm) { return mask(btm ^ 4); } bool is_canonical( ) const { return (_test == BoolTest::ne || _test == BoolTest::lt || _test == BoolTest::le || _test == BoolTest::overflow); } bool is_less( ) const { return _test == BoolTest::lt || _test == BoolTest::le; } bool is_greater( ) const { return _test == BoolTest::gt || _test == BoolTest::ge; } diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index 2b40ca771981..e3395387138f 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -2163,6 +2163,99 @@ Node* OrVNode::Identity(PhaseGVN* phase) { return redundant_logical_identity(this); } +// Returns whether (XorV (VectorMaskCmp) -1) can be optimized by negating the +// comparison operation. +bool VectorMaskCmpNode::predicate_can_be_negated() { + switch (_predicate) { + case BoolTest::eq: + case BoolTest::ne: + // eq and ne also apply to floating-point special values like NaN and infinities. + return true; + case BoolTest::le: + case BoolTest::ge: + case BoolTest::lt: + case BoolTest::gt: + case BoolTest::ule: + case BoolTest::uge: + case BoolTest::ult: + case BoolTest::ugt: { + BasicType bt = vect_type()->element_basic_type(); + // For float and double, we don't know if either comparison operand is a + // NaN, NaN {le|ge|lt|gt} anything is false, resulting in inconsistent + // results before and after negation. + return is_integral_type(bt); + } + default: + return false; + } +} + +// This function transforms the following patterns: +// +// For integer types: +// (XorV (VectorMaskCmp src1 src2 cond) (Replicate -1)) +// => (VectorMaskCmp src1 src2 ncond) +// (XorVMask (VectorMaskCmp src1 src2 cond) (MaskAll m1)) +// => (VectorMaskCmp src1 src2 ncond) +// (XorV (VectorMaskCast (VectorMaskCmp src1 src2 cond)) (Replicate -1)) +// => (VectorMaskCast (VectorMaskCmp src1 src2 ncond)) +// (XorVMask (VectorMaskCast (VectorMaskCmp src1 src2 cond)) (MaskAll m1)) +// => (VectorMaskCast (VectorMaskCmp src1 src2 ncond)) +// cond can be eq, ne, le, ge, lt, gt, ule, uge, ult and ugt. +// ncond is the negative comparison of cond. +// +// For float and double types: +// (XorV (VectorMaskCast (VectorMaskCmp src1 src2 cond)) (Replicate -1)) +// => (VectorMaskCast (VectorMaskCmp src1 src2 ncond)) +// (XorVMask (VectorMaskCast (VectorMaskCmp src1 src2 cond)) (MaskAll m1)) +// => (VectorMaskCast (VectorMaskCmp src1 src2 ncond)) +// cond can be eq or ne. +Node* XorVNode::Ideal_XorV_VectorMaskCmp(PhaseGVN* phase, bool can_reshape) { + Node* in1 = in(1); + Node* in2 = in(2); + // Transformations for predicated vectors are not supported for now. + if (is_predicated_vector() || + in1->is_predicated_vector() || + in2->is_predicated_vector()) { + return nullptr; + } + + // XorV/XorVMask is commutative, swap VectorMaskCmp/VectorMaskCast to in1. + if (VectorNode::is_all_ones_vector(in1)) { + swap(in1, in2); + } + + bool with_vector_mask_cast = false; + // Required conditions: + // 1. VectorMaskCast and VectorMaskCmp should only have a single use, + // otherwise the optimization may be unprofitable. + // 2. The predicate of VectorMaskCmp should be negatable. + // 3. The second input should be an all true vector mask. + if (in1->Opcode() == Op_VectorMaskCast) { + if (in1->outcnt() != 1) { + return nullptr; + } + with_vector_mask_cast = true; + in1 = in1->in(1); + } + if (in1->Opcode() != Op_VectorMaskCmp || + in1->outcnt() != 1 || + !in1->as_VectorMaskCmp()->predicate_can_be_negated() || + !VectorNode::is_all_ones_vector(in2)) { + return nullptr; + } + + BoolTest::mask neg_cond = BoolTest::negate_mask((in1->as_VectorMaskCmp())->get_predicate()); + ConINode* predicate_node = phase->intcon(neg_cond); + const TypeVect* vt = in1->as_Vector()->vect_type(); + Node* res = new VectorMaskCmpNode(neg_cond, in1->in(1), in1->in(2), predicate_node, vt); + if (with_vector_mask_cast) { + // We optimized out a VectorMaskCast, regenerate one to ensure type correctness. + res = new VectorMaskCastNode(phase->transform(res), vect_type()); + } + return res; +} + Node* XorVNode::Ideal(PhaseGVN* phase, bool can_reshape) { // (XorV src src) => (Replicate zero) // (XorVMask src src) => (MaskAll zero) @@ -2176,6 +2269,11 @@ Node* XorVNode::Ideal(PhaseGVN* phase, bool can_reshape) { Node* zero = phase->transform(phase->zerocon(bt)); return VectorNode::scalar2vector(zero, length(), bt, bottom_type()->isa_vectmask() != nullptr); } + + Node* res = Ideal_XorV_VectorMaskCmp(phase, can_reshape); + if (res != nullptr) { + return res; + } return VectorNode::Ideal(phase, can_reshape); } diff --git a/src/hotspot/share/opto/vectornode.hpp b/src/hotspot/share/opto/vectornode.hpp index e72c3880c798..d3aae937426c 100644 --- a/src/hotspot/share/opto/vectornode.hpp +++ b/src/hotspot/share/opto/vectornode.hpp @@ -1012,6 +1012,7 @@ class XorVNode : public VectorNode { XorVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1,in2,vt) {} virtual int Opcode() const; virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); + Node* Ideal_XorV_VectorMaskCmp(PhaseGVN* phase, bool can_reshape); }; //------------------------------XorReductionVNode-------------------------------------- @@ -1684,6 +1685,7 @@ class VectorMaskCmpNode : public VectorNode { virtual bool cmp( const Node &n ) const { return VectorNode::cmp(n) && _predicate == ((VectorMaskCmpNode&)n)._predicate; } + bool predicate_can_be_negated(); BoolTest::mask get_predicate() { return _predicate; } #ifndef PRODUCT virtual void dump_spec(outputStream *st) const; diff --git a/src/java.base/share/classes/sun/net/www/http/HttpClient.java b/src/java.base/share/classes/sun/net/www/http/HttpClient.java index 82ab4c199a5c..dc8f7ad67955 100644 --- a/src/java.base/share/classes/sun/net/www/http/HttpClient.java +++ b/src/java.base/share/classes/sun/net/www/http/HttpClient.java @@ -27,6 +27,7 @@ import java.io.*; import java.net.*; +import java.net.Proxy.Type; import java.util.Locale; import java.util.Objects; import java.util.OptionalInt; @@ -182,6 +183,13 @@ int getKeepAliveTimeout() { return keepAliveTimeout; } + public Proxy getHttpProxy() { + if (proxy != null && proxy.type() == Type.HTTP) { + return proxy; + } + return null; + } + static String normalizeCBT(String s) { if (s == null || s.equals("never")) { return "never"; diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java index d19d30504bc5..f8c6261f9654 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java @@ -366,6 +366,9 @@ private static Set schemesListToSet(String list) { private boolean tryTransparentNTLMProxy = true; private boolean useProxyResponseCode = false; + // used when redirecting to compare current and previous proxies + private Proxy lastProxy; + /* Used by Windows specific code */ private Object authObj; @@ -1371,7 +1374,6 @@ private InputStream getInputStream0() throws IOException { // If the user has set either of these headers then do not remove them isUserServerAuth = requests.getKey("Authorization") != -1; isUserProxyAuth = requests.getKey("Proxy-Authorization") != -1; - try { do { if (!checkReuseConnection()) @@ -1381,6 +1383,14 @@ private InputStream getInputStream0() throws IOException { return cachedInputStream; } + // we may need to remove proxy-authorization + Proxy p = http.getHttpProxy(); + // if we're not using a proxy or if the proxy to be used is not + // the same as the originally set one, then remove it + if (p == null || (lastProxy != null && !lastProxy.equals(p))) { + requests.remove("Proxy-Authorization"); + lastProxy = null; + } /* REMIND: This exists to fix the HttpsURLConnection subclass. * Hotjava needs to run on JDK1.1FCS. Do proper fix once a * proper solution for SSL can be found. @@ -1411,7 +1421,7 @@ private InputStream getInputStream0() throws IOException { disconnectInternal(); throw new IOException ("Invalid Http response"); } - if (respCode == HTTP_PROXY_AUTH) { + if (respCode == HTTP_PROXY_AUTH && tunnelState() != TunnelState.TUNNELING) { if (streaming()) { disconnectInternal(); throw new HttpRetryException ( @@ -1988,6 +1998,7 @@ private void doTunneling0() throws IOException { if (respCode == HTTP_OK) { setTunnelState(TunnelState.TUNNELING); + savedRequests.remove("Proxy-Authorization"); break; } // we don't know how to deal with other response code @@ -2510,6 +2521,7 @@ private boolean followRedirect0(String loc, int stat, URL locUrl) { assert isLockHeldByCurrentThread(); + lastProxy = http.getHttpProxy(); disconnectInternal(); if (streaming()) { throw new HttpRetryException (RETRY_MSG3, stat, loc); diff --git a/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java b/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java index 8bb7def0f575..5d203b5c6ccd 100644 --- a/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java +++ b/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -54,7 +54,7 @@ class ServerHandshakeContext extends HandshakeContext { private static final long DEFAULT_STATUS_RESP_DELAY = 5000L; final long statusRespTimeout; boolean acceptCliHelloFragments = false; - + boolean sentHRR = false; ServerHandshakeContext(SSLContextImpl sslContext, TransportContext conContext) throws IOException { diff --git a/src/java.base/share/classes/sun/security/ssl/ServerHello.java b/src/java.base/share/classes/sun/security/ssl/ServerHello.java index 9fc364944f25..df77acf88cac 100644 --- a/src/java.base/share/classes/sun/security/ssl/ServerHello.java +++ b/src/java.base/share/classes/sun/security/ssl/ServerHello.java @@ -788,6 +788,15 @@ private T13HelloRetryRequestProducer() { public byte[] produce(ConnectionContext context, HandshakeMessage message) throws IOException { ServerHandshakeContext shc = (ServerHandshakeContext) context; + + + if (shc.sentHRR) { + throw shc.conContext.fatal( + Alert.HANDSHAKE_FAILURE, + "TLS 1.3 server MUST NOT send a second HelloRetryRequest " + + "in the same connection"); + } + ClientHelloMessage clientHello = (ClientHelloMessage) message; // negotiate the cipher suite. @@ -823,6 +832,7 @@ public byte[] produce(ConnectionContext context, // Output the handshake message. hhrm.write(shc.handshakeOutput); shc.handshakeOutput.flush(); + shc.sentHRR = true; // In TLS1.3 middlebox compatibility mode the server sends a // dummy change_cipher_spec record immediately after its diff --git a/src/java.base/share/classes/sun/security/x509/AlgorithmId.java b/src/java.base/share/classes/sun/security/x509/AlgorithmId.java index 7d525a9add7b..8d2c761a011c 100644 --- a/src/java.base/share/classes/sun/security/x509/AlgorithmId.java +++ b/src/java.base/share/classes/sun/security/x509/AlgorithmId.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -127,10 +127,35 @@ public AlgorithmId(ObjectIdentifier oid, AlgorithmParameters algparams) { public AlgorithmId(ObjectIdentifier oid, DerValue params) throws IOException { this.algid = oid; - if (params != null) { - encodedParams = params.toByteArray(); - decodeParams(); + + if (params == null) { + this.encodedParams = null; + this.algParams = null; + return; + } + + /* + * If the parameters field explicitly contains an ASN.1 NULL, treat it as + * "no parameters" rather than storing a literal NULL encoding. + * + * This canonicalization ensures consistent encoding/decoding behavior: + * - Algorithms that omit parameters and those that encode explicit NULL + * are treated equivalently (encodedParams == null). + */ + if (params.tag == DerValue.tag_Null) { + if (params.length() != 0) { + throw new IOException("Invalid ASN.1 NULL in AlgorithmId parameters: " + + "non-zero length"); + } + // Canonicalize to "no parameters" representation for consistency + this.encodedParams = null; + this.algParams = null; + return; } + + // Normal case: non-NULL params -> store and decode + this.encodedParams = params.toByteArray(); + decodeParams(); } protected void decodeParams() throws IOException { @@ -163,38 +188,10 @@ public void encode(DerOutputStream out) { bytes.putOID(algid); if (encodedParams == null) { - // MessageDigest algorithms usually have a NULL parameters even - // if most RFCs suggested absent. - // RSA key and signature algorithms requires the NULL parameters - // to be present, see A.1 and A.2.4 of RFC 8017. - if (algid.equals(RSAEncryption_oid) - || algid.equals(MD2_oid) - || algid.equals(MD5_oid) - || algid.equals(SHA_oid) - || algid.equals(SHA224_oid) - || algid.equals(SHA256_oid) - || algid.equals(SHA384_oid) - || algid.equals(SHA512_oid) - || algid.equals(SHA512_224_oid) - || algid.equals(SHA512_256_oid) - || algid.equals(SHA3_224_oid) - || algid.equals(SHA3_256_oid) - || algid.equals(SHA3_384_oid) - || algid.equals(SHA3_512_oid) - || algid.equals(SHA1withRSA_oid) - || algid.equals(SHA224withRSA_oid) - || algid.equals(SHA256withRSA_oid) - || algid.equals(SHA384withRSA_oid) - || algid.equals(SHA512withRSA_oid) - || algid.equals(SHA512$224withRSA_oid) - || algid.equals(SHA512$256withRSA_oid) - || algid.equals(MD2withRSA_oid) - || algid.equals(MD5withRSA_oid) - || algid.equals(SHA3_224withRSA_oid) - || algid.equals(SHA3_256withRSA_oid) - || algid.equals(SHA3_384withRSA_oid) - || algid.equals(SHA3_512withRSA_oid)) { + if (OIDS_REQUIRING_NULL.contains(algid.toString())) { bytes.putNull(); + } else { + // Parameters omitted } } else { bytes.writeBytes(encodedParams); @@ -646,30 +643,54 @@ private static ConcurrentHashMap collectOIDAliases() { public static final ObjectIdentifier MGF1_oid = ObjectIdentifier.of(KnownOIDs.MGF1); - public static final ObjectIdentifier SHA1withRSA_oid = - ObjectIdentifier.of(KnownOIDs.SHA1withRSA); - public static final ObjectIdentifier SHA224withRSA_oid = - ObjectIdentifier.of(KnownOIDs.SHA224withRSA); - public static final ObjectIdentifier SHA256withRSA_oid = - ObjectIdentifier.of(KnownOIDs.SHA256withRSA); - public static final ObjectIdentifier SHA384withRSA_oid = - ObjectIdentifier.of(KnownOIDs.SHA384withRSA); - public static final ObjectIdentifier SHA512withRSA_oid = - ObjectIdentifier.of(KnownOIDs.SHA512withRSA); - public static final ObjectIdentifier SHA512$224withRSA_oid = - ObjectIdentifier.of(KnownOIDs.SHA512$224withRSA); - public static final ObjectIdentifier SHA512$256withRSA_oid = - ObjectIdentifier.of(KnownOIDs.SHA512$256withRSA); - public static final ObjectIdentifier MD2withRSA_oid = - ObjectIdentifier.of(KnownOIDs.MD2withRSA); - public static final ObjectIdentifier MD5withRSA_oid = - ObjectIdentifier.of(KnownOIDs.MD5withRSA); - public static final ObjectIdentifier SHA3_224withRSA_oid = - ObjectIdentifier.of(KnownOIDs.SHA3_224withRSA); - public static final ObjectIdentifier SHA3_256withRSA_oid = - ObjectIdentifier.of(KnownOIDs.SHA3_256withRSA); - public static final ObjectIdentifier SHA3_384withRSA_oid = - ObjectIdentifier.of(KnownOIDs.SHA3_384withRSA); - public static final ObjectIdentifier SHA3_512withRSA_oid = - ObjectIdentifier.of(KnownOIDs.SHA3_512withRSA); + /* Set of OIDs that must explicitly encode a NULL parameter in AlgorithmIdentifier. + * References: + - RFC 8017 (PKCS #1) §A.1, §A.2.4: RSA key and signature algorithms + - RFC 9879 (HMAC) §4: HMAC algorithm identifiers + - RFC 9688 (HMAC with SHA-3) §4.3: HMAC-SHA3 algorithms MUST omit parameters + */ + private static final Set OIDS_REQUIRING_NULL = Set.of( + // MessageDigest algorithms usually have a NULL parameters even + // if most RFCs suggested absent. + KnownOIDs.MD2.value(), + KnownOIDs.MD5.value(), + KnownOIDs.SHA_1.value(), + KnownOIDs.SHA_224.value(), + KnownOIDs.SHA_256.value(), + KnownOIDs.SHA_384.value(), + KnownOIDs.SHA_512.value(), + KnownOIDs.SHA_512$224.value(), + KnownOIDs.SHA_512$256.value(), + KnownOIDs.SHA3_224.value(), + KnownOIDs.SHA3_256.value(), + KnownOIDs.SHA3_384.value(), + KnownOIDs.SHA3_512.value(), + + //--- RSA key and signature algorithms (RFC 8017 §A.1, §A.2.4) + KnownOIDs.RSA.value(), + KnownOIDs.SHA1withRSA.value(), + KnownOIDs.SHA224withRSA.value(), + KnownOIDs.SHA256withRSA.value(), + KnownOIDs.SHA384withRSA.value(), + KnownOIDs.SHA512withRSA.value(), + KnownOIDs.SHA512$224withRSA.value(), + KnownOIDs.SHA512$256withRSA.value(), + KnownOIDs.MD2withRSA.value(), + KnownOIDs.MD5withRSA.value(), + KnownOIDs.SHA3_224withRSA.value(), + KnownOIDs.SHA3_256withRSA.value(), + KnownOIDs.SHA3_384withRSA.value(), + KnownOIDs.SHA3_512withRSA.value(), + + // HMACs per RFC 9879 (Section 4): these require explicit NULL parameters + // Note: HMAC-SHA3 algorithms (RFC 9688 §4.3) MUST omit parameters, + // so they are intentionally excluded from this list. + KnownOIDs.HmacSHA1.value(), + KnownOIDs.HmacSHA224.value(), + KnownOIDs.HmacSHA256.value(), + KnownOIDs.HmacSHA384.value(), + KnownOIDs.HmacSHA512.value(), + KnownOIDs.HmacSHA512$224.value(), + KnownOIDs.HmacSHA512$256.value() + ); } diff --git a/src/java.desktop/share/classes/sun/font/HBShaper.java b/src/java.desktop/share/classes/sun/font/HBShaper.java index 7d3f58fb88f4..0060d7c63762 100644 --- a/src/java.desktop/share/classes/sun/font/HBShaper.java +++ b/src/java.desktop/share/classes/sun/font/HBShaper.java @@ -394,7 +394,9 @@ private static int get_glyph_v_advance( */ private static class IntPtr { MemorySegment seg; + @SuppressWarnings("restricted") IntPtr(MemorySegment seg) { + this.seg = seg.reinterpret(4); } void set(int i) { diff --git a/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolverSpi.java b/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolverSpi.java index 357088262605..1e7bd76413c9 100644 --- a/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolverSpi.java +++ b/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolverSpi.java @@ -51,4 +51,29 @@ public abstract XMLSignatureInput engineResolveURI(ResourceResolverContext conte */ public abstract boolean engineCanResolveURI(ResourceResolverContext context); + /** + * Returns the scheme for a URI. + * + * @param uri the URI + * @return the scheme, or {@code null} if none + */ + protected static final String scheme(String uri) { + if (uri == null) { + return null; + } + char[] uriChars = uri.toCharArray(); + // Similar to java.net.URI::parse. Find ':' before any of '/', '?', + // or '#', and treat the characters before it as scheme. + for (int i = 0; i < uriChars.length; i++) { + if (uriChars[i] == '/' || uriChars[i] == '?' || uriChars[i] == '#') { + return null; + } + if (uriChars[i] == ':') { + // No validation on the output since we only care if it's + // empty or equal to specific values. + return uri.substring(0, i); + } + } + return null; + } } diff --git a/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverDirectHTTP.java b/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverDirectHTTP.java index deda69e98b96..dafa851f3d0c 100644 --- a/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverDirectHTTP.java +++ b/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverDirectHTTP.java @@ -207,6 +207,8 @@ private URLConnection openConnection(URL url, ResourceResolverContext context) t */ @Override public boolean engineCanResolveURI(ResourceResolverContext context) { + LOG.debug("I was asked whether I can resolve {}", context.uriToResolve); + if (context.uriToResolve == null) { LOG.debug("quick fail, uri == null"); return false; @@ -217,11 +219,15 @@ public boolean engineCanResolveURI(ResourceResolverContext context) { return false; } - LOG.debug("I was asked whether I can resolve {}", context.uriToResolve); + String uriToResolveScheme = scheme(context.uriToResolve); - if (context.uriToResolve.startsWith("http:") || - context.uriToResolve.startsWith("https:") || - context.baseUri != null && (context.baseUri.startsWith("http:") || context.baseUri.startsWith("https:"))) { + if (uriToResolveScheme == null) { + String baseUriScheme = scheme(context.baseUri); + if ("http".equals(baseUriScheme) || "https".equals(baseUriScheme)) { + LOG.debug("I state that I can resolve {}", context.uriToResolve); + return true; + } + } else if (uriToResolveScheme.equals("http") || uriToResolveScheme.equals("https")) { LOG.debug("I state that I can resolve {}", context.uriToResolve); return true; } diff --git a/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java b/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java index d3970a3ea694..2a96866cf8be 100644 --- a/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java +++ b/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java @@ -72,20 +72,23 @@ public boolean engineCanResolveURI(ResourceResolverContext context) { return false; } - if (context.uriToResolve.isEmpty() || context.uriToResolve.charAt(0) == '#' || - context.uriToResolve.startsWith("http:") || context.uriToResolve.startsWith("https:")) { + if (context.uriToResolve.isEmpty() || context.uriToResolve.charAt(0) == '#') { return false; } - try { - LOG.debug("I was asked whether I can resolve {}", context.uriToResolve); + LOG.debug("I was asked whether I can resolve {}", context.uriToResolve); + + String uriToResolveScheme = scheme(context.uriToResolve); - if (context.uriToResolve.startsWith("file:") || context.baseUri.startsWith("file:")) { + if (uriToResolveScheme == null) { + String baseUriScheme = scheme(context.baseUri); + if ("file".equals(baseUriScheme)) { LOG.debug("I state that I can resolve {}", context.uriToResolve); return true; } - } catch (Exception e) { - LOG.debug(e.getMessage(), e); + } else if (uriToResolveScheme.equals("file")) { + LOG.debug("I state that I can resolve {}", context.uriToResolve); + return true; } LOG.debug("But I can't"); diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java index 689c8b24743e..d53589dc388e 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java @@ -51,6 +51,7 @@ import jdk.tools.jlink.internal.plugins.DefaultStripDebugPlugin; import jdk.tools.jlink.internal.plugins.ExcludeJmodSectionPlugin; import jdk.tools.jlink.internal.plugins.PluginsResourceBundle; +import jdk.tools.jlink.internal.plugins.StripJavaDebugAttributesPlugin; import jdk.tools.jlink.plugin.Plugin; import jdk.tools.jlink.plugin.Plugin.Category; @@ -418,6 +419,9 @@ private PluginsConfiguration getPluginsConfig(Path output, Map l List pluginsList = new ArrayList<>(); Set seenPlugins = new HashSet<>(); + // reference to the enabled DefaultStripDebugPlugin + DefaultStripDebugPlugin defaultStripDebugPlugin = null; + for (Entry>> entry : pluginToMaps.entrySet()) { Plugin plugin = entry.getKey(); List> argsMaps = entry.getValue(); @@ -438,6 +442,10 @@ private PluginsConfiguration getPluginsConfig(Path output, Map l } if (!Utils.isDisabled(plugin)) { + if (plugin instanceof DefaultStripDebugPlugin p) { + defaultStripDebugPlugin = p; + } + // make sure that --strip-debug and --strip-native-debug-symbols // aren't being used at the same time. --strip-debug invokes --strip-native-debug-symbols on // platforms that support it, so it makes little sense to allow both at the same time. @@ -452,6 +460,11 @@ private PluginsConfiguration getPluginsConfig(Path output, Map l } } + // disable StripJavaDebugAttributesPlugin within DefaultStripDebug plugin if both enabled + if (seenPlugins.contains(StripJavaDebugAttributesPlugin.NAME) && defaultStripDebugPlugin != null) { + defaultStripDebugPlugin.enableJavaStripPlugin(false); + } + // recreate or postprocessing don't require an output directory. ImageBuilder builder = null; if (output != null) { diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/DefaultStripDebugPlugin.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/DefaultStripDebugPlugin.java index e497083cc949..b3644bdde857 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/DefaultStripDebugPlugin.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/DefaultStripDebugPlugin.java @@ -47,6 +47,8 @@ public final class DefaultStripDebugPlugin extends AbstractPlugin { private final Plugin javaStripPlugin; private final NativePluginFactory stripNativePluginFactory; + private boolean isJavaStripPluginEnabled = true; + public DefaultStripDebugPlugin() { this(new StripJavaDebugAttributesPlugin(), new DefaultNativePluginFactory()); @@ -59,6 +61,10 @@ public DefaultStripDebugPlugin(Plugin javaStripPlugin, this.stripNativePluginFactory = nativeStripPluginFact; } + public void enableJavaStripPlugin(boolean enableJavaStripPlugin) { + isJavaStripPluginEnabled = enableJavaStripPlugin; + } + @Override public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) { Plugin stripNativePlugin = stripNativePluginFactory.create(); @@ -66,14 +72,21 @@ public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) { Map stripNativeConfig = Map.of( STRIP_NATIVE_DEBUG_PLUGIN, EXCLUDE_DEBUGINFO); stripNativePlugin.configure(stripNativeConfig); + + if (!isJavaStripPluginEnabled) { + return stripNativePlugin.transform(in, out); + } + ResourcePoolManager outRes = new ResourcePoolManager(in.byteOrder(), ((ResourcePoolImpl)in).getStringTable()); ResourcePool strippedJava = javaStripPlugin.transform(in, outRes.resourcePoolBuilder()); return stripNativePlugin.transform(strippedJava, out); - } else { + } else if (isJavaStripPluginEnabled) { return javaStripPlugin.transform(in, out); + } else { + return in; } } diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StripJavaDebugAttributesPlugin.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StripJavaDebugAttributesPlugin.java index fff585bb2dd3..4a6b54a3b6c8 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StripJavaDebugAttributesPlugin.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StripJavaDebugAttributesPlugin.java @@ -43,13 +43,14 @@ */ public final class StripJavaDebugAttributesPlugin extends AbstractPlugin { private final Predicate predicate; + public static final String NAME = "strip-java-debug-attributes"; public StripJavaDebugAttributesPlugin() { this((path) -> false); } StripJavaDebugAttributesPlugin(Predicate predicate) { - super("strip-java-debug-attributes"); + super(NAME); this.predicate = predicate; } @@ -69,11 +70,9 @@ public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) { ClassFile.LineNumbersOption.DROP_LINE_NUMBERS); byte[] content = ClassFile.of().transformClass(clm, ClassTransform .dropping(cle -> cle instanceof SourceFileAttribute - || cle instanceof SourceDebugExtensionAttribute) - .andThen(ClassTransform.transformingMethods(MethodTransform - .dropping(me -> me instanceof MethodParametersAttribute) - .andThen(MethodTransform - .transformingCode(CodeTransform.ACCEPT_ALL))))); + || cle instanceof SourceDebugExtensionAttribute) + .andThen(ClassTransform.transformingMethods(MethodTransform + .transformingCode(CodeTransform.ACCEPT_ALL)))); res = resource.copyWithContent(content); } } diff --git a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups index 246399d7438a..58c3af6d02a7 100644 --- a/test/hotspot/jtreg/TEST.groups +++ b/test/hotspot/jtreg/TEST.groups @@ -320,7 +320,8 @@ tier1_gc_shenandoah = \ gc/shenandoah/compiler/ \ gc/shenandoah/mxbeans/ \ gc/shenandoah/TestSmallHeap.java \ - gc/shenandoah/oom/ + gc/shenandoah/oom/ \ + gtest/ShenandoahGtests.java tier2_gc_shenandoah = \ runtime/MemberName/MemberNameLeak.java \ diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index fa7766d910c0..a6c89511e541 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -2279,6 +2279,11 @@ public class IRNode { vectorNode(VECTOR_MASK_CMP_D, "VectorMaskCmp", TYPE_DOUBLE); } + public static final String VECTOR_MASK_CMP = PREFIX + "VECTOR_MASK_CMP" + POSTFIX; + static { + beforeMatchingNameRegex(VECTOR_MASK_CMP, "VectorMaskCmp"); + } + public static final String VECTOR_CAST_B2S = VECTOR_PREFIX + "VECTOR_CAST_B2S" + POSTFIX; static { vectorNode(VECTOR_CAST_B2S, "VectorCastB2X", TYPE_SHORT); @@ -2749,6 +2754,11 @@ public class IRNode { vectorNode(XOR_VL, "XorV", TYPE_LONG); } + public static final String XOR_V = PREFIX + "XOR_V" + POSTFIX; + static { + beforeMatchingNameRegex(XOR_V, "XorV"); + } + public static final String XOR_V_MASK = PREFIX + "XOR_V_MASK" + POSTFIX; static { beforeMatchingNameRegex(XOR_V_MASK, "XorVMask"); diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCompareNotTest.java b/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCompareNotTest.java new file mode 100644 index 000000000000..851113ea4dee --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCompareNotTest.java @@ -0,0 +1,1299 @@ +/* + * Copyright (c) 2025, NVIDIA CORPORATION & 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.vectorapi; + +import compiler.lib.generators.*; +import compiler.lib.ir_framework.*; +import jdk.incubator.vector.*; +import jdk.test.lib.Asserts; + +/* + * @test + * @bug 8354242 + * @key randomness + * @library /test/lib / + * @summary test combining vector not operation with compare + * @modules jdk.incubator.vector + * + * @run driver compiler.vectorapi.VectorMaskCompareNotTest + */ + +public class VectorMaskCompareNotTest { + private static int LENGTH = 128; + + private static final VectorSpecies B_SPECIES = VectorSpecies.ofLargestShape(byte.class); + private static final VectorSpecies S_SPECIES = VectorSpecies.ofLargestShape(short.class); + private static final VectorSpecies I_SPECIES = VectorSpecies.ofLargestShape(int.class); + private static final VectorSpecies L_SPECIES = VectorSpecies.ofLargestShape(long.class); + private static final VectorSpecies F_SPECIES = VectorSpecies.ofLargestShape(float.class); + private static final VectorSpecies D_SPECIES = VectorSpecies.ofLargestShape(double.class); + + // Vector species for vector mask cast operation between int and long types, + // they must have the same number of elements. + // For other types, use a vector species of the specified width. + private static final VectorSpecies L_SPECIES_FOR_CAST = VectorSpecies.ofLargestShape(long.class); + private static final VectorSpecies I_SPECIES_FOR_CAST = VectorSpecies.of(int.class, VectorShape.forBitSize(L_SPECIES_FOR_CAST.vectorBitSize() / 2)); + + private static final Generators RD = Generators.G; + + private static byte[] ba; + private static byte[] bb; + private static short[] sa; + private static short[] sb; + private static int[] ia; + private static int[] ib; + private static int[] ic; + private static long[] la; + private static long[] lb; + private static float[] fa; + private static float[] fb; + private static float[] fnan; + private static float[] fpinf; + private static float[] fninf; + private static double[] da; + private static double[] db; + private static double[] dnan; + private static double[] dpinf; + private static double[] dninf; + private static boolean[] mr; + + static { + ba = new byte[LENGTH]; + bb = new byte[LENGTH]; + sa = new short[LENGTH]; + sb = new short[LENGTH]; + ia = new int[LENGTH]; + ib = new int[LENGTH]; + ic = new int[LENGTH]; + la = new long[LENGTH]; + lb = new long[LENGTH]; + fa = new float[LENGTH]; + fb = new float[LENGTH]; + fnan = new float[LENGTH]; + fpinf = new float[LENGTH]; + fninf = new float[LENGTH]; + da = new double[LENGTH]; + db = new double[LENGTH]; + dnan = new double[LENGTH]; + dpinf = new double[LENGTH]; + dninf = new double[LENGTH]; + mr = new boolean[LENGTH]; + + Generator iGen = RD.ints(); + Generator lGen = RD.longs(); + // Use uniform generators for floating point numbers not to generate NaN values. + Generator fGen = RD.uniformFloats(Float.MIN_VALUE, Float.MAX_VALUE); + Generator dGen = RD.uniformDoubles(Double.MIN_VALUE, Double.MAX_VALUE); + for (int i = 0; i < LENGTH; i++) { + ba[i] = iGen.next().byteValue(); + bb[i] = iGen.next().byteValue(); + sa[i] = iGen.next().shortValue(); + sb[i] = iGen.next().shortValue(); + ia[i] = iGen.next(); + ib[i] = iGen.next(); + la[i] = lGen.next(); + lb[i] = lGen.next(); + fa[i] = fGen.next(); + fb[i] = fGen.next(); + fnan[i] = Float.NaN; + fpinf[i] = Float.POSITIVE_INFINITY; + fninf[i] = Float.NEGATIVE_INFINITY; + da[i] = dGen.next(); + db[i] = dGen.next(); + dnan[i] = Double.NaN; + dpinf[i] = Double.POSITIVE_INFINITY; + dninf[i] = Double.NEGATIVE_INFINITY; + } + } + + public static int compareUnsigned(Number a, Number b) { + if (a instanceof Byte) { + return Integer.compareUnsigned(Byte.toUnsignedInt(a.byteValue()), Byte.toUnsignedInt(b.byteValue())); + } else if (a instanceof Short) { + return Integer.compareUnsigned(Short.toUnsignedInt(a.shortValue()), Short.toUnsignedInt(b.shortValue())); + } else if (a instanceof Integer) { + return Integer.compareUnsigned(a.intValue(), b.intValue()); + } else if (a instanceof Long) { + return Long.compareUnsigned(a.longValue(), b.longValue()); + } else { + throw new IllegalArgumentException("Unsupported type for unsigned comparison: " + a.getClass() + ", " + b.getClass()); + } + } + + public static > void compareResults(T a, T b, boolean r, VectorOperators.Comparison op) { + if (op == VectorOperators.EQ) { + // For floating point numbers, a is not NaN, b may be NaN. If b is NaN, + // a.compareTo(b) will return 1, 1 != 0 is true, r is expected to be true. + Asserts.assertEquals(a.compareTo(b) != 0, r); + } else if (op == VectorOperators.NE) { + // For floating point numbers, a is not NaN, b may be NaN. If b is NaN, + // a.compareTo(b) will return 1, 1 == 0 is false, r is expected to be false. + Asserts.assertEquals(a.compareTo(b) == 0, r); + } else if (op == VectorOperators.LE) { + Asserts.assertEquals(a.compareTo(b) > 0, r); + } else if (op == VectorOperators.GE) { + Asserts.assertEquals(a.compareTo(b) < 0, r); + } else if (op == VectorOperators.LT) { + Asserts.assertEquals(a.compareTo(b) >= 0, r); + } else if (op == VectorOperators.GT) { + Asserts.assertEquals(a.compareTo(b) <= 0, r); + } else if (op == VectorOperators.ULE) { + Asserts.assertEquals(compareUnsigned(a, b) > 0, r); + } else if (op == VectorOperators.UGE) { + Asserts.assertEquals(compareUnsigned(a, b) < 0, r); + } else if (op == VectorOperators.ULT) { + Asserts.assertEquals(compareUnsigned(a, b) >= 0, r); + } else if (op == VectorOperators.UGT) { + Asserts.assertEquals(compareUnsigned(a, b) <= 0, r); + } else { + throw new IllegalArgumentException("Unknown comparison operator: " + op); + } + } + + @DontInline + public static void verifyResultsByte(VectorSpecies vs, VectorOperators.Comparison op) { + for (int i = 0; i < vs.length(); i++) { + compareResults(ba[i], bb[i], mr[i], op); + } + } + + @DontInline + public static void verifyResultsShort(VectorSpecies vs, VectorOperators.Comparison op) { + for (int i = 0; i < vs.length(); i++) { + compareResults(sa[i], sb[i], mr[i], op); + } + } + + @DontInline + public static void verifyResultsInt(VectorSpecies vs, VectorOperators.Comparison op) { + for (int i = 0; i < vs.length(); i++) { + compareResults(ia[i], ib[i], mr[i], op); + } + } + + @DontInline + public static void verifyResultsLong(VectorSpecies vs, VectorOperators.Comparison op) { + for (int i = 0; i < vs.length(); i++) { + compareResults(la[i], lb[i], mr[i], op); + } + } + + @DontInline + public static void verifyResultsFloat(VectorSpecies vs, VectorOperators.Comparison op, float[] a, float[] b) { + for (int i = 0; i < vs.length(); i++) { + compareResults(a[i], b[i], mr[i], op); + } + } + + @DontInline + public static void verifyResultsDouble(VectorSpecies vs, VectorOperators.Comparison op, double[] a, double[] b) { + for (int i = 0; i < vs.length(); i++) { + compareResults(a[i], b[i], mr[i], op); + } + } + + interface VectorMaskOperator { + public VectorMask apply(VectorMask m); + } + + @ForceInline + public static void testCompareMaskNotByte(VectorSpecies vs, VectorOperators.Comparison op, VectorMaskOperator func) { + ByteVector av = ByteVector.fromArray(vs, ba, 0); + ByteVector bv = ByteVector.fromArray(vs, bb, 0); + VectorMask m = av.compare(op, bv); + func.apply(m).intoArray(mr, 0); + } + + @ForceInline + public static void testCompareMaskNotShort(VectorSpecies vs, VectorOperators.Comparison op, VectorMaskOperator func) { + ShortVector av = ShortVector.fromArray(vs, sa, 0); + ShortVector bv = ShortVector.fromArray(vs, sb, 0); + VectorMask m = av.compare(op, bv); + func.apply(m).intoArray(mr, 0); + } + + @ForceInline + public static void testCompareMaskNotInt(VectorSpecies vs, VectorOperators.Comparison op, VectorMaskOperator func) { + IntVector av = IntVector.fromArray(vs, ia, 0); + IntVector bv = IntVector.fromArray(vs, ib, 0); + VectorMask m = av.compare(op, bv); + func.apply(m).intoArray(mr, 0); + } + + @ForceInline + public static void testCompareMaskNotLong(VectorSpecies vs, VectorOperators.Comparison op, VectorMaskOperator func) { + LongVector av = LongVector.fromArray(vs, la, 0); + LongVector bv = LongVector.fromArray(vs, lb, 0); + VectorMask m = av.compare(op, bv); + func.apply(m).intoArray(mr, 0); + } + + @ForceInline + public static void testCompareMaskNotFloat(VectorSpecies vs, VectorOperators.Comparison op, float[] a, float[] b, VectorMaskOperator func) { + FloatVector av = FloatVector.fromArray(vs, a, 0); + FloatVector bv = FloatVector.fromArray(vs, b, 0); + VectorMask m = av.compare(op, bv); + func.apply(m).intoArray(mr, 0); + } + + @ForceInline + public static void testCompareMaskNotDouble(VectorSpecies vs, VectorOperators.Comparison op, double[] a, double[] b, VectorMaskOperator func) { + DoubleVector av = DoubleVector.fromArray(vs, a, 0); + DoubleVector bv = DoubleVector.fromArray(vs, b, 0); + VectorMask m = av.compare(op, bv); + func.apply(m).intoArray(mr, 0); + } + + // Byte tests + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 1", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareEQMaskNotByte() { + testCompareMaskNotByte(B_SPECIES, VectorOperators.EQ, (m) -> { return m.not(); }); + verifyResultsByte(B_SPECIES, VectorOperators.EQ); + testCompareMaskNotByte(B_SPECIES, VectorOperators.EQ, (m) -> { return B_SPECIES.maskAll(true).xor(m); }); + verifyResultsByte(B_SPECIES, VectorOperators.EQ); + + testCompareMaskNotByte(ByteVector.SPECIES_64, VectorOperators.EQ, (m) -> { return m.cast(ShortVector.SPECIES_128).not(); }); + verifyResultsByte(ByteVector.SPECIES_64, VectorOperators.EQ); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 1", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareNEMaskNotByte() { + testCompareMaskNotByte(B_SPECIES, VectorOperators.NE, (m) -> { return m.not(); }); + verifyResultsByte(B_SPECIES, VectorOperators.NE); + testCompareMaskNotByte(B_SPECIES, VectorOperators.NE, (m) -> { return B_SPECIES.maskAll(true).xor(m); }); + verifyResultsByte(B_SPECIES, VectorOperators.NE); + + testCompareMaskNotByte(ByteVector.SPECIES_64, VectorOperators.NE, (m) -> { return m.cast(ShortVector.SPECIES_128).not(); }); + verifyResultsByte(ByteVector.SPECIES_64, VectorOperators.NE); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 1", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareLTMaskNotByte() { + testCompareMaskNotByte(B_SPECIES, VectorOperators.LT, (m) -> { return m.not(); }); + verifyResultsByte(B_SPECIES, VectorOperators.LT); + testCompareMaskNotByte(B_SPECIES, VectorOperators.LT, (m) -> { return B_SPECIES.maskAll(true).xor(m); }); + verifyResultsByte(B_SPECIES, VectorOperators.LT); + + testCompareMaskNotByte(ByteVector.SPECIES_64, VectorOperators.LT, (m) -> { return m.cast(ShortVector.SPECIES_128).not(); }); + verifyResultsByte(ByteVector.SPECIES_64, VectorOperators.LT); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 1", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareGTMaskNotByte() { + testCompareMaskNotByte(B_SPECIES, VectorOperators.GT, (m) -> { return m.not(); }); + verifyResultsByte(B_SPECIES, VectorOperators.GT); + testCompareMaskNotByte(B_SPECIES, VectorOperators.GT, (m) -> { return B_SPECIES.maskAll(true).xor(m); }); + verifyResultsByte(B_SPECIES, VectorOperators.GT); + + testCompareMaskNotByte(ByteVector.SPECIES_64, VectorOperators.GT, (m) -> { return m.cast(ShortVector.SPECIES_128).not(); }); + verifyResultsByte(ByteVector.SPECIES_64, VectorOperators.GT); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 1", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareLEMaskNotByte() { + testCompareMaskNotByte(B_SPECIES, VectorOperators.LE, (m) -> { return m.not(); }); + verifyResultsByte(B_SPECIES, VectorOperators.LE); + testCompareMaskNotByte(B_SPECIES, VectorOperators.LE, (m) -> { return B_SPECIES.maskAll(true).xor(m); }); + verifyResultsByte(B_SPECIES, VectorOperators.LE); + + testCompareMaskNotByte(ByteVector.SPECIES_64, VectorOperators.LE, (m) -> { return m.cast(ShortVector.SPECIES_128).not(); }); + verifyResultsByte(ByteVector.SPECIES_64, VectorOperators.LE); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 1", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareGEMaskNotByte() { + testCompareMaskNotByte(B_SPECIES, VectorOperators.GE, (m) -> { return m.not(); }); + verifyResultsByte(B_SPECIES, VectorOperators.GE); + testCompareMaskNotByte(B_SPECIES, VectorOperators.GE, (m) -> { return B_SPECIES.maskAll(true).xor(m); }); + verifyResultsByte(B_SPECIES, VectorOperators.GE); + + testCompareMaskNotByte(ByteVector.SPECIES_64, VectorOperators.GE, (m) -> { return m.cast(ShortVector.SPECIES_128).not(); }); + verifyResultsByte(ByteVector.SPECIES_64, VectorOperators.GE); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 1", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareULTMaskNotByte() { + testCompareMaskNotByte(B_SPECIES, VectorOperators.ULT, (m) -> { return m.not(); }); + verifyResultsByte(B_SPECIES, VectorOperators.ULT); + testCompareMaskNotByte(B_SPECIES, VectorOperators.ULT, (m) -> { return B_SPECIES.maskAll(true).xor(m); }); + verifyResultsByte(B_SPECIES, VectorOperators.ULT); + + testCompareMaskNotByte(ByteVector.SPECIES_64, VectorOperators.ULT, (m) -> { return m.cast(ShortVector.SPECIES_128).not(); }); + verifyResultsByte(ByteVector.SPECIES_64, VectorOperators.ULT); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 1", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareUGTMaskNotByte() { + testCompareMaskNotByte(B_SPECIES, VectorOperators.UGT, (m) -> { return m.not(); }); + verifyResultsByte(B_SPECIES, VectorOperators.UGT); + testCompareMaskNotByte(B_SPECIES, VectorOperators.UGT, (m) -> { return B_SPECIES.maskAll(true).xor(m); }); + verifyResultsByte(B_SPECIES, VectorOperators.UGT); + + testCompareMaskNotByte(ByteVector.SPECIES_64, VectorOperators.UGT, (m) -> { return m.cast(ShortVector.SPECIES_128).not(); }); + verifyResultsByte(ByteVector.SPECIES_64, VectorOperators.UGT); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 1", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareULEMaskNotByte() { + testCompareMaskNotByte(B_SPECIES, VectorOperators.ULE, (m) -> { return m.not(); }); + verifyResultsByte(B_SPECIES, VectorOperators.ULE); + testCompareMaskNotByte(B_SPECIES, VectorOperators.ULE, (m) -> { return B_SPECIES.maskAll(true).xor(m); }); + verifyResultsByte(B_SPECIES, VectorOperators.ULE); + + testCompareMaskNotByte(ByteVector.SPECIES_64, VectorOperators.ULE, (m) -> { return m.cast(ShortVector.SPECIES_128).not(); }); + verifyResultsByte(ByteVector.SPECIES_64, VectorOperators.ULE); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 1", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareUGEMaskNotByte() { + testCompareMaskNotByte(B_SPECIES, VectorOperators.UGE, (m) -> { return m.not(); }); + verifyResultsByte(B_SPECIES, VectorOperators.UGE); + testCompareMaskNotByte(B_SPECIES, VectorOperators.UGE, (m) -> { return B_SPECIES.maskAll(true).xor(m); }); + verifyResultsByte(B_SPECIES, VectorOperators.UGE); + + testCompareMaskNotByte(ByteVector.SPECIES_64, VectorOperators.UGE, (m) -> { return m.cast(ShortVector.SPECIES_128).not(); }); + verifyResultsByte(ByteVector.SPECIES_64, VectorOperators.UGE); + } + + // Short tests + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 2", + IRNode.VECTOR_MASK_CMP, "= 4" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareEQMaskNotShort() { + testCompareMaskNotShort(S_SPECIES, VectorOperators.EQ, (m) -> { return m.not(); }); + verifyResultsShort(S_SPECIES, VectorOperators.EQ); + testCompareMaskNotShort(S_SPECIES, VectorOperators.EQ, (m) -> { return S_SPECIES.maskAll(true).xor(m); }); + verifyResultsShort(S_SPECIES, VectorOperators.EQ); + + testCompareMaskNotShort(ShortVector.SPECIES_64, VectorOperators.EQ, (m) -> { return IntVector.SPECIES_128.maskAll(true).xor(m.cast(IntVector.SPECIES_128)); }); + verifyResultsShort(ShortVector.SPECIES_64, VectorOperators.EQ); + testCompareMaskNotShort(ShortVector.SPECIES_128, VectorOperators.EQ, (m) -> { return m.cast(ByteVector.SPECIES_64).not(); }); + verifyResultsShort(ShortVector.SPECIES_128, VectorOperators.EQ); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 2", + IRNode.VECTOR_MASK_CMP, "= 4" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareNEMaskNotShort() { + testCompareMaskNotShort(S_SPECIES, VectorOperators.NE, (m) -> { return m.not(); }); + verifyResultsShort(S_SPECIES, VectorOperators.NE); + testCompareMaskNotShort(S_SPECIES, VectorOperators.NE, (m) -> { return S_SPECIES.maskAll(true).xor(m); }); + verifyResultsShort(S_SPECIES, VectorOperators.NE); + + testCompareMaskNotShort(ShortVector.SPECIES_64, VectorOperators.NE, (m) -> { return IntVector.SPECIES_128.maskAll(true).xor(m.cast(IntVector.SPECIES_128)); }); + verifyResultsShort(ShortVector.SPECIES_64, VectorOperators.NE); + testCompareMaskNotShort(ShortVector.SPECIES_128, VectorOperators.NE, (m) -> { return m.cast(ByteVector.SPECIES_64).not(); }); + verifyResultsShort(ShortVector.SPECIES_128, VectorOperators.NE); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 2", + IRNode.VECTOR_MASK_CMP, "= 4" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareLTMaskNotShort() { + testCompareMaskNotShort(S_SPECIES, VectorOperators.LT, (m) -> { return m.not(); }); + verifyResultsShort(S_SPECIES, VectorOperators.LT); + testCompareMaskNotShort(S_SPECIES, VectorOperators.LT, (m) -> { return S_SPECIES.maskAll(true).xor(m); }); + verifyResultsShort(S_SPECIES, VectorOperators.LT); + + testCompareMaskNotShort(ShortVector.SPECIES_64, VectorOperators.LT, (m) -> { return IntVector.SPECIES_128.maskAll(true).xor(m.cast(IntVector.SPECIES_128)); }); + verifyResultsShort(ShortVector.SPECIES_64, VectorOperators.LT); + testCompareMaskNotShort(ShortVector.SPECIES_128, VectorOperators.LT, (m) -> { return m.cast(ByteVector.SPECIES_64).not(); }); + verifyResultsShort(ShortVector.SPECIES_128, VectorOperators.LT); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 2", + IRNode.VECTOR_MASK_CMP, "= 4" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareGTMaskNotShort() { + testCompareMaskNotShort(S_SPECIES, VectorOperators.GT, (m) -> { return m.not(); }); + verifyResultsShort(S_SPECIES, VectorOperators.GT); + testCompareMaskNotShort(S_SPECIES, VectorOperators.GT, (m) -> { return S_SPECIES.maskAll(true).xor(m); }); + verifyResultsShort(S_SPECIES, VectorOperators.GT); + + testCompareMaskNotShort(ShortVector.SPECIES_64, VectorOperators.GT, (m) -> { return IntVector.SPECIES_128.maskAll(true).xor(m.cast(IntVector.SPECIES_128)); }); + verifyResultsShort(ShortVector.SPECIES_64, VectorOperators.GT); + testCompareMaskNotShort(ShortVector.SPECIES_128, VectorOperators.GT, (m) -> { return m.cast(ByteVector.SPECIES_64).not(); }); + verifyResultsShort(ShortVector.SPECIES_128, VectorOperators.GT); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 2", + IRNode.VECTOR_MASK_CMP, "= 4" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareLEMaskNotShort() { + testCompareMaskNotShort(S_SPECIES, VectorOperators.LE, (m) -> { return m.not(); }); + verifyResultsShort(S_SPECIES, VectorOperators.LE); + testCompareMaskNotShort(S_SPECIES, VectorOperators.LE, (m) -> { return S_SPECIES.maskAll(true).xor(m); }); + verifyResultsShort(S_SPECIES, VectorOperators.LE); + + testCompareMaskNotShort(ShortVector.SPECIES_64, VectorOperators.LE, (m) -> { return IntVector.SPECIES_128.maskAll(true).xor(m.cast(IntVector.SPECIES_128)); }); + verifyResultsShort(ShortVector.SPECIES_64, VectorOperators.LE); + testCompareMaskNotShort(ShortVector.SPECIES_128, VectorOperators.LE, (m) -> { return m.cast(ByteVector.SPECIES_64).not(); }); + verifyResultsShort(ShortVector.SPECIES_128, VectorOperators.LE); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 2", + IRNode.VECTOR_MASK_CMP, "= 4" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareGEMaskNotShort() { + testCompareMaskNotShort(S_SPECIES, VectorOperators.GE, (m) -> { return m.not(); }); + verifyResultsShort(S_SPECIES, VectorOperators.GE); + testCompareMaskNotShort(S_SPECIES, VectorOperators.GE, (m) -> { return S_SPECIES.maskAll(true).xor(m); }); + verifyResultsShort(S_SPECIES, VectorOperators.GE); + + testCompareMaskNotShort(ShortVector.SPECIES_64, VectorOperators.GE, (m) -> { return IntVector.SPECIES_128.maskAll(true).xor(m.cast(IntVector.SPECIES_128)); }); + verifyResultsShort(ShortVector.SPECIES_64, VectorOperators.GE); + testCompareMaskNotShort(ShortVector.SPECIES_128, VectorOperators.GE, (m) -> { return m.cast(ByteVector.SPECIES_64).not(); }); + verifyResultsShort(ShortVector.SPECIES_128, VectorOperators.GE); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 2", + IRNode.VECTOR_MASK_CMP, "= 4" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareULTMaskNotShort() { + testCompareMaskNotShort(S_SPECIES, VectorOperators.ULT, (m) -> { return m.not(); }); + verifyResultsShort(S_SPECIES, VectorOperators.ULT); + testCompareMaskNotShort(S_SPECIES, VectorOperators.ULT, (m) -> { return S_SPECIES.maskAll(true).xor(m); }); + verifyResultsShort(S_SPECIES, VectorOperators.ULT); + + testCompareMaskNotShort(ShortVector.SPECIES_64, VectorOperators.ULT, (m) -> { return IntVector.SPECIES_128.maskAll(true).xor(m.cast(IntVector.SPECIES_128)); }); + verifyResultsShort(ShortVector.SPECIES_64, VectorOperators.ULT); + testCompareMaskNotShort(ShortVector.SPECIES_128, VectorOperators.ULT, (m) -> { return m.cast(ByteVector.SPECIES_64).not(); }); + verifyResultsShort(ShortVector.SPECIES_128, VectorOperators.ULT); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 2", + IRNode.VECTOR_MASK_CMP, "= 4" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareUGTMaskNotShort() { + testCompareMaskNotShort(S_SPECIES, VectorOperators.UGT, (m) -> { return m.not(); }); + verifyResultsShort(S_SPECIES, VectorOperators.UGT); + testCompareMaskNotShort(S_SPECIES, VectorOperators.UGT, (m) -> { return S_SPECIES.maskAll(true).xor(m); }); + verifyResultsShort(S_SPECIES, VectorOperators.UGT); + + testCompareMaskNotShort(ShortVector.SPECIES_64, VectorOperators.UGT, (m) -> { return IntVector.SPECIES_128.maskAll(true).xor(m.cast(IntVector.SPECIES_128)); }); + verifyResultsShort(ShortVector.SPECIES_64, VectorOperators.UGT); + testCompareMaskNotShort(ShortVector.SPECIES_128, VectorOperators.UGT, (m) -> { return m.cast(ByteVector.SPECIES_64).not(); }); + verifyResultsShort(ShortVector.SPECIES_128, VectorOperators.UGT); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 2", + IRNode.VECTOR_MASK_CMP, "= 4" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareULEMaskNotShort() { + testCompareMaskNotShort(S_SPECIES, VectorOperators.ULE, (m) -> { return m.not(); }); + verifyResultsShort(S_SPECIES, VectorOperators.ULE); + testCompareMaskNotShort(S_SPECIES, VectorOperators.ULE, (m) -> { return S_SPECIES.maskAll(true).xor(m); }); + verifyResultsShort(S_SPECIES, VectorOperators.ULE); + + testCompareMaskNotShort(ShortVector.SPECIES_64, VectorOperators.ULE, (m) -> { return IntVector.SPECIES_128.maskAll(true).xor(m.cast(IntVector.SPECIES_128)); }); + verifyResultsShort(ShortVector.SPECIES_64, VectorOperators.ULE); + testCompareMaskNotShort(ShortVector.SPECIES_128, VectorOperators.ULE, (m) -> { return m.cast(ByteVector.SPECIES_64).not(); }); + verifyResultsShort(ShortVector.SPECIES_128, VectorOperators.ULE); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 2", + IRNode.VECTOR_MASK_CMP, "= 4" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareUGEMaskNotShort() { + testCompareMaskNotShort(S_SPECIES, VectorOperators.UGE, (m) -> { return m.not(); }); + verifyResultsShort(S_SPECIES, VectorOperators.UGE); + testCompareMaskNotShort(S_SPECIES, VectorOperators.UGE, (m) -> { return S_SPECIES.maskAll(true).xor(m); }); + verifyResultsShort(S_SPECIES, VectorOperators.UGE); + + testCompareMaskNotShort(ShortVector.SPECIES_64, VectorOperators.UGE, (m) -> { return IntVector.SPECIES_128.maskAll(true).xor(m.cast(IntVector.SPECIES_128)); }); + verifyResultsShort(ShortVector.SPECIES_64, VectorOperators.UGE); + testCompareMaskNotShort(ShortVector.SPECIES_128, VectorOperators.UGE, (m) -> { return m.cast(ByteVector.SPECIES_64).not(); }); + verifyResultsShort(ShortVector.SPECIES_128, VectorOperators.UGE); + } + + // Int tests + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 2", + IRNode.VECTOR_MASK_CMP, "= 4" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareEQMaskNotInt() { + testCompareMaskNotInt(I_SPECIES, VectorOperators.EQ, (m) -> { return m.not(); }); + verifyResultsInt(I_SPECIES, VectorOperators.EQ); + testCompareMaskNotInt(I_SPECIES, VectorOperators.EQ, (m) -> { return I_SPECIES.maskAll(true).xor(m); }); + verifyResultsInt(I_SPECIES, VectorOperators.EQ); + + testCompareMaskNotInt(I_SPECIES_FOR_CAST, VectorOperators.EQ, (m) -> { return L_SPECIES_FOR_CAST.maskAll(true).xor(m.cast(L_SPECIES_FOR_CAST)); }); + verifyResultsInt(I_SPECIES_FOR_CAST, VectorOperators.EQ); + testCompareMaskNotInt(IntVector.SPECIES_128, VectorOperators.EQ, (m) -> { return m.cast(ShortVector.SPECIES_64).not(); }); + verifyResultsInt(IntVector.SPECIES_128, VectorOperators.EQ); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 2", + IRNode.VECTOR_MASK_CMP, "= 4" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareNEMaskNotInt() { + testCompareMaskNotInt(I_SPECIES, VectorOperators.NE, (m) -> { return m.not(); }); + verifyResultsInt(I_SPECIES, VectorOperators.NE); + testCompareMaskNotInt(I_SPECIES, VectorOperators.NE, (m) -> { return I_SPECIES.maskAll(true).xor(m); }); + verifyResultsInt(I_SPECIES, VectorOperators.NE); + + testCompareMaskNotInt(I_SPECIES_FOR_CAST, VectorOperators.NE, (m) -> { return L_SPECIES_FOR_CAST.maskAll(true).xor(m.cast(L_SPECIES_FOR_CAST)); }); + verifyResultsInt(I_SPECIES_FOR_CAST, VectorOperators.NE); + testCompareMaskNotInt(IntVector.SPECIES_128, VectorOperators.NE, (m) -> { return m.cast(ShortVector.SPECIES_64).not(); }); + verifyResultsInt(IntVector.SPECIES_128, VectorOperators.NE); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 2", + IRNode.VECTOR_MASK_CMP, "= 4" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareLTMaskNotInt() { + testCompareMaskNotInt(I_SPECIES, VectorOperators.LT, (m) -> { return m.not(); }); + verifyResultsInt(I_SPECIES, VectorOperators.LT); + testCompareMaskNotInt(I_SPECIES, VectorOperators.LT, (m) -> { return I_SPECIES.maskAll(true).xor(m); }); + verifyResultsInt(I_SPECIES, VectorOperators.LT); + + testCompareMaskNotInt(I_SPECIES_FOR_CAST, VectorOperators.LT, (m) -> { return L_SPECIES_FOR_CAST.maskAll(true).xor(m.cast(L_SPECIES_FOR_CAST)); }); + verifyResultsInt(I_SPECIES_FOR_CAST, VectorOperators.LT); + testCompareMaskNotInt(IntVector.SPECIES_128, VectorOperators.LT, (m) -> { return m.cast(ShortVector.SPECIES_64).not(); }); + verifyResultsInt(IntVector.SPECIES_128, VectorOperators.LT); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 2", + IRNode.VECTOR_MASK_CMP, "= 4" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareGTMaskNotInt() { + testCompareMaskNotInt(I_SPECIES, VectorOperators.GT, (m) -> { return m.not(); }); + verifyResultsInt(I_SPECIES, VectorOperators.GT); + testCompareMaskNotInt(I_SPECIES, VectorOperators.GT, (m) -> { return I_SPECIES.maskAll(true).xor(m); }); + verifyResultsInt(I_SPECIES, VectorOperators.GT); + + testCompareMaskNotInt(I_SPECIES_FOR_CAST, VectorOperators.GT, (m) -> { return L_SPECIES_FOR_CAST.maskAll(true).xor(m.cast(L_SPECIES_FOR_CAST)); }); + verifyResultsInt(I_SPECIES_FOR_CAST, VectorOperators.GT); + testCompareMaskNotInt(IntVector.SPECIES_128, VectorOperators.GT, (m) -> { return m.cast(ShortVector.SPECIES_64).not(); }); + verifyResultsInt(IntVector.SPECIES_128, VectorOperators.GT); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 2", + IRNode.VECTOR_MASK_CMP, "= 4" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareLEMaskNotInt() { + testCompareMaskNotInt(I_SPECIES, VectorOperators.LE, (m) -> { return m.not(); }); + verifyResultsInt(I_SPECIES, VectorOperators.LE); + testCompareMaskNotInt(I_SPECIES, VectorOperators.LE, (m) -> { return I_SPECIES.maskAll(true).xor(m); }); + verifyResultsInt(I_SPECIES, VectorOperators.LE); + + testCompareMaskNotInt(I_SPECIES_FOR_CAST, VectorOperators.LE, (m) -> { return L_SPECIES_FOR_CAST.maskAll(true).xor(m.cast(L_SPECIES_FOR_CAST)); }); + verifyResultsInt(I_SPECIES_FOR_CAST, VectorOperators.LE); + testCompareMaskNotInt(IntVector.SPECIES_128, VectorOperators.LE, (m) -> { return m.cast(ShortVector.SPECIES_64).not(); }); + verifyResultsInt(IntVector.SPECIES_128, VectorOperators.LE); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 2", + IRNode.VECTOR_MASK_CMP, "= 4" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareGEMaskNotInt() { + testCompareMaskNotInt(I_SPECIES, VectorOperators.GE, (m) -> { return m.not(); }); + verifyResultsInt(I_SPECIES, VectorOperators.GE); + testCompareMaskNotInt(I_SPECIES, VectorOperators.GE, (m) -> { return I_SPECIES.maskAll(true).xor(m); }); + verifyResultsInt(I_SPECIES, VectorOperators.GE); + + testCompareMaskNotInt(I_SPECIES_FOR_CAST, VectorOperators.GE, (m) -> { return L_SPECIES_FOR_CAST.maskAll(true).xor(m.cast(L_SPECIES_FOR_CAST)); }); + verifyResultsInt(I_SPECIES_FOR_CAST, VectorOperators.GE); + testCompareMaskNotInt(IntVector.SPECIES_128, VectorOperators.GE, (m) -> { return m.cast(ShortVector.SPECIES_64).not(); }); + verifyResultsInt(IntVector.SPECIES_128, VectorOperators.GE); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 2", + IRNode.VECTOR_MASK_CMP, "= 4" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareULTMaskNotInt() { + testCompareMaskNotInt(I_SPECIES, VectorOperators.ULT, (m) -> { return m.not(); }); + verifyResultsInt(I_SPECIES, VectorOperators.ULT); + testCompareMaskNotInt(I_SPECIES, VectorOperators.ULT, (m) -> { return I_SPECIES.maskAll(true).xor(m); }); + verifyResultsInt(I_SPECIES, VectorOperators.ULT); + + testCompareMaskNotInt(I_SPECIES_FOR_CAST, VectorOperators.ULT, (m) -> { return L_SPECIES_FOR_CAST.maskAll(true).xor(m.cast(L_SPECIES_FOR_CAST)); }); + verifyResultsInt(I_SPECIES_FOR_CAST, VectorOperators.ULT); + testCompareMaskNotInt(IntVector.SPECIES_128, VectorOperators.ULT, (m) -> { return m.cast(ShortVector.SPECIES_64).not(); }); + verifyResultsInt(IntVector.SPECIES_128, VectorOperators.ULT); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 2", + IRNode.VECTOR_MASK_CMP, "= 4" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareUGTMaskNotInt() { + testCompareMaskNotInt(I_SPECIES, VectorOperators.UGT, (m) -> { return m.not(); }); + verifyResultsInt(I_SPECIES, VectorOperators.UGT); + testCompareMaskNotInt(I_SPECIES, VectorOperators.UGT, (m) -> { return I_SPECIES.maskAll(true).xor(m); }); + verifyResultsInt(I_SPECIES, VectorOperators.UGT); + + testCompareMaskNotInt(I_SPECIES_FOR_CAST, VectorOperators.UGT, (m) -> { return L_SPECIES_FOR_CAST.maskAll(true).xor(m.cast(L_SPECIES_FOR_CAST)); }); + verifyResultsInt(I_SPECIES_FOR_CAST, VectorOperators.UGT); + testCompareMaskNotInt(IntVector.SPECIES_128, VectorOperators.UGT, (m) -> { return m.cast(ShortVector.SPECIES_64).not(); }); + verifyResultsInt(IntVector.SPECIES_128, VectorOperators.UGT); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 2", + IRNode.VECTOR_MASK_CMP, "= 4" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareULEMaskNotInt() { + testCompareMaskNotInt(I_SPECIES, VectorOperators.ULE, (m) -> { return m.not(); }); + verifyResultsInt(I_SPECIES, VectorOperators.ULE); + testCompareMaskNotInt(I_SPECIES, VectorOperators.ULE, (m) -> { return I_SPECIES.maskAll(true).xor(m); }); + verifyResultsInt(I_SPECIES, VectorOperators.ULE); + + testCompareMaskNotInt(I_SPECIES_FOR_CAST, VectorOperators.ULE, (m) -> { return L_SPECIES_FOR_CAST.maskAll(true).xor(m.cast(L_SPECIES_FOR_CAST)); }); + verifyResultsInt(I_SPECIES_FOR_CAST, VectorOperators.ULE); + testCompareMaskNotInt(IntVector.SPECIES_128, VectorOperators.ULE, (m) -> { return m.cast(ShortVector.SPECIES_64).not(); }); + verifyResultsInt(IntVector.SPECIES_128, VectorOperators.ULE); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 2", + IRNode.VECTOR_MASK_CMP, "= 4" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareUGEMaskNotInt() { + testCompareMaskNotInt(I_SPECIES, VectorOperators.UGE, (m) -> { return m.not(); }); + verifyResultsInt(I_SPECIES, VectorOperators.UGE); + testCompareMaskNotInt(I_SPECIES, VectorOperators.UGE, (m) -> { return I_SPECIES.maskAll(true).xor(m); }); + verifyResultsInt(I_SPECIES, VectorOperators.UGE); + + testCompareMaskNotInt(I_SPECIES_FOR_CAST, VectorOperators.UGE, (m) -> { return L_SPECIES_FOR_CAST.maskAll(true).xor(m.cast(L_SPECIES_FOR_CAST)); }); + verifyResultsInt(I_SPECIES_FOR_CAST, VectorOperators.UGE); + testCompareMaskNotInt(IntVector.SPECIES_128, VectorOperators.UGE, (m) -> { return m.cast(ShortVector.SPECIES_64).not(); }); + verifyResultsInt(IntVector.SPECIES_128, VectorOperators.UGE); + } + + // Long tests + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 1", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareEQMaskNotLong() { + testCompareMaskNotLong(L_SPECIES, VectorOperators.EQ, (m) -> { return m.not(); }); + verifyResultsLong(L_SPECIES, VectorOperators.EQ); + testCompareMaskNotLong(L_SPECIES, VectorOperators.EQ, (m) -> { return L_SPECIES.maskAll(true).xor(m); }); + verifyResultsLong(L_SPECIES, VectorOperators.EQ); + + testCompareMaskNotLong(L_SPECIES_FOR_CAST, VectorOperators.EQ, (m) -> { return m.cast(I_SPECIES_FOR_CAST).not(); }); + verifyResultsLong(L_SPECIES_FOR_CAST, VectorOperators.EQ); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 1", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareNEMaskNotLong() { + testCompareMaskNotLong(L_SPECIES, VectorOperators.NE, (m) -> { return m.not(); }); + verifyResultsLong(L_SPECIES, VectorOperators.NE); + testCompareMaskNotLong(L_SPECIES, VectorOperators.NE, (m) -> { return L_SPECIES.maskAll(true).xor(m); }); + verifyResultsLong(L_SPECIES, VectorOperators.NE); + + testCompareMaskNotLong(L_SPECIES_FOR_CAST, VectorOperators.NE, (m) -> { return m.cast(I_SPECIES_FOR_CAST).not(); }); + verifyResultsLong(L_SPECIES_FOR_CAST, VectorOperators.NE); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 1", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareLTMaskNotLong() { + testCompareMaskNotLong(L_SPECIES, VectorOperators.LT, (m) -> { return m.not(); }); + verifyResultsLong(L_SPECIES, VectorOperators.LT); + testCompareMaskNotLong(L_SPECIES, VectorOperators.LT, (m) -> { return L_SPECIES.maskAll(true).xor(m); }); + verifyResultsLong(L_SPECIES, VectorOperators.LT); + + testCompareMaskNotLong(L_SPECIES_FOR_CAST, VectorOperators.LT, (m) -> { return m.cast(I_SPECIES_FOR_CAST).not(); }); + verifyResultsLong(L_SPECIES_FOR_CAST, VectorOperators.LT); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 1", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareGTMaskNotLong() { + testCompareMaskNotLong(L_SPECIES, VectorOperators.GT, (m) -> { return m.not(); }); + verifyResultsLong(L_SPECIES, VectorOperators.GT); + testCompareMaskNotLong(L_SPECIES, VectorOperators.GT, (m) -> { return L_SPECIES.maskAll(true).xor(m); }); + verifyResultsLong(L_SPECIES, VectorOperators.GT); + + testCompareMaskNotLong(L_SPECIES_FOR_CAST, VectorOperators.GT, (m) -> { return m.cast(I_SPECIES_FOR_CAST).not(); }); + verifyResultsLong(L_SPECIES_FOR_CAST, VectorOperators.GT); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 1", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareLEMaskNotLong() { + testCompareMaskNotLong(L_SPECIES, VectorOperators.LE, (m) -> { return m.not(); }); + verifyResultsLong(L_SPECIES, VectorOperators.LE); + testCompareMaskNotLong(L_SPECIES, VectorOperators.LE, (m) -> { return L_SPECIES.maskAll(true).xor(m); }); + verifyResultsLong(L_SPECIES, VectorOperators.LE); + + testCompareMaskNotLong(L_SPECIES_FOR_CAST, VectorOperators.LE, (m) -> { return m.cast(I_SPECIES_FOR_CAST).not(); }); + verifyResultsLong(L_SPECIES_FOR_CAST, VectorOperators.LE); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 1", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareGEMaskNotLong() { + testCompareMaskNotLong(L_SPECIES, VectorOperators.GE, (m) -> { return m.not(); }); + verifyResultsLong(L_SPECIES, VectorOperators.GE); + testCompareMaskNotLong(L_SPECIES, VectorOperators.GE, (m) -> { return L_SPECIES.maskAll(true).xor(m); }); + verifyResultsLong(L_SPECIES, VectorOperators.GE); + + testCompareMaskNotLong(L_SPECIES_FOR_CAST, VectorOperators.GE, (m) -> { return m.cast(I_SPECIES_FOR_CAST).not(); }); + verifyResultsLong(L_SPECIES_FOR_CAST, VectorOperators.GE); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 1", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareULTMaskNotLong() { + testCompareMaskNotLong(L_SPECIES, VectorOperators.ULT, (m) -> { return m.not(); }); + verifyResultsLong(L_SPECIES, VectorOperators.ULT); + testCompareMaskNotLong(L_SPECIES, VectorOperators.ULT, (m) -> { return L_SPECIES.maskAll(true).xor(m); }); + verifyResultsLong(L_SPECIES, VectorOperators.ULT); + + testCompareMaskNotLong(L_SPECIES_FOR_CAST, VectorOperators.ULT, (m) -> { return m.cast(I_SPECIES_FOR_CAST).not(); }); + verifyResultsLong(L_SPECIES_FOR_CAST, VectorOperators.ULT); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 1", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareUGTMaskNotLong() { + testCompareMaskNotLong(L_SPECIES, VectorOperators.UGT, (m) -> { return m.not(); }); + verifyResultsLong(L_SPECIES, VectorOperators.UGT); + testCompareMaskNotLong(L_SPECIES, VectorOperators.UGT, (m) -> { return L_SPECIES.maskAll(true).xor(m); }); + verifyResultsLong(L_SPECIES, VectorOperators.UGT); + + testCompareMaskNotLong(L_SPECIES_FOR_CAST, VectorOperators.UGT, (m) -> { return m.cast(I_SPECIES_FOR_CAST).not(); }); + verifyResultsLong(L_SPECIES_FOR_CAST, VectorOperators.UGT); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 1", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareULEMaskNotLong() { + testCompareMaskNotLong(L_SPECIES, VectorOperators.ULE, (m) -> { return m.not(); }); + verifyResultsLong(L_SPECIES, VectorOperators.ULE); + testCompareMaskNotLong(L_SPECIES, VectorOperators.ULE, (m) -> { return L_SPECIES.maskAll(true).xor(m); }); + verifyResultsLong(L_SPECIES, VectorOperators.ULE); + + testCompareMaskNotLong(L_SPECIES_FOR_CAST, VectorOperators.ULE, (m) -> { return m.cast(I_SPECIES_FOR_CAST).not(); }); + verifyResultsLong(L_SPECIES_FOR_CAST, VectorOperators.ULE); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CAST, "= 1", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareUGEMaskNotLong() { + testCompareMaskNotLong(L_SPECIES, VectorOperators.UGE, (m) -> { return m.not(); }); + verifyResultsLong(L_SPECIES, VectorOperators.UGE); + testCompareMaskNotLong(L_SPECIES, VectorOperators.UGE, (m) -> { return L_SPECIES.maskAll(true).xor(m); }); + verifyResultsLong(L_SPECIES, VectorOperators.UGE); + + testCompareMaskNotLong(L_SPECIES_FOR_CAST, VectorOperators.UGE, (m) -> { return m.cast(I_SPECIES_FOR_CAST).not(); }); + verifyResultsLong(L_SPECIES_FOR_CAST, VectorOperators.UGE); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareEQMaskNotFloat() { + testCompareMaskNotFloat(F_SPECIES, VectorOperators.EQ, fa, fb, (m) -> { return m.not(); }); + verifyResultsFloat(F_SPECIES, VectorOperators.EQ, fa, fb); + testCompareMaskNotFloat(F_SPECIES, VectorOperators.EQ, fa, fb, (m) -> { return F_SPECIES.maskAll(true).xor(m); }); + verifyResultsFloat(F_SPECIES, VectorOperators.EQ, fa, fb); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareNEMaskNotFloat() { + testCompareMaskNotFloat(F_SPECIES, VectorOperators.NE, fa, fb, (m) -> { return m.not(); }); + verifyResultsFloat(F_SPECIES, VectorOperators.NE, fa, fb); + testCompareMaskNotFloat(F_SPECIES, VectorOperators.NE, fa, fb, (m) -> { return F_SPECIES.maskAll(true).xor(m); }); + verifyResultsFloat(F_SPECIES, VectorOperators.NE, fa, fb); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareEQMaskNotFloatNaN() { + testCompareMaskNotFloat(F_SPECIES, VectorOperators.EQ, fa, fnan, (m) -> { return m.not(); }); + verifyResultsFloat(F_SPECIES, VectorOperators.EQ, fa, fnan); + testCompareMaskNotFloat(F_SPECIES, VectorOperators.EQ, fa, fnan, (m) -> { return F_SPECIES.maskAll(true).xor(m); }); + verifyResultsFloat(F_SPECIES, VectorOperators.EQ, fa, fnan); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareNEMaskNotFloatNaN() { + testCompareMaskNotFloat(F_SPECIES, VectorOperators.NE, fa, fnan, (m) -> { return m.not(); }); + verifyResultsFloat(F_SPECIES, VectorOperators.NE, fa, fnan); + testCompareMaskNotFloat(F_SPECIES, VectorOperators.NE, fa, fnan, (m) -> { return F_SPECIES.maskAll(true).xor(m); }); + verifyResultsFloat(F_SPECIES, VectorOperators.NE, fa, fnan); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareEQMaskNotFloatPositiveInfinity() { + testCompareMaskNotFloat(F_SPECIES, VectorOperators.EQ, fa, fpinf, (m) -> { return m.not(); }); + verifyResultsFloat(F_SPECIES, VectorOperators.EQ, fa, fpinf); + testCompareMaskNotFloat(F_SPECIES, VectorOperators.EQ, fa, fpinf, (m) -> { return F_SPECIES.maskAll(true).xor(m); }); + verifyResultsFloat(F_SPECIES, VectorOperators.EQ, fa, fpinf); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareNEMaskNotFloatPositiveInfinity() { + testCompareMaskNotFloat(F_SPECIES, VectorOperators.NE, fa, fpinf, (m) -> { return m.not(); }); + verifyResultsFloat(F_SPECIES, VectorOperators.NE, fa, fpinf); + testCompareMaskNotFloat(F_SPECIES, VectorOperators.NE, fa, fpinf, (m) -> { return F_SPECIES.maskAll(true).xor(m); }); + verifyResultsFloat(F_SPECIES, VectorOperators.NE, fa, fpinf); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareEQMaskNotFloatNegativeInfinity() { + testCompareMaskNotFloat(F_SPECIES, VectorOperators.EQ, fa, fninf, (m) -> { return m.not(); }); + verifyResultsFloat(F_SPECIES, VectorOperators.EQ, fa, fninf); + testCompareMaskNotFloat(F_SPECIES, VectorOperators.EQ, fa, fninf, (m) -> { return F_SPECIES.maskAll(true).xor(m); }); + verifyResultsFloat(F_SPECIES, VectorOperators.EQ, fa, fninf); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true", "rvv", "true" }) + public static void testCompareNEMaskNotFloatNegativeInfinity() { + testCompareMaskNotFloat(F_SPECIES, VectorOperators.NE, fa, fninf, (m) -> { return m.not(); }); + verifyResultsFloat(F_SPECIES, VectorOperators.NE, fa, fninf); + testCompareMaskNotFloat(F_SPECIES, VectorOperators.NE, fa, fninf, (m) -> { return F_SPECIES.maskAll(true).xor(m); }); + verifyResultsFloat(F_SPECIES, VectorOperators.NE, fa, fninf); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareEQMaskNotDouble() { + testCompareMaskNotDouble(D_SPECIES, VectorOperators.EQ, da, db, (m) -> { return m.not(); }); + verifyResultsDouble(D_SPECIES, VectorOperators.EQ, da, db); + testCompareMaskNotDouble(D_SPECIES, VectorOperators.EQ, da, db, (m) -> { return D_SPECIES.maskAll(true).xor(m); }); + verifyResultsDouble(D_SPECIES, VectorOperators.EQ, da, db); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareNEMaskNotDouble() { + testCompareMaskNotDouble(D_SPECIES, VectorOperators.NE, da, db, (m) -> { return m.not(); }); + verifyResultsDouble(D_SPECIES, VectorOperators.NE, da, db); + testCompareMaskNotDouble(D_SPECIES, VectorOperators.NE, da, db, (m) -> { return D_SPECIES.maskAll(true).xor(m); }); + verifyResultsDouble(D_SPECIES, VectorOperators.NE, da, db); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareEQMaskNotDoubleNaN() { + testCompareMaskNotDouble(D_SPECIES, VectorOperators.EQ, da, dnan, (m) -> { return m.not(); }); + verifyResultsDouble(D_SPECIES, VectorOperators.EQ, da, dnan); + testCompareMaskNotDouble(D_SPECIES, VectorOperators.EQ, da, dnan, (m) -> { return D_SPECIES.maskAll(true).xor(m); }); + verifyResultsDouble(D_SPECIES, VectorOperators.EQ, da, dnan); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareNEMaskNotDoubleNaN() { + testCompareMaskNotDouble(D_SPECIES, VectorOperators.NE, da, dnan, (m) -> { return m.not(); }); + verifyResultsDouble(D_SPECIES, VectorOperators.NE, da, dnan); + testCompareMaskNotDouble(D_SPECIES, VectorOperators.NE, da, dnan, (m) -> { return D_SPECIES.maskAll(true).xor(m); }); + verifyResultsDouble(D_SPECIES, VectorOperators.NE, da, dnan); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareEQMaskNotDoublePositiveInfinity() { + testCompareMaskNotDouble(D_SPECIES, VectorOperators.EQ, da, dpinf, (m) -> { return m.not(); }); + verifyResultsDouble(D_SPECIES, VectorOperators.EQ, da, dpinf); + testCompareMaskNotDouble(D_SPECIES, VectorOperators.EQ, da, dpinf, (m) -> { return D_SPECIES.maskAll(true).xor(m); }); + verifyResultsDouble(D_SPECIES, VectorOperators.EQ, da, dpinf); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareNEMaskNotDoublePositiveInfinity() { + testCompareMaskNotDouble(D_SPECIES, VectorOperators.NE, da, dpinf, (m) -> { return m.not(); }); + verifyResultsDouble(D_SPECIES, VectorOperators.NE, da, dpinf); + testCompareMaskNotDouble(D_SPECIES, VectorOperators.NE, da, dpinf, (m) -> { return D_SPECIES.maskAll(true).xor(m); }); + verifyResultsDouble(D_SPECIES, VectorOperators.NE, da, dpinf); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareEQMaskNotDoubleNegativeInfinity() { + testCompareMaskNotDouble(D_SPECIES, VectorOperators.EQ, da, dninf, (m) -> { return m.not(); }); + verifyResultsDouble(D_SPECIES, VectorOperators.EQ, da, dninf); + testCompareMaskNotDouble(D_SPECIES, VectorOperators.EQ, da, dninf, (m) -> { return D_SPECIES.maskAll(true).xor(m); }); + verifyResultsDouble(D_SPECIES, VectorOperators.EQ, da, dninf); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 0", + IRNode.XOR_V, "= 0", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" }) + public static void testCompareNEMaskNotDoubleNegativeInfinity() { + testCompareMaskNotDouble(D_SPECIES, VectorOperators.NE, da, dninf, (m) -> { return m.not(); }); + verifyResultsDouble(D_SPECIES, VectorOperators.NE, da, dninf); + testCompareMaskNotDouble(D_SPECIES, VectorOperators.NE, da, dninf, (m) -> { return D_SPECIES.maskAll(true).xor(m); }); + verifyResultsDouble(D_SPECIES, VectorOperators.NE, da, dninf); + } + + // negative tests + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 2", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureOr = { "sve", "true", "avx512", "true", "rvv", "true" }) + @IR(counts = { IRNode.XOR_V, "= 2", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + @IR(counts = { IRNode.XOR_V, "= 2", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureAnd = { "avx2", "true" }) + public static void testCompareMaskNotByteNegative() { + testCompareMaskNotByte(B_SPECIES, VectorOperators.EQ, (m) -> { + // The vector mask is used multiple times. + ic[0] = m.trueCount(); + return m.not(); + }); + verifyResultsByte(B_SPECIES, VectorOperators.EQ); + + // One of the operands of XOR is not all ones vector. + testCompareMaskNotByte(B_SPECIES, VectorOperators.EQ, (m) -> { return B_SPECIES.maskAll(false).xor(m); }); + verifyResultsByte(B_SPECIES, VectorOperators.NE); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 2", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureOr = { "sve", "true", "avx512", "true", "rvv", "true" }) + @IR(counts = { IRNode.XOR_V, "= 2", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + @IR(counts = { IRNode.XOR_V, "= 2", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureAnd = { "avx2", "true" }) + public static void testCompareMaskNotShortNegative() { + testCompareMaskNotShort(S_SPECIES, VectorOperators.EQ, (m) -> { + // The vector mask is used multiple times. + ic[0] = m.trueCount(); + return m.not(); + }); + verifyResultsShort(S_SPECIES, VectorOperators.EQ); + + // One of the operands of XOR is not all ones vector. + testCompareMaskNotShort(S_SPECIES, VectorOperators.EQ, (m) -> { return S_SPECIES.maskAll(false).xor(m); }); + verifyResultsShort(S_SPECIES, VectorOperators.NE); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 2", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureOr = { "sve", "true", "avx512", "true", "rvv", "true" }) + @IR(counts = { IRNode.XOR_V, "= 2", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + @IR(counts = { IRNode.XOR_V, "= 2", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureAnd = { "avx2", "true" }) + public static void testCompareMaskNotIntNegative() { + testCompareMaskNotInt(I_SPECIES, VectorOperators.EQ, (m) -> { + // The vector mask is used multiple times. + ic[0] = m.trueCount(); + return m.not(); + }); + verifyResultsInt(I_SPECIES, VectorOperators.EQ); + + // One of the operands of XOR is not all ones vector. + testCompareMaskNotInt(I_SPECIES, VectorOperators.EQ, (m) -> { return I_SPECIES.maskAll(false).xor(m); }); + verifyResultsInt(I_SPECIES, VectorOperators.NE); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 2", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureOr = { "sve", "true", "avx512", "true", "rvv", "true" }) + @IR(counts = { IRNode.XOR_V, "= 2", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + @IR(counts = { IRNode.XOR_V, "= 2", + IRNode.VECTOR_MASK_CMP, "= 2" }, + applyIfCPUFeatureAnd = { "avx2", "true" }) + public static void testCompareMaskNotLongNegative() { + testCompareMaskNotLong(L_SPECIES, VectorOperators.EQ, (m) -> { + // The vector mask is used multiple times. + ic[0] = m.trueCount(); + return m.not(); + }); + verifyResultsLong(L_SPECIES, VectorOperators.EQ); + + // One of the operands of XOR is not all ones vector. + testCompareMaskNotLong(L_SPECIES, VectorOperators.EQ, (m) -> { return L_SPECIES.maskAll(false).xor(m); }); + verifyResultsLong(L_SPECIES, VectorOperators.NE); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 3", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureOr = { "sve", "true", "avx512", "true", "rvv", "true" }) + @IR(counts = { IRNode.XOR_V, "= 3", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + @IR(counts = { IRNode.XOR_V, "= 3", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureAnd = { "avx2", "true" }) + public static void testCompareMaskNotFloatNegative() { + testCompareMaskNotFloat(F_SPECIES, VectorOperators.EQ, fa, fb, (m) -> { + // The vector mask is used multiple times. + ic[0] = m.trueCount(); + return m.not(); + }); + verifyResultsFloat(F_SPECIES, VectorOperators.EQ, fa, fb); + + // One of the operands of XOR is not all ones vector. + testCompareMaskNotFloat(F_SPECIES, VectorOperators.EQ, fa, fb, (m) -> { return F_SPECIES.maskAll(false).xor(m); }); + verifyResultsFloat(F_SPECIES, VectorOperators.NE, fa, fb); + + // Float vectors use the LT comparison. + testCompareMaskNotFloat(F_SPECIES, VectorOperators.LT, fa, fb, (m) -> { return m.not(); }); + verifyResultsFloat(F_SPECIES, VectorOperators.LT, fa, fb); + } + + @Test + @IR(counts = { IRNode.XOR_V_MASK, "= 3", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureOr = { "sve", "true", "avx512", "true", "rvv", "true" }) + @IR(counts = { IRNode.XOR_V, "= 3", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + @IR(counts = { IRNode.XOR_V, "= 3", + IRNode.VECTOR_MASK_CMP, "= 3" }, + applyIfCPUFeatureAnd = { "avx2", "true" }) + public static void testCompareMaskNotDoubleNegative() { + testCompareMaskNotDouble(D_SPECIES, VectorOperators.EQ, da, db, (m) -> { + // The vector mask is used multiple times. + ic[0] = m.trueCount(); + return m.not(); + }); + verifyResultsDouble(D_SPECIES, VectorOperators.EQ, da, db); + + // One of the operands of XOR is not all ones vector. + testCompareMaskNotDouble(D_SPECIES, VectorOperators.EQ, da, db, (m) -> { return D_SPECIES.maskAll(false).xor(m); }); + verifyResultsDouble(D_SPECIES, VectorOperators.NE, da, db); + + // Double vectors use the LT comparison. + testCompareMaskNotDouble(D_SPECIES, VectorOperators.LT, da, db, (m) -> { return m.not(); }); + verifyResultsDouble(D_SPECIES, VectorOperators.LT, da, db); + } + + public static void main(String[] args) { + TestFramework testFramework = new TestFramework(); + testFramework.setDefaultWarmup(5000) + .addFlags("--add-modules=jdk.incubator.vector") + .start(); + } +} diff --git a/test/hotspot/jtreg/gtest/ShenandoahGtests.java b/test/hotspot/jtreg/gtest/ShenandoahGtests.java new file mode 100644 index 000000000000..1e8c404fc129 --- /dev/null +++ b/test/hotspot/jtreg/gtest/ShenandoahGtests.java @@ -0,0 +1,31 @@ +/* + * 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 + * @summary Run Shenandoah gtests + * @library /test/lib + * @requires vm.gc.Shenandoah + * @requires vm.debug + * @run main/native GTestWrapper --gtest_filter=Shenandoah* + */ diff --git a/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPSetAuthenticatorTest.java b/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPSetAuthenticatorTest.java index 4d6a74e760b0..723d203c93ad 100644 --- a/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPSetAuthenticatorTest.java +++ b/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPSetAuthenticatorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, 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 @@ -68,13 +68,11 @@ * @run main/othervm -Dhttp.auth.digest.reEnabledAlgorithms=MD5 HTTPSetAuthenticatorTest DIGEST PROXY305 * @run main/othervm -Dhttp.auth.digest.reEnabledAlgorithms=MD5 HTTPSetAuthenticatorTest DIGEST SERVER307 * @run main/othervm HTTPSetAuthenticatorTest BASIC SERVER - * @run main/othervm HTTPSetAuthenticatorTest BASIC PROXY + * @run main/othervm -Djdk.http.auth.tunneling.disabledSchemes= HTTPSetAuthenticatorTest BASIC PROXY * @run main/othervm HTTPSetAuthenticatorTest BASIC PROXY305 * @run main/othervm HTTPSetAuthenticatorTest BASIC SERVER307 * @run main/othervm HTTPSetAuthenticatorTest BASICSERVER SERVER * @run main/othervm HTTPSetAuthenticatorTest BASICSERVER SERVER307 - * - * @author danielfuchs */ public class HTTPSetAuthenticatorTest extends HTTPTest { diff --git a/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTestServer.java b/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTestServer.java index aa158c3b6678..6ceb281e364c 100644 --- a/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTestServer.java +++ b/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTestServer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, 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 @@ -53,6 +53,7 @@ import java.util.Arrays; import java.util.Base64; import java.util.HexFormat; +import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.Random; @@ -60,6 +61,7 @@ import java.util.stream.Collectors; import javax.net.ssl.SSLContext; import sun.net.www.HeaderParser; +import sun.net.www.MessageHeader; /** * A simple HTTP server that supports Digest authentication. @@ -344,10 +346,11 @@ public static HTTPTestServer createServer(HttpProtocolType protocol, Objects.requireNonNull(auth); HttpServer impl = createHttpServer(protocol); + AuthResponder authResponder = createAuthResponder(schemeType, auth, authType, algorithm); final HTTPTestServer server = new HTTPTestServer(impl, null, delegate); final HttpHandler hh = server.createHandler(schemeType, auth, authType); HttpContext ctxt = impl.createContext(path, hh); - server.configureAuthentication(ctxt, schemeType, auth, authType, algorithm); + server.configureAuthentication(ctxt, schemeType, authResponder, authType); impl.start(); return server; } @@ -363,12 +366,19 @@ public static HTTPTestServer createProxy(HttpProtocolType protocol, Objects.requireNonNull(auth); HttpServer impl = createHttpServer(protocol); + AuthResponder authResponder = createAuthResponder(schemeType, auth, authType, null); final HTTPTestServer server = protocol == HttpProtocolType.HTTPS - ? new HttpsProxyTunnel(impl, null, delegate) + ? new HttpsProxyTunnel(impl, null, delegate, authResponder) : new HTTPTestServer(impl, null, delegate); final HttpHandler hh = server.createHandler(schemeType, auth, authType); HttpContext ctxt = impl.createContext(path, hh); - server.configureAuthentication(ctxt, schemeType, auth, authType, null); + if (protocol == HttpProtocolType.HTTPS) { + server.configureAuthentication(ctxt, HttpSchemeType.NONE, + new NoAuthResponder(auth, HttpAuthType.SERVER), + HttpAuthType.SERVER); + } else { + server.configureAuthentication(ctxt, schemeType, authResponder, authType); + } impl.start(); return server; @@ -441,16 +451,16 @@ private HttpHandler createHandler(HttpSchemeType schemeType, private void configureAuthentication(HttpContext ctxt, HttpSchemeType schemeType, - HttpTestAuthenticator auth, - HttpAuthType authType, String algorithm) { + AuthResponder authResponder, + HttpAuthType authType) { switch(schemeType) { case DIGEST: // DIGEST authentication is handled by the handler. - ctxt.getFilters().add(new HttpDigestFilter(auth, authType, algorithm)); + ctxt.getFilters().add(new HttpDigestFilter(authResponder)); break; case BASIC: // BASIC authentication is handled by the filter. - ctxt.getFilters().add(new HttpBasicFilter(auth, authType)); + ctxt.getFilters().add(new HttpBasicFilter(authResponder)); break; case BASICSERVER: switch(authType) { @@ -458,14 +468,14 @@ private void configureAuthentication(HttpContext ctxt, // HttpServer can't support Proxy-type authentication // => we do as if BASIC had been specified, and we will // handle authentication in the handler. - ctxt.getFilters().add(new HttpBasicFilter(auth, authType)); + ctxt.getFilters().add(new HttpBasicFilter(authResponder)); break; case SERVER: case SERVER307: // Basic authentication is handled by HttpServer // directly => the filter should not perform // authentication again. - setContextAuthenticator(ctxt, auth); - ctxt.getFilters().add(new HttpNoAuthFilter(authType)); + setContextAuthenticator(ctxt, authResponder.authenticator); + ctxt.getFilters().add(new HttpNoAuthFilter(authResponder)); break; default: throw new InternalError("Invalid combination scheme=" @@ -473,7 +483,7 @@ private void configureAuthentication(HttpContext ctxt, } case NONE: // No authentication at all. - ctxt.getFilters().add(new HttpNoAuthFilter(authType)); + ctxt.getFilters().add(new HttpNoAuthFilter(authResponder)); break; default: throw new InternalError("No such scheme: " + schemeType); @@ -485,38 +495,230 @@ private HttpHandler create300Handler(URL proxyURL, return new Http3xxHandler(proxyURL, type, code300); } - // Abstract HTTP filter class. - private abstract static class AbstractHttpFilter extends Filter { - + private static abstract class AuthResponder { final HttpAuthType authType; + final HttpTestAuthenticator authenticator; final String type; - public AbstractHttpFilter(HttpAuthType authType, String type) { + + AuthResponder(HttpTestAuthenticator authenticator, + HttpAuthType authType, + String scheme) { + this.authenticator = authenticator; this.authType = authType; - this.type = type; + this.type = authType == HttpAuthType.PROXY + ? scheme + " Proxy" + : scheme + " Server"; } - String getLocation() { - return "Location"; - } - String getAuthenticate() { + final String authenticateHeader() { return authType == HttpAuthType.PROXY - ? "Proxy-Authenticate" : "WWW-Authenticate"; + ? "Proxy-Authenticate" + : "WWW-Authenticate"; } - String getAuthorization() { + final String authorizationHeader() { return authType == HttpAuthType.PROXY - ? "Proxy-Authorization" : "Authorization"; + ? "Proxy-Authorization" + : "Authorization"; } - int getUnauthorizedCode() { + int unauthorizedCode() { return authType == HttpAuthType.PROXY ? HttpURLConnection.HTTP_PROXY_AUTH : HttpURLConnection.HTTP_UNAUTHORIZED; } - String getKeepAlive() { - return "keep-alive"; - } - String getConnection() { + String unauthorizedString() { return authType == HttpAuthType.PROXY - ? "Proxy-Connection" : "Connection"; + ? "Proxy Authentication Required" + : "Unauthorized"; + } + String type() { return type;} + abstract String generateAuthenticateChallenge(); + abstract boolean isAuthentified(String method, Iterator authValues); + } + + private static final class BasicAuthResponder extends AuthResponder { + BasicAuthResponder(HttpTestAuthenticator authenticator, HttpAuthType authType) { + super(authenticator, authType, "Basic"); + } + + @Override + String generateAuthenticateChallenge() { + return "Basic realm=\"" + authenticator.getRealm() + "\""; + } + + @Override + boolean isAuthentified(String method, Iterator authValues) { + while(authValues.hasNext()) { + String a = authValues.next(); + System.out.println(type + ": processing " + a); + int sp = a.indexOf(' '); + if (sp < 0) return false; + String scheme = a.substring(0, sp); + if (!"Basic".equalsIgnoreCase(scheme)) { + System.out.println(type + ": Unsupported scheme '" + + scheme +"'"); + return false; + } + if (a.length() <= sp+1) { + System.out.println(type + ": value too short for '" + + scheme +"'"); + return false; + } + a = a.substring(sp+1); + return validate(a); + } + return false; + } + + boolean validate(String a) { + byte[] b = Base64.getDecoder().decode(a); + String userpass = new String (b); + int colon = userpass.indexOf (':'); + String uname = userpass.substring (0, colon); + String pass = userpass.substring (colon+1); + return authenticator.getUserName().equals(uname) && + new String(authenticator.getPassword(uname)).equals(pass); + } + + } + + private static final class DigestAuthResponder extends AuthResponder { + // This is a very basic DIGEST - used only for the purpose of testing + // the client implementation. Therefore we can get away with never + // updating the server nonce as it makes the implementation of the + // server side digest simpler. + private final byte[] nonce; + private final String ns; + private final String algorithm; + DigestAuthResponder(HttpTestAuthenticator authenticator, HttpAuthType authType, String algorithm) { + super(authenticator, authType, "Digest"); + nonce = new byte[16]; + new Random(Instant.now().toEpochMilli()).nextBytes(nonce); + ns = new BigInteger(1, nonce).toString(16); + this.algorithm = (algorithm == null) ? "MD5" : algorithm; + } + + @Override + String generateAuthenticateChallenge() { + return "Digest realm=\"" + authenticator.getRealm() + "\"," + + "\r\n qop=\"auth\", " + "algorithm=\"" + algorithm + "\", " + + "\r\n nonce=\"" + ns +"\""; + } + + @Override + boolean isAuthentified(String method, Iterator authValues) { + while(authValues.hasNext()) { + String a = authValues.next(); + System.out.println(type + ": processing " + a); + int sp = a.indexOf(' '); + if (sp < 0) return false; + String scheme = a.substring(0, sp); + if (!"Digest".equalsIgnoreCase(scheme)) { + System.out.println(type + ": Unsupported scheme '" + scheme +"'"); + return false; + } + if (a.length() <= sp+1) { + System.out.println(type + ": value too short for '" + scheme +"'"); + return false; + } + a = a.substring(sp+1); + DigestResponse dgr = DigestResponse.create(a); + return validate(method, dgr); + } + return false; + } + + boolean validate(String reqMethod, DigestResponse dg) { + if (!this.algorithm.equalsIgnoreCase(dg.getAlgorithm("MD5"))) { + System.out.println(type + ": Unsupported algorithm " + + dg.algorithm); + return false; + } + if (!"auth".equalsIgnoreCase(dg.getQoP("auth"))) { + System.out.println(type + ": Unsupported qop " + + dg.qop); + return false; + } + try { + if (!dg.nonce.equals(ns)) { + System.out.println(type + ": bad nonce returned by client: " + + nonce + " expected " + ns); + return false; + } + if (dg.response == null) { + System.out.println(type + ": missing digest response."); + return false; + } + char[] pa = authenticator.getPassword(dg.username); + return verify(reqMethod, dg, pa); + } catch(IllegalArgumentException | SecurityException + | NoSuchAlgorithmException e) { + System.out.println(type + ": " + e.getMessage()); + return false; + } + } + + boolean verify(String reqMethod, DigestResponse dg, char[] pw) + throws NoSuchAlgorithmException { + String response = DigestResponse.computeDigest(true, reqMethod, pw, algorithm, dg); + if (!dg.response.equals(response)) { + System.out.println(type + ": bad response returned by client: " + + dg.response + " expected " + response); + return false; + } else { + System.out.println(type + ": verified response " + response); + } + return true; + } + + } + + private static final class NoAuthResponder extends AuthResponder { + NoAuthResponder(HttpTestAuthenticator authenticator, HttpAuthType authType) { + super(authenticator, authType, "NoAuth"); + } + + @Override + String generateAuthenticateChallenge() { + throw new InternalError("Should not reach here"); + } + + @Override + boolean isAuthentified(String method, Iterator authValues) { + return true; + } + } + + private static AuthResponder createAuthResponder(HttpSchemeType schemeType, + HttpTestAuthenticator authenticator, + HttpAuthType authType, + String algorithm) { + switch (schemeType) { + case BASIC, BASICSERVER: return new BasicAuthResponder(authenticator, authType); + case DIGEST: return new DigestAuthResponder(authenticator, authType, algorithm); + case NONE: return new NoAuthResponder(authenticator, authType); + default: throw new IllegalArgumentException( + "Unknown authentication scheme: " + schemeType); + } + } + + // Abstract HTTP filter class. + private abstract static class AbstractHttpFilter extends Filter { + + final AuthResponder authResponder; + final String type; + public AbstractHttpFilter(AuthResponder authResponder) { + this.authResponder = authResponder; + this.type = authResponder.type(); + } + + final String getAuthenticate() { + return authResponder.authenticateHeader(); + } + final String getAuthorization() { + return authResponder.authorizationHeader(); + } + final int getUnauthorizedCode() { + return authResponder.unauthorizedCode(); } protected abstract boolean isAuthentified(HttpExchange he) throws IOException; protected abstract void requestAuthentication(HttpExchange he) throws IOException; @@ -694,11 +896,10 @@ public static DigestResponse create(String raw) { } - private class HttpNoAuthFilter extends AbstractHttpFilter { + private static final class HttpNoAuthFilter extends AbstractHttpFilter { - public HttpNoAuthFilter(HttpAuthType authType) { - super(authType, authType == HttpAuthType.SERVER - ? "NoAuth Server" : "NoAuth Proxy"); + public HttpNoAuthFilter(AuthResponder authResponder) { + super(authResponder); } @Override @@ -720,19 +921,15 @@ public String description() { // An HTTP Filter that performs Basic authentication private class HttpBasicFilter extends AbstractHttpFilter { - - private final HttpTestAuthenticator auth; - public HttpBasicFilter(HttpTestAuthenticator auth, HttpAuthType authType) { - super(authType, authType == HttpAuthType.SERVER - ? "Basic Server" : "Basic Proxy"); - this.auth = auth; + public HttpBasicFilter(AuthResponder authResponder) { + super(authResponder); } @Override protected void requestAuthentication(HttpExchange he) throws IOException { - he.getResponseHeaders().add(getAuthenticate(), - "Basic realm=\"" + auth.getRealm() + "\""); + String challenge = authResponder.generateAuthenticateChallenge(); + he.getResponseHeaders().add(getAuthenticate(), challenge); System.out.println(type + ": Requesting Basic Authentication " + he.getResponseHeaders().getFirst(getAuthenticate())); } @@ -742,39 +939,12 @@ protected boolean isAuthentified(HttpExchange he) { if (he.getRequestHeaders().containsKey(getAuthorization())) { List authorization = he.getRequestHeaders().get(getAuthorization()); - for (String a : authorization) { - System.out.println(type + ": processing " + a); - int sp = a.indexOf(' '); - if (sp < 0) return false; - String scheme = a.substring(0, sp); - if (!"Basic".equalsIgnoreCase(scheme)) { - System.out.println(type + ": Unsupported scheme '" - + scheme +"'"); - return false; - } - if (a.length() <= sp+1) { - System.out.println(type + ": value too short for '" - + scheme +"'"); - return false; - } - a = a.substring(sp+1); - return validate(a); - } - return false; + return authResponder.isAuthentified(he.getRequestMethod(), + authorization.iterator()); } return false; } - boolean validate(String a) { - byte[] b = Base64.getDecoder().decode(a); - String userpass = new String (b); - int colon = userpass.indexOf (':'); - String uname = userpass.substring (0, colon); - String pass = userpass.substring (colon+1); - return auth.getUserName().equals(uname) && - new String(auth.getPassword(uname)).equals(pass); - } - @Override public String description() { return "Filter for " + type; @@ -786,31 +956,14 @@ public String description() { // An HTTP Filter that performs Digest authentication private class HttpDigestFilter extends AbstractHttpFilter { - // This is a very basic DIGEST - used only for the purpose of testing - // the client implementation. Therefore we can get away with never - // updating the server nonce as it makes the implementation of the - // server side digest simpler. - private final HttpTestAuthenticator auth; - private final byte[] nonce; - private final String ns; - private final String algorithm; - public HttpDigestFilter(HttpTestAuthenticator auth, HttpAuthType authType, String algorithm) { - super(authType, authType == HttpAuthType.SERVER - ? "Digest Server" : "Digest Proxy"); - this.auth = auth; - nonce = new byte[16]; - new Random(Instant.now().toEpochMilli()).nextBytes(nonce); - ns = new BigInteger(1, nonce).toString(16); - this.algorithm = (algorithm == null) ? "MD5" : algorithm; + public HttpDigestFilter(AuthResponder authResponder) { + super(authResponder); } @Override protected void requestAuthentication(HttpExchange he) throws IOException { - he.getResponseHeaders().add(getAuthenticate(), - "Digest realm=\"" + auth.getRealm() + "\"," - + "\r\n qop=\"auth\", " + "algorithm=\"" + algorithm + "\", " - + "\r\n nonce=\"" + ns +"\""); + he.getResponseHeaders().add(getAuthenticate(), authResponder.generateAuthenticateChallenge()); System.out.println(type + ": Requesting Digest Authentication " + he.getResponseHeaders().getFirst(getAuthenticate())); } @@ -819,71 +972,11 @@ protected void requestAuthentication(HttpExchange he) protected boolean isAuthentified(HttpExchange he) { if (he.getRequestHeaders().containsKey(getAuthorization())) { List authorization = he.getRequestHeaders().get(getAuthorization()); - for (String a : authorization) { - System.out.println(type + ": processing " + a); - int sp = a.indexOf(' '); - if (sp < 0) return false; - String scheme = a.substring(0, sp); - if (!"Digest".equalsIgnoreCase(scheme)) { - System.out.println(type + ": Unsupported scheme '" + scheme +"'"); - return false; - } - if (a.length() <= sp+1) { - System.out.println(type + ": value too short for '" + scheme +"'"); - return false; - } - a = a.substring(sp+1); - DigestResponse dgr = DigestResponse.create(a); - return validate(he.getRequestMethod(), dgr); - } - return false; + return authResponder.isAuthentified(he.getRequestMethod(), authorization.iterator()); } return false; } - boolean validate(String reqMethod, DigestResponse dg) { - if (!this.algorithm.equalsIgnoreCase(dg.getAlgorithm("MD5"))) { - System.out.println(type + ": Unsupported algorithm " - + dg.algorithm); - return false; - } - if (!"auth".equalsIgnoreCase(dg.getQoP("auth"))) { - System.out.println(type + ": Unsupported qop " - + dg.qop); - return false; - } - try { - if (!dg.nonce.equals(ns)) { - System.out.println(type + ": bad nonce returned by client: " - + nonce + " expected " + ns); - return false; - } - if (dg.response == null) { - System.out.println(type + ": missing digest response."); - return false; - } - char[] pa = auth.getPassword(dg.username); - return verify(reqMethod, dg, pa); - } catch(IllegalArgumentException | SecurityException - | NoSuchAlgorithmException e) { - System.out.println(type + ": " + e.getMessage()); - return false; - } - } - - boolean verify(String reqMethod, DigestResponse dg, char[] pw) - throws NoSuchAlgorithmException { - String response = DigestResponse.computeDigest(true, reqMethod, pw, algorithm, dg); - if (!dg.response.equals(response)) { - System.out.println(type + ": bad response returned by client: " - + dg.response + " expected " + response); - return false; - } else { - System.out.println(type + ": verified response " + response); - } - return true; - } - @Override public String description() { return "Filter for DIGEST authentication"; @@ -979,22 +1072,23 @@ public void configure (HttpsParameters params) { } } - // This is a bit hacky: HttpsProxyTunnel is an HTTPTestServer hidden - // behind a fake proxy that only understands CONNECT requests. - // The fake proxy is just a server socket that intercept the - // CONNECT and then redirect streams to the real server. + // The HttpsProxyTunnel is a proxy that only understands + // CONNECT requests. It is only used for tunnelling, but + // supports Proxy Authentication with the help of an + // AuthResponder static class HttpsProxyTunnel extends HTTPTestServer implements Runnable { final ServerSocket ss; + final AuthResponder authResponder; private volatile boolean stop; public HttpsProxyTunnel(HttpServer server, HTTPTestServer target, - HttpHandler delegate) + HttpHandler delegate, AuthResponder authResponder) throws IOException { super(server, target, delegate); System.out.flush(); - System.err.println("WARNING: HttpsProxyTunnel is an experimental test class"); + this.authResponder = authResponder; ss = ServerSocketFactory.create(); start(); } @@ -1048,28 +1142,6 @@ public InetSocketAddress getProxyAddress() { return new InetSocketAddress(ss.getInetAddress(), ss.getLocalPort()); } - // This is a bit shaky. It doesn't handle continuation - // lines, but our client shouldn't send any. - // Read a line from the input stream, swallowing the final - // \r\n sequence. Stops at the first \n, doesn't complain - // if it wasn't preceded by '\r'. - // - String readLine(InputStream r) throws IOException { - StringBuilder b = new StringBuilder(); - int c; - while ((c = r.read()) != -1) { - if (c == '\n') break; - b.appendCodePoint(c); - } - if (b.length() == 0) { - return ""; - } - if (b.codePointAt(b.length() -1) == '\r') { - b.delete(b.length() -1, b.length()); - } - return b.toString(); - } - @Override public void run() { Socket clientConnection = null; @@ -1137,6 +1209,37 @@ public void run() { } } + private boolean isAuthentified(MessageHeader request) { + String requestLine = request.getValue(0); + String method = requestLine.substring(0, requestLine.indexOf(' ')); + assert "CONNECT".equals(method); + return authResponder.isAuthentified(method, + request.multiValueIterator(authResponder.authorizationHeader())); + } + + private String challengeResponse() { + return "HTTP/1.1 " + authResponder.unauthorizedCode() + " " + + authResponder.unauthorizedString() + + "\r\nContent-Length: 0\r\n" + + authResponder.authenticateHeader() + ": " + + authResponder.generateAuthenticateChallenge() + + "\r\n\r\n"; + } + + private String okResponse() { + return "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"; + } + + private String badGatewayResponse() { + return "HTTP/1.1 502 Bad Gateway\r\nContent-Length: 0\r\n\r\n"; + } + + private void sendResponse(PrintWriter pw, String response) { + System.out.println("Tunnel: Sending " + response); + pw.print(response); + pw.flush(); + } + private void processRequestAndWaitToComplete(final Socket clientConnection) throws IOException, InterruptedException { final Socket targetConnection; @@ -1146,32 +1249,24 @@ private void processRequestAndWaitToComplete(final Socket clientConnection) clientConnection.getOutputStream(), "UTF-8"); PrintWriter pw = new PrintWriter(w); System.out.println("Tunnel: Reading request line"); - String requestLine = readLine(ccis); + MessageHeader request = new MessageHeader(ccis); + String requestLine = request.getValue(0); System.out.println("Tunnel: Request line: " + requestLine); - if (requestLine.startsWith("CONNECT ")) { - // We should probably check that the next word following - // CONNECT is the host:port of our HTTPS serverImpl. - // Some improvement for a followup! - - // Read all headers until we find the empty line that - // signals the end of all headers. - while(!requestLine.equals("")) { - System.out.println("Tunnel: Reading header: " - + (requestLine = readLine(ccis))); + if (requestLine != null && requestLine.startsWith("CONNECT ")) { + if (!isAuthentified(request)) { + sendResponse(pw, challengeResponse()); + return; } - targetConnection = new Socket( serverImpl.getAddress().getAddress(), serverImpl.getAddress().getPort()); // Then send the 200 OK response to the client - System.out.println("Tunnel: Sending " - + "HTTP/1.1 200 OK\r\n\r\n"); - pw.print("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); - pw.flush(); + sendResponse(pw, okResponse()); } else { // This should not happen. If it does then consider it a // client error and throw an IOException + sendResponse(pw, badGatewayResponse()); System.out.println("Tunnel: Throwing an IOException due to unexpected" + " request line: " + requestLine); throw new IOException("Client request error - Unexpected request line"); diff --git a/test/jdk/sun/security/x509/AlgorithmId/AlgorithmIdEqualsHashCode.java b/test/jdk/sun/security/x509/AlgorithmId/AlgorithmIdEqualsHashCode.java index ff91e3dff81f..be3da70f8510 100644 --- a/test/jdk/sun/security/x509/AlgorithmId/AlgorithmIdEqualsHashCode.java +++ b/test/jdk/sun/security/x509/AlgorithmId/AlgorithmIdEqualsHashCode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,16 +24,19 @@ /* * @test * @author Gary Ellison - * @bug 4170635 8258247 + * @bug 4170635 8258247 8367008 + * @library /test/lib * @summary Verify equals()/hashCode() contract honored * @modules java.base/sun.security.x509 java.base/sun.security.util */ -import java.io.*; +import java.io.IOException; import java.security.AlgorithmParameters; import java.security.spec.MGF1ParameterSpec; import java.security.spec.PSSParameterSpec; +import jdk.test.lib.Asserts; + import sun.security.util.DerValue; import sun.security.x509.*; @@ -97,5 +100,20 @@ public static void main(String[] args) throws Exception { } else { System.out.println("PASSED equals() test"); } + + // Construct an AlgorithmId with explicit DER NULL parameters + DerValue explicitNullParams = new DerValue(DerValue.tag_Null, new byte[0]); + AlgorithmId aiNullParams = new AlgorithmId(AlgorithmId.SHA256_oid, + explicitNullParams); + // The constructor should canonicalize this to "no parameters" + Asserts.assertTrue(aiNullParams.getEncodedParams() == null); + AlgorithmId aiNormal = AlgorithmId.get("SHA-256"); + Asserts.assertEquals(aiNullParams, aiNormal); + Asserts.assertEquals(aiNullParams.hashCode(), aiNormal.hashCode()); + + // Test invalid ASN.1 NULL (non-zero length) + DerValue invalidNull = new DerValue(DerValue.tag_Null, new byte[]{0x00}); + Asserts.assertThrows(IOException.class, + () -> new AlgorithmId(AlgorithmId.SHA256_oid, invalidNull)); } } diff --git a/test/jdk/sun/security/x509/AlgorithmId/NullParams.java b/test/jdk/sun/security/x509/AlgorithmId/NullParams.java index 733ab9fa5223..0b542997a199 100644 --- a/test/jdk/sun/security/x509/AlgorithmId/NullParams.java +++ b/test/jdk/sun/security/x509/AlgorithmId/NullParams.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -67,6 +67,13 @@ public static void main(String[] args) throws Exception { test("SHA3-256withRSA", true); test("SHA3-384withRSA", true); test("SHA3-512withRSA", true); + test("HmacSHA1", true); + test("HmacSHA224", true); + test("HmacSHA256", true); + test("HmacSHA384", true); + test("HmacSHA512", true); + test("HmacSHA512/224", true); + test("HmacSHA512/256", true); // Full old list: must be absent test("SHA1withECDSA", false); @@ -83,7 +90,6 @@ public static void main(String[] args) throws Exception { // Others test("DSA", false); test("SHA1withDSA", false); - test("HmacSHA1", false); if (failed) { throw new RuntimeException("At least one failed"); diff --git a/test/jdk/tools/jlink/plugins/DefaultStripDebugPluginTest.java b/test/jdk/tools/jlink/plugins/DefaultStripDebugPluginTest.java index 10d534348fab..379b89adc9ff 100644 --- a/test/jdk/tools/jlink/plugins/DefaultStripDebugPluginTest.java +++ b/test/jdk/tools/jlink/plugins/DefaultStripDebugPluginTest.java @@ -73,10 +73,48 @@ public void testNoNativeStripPluginPresent() { } } + // Disable embedded strip Java plugin, with native plugin present. + public void testOnlyNativePlugin() { + MockStripPlugin javaPlugin = new MockStripPlugin(false); + MockStripPlugin nativePlugin = new MockStripPlugin(true); + TestNativeStripPluginFactory nativeFactory = + new TestNativeStripPluginFactory(nativePlugin); + DefaultStripDebugPlugin plugin = new DefaultStripDebugPlugin(javaPlugin, + nativeFactory); + plugin.enableJavaStripPlugin(false); + + ResourcePoolManager inManager = new ResourcePoolManager(); + ResourcePool pool = plugin.transform(inManager.resourcePool(), + inManager.resourcePoolBuilder()); + if (pool.findEntry(MockStripPlugin.JAVA_PATH).isPresent() || + !pool.findEntry(MockStripPlugin.NATIVE_PATH).isPresent()) { + throw new AssertionError("Expected only native to get called"); + } + } + + // Disable embedded strip Java plugin, and without native plugin present. + public void testNoOperation() { + MockStripPlugin javaPlugin = new MockStripPlugin(false); + TestNativeStripPluginFactory nativeFactory = + new TestNativeStripPluginFactory(null); + DefaultStripDebugPlugin plugin = new DefaultStripDebugPlugin(javaPlugin, + nativeFactory); + plugin.enableJavaStripPlugin(false); + ResourcePoolManager inManager = new ResourcePoolManager(); + ResourcePool pool = plugin.transform(inManager.resourcePool(), + inManager.resourcePoolBuilder()); + if (pool.findEntry(MockStripPlugin.JAVA_PATH).isPresent() || + pool.findEntry(MockStripPlugin.NATIVE_PATH).isPresent()) { + throw new AssertionError("Expected both native and java not called"); + } + } + public static void main(String[] args) { DefaultStripDebugPluginTest test = new DefaultStripDebugPluginTest(); test.testNoNativeStripPluginPresent(); test.testWithNativeStripPresent(); + test.testOnlyNativePlugin(); + test.testNoOperation(); } public static class MockStripPlugin implements Plugin { diff --git a/test/jdk/tools/jlink/plugins/StripParameterNamesTest.java b/test/jdk/tools/jlink/plugins/StripParameterNamesTest.java new file mode 100644 index 000000000000..c270adec0493 --- /dev/null +++ b/test/jdk/tools/jlink/plugins/StripParameterNamesTest.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.FieldSource; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Stream; +import java.util.spi.ToolProvider; + +import jdk.test.lib.util.FileUtils; +import tests.JImageGenerator; +import tests.JImageGenerator.InMemorySourceFile; +import tests.Result; + +/* + * @test + * @summary Test jlink strip debug plugins handle method parameter names. + * @bug 8347007 + * @library ../../lib + * @library /test/lib + * @modules java.base/jdk.internal.jimage + * jdk.jlink/jdk.tools.jlink.internal + * jdk.jlink/jdk.tools.jlink.plugin + * jdk.jlink/jdk.tools.jmod + * jdk.jlink/jdk.tools.jimage + * jdk.compiler + * @build jdk.test.lib.util.FileUtils + * tests.* + * @run junit/othervm StripParameterNamesTest + */ +public class StripParameterNamesTest { + private static final ToolProvider JAVAC_TOOL = ToolProvider.findFirst("javac") + .orElseThrow(() -> new RuntimeException("javac tool not found")); + + private static Path src = Path.of("src").toAbsolutePath(); + private static List testJmods = new ArrayList<>(); + + record Jmod(Path moduleDir, boolean withDebugInfo, boolean withParameterNames) {} + + @BeforeAll + public static void setup() throws IOException { + Files.createDirectory(src); + var mainClassSource = new InMemorySourceFile("test", "InspectParameterNames", """ + package test; + + public class InspectParameterNames { + int add(int a, int b) { + return a + b; + } + + public static boolean hasParameterNames() throws NoSuchMethodException { + // Get add method in the class + var method = InspectParameterNames.class.getDeclaredMethod("add", int.class, int.class); + + // Get method parameters + var parameters = method.getParameters(); + + // validate parameter names + return parameters[0].getName().equals("a") && parameters[1].getName().equals("b"); + } + + public static void main(String[] args) throws NoSuchMethodException { + System.out.println(hasParameterNames()); + } + } + """); + var moduleDir = JImageGenerator.generateSources(src, "bug8347007x", List.of(mainClassSource)); + JImageGenerator.generateModuleInfo(moduleDir, List.of("test")); + testJmods.add(buildJmod(true, true)); + testJmods.add(buildJmod(true, false)); + testJmods.add(buildJmod(false, true)); + testJmods.add(buildJmod(false, false)); + } + + @AfterEach + public void cleanup() throws IOException { + FileUtils.deleteFileTreeWithRetry(Path.of("img")); + } + + static void report(String command, List args) { + System.out.println(command + " " + String.join(" ", args)); + } + + static void javac(List args) { + report("javac", args); + JAVAC_TOOL.run(System.out, System.err, args.toArray(new String[0])); + } + + /** + * Build jmods from the module source path + */ + static Jmod buildJmod(boolean withDebugInfo, boolean withParameterNames) { + String dirName = "jmods"; + List options = new ArrayList<>(); + + if (withDebugInfo) { + options.add("-g"); + dirName += "g"; + } + + if (withParameterNames) { + options.add("-parameters"); + dirName += "p"; + } + + Path moduleDir = Path.of(dirName).toAbsolutePath(); + + options.add("-d"); + options.add(moduleDir.toString()); + options.add("--module-source-path"); + options.add(src.toString()); + options.add("--module"); + options.add("bug8347007x"); + + javac(options); + return new Jmod(moduleDir, withDebugInfo, withParameterNames); + } + + Result buildImage(Path modulePath, Path imageDir, String... options) { + var jlinkTask = JImageGenerator.getJLinkTask() + .modulePath(modulePath.toString()) + .output(imageDir); + + for (var option: options) { + jlinkTask.option(option); + } + + return jlinkTask.addMods("bug8347007x") + .call(); + } + + void assertHasParameterNames(Path imageDir, boolean expected) throws IOException, InterruptedException { + Path binDir = imageDir.resolve("bin").toAbsolutePath(); + Path bin = binDir.resolve("java"); + + ProcessBuilder processBuilder = new ProcessBuilder(bin.toString(), + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+BytecodeVerificationLocal", + "-m", "bug8347007x/test.InspectParameterNames"); + processBuilder.directory(binDir.toFile()); + Process process = processBuilder.start(); + int exitCode = process.waitFor(); + var output = process.inputReader().readLine(); + System.out.println(output); + assertEquals(expected, Boolean.parseBoolean(output)); + } + + Stream provideTestJmods() { + return testJmods.stream(); + } + + @ParameterizedTest + @FieldSource("testJmods") + public void testDefaultBehavior(Jmod jmod) throws Exception { + var imageDir = Path.of("img"); + buildImage(jmod.moduleDir(), imageDir) + .assertSuccess(); + var hasParameter = jmod.withParameterNames(); + assertHasParameterNames(imageDir, hasParameter); + } + + @ParameterizedTest + @FieldSource("testJmods") + public void testStripDebug(Jmod jmod) throws Exception { + var imageDir = Path.of("img"); + buildImage(jmod.moduleDir(), imageDir, + "--strip-debug") + .assertSuccess(); + var hasParameter = jmod.withParameterNames(); + assertHasParameterNames(imageDir, hasParameter); + } + + @Test + public void testBothStripOptions() throws Exception { + var imageDir = Path.of("img"); + var jmod = testJmods.get(0); + buildImage(jmod.moduleDir(), imageDir, + "--strip-debug", "--strip-java-debug-attributes") + .assertSuccess(); + assertHasParameterNames(imageDir, jmod.withParameterNames()); + } + + @ParameterizedTest + @FieldSource("testJmods") + public void testOnlyStripJavaDebugAttributes() throws Exception { + var imageDir = Path.of("img"); + var jmod = testJmods.get(0); + buildImage(jmod.moduleDir(), imageDir, + "--strip-java-debug-attributes") + .assertSuccess(); + assertHasParameterNames(imageDir, jmod.withParameterNames()); + } +} \ No newline at end of file diff --git a/test/lib/jdk/test/lib/security/XMLUtils.java b/test/lib/jdk/test/lib/security/XMLUtils.java index e70a30d9b3d2..62090c9c8619 100644 --- a/test/lib/jdk/test/lib/security/XMLUtils.java +++ b/test/lib/jdk/test/lib/security/XMLUtils.java @@ -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 @@ -187,6 +187,7 @@ public static Signer signer(PrivateKey privateKey) public static class Signer { + private String baseURI = null; final PrivateKey privateKey; // signer key, never null X509Certificate cert; // certificate, optional @@ -253,6 +254,11 @@ public Signer prop(String name, Object o) { return this; } + public Signer baseURI(String base) { + this.baseURI = base; + return this; + } + // Signs different sources // Signs an XML file in detached mode @@ -341,6 +347,9 @@ private DOMSignContext withProps(DOMSignContext ctxt) { for (var e : props.entrySet()) { ctxt.setProperty(e.getKey(), e.getValue()); } + if (baseURI != null) { + ctxt.setBaseURI(baseURI); + } return ctxt; } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/vector/MaskCompareNotBenchmark.java b/test/micro/org/openjdk/bench/jdk/incubator/vector/MaskCompareNotBenchmark.java new file mode 100644 index 000000000000..d83bc126a1d0 --- /dev/null +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/MaskCompareNotBenchmark.java @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2025, NVIDIA CORPORATION & 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.jdk.incubator.vector; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.*; + +import jdk.incubator.vector.*; +import java.lang.invoke.*; +import java.util.concurrent.TimeUnit; +import java.util.Random; + +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.SECONDS) +@State(Scope.Thread) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(value = 2, jvmArgs = { "--add-modules=jdk.incubator.vector" }) +public abstract class MaskCompareNotBenchmark { + @Param({"4096"}) + protected int ARRAYLEN; + + // Abstract method to get comparison operator from subclasses + protected abstract String getComparisonOperatorName(); + + // To get compile-time constants for comparison operation + static final MutableCallSite MUTABLE_COMPARISON_CONSTANT = new MutableCallSite(MethodType.methodType(VectorOperators.Comparison.class)); + static final MethodHandle MUTABLE_COMPARISON_CONSTANT_HANDLE = MUTABLE_COMPARISON_CONSTANT.dynamicInvoker(); + + private static Random r = new Random(); + + protected static final VectorSpecies B_SPECIES = ByteVector.SPECIES_MAX; + protected static final VectorSpecies S_SPECIES = ShortVector.SPECIES_MAX; + protected static final VectorSpecies I_SPECIES = IntVector.SPECIES_MAX; + protected static final VectorSpecies L_SPECIES = LongVector.SPECIES_MAX; + protected static final VectorSpecies F_SPECIES = FloatVector.SPECIES_MAX; + protected static final VectorSpecies D_SPECIES = DoubleVector.SPECIES_MAX; + + protected boolean[] mr; + protected byte[] ba; + protected byte[] bb; + protected short[] sa; + protected short[] sb; + protected int[] ia; + protected int[] ib; + protected long[] la; + protected long[] lb; + protected float[] fa; + protected float[] fb; + protected double[] da; + protected double[] db; + + @Setup + public void init() throws Throwable { + mr = new boolean[ARRAYLEN]; + ba = new byte[ARRAYLEN]; + bb = new byte[ARRAYLEN]; + sa = new short[ARRAYLEN]; + sb = new short[ARRAYLEN]; + ia = new int[ARRAYLEN]; + ib = new int[ARRAYLEN]; + la = new long[ARRAYLEN]; + lb = new long[ARRAYLEN]; + fa = new float[ARRAYLEN]; + fb = new float[ARRAYLEN]; + da = new double[ARRAYLEN]; + db = new double[ARRAYLEN]; + + for (int i = 0; i < ARRAYLEN; i++) { + mr[i] = r.nextBoolean(); + ba[i] = (byte) r.nextInt(); + bb[i] = (byte) r.nextInt(); + sa[i] = (short) r.nextInt(); + sb[i] = (short) r.nextInt(); + ia[i] = r.nextInt(); + ib[i] = r.nextInt(); + la[i] = r.nextLong(); + lb[i] = r.nextLong(); + fa[i] = r.nextFloat(); + fb[i] = r.nextFloat(); + da[i] = r.nextDouble(); + db[i] = r.nextDouble(); + } + + VectorOperators.Comparison comparisonOp = getComparisonOperator(getComparisonOperatorName()); + MethodHandle constant = MethodHandles.constant(VectorOperators.Comparison.class, comparisonOp); + MUTABLE_COMPARISON_CONSTANT.setTarget(constant); + } + + @CompilerControl(CompilerControl.Mode.INLINE) + private static VectorOperators.Comparison getComparisonOperator(String op) { + switch (op) { + case "EQ": return VectorOperators.EQ; + case "NE": return VectorOperators.NE; + case "LT": return VectorOperators.LT; + case "LE": return VectorOperators.LE; + case "GT": return VectorOperators.GT; + case "GE": return VectorOperators.GE; + case "ULT": return VectorOperators.ULT; + case "ULE": return VectorOperators.ULE; + case "UGT": return VectorOperators.UGT; + case "UGE": return VectorOperators.UGE; + default: throw new IllegalArgumentException("Unknown comparison operator: " + op); + } + } + + @CompilerControl(CompilerControl.Mode.INLINE) + protected VectorOperators.Comparison comparison_con() throws Throwable { + return (VectorOperators.Comparison) MUTABLE_COMPARISON_CONSTANT_HANDLE.invokeExact(); + } + + // Subclasses with different comparison operators + public static class IntegerComparisons extends MaskCompareNotBenchmark { + @Param({"EQ", "NE", "LT", "LE", "GT", "GE", "ULT", "ULE", "UGT", "UGE"}) + public String COMPARISON_OP; + + @Override + protected String getComparisonOperatorName() { + return COMPARISON_OP; + } + + @Benchmark + public void testCompareMaskNotByte() throws Throwable { + VectorOperators.Comparison op = comparison_con(); + ByteVector bv = ByteVector.fromArray(B_SPECIES, bb, 0); + for (int j = 0; j < ARRAYLEN; j += B_SPECIES.length()) { + ByteVector av = ByteVector.fromArray(B_SPECIES, ba, j); + VectorMask m = av.compare(op, bv).not(); + m.intoArray(mr, j); + } + } + + @Benchmark + public void testCompareMaskNotShort() throws Throwable { + VectorOperators.Comparison op = comparison_con(); + ShortVector bv = ShortVector.fromArray(S_SPECIES, sb, 0); + for (int j = 0; j < ARRAYLEN; j += S_SPECIES.length()) { + ShortVector av = ShortVector.fromArray(S_SPECIES, sa, j); + VectorMask m = av.compare(op, bv).not(); + m.intoArray(mr, j); + } + } + + @Benchmark + public void testCompareMaskNotInt() throws Throwable { + VectorOperators.Comparison op = comparison_con(); + IntVector bv = IntVector.fromArray(I_SPECIES, ib, 0); + for (int j = 0; j < ARRAYLEN; j += I_SPECIES.length()) { + IntVector av = IntVector.fromArray(I_SPECIES, ia, j); + VectorMask m = av.compare(op, bv).not(); + m.intoArray(mr, j); + } + } + + @Benchmark + public void testCompareMaskNotLong() throws Throwable { + VectorOperators.Comparison op = comparison_con(); + LongVector bv = LongVector.fromArray(L_SPECIES, lb, 0); + for (int j = 0; j < ARRAYLEN; j += L_SPECIES.length()) { + LongVector av = LongVector.fromArray(L_SPECIES, la, j); + VectorMask m = av.compare(op, bv).not(); + m.intoArray(mr, j); + } + } + } + + public static class FloatingPointComparisons extends MaskCompareNotBenchmark { + // "ULT", "ULE", "UGT", "UGE" are not supported for floating point types + @Param({"EQ", "NE", "LT", "LE", "GT", "GE"}) + public String COMPARISON_OP; + + @Override + protected String getComparisonOperatorName() { + return COMPARISON_OP; + } + + @Benchmark + public void testCompareMaskNotFloat() throws Throwable { + VectorOperators.Comparison op = comparison_con(); + FloatVector bv = FloatVector.fromArray(F_SPECIES, fb, 0); + for (int j = 0; j < ARRAYLEN; j += F_SPECIES.length()) { + FloatVector av = FloatVector.fromArray(F_SPECIES, fa, j); + VectorMask m = av.compare(op, bv).not(); + m.intoArray(mr, j); + } + } + + @Benchmark + public void testCompareMaskNotDouble() throws Throwable { + VectorOperators.Comparison op = comparison_con(); + DoubleVector bv = DoubleVector.fromArray(D_SPECIES, db, 0); + for (int j = 0; j < ARRAYLEN; j += D_SPECIES.length()) { + DoubleVector av = DoubleVector.fromArray(D_SPECIES, da, j); + VectorMask m = av.compare(op, bv).not(); + m.intoArray(mr, j); + } + } + } +}