# HG changeset patch # User mgronlun # Date 1570656176 -7200 # Node ID ec9da3c0eaec5972d2cff2277c3b9ac95a33e121 # Parent 2a49d43aaa6a2ccfa43c8b86f76174c8f4cf88b1# Parent e84d8379815ba0d3e50fb096d28c25894cb50b8c Merge diff -r 2a49d43aaa6a -r ec9da3c0eaec make/CreateJmods.gmk --- a/make/CreateJmods.gmk Wed Oct 09 23:21:07 2019 +0200 +++ b/make/CreateJmods.gmk Wed Oct 09 23:22:56 2019 +0200 @@ -86,16 +86,18 @@ # from there. These files were explicitly filtered or modified in -copy # targets. For the rest, just pick up everything from the source legal dirs. LEGAL_NOTICES := \ - $(SUPPORT_OUTPUTDIR)/modules_legal/common \ + $(wildcard $(SUPPORT_OUTPUTDIR)/modules_legal/common) \ $(if $(wildcard $(SUPPORT_OUTPUTDIR)/modules_legal/$(MODULE)), \ $(wildcard $(SUPPORT_OUTPUTDIR)/modules_legal/$(MODULE)), \ $(call FindModuleLegalSrcDirs, $(MODULE)) \ ) -LEGAL_NOTICES_PATH := $(call PathList, $(LEGAL_NOTICES)) -DEPS += $(call FindFiles, $(LEGAL_NOTICES)) +ifneq ($(strip $(LEGAL_NOTICES)), ) + LEGAL_NOTICES_PATH := $(call PathList, $(LEGAL_NOTICES)) + DEPS += $(call FindFiles, $(LEGAL_NOTICES)) -JMOD_FLAGS += --legal-notices $(LEGAL_NOTICES_PATH) + JMOD_FLAGS += --legal-notices $(LEGAL_NOTICES_PATH) +endif ifeq ($(filter-out jdk.incubator.%, $(MODULE)), ) JMOD_FLAGS += --do-not-resolve-by-default diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/cpu/aarch64/aarch64.ad --- a/src/hotspot/cpu/aarch64/aarch64.ad Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/cpu/aarch64/aarch64.ad Wed Oct 09 23:22:56 2019 +0200 @@ -2513,17 +2513,8 @@ __ INSN(REG, as_Register(BASE)); \ } -typedef void (MacroAssembler::* mem_insn)(Register Rt, const Address &adr); -typedef void (MacroAssembler::* mem_float_insn)(FloatRegister Rt, const Address &adr); -typedef void (MacroAssembler::* mem_vector_insn)(FloatRegister Rt, - MacroAssembler::SIMD_RegVariant T, const Address &adr); - - // Used for all non-volatile memory accesses. The use of - // $mem->opcode() to discover whether this pattern uses sign-extended - // offsets is something of a kludge. - static void loadStore(MacroAssembler masm, mem_insn insn, - Register reg, int opcode, - Register base, int index, int size, int disp) + +static Address mem2address(int opcode, Register base, int index, int size, int disp) { Address::extend scale; @@ -2542,16 +2533,34 @@ } if (index == -1) { - (masm.*insn)(reg, Address(base, disp)); + return Address(base, disp); } else { assert(disp == 0, "unsupported address mode: disp = %d", disp); - (masm.*insn)(reg, Address(base, as_Register(index), scale)); + return Address(base, as_Register(index), scale); } } + +typedef void (MacroAssembler::* mem_insn)(Register Rt, const Address &adr); +typedef void (MacroAssembler::* mem_insn2)(Register Rt, Register adr); +typedef void (MacroAssembler::* mem_float_insn)(FloatRegister Rt, const Address &adr); +typedef void (MacroAssembler::* mem_vector_insn)(FloatRegister Rt, + MacroAssembler::SIMD_RegVariant T, const Address &adr); + + // Used for all non-volatile memory accesses. The use of + // $mem->opcode() to discover whether this pattern uses sign-extended + // offsets is something of a kludge. + static void loadStore(MacroAssembler masm, mem_insn insn, + Register reg, int opcode, + Register base, int index, int size, int disp) + { + Address addr = mem2address(opcode, base, index, size, disp); + (masm.*insn)(reg, addr); + } + static void loadStore(MacroAssembler masm, mem_float_insn insn, - FloatRegister reg, int opcode, - Register base, int index, int size, int disp) + FloatRegister reg, int opcode, + Register base, int index, int size, int disp) { Address::extend scale; @@ -2573,8 +2582,8 @@ } static void loadStore(MacroAssembler masm, mem_vector_insn insn, - FloatRegister reg, MacroAssembler::SIMD_RegVariant T, - int opcode, Register base, int index, int size, int disp) + FloatRegister reg, MacroAssembler::SIMD_RegVariant T, + int opcode, Register base, int index, int size, int disp) { if (index == -1) { (masm.*insn)(reg, T, Address(base, disp)); @@ -3791,7 +3800,7 @@ static const int hi[Op_RegL + 1] = { // enum name 0, // Op_Node 0, // Op_Set - OptoReg::Bad, // Op_RegN + OptoReg::Bad, // Op_RegN OptoReg::Bad, // Op_RegI R0_H_num, // Op_RegP OptoReg::Bad, // Op_RegF @@ -6923,7 +6932,7 @@ instruct loadP(iRegPNoSp dst, memory mem) %{ match(Set dst (LoadP mem)); - predicate(!needs_acquiring_load(n)); + predicate(!needs_acquiring_load(n) && (n->as_Load()->barrier_data() == 0)); ins_cost(4 * INSN_COST); format %{ "ldr $dst, $mem\t# ptr" %} @@ -7616,6 +7625,7 @@ instruct loadP_volatile(iRegPNoSp dst, /* sync_memory*/indirect mem) %{ match(Set dst (LoadP mem)); + predicate(n->as_Load()->barrier_data() == 0); ins_cost(VOLATILE_REF_COST); format %{ "ldar $dst, $mem\t# ptr" %} @@ -8552,6 +8562,7 @@ instruct compareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval, rFlagsReg cr) %{ match(Set res (CompareAndSwapP mem (Binary oldval newval))); + predicate(n->as_LoadStore()->barrier_data() == 0); ins_cost(2 * VOLATILE_REF_COST); effect(KILL cr); @@ -8665,7 +8676,7 @@ instruct compareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval, rFlagsReg cr) %{ - predicate(needs_acquiring_load_exclusive(n)); + predicate(needs_acquiring_load_exclusive(n) && (n->as_LoadStore()->barrier_data() == 0)); match(Set res (CompareAndSwapP mem (Binary oldval newval))); ins_cost(VOLATILE_REF_COST); @@ -8796,6 +8807,7 @@ %} instruct compareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, rFlagsReg cr) %{ + predicate(n->as_LoadStore()->barrier_data() == 0); match(Set res (CompareAndExchangeP mem (Binary oldval newval))); ins_cost(2 * VOLATILE_REF_COST); effect(TEMP_DEF res, KILL cr); @@ -8895,7 +8907,7 @@ %} instruct compareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, rFlagsReg cr) %{ - predicate(needs_acquiring_load_exclusive(n)); + predicate(needs_acquiring_load_exclusive(n) && (n->as_LoadStore()->barrier_data() == 0)); match(Set res (CompareAndExchangeP mem (Binary oldval newval))); ins_cost(VOLATILE_REF_COST); effect(TEMP_DEF res, KILL cr); @@ -8996,6 +9008,7 @@ %} instruct weakCompareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval, rFlagsReg cr) %{ + predicate(n->as_LoadStore()->barrier_data() == 0); match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); ins_cost(2 * VOLATILE_REF_COST); effect(KILL cr); @@ -9103,8 +9116,8 @@ %} instruct weakCompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval, rFlagsReg cr) %{ - predicate(needs_acquiring_load_exclusive(n)); match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); + predicate(needs_acquiring_load_exclusive(n) && (n->as_LoadStore()->barrier_data() == 0)); ins_cost(VOLATILE_REF_COST); effect(KILL cr); format %{ @@ -9154,6 +9167,7 @@ %} instruct get_and_setP(indirect mem, iRegP newv, iRegPNoSp prev) %{ + predicate(n->as_LoadStore()->barrier_data() == 0); match(Set prev (GetAndSetP mem newv)); ins_cost(2 * VOLATILE_REF_COST); format %{ "atomic_xchg $prev, $newv, [$mem]" %} @@ -9197,7 +9211,7 @@ %} instruct get_and_setPAcq(indirect mem, iRegP newv, iRegPNoSp prev) %{ - predicate(needs_acquiring_load_exclusive(n)); + predicate(needs_acquiring_load_exclusive(n) && (n->as_LoadStore()->barrier_data() == 0)); match(Set prev (GetAndSetP mem newv)); ins_cost(VOLATILE_REF_COST); format %{ "atomic_xchg_acq $prev, $newv, [$mem]" %} diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/cpu/aarch64/compiledIC_aarch64.cpp --- a/src/hotspot/cpu/aarch64/compiledIC_aarch64.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/cpu/aarch64/compiledIC_aarch64.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2018, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -162,16 +162,12 @@ // Creation also verifies the object. NativeMovConstReg* method_holder = nativeMovConstReg_at(stub + NativeInstruction::instruction_size); -#ifndef PRODUCT - NativeGeneralJump* jump = nativeGeneralJump_at(method_holder->next_instruction_address()); - // read the value once - volatile intptr_t data = method_holder->data(); - assert(data == 0 || data == (intptr_t)callee(), - "a) MT-unsafe modification of inline cache"); - assert(data == 0 || jump->jump_destination() == entry, - "b) MT-unsafe modification of inline cache"); +#ifdef ASSERT + NativeGeneralJump* jump = nativeGeneralJump_at(method_holder->next_instruction_address()); + verify_mt_safe(callee, entry, method_holder, jump); #endif + // Update stub. method_holder->set_data((intptr_t)callee()); NativeGeneralJump::insert_unconditional(method_holder->next_instruction_address(), entry); diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp --- a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -24,22 +24,23 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" #include "code/codeBlob.hpp" +#include "code/vmreg.inline.hpp" #include "gc/z/zBarrier.inline.hpp" #include "gc/z/zBarrierSet.hpp" #include "gc/z/zBarrierSetAssembler.hpp" #include "gc/z/zBarrierSetRuntime.hpp" +#include "gc/z/zThreadLocalData.hpp" #include "memory/resourceArea.hpp" +#include "runtime/sharedRuntime.hpp" +#include "utilities/macros.hpp" #ifdef COMPILER1 #include "c1/c1_LIRAssembler.hpp" #include "c1/c1_MacroAssembler.hpp" #include "gc/z/c1/zBarrierSetC1.hpp" #endif // COMPILER1 - -#include "gc/z/zThreadLocalData.hpp" - -ZBarrierSetAssembler::ZBarrierSetAssembler() : - _load_barrier_slow_stub(), - _load_barrier_weak_slow_stub() {} +#ifdef COMPILER2 +#include "gc/z/c2/zBarrierSetC2.hpp" +#endif // COMPILER2 #ifdef PRODUCT #define BLOCK_COMMENT(str) /* nothing */ @@ -66,7 +67,7 @@ assert_different_registers(rscratch1, rscratch2, src.base()); assert_different_registers(rscratch1, rscratch2, dst); - RegSet savedRegs = RegSet::range(r0,r28) - RegSet::of(dst, rscratch1, rscratch2); + RegSet savedRegs = RegSet::range(r0, r28) - RegSet::of(dst, rscratch1, rscratch2); Label done; @@ -206,7 +207,8 @@ // The Address offset is too large to direct load - -784. Our range is +127, -128. __ mov(tmp, (long int)(in_bytes(ZThreadLocalData::address_bad_mask_offset()) - - in_bytes(JavaThread::jni_environment_offset()))); + in_bytes(JavaThread::jni_environment_offset()))); + // Load address bad mask __ add(tmp, jni_env, tmp); __ ldr(tmp, Address(tmp)); @@ -294,12 +296,12 @@ __ prologue("zgc_load_barrier stub", false); // We don't use push/pop_clobbered_registers() - we need to pull out the result from r0. - for (int i = 0; i < 32; i +=2) { - __ stpd(as_FloatRegister(i), as_FloatRegister(i+1), Address(__ pre(sp,-16))); + for (int i = 0; i < 32; i += 2) { + __ stpd(as_FloatRegister(i), as_FloatRegister(i + 1), Address(__ pre(sp,-16))); } - RegSet saveRegs = RegSet::range(r0,r28) - RegSet::of(r0); - __ push(saveRegs, sp); + const RegSet save_regs = RegSet::range(r1, r28); + __ push(save_regs, sp); // Setup arguments __ load_parameter(0, c_rarg0); @@ -307,98 +309,161 @@ __ call_VM_leaf(ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_addr(decorators), 2); - __ pop(saveRegs, sp); + __ pop(save_regs, sp); - for (int i = 30; i >0; i -=2) { - __ ldpd(as_FloatRegister(i), as_FloatRegister(i+1), Address(__ post(sp, 16))); - } + for (int i = 30; i >= 0; i -= 2) { + __ ldpd(as_FloatRegister(i), as_FloatRegister(i + 1), Address(__ post(sp, 16))); + } __ epilogue(); } #endif // COMPILER1 +#ifdef COMPILER2 + +OptoReg::Name ZBarrierSetAssembler::refine_register(const Node* node, OptoReg::Name opto_reg) { + if (!OptoReg::is_reg(opto_reg)) { + return OptoReg::Bad; + } + + const VMReg vm_reg = OptoReg::as_VMReg(opto_reg); + if (vm_reg->is_FloatRegister()) { + return opto_reg & ~1; + } + + return opto_reg; +} + #undef __ -#define __ cgen->assembler()-> +#define __ _masm-> + +class ZSaveLiveRegisters { +private: + MacroAssembler* const _masm; + RegSet _gp_regs; + RegSet _fp_regs; + +public: + void initialize(ZLoadBarrierStubC2* stub) { + // Create mask of live registers + RegMask live = stub->live(); -// Generates a register specific stub for calling -// ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded() or -// ZBarrierSetRuntime::load_barrier_on_weak_oop_field_preloaded(). -// -// The raddr register serves as both input and output for this stub. When the stub is -// called the raddr register contains the object field address (oop*) where the bad oop -// was loaded from, which caused the slow path to be taken. On return from the stub the -// raddr register contains the good/healed oop returned from -// ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded() or -// ZBarrierSetRuntime::load_barrier_on_weak_oop_field_preloaded(). -static address generate_load_barrier_stub(StubCodeGenerator* cgen, Register raddr, DecoratorSet decorators) { - // Don't generate stub for invalid registers - if (raddr == zr || raddr == r29 || raddr == r30) { - return NULL; + // Record registers that needs to be saved/restored + while (live.is_NotEmpty()) { + const OptoReg::Name opto_reg = live.find_first_elem(); + live.Remove(opto_reg); + if (OptoReg::is_reg(opto_reg)) { + const VMReg vm_reg = OptoReg::as_VMReg(opto_reg); + if (vm_reg->is_Register()) { + _gp_regs += RegSet::of(vm_reg->as_Register()); + } else if (vm_reg->is_FloatRegister()) { + _fp_regs += RegSet::of((Register)vm_reg->as_FloatRegister()); + } else { + fatal("Unknown register type"); + } + } + } + + // Remove C-ABI SOE registers, scratch regs and _ref register that will be updated + _gp_regs -= RegSet::range(r19, r30) + RegSet::of(r8, r9, stub->ref()); + } + + ZSaveLiveRegisters(MacroAssembler* masm, ZLoadBarrierStubC2* stub) : + _masm(masm), + _gp_regs(), + _fp_regs() { + + // Figure out what registers to save/restore + initialize(stub); + + // Save registers + __ push(_gp_regs, sp); + __ push_fp(_fp_regs, sp); } - // Create stub name - char name[64]; - const bool weak = (decorators & ON_WEAK_OOP_REF) != 0; - os::snprintf(name, sizeof(name), "zgc_load_barrier%s_stub_%s", weak ? "_weak" : "", raddr->name()); + ~ZSaveLiveRegisters() { + // Restore registers + __ pop_fp(_fp_regs, sp); + __ pop(_gp_regs, sp); + } +}; + +#undef __ +#define __ _masm-> - __ align(CodeEntryAlignment); - StubCodeMark mark(cgen, "StubRoutines", os::strdup(name, mtCode)); - address start = __ pc(); +class ZSetupArguments { +private: + MacroAssembler* const _masm; + const Register _ref; + const Address _ref_addr; + +public: + ZSetupArguments(MacroAssembler* masm, ZLoadBarrierStubC2* stub) : + _masm(masm), + _ref(stub->ref()), + _ref_addr(stub->ref_addr()) { - // Save live registers - RegSet savedRegs = RegSet::range(r0,r18) - RegSet::of(raddr); - - __ enter(); - __ push(savedRegs, sp); - - // Setup arguments - if (raddr != c_rarg1) { - __ mov(c_rarg1, raddr); + // Setup arguments + if (_ref_addr.base() == noreg) { + // No self healing + if (_ref != c_rarg0) { + __ mov(c_rarg0, _ref); + } + __ mov(c_rarg1, 0); + } else { + // Self healing + if (_ref == c_rarg0) { + // _ref is already at correct place + __ lea(c_rarg1, _ref_addr); + } else if (_ref != c_rarg1) { + // _ref is in wrong place, but not in c_rarg1, so fix it first + __ lea(c_rarg1, _ref_addr); + __ mov(c_rarg0, _ref); + } else if (_ref_addr.base() != c_rarg0 && _ref_addr.index() != c_rarg0) { + assert(_ref == c_rarg1, "Mov ref first, vacating c_rarg0"); + __ mov(c_rarg0, _ref); + __ lea(c_rarg1, _ref_addr); + } else { + assert(_ref == c_rarg1, "Need to vacate c_rarg1 and _ref_addr is using c_rarg0"); + if (_ref_addr.base() == c_rarg0 || _ref_addr.index() == c_rarg0) { + __ mov(rscratch2, c_rarg1); + __ lea(c_rarg1, _ref_addr); + __ mov(c_rarg0, rscratch2); + } else { + ShouldNotReachHere(); + } + } + } } - __ ldr(c_rarg0, Address(raddr)); + ~ZSetupArguments() { + // Transfer result + if (_ref != r0) { + __ mov(_ref, r0); + } + } +}; + +#undef __ +#define __ masm-> - // Call barrier function - __ call_VM_leaf(ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_addr(decorators), c_rarg0, c_rarg1); +void ZBarrierSetAssembler::generate_c2_load_barrier_stub(MacroAssembler* masm, ZLoadBarrierStubC2* stub) const { + BLOCK_COMMENT("ZLoadBarrierStubC2"); + + // Stub entry + __ bind(*stub->entry()); - // Move result returned in r0 to raddr, if needed - if (raddr != r0) { - __ mov(raddr, r0); + { + ZSaveLiveRegisters save_live_registers(masm, stub); + ZSetupArguments setup_arguments(masm, stub); + __ mov(rscratch1, stub->slow_path()); + __ blr(rscratch1); } - __ pop(savedRegs, sp); - __ leave(); - __ ret(lr); - - return start; + // Stub exit + __ b(*stub->continuation()); } #undef __ -static void barrier_stubs_init_inner(const char* label, const DecoratorSet decorators, address* stub) { - const int nregs = 28; // Exclude FP, XZR, SP from calculation. - const int code_size = nregs * 254; // Rough estimate of code size - - ResourceMark rm; - - CodeBuffer buf(BufferBlob::create(label, code_size)); - StubCodeGenerator cgen(&buf); - - for (int i = 0; i < nregs; i++) { - const Register reg = as_Register(i); - stub[i] = generate_load_barrier_stub(&cgen, reg, decorators); - } -} - -void ZBarrierSetAssembler::barrier_stubs_init() { - barrier_stubs_init_inner("zgc_load_barrier_stubs", ON_STRONG_OOP_REF, _load_barrier_slow_stub); - barrier_stubs_init_inner("zgc_load_barrier_weak_stubs", ON_WEAK_OOP_REF, _load_barrier_weak_slow_stub); -} - -address ZBarrierSetAssembler::load_barrier_slow_stub(Register reg) { - return _load_barrier_slow_stub[reg->encoding()]; -} - -address ZBarrierSetAssembler::load_barrier_weak_slow_stub(Register reg) { - return _load_barrier_weak_slow_stub[reg->encoding()]; -} +#endif // COMPILER2 diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp --- a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -24,6 +24,12 @@ #ifndef CPU_AARCH64_GC_Z_ZBARRIERSETASSEMBLER_AARCH64_HPP #define CPU_AARCH64_GC_Z_ZBARRIERSETASSEMBLER_AARCH64_HPP +#include "code/vmreg.hpp" +#include "oops/accessDecorators.hpp" +#ifdef COMPILER2 +#include "opto/optoreg.hpp" +#endif // COMPILER2 + #ifdef COMPILER1 class LIR_Assembler; class LIR_OprDesc; @@ -32,14 +38,13 @@ class ZLoadBarrierStubC1; #endif // COMPILER1 +#ifdef COMPILER2 +class Node; +class ZLoadBarrierStubC2; +#endif // COMPILER2 + class ZBarrierSetAssembler : public ZBarrierSetAssemblerBase { -private: - address _load_barrier_slow_stub[RegisterImpl::number_of_registers]; - address _load_barrier_weak_slow_stub[RegisterImpl::number_of_registers]; - public: - ZBarrierSetAssembler(); - virtual void load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, @@ -83,10 +88,13 @@ DecoratorSet decorators) const; #endif // COMPILER1 - virtual void barrier_stubs_init(); +#ifdef COMPILER2 + OptoReg::Name refine_register(const Node* node, + OptoReg::Name opto_reg); - address load_barrier_slow_stub(Register reg); - address load_barrier_weak_slow_stub(Register reg); + void generate_c2_load_barrier_stub(MacroAssembler* masm, + ZLoadBarrierStubC2* stub) const; +#endif // COMPILER2 }; #endif // CPU_AARCH64_GC_Z_ZBARRIERSETASSEMBLER_AARCH64_HPP diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad --- a/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad Wed Oct 09 23:22:56 2019 +0200 @@ -24,155 +24,244 @@ source_hpp %{ #include "gc/z/c2/zBarrierSetC2.hpp" +#include "gc/z/zThreadLocalData.hpp" %} source %{ -#include "gc/z/zBarrierSetAssembler.hpp" - -static void z_load_barrier_slow_reg(MacroAssembler& _masm, Register dst, - Register base, int index, int scale, - int disp, bool weak) { - const address stub = weak ? ZBarrierSet::assembler()->load_barrier_weak_slow_stub(dst) - : ZBarrierSet::assembler()->load_barrier_slow_stub(dst); +static void z_load_barrier(MacroAssembler& _masm, const MachNode* node, Address ref_addr, Register ref, Register tmp, bool weak) { + ZLoadBarrierStubC2* const stub = ZLoadBarrierStubC2::create(node, ref_addr, ref, tmp, weak); + __ ldr(tmp, Address(rthread, ZThreadLocalData::address_bad_mask_offset())); + __ andr(tmp, tmp, ref); + __ cbnz(tmp, *stub->entry()); + __ bind(*stub->continuation()); +} - if (index == -1) { - if (disp != 0) { - __ lea(dst, Address(base, disp)); - } else { - __ mov(dst, base); - } - } else { - Register index_reg = as_Register(index); - if (disp == 0) { - __ lea(dst, Address(base, index_reg, Address::lsl(scale))); - } else { - __ lea(dst, Address(base, disp)); - __ lea(dst, Address(dst, index_reg, Address::lsl(scale))); - } - } - - __ far_call(RuntimeAddress(stub)); +static void z_load_barrier_slow_path(MacroAssembler& _masm, const MachNode* node, Address ref_addr, Register ref, Register tmp) { + ZLoadBarrierStubC2* const stub = ZLoadBarrierStubC2::create(node, ref_addr, ref, tmp, false /* weak */); + __ b(*stub->entry()); + __ bind(*stub->continuation()); } %} -// -// Execute ZGC load barrier (strong) slow path -// -instruct loadBarrierSlowReg(iRegP dst, memory src, rFlagsReg cr, - vRegD_V0 v0, vRegD_V1 v1, vRegD_V2 v2, vRegD_V3 v3, vRegD_V4 v4, - vRegD_V5 v5, vRegD_V6 v6, vRegD_V7 v7, vRegD_V8 v8, vRegD_V9 v9, - vRegD_V10 v10, vRegD_V11 v11, vRegD_V12 v12, vRegD_V13 v13, vRegD_V14 v14, - vRegD_V15 v15, vRegD_V16 v16, vRegD_V17 v17, vRegD_V18 v18, vRegD_V19 v19, - vRegD_V20 v20, vRegD_V21 v21, vRegD_V22 v22, vRegD_V23 v23, vRegD_V24 v24, - vRegD_V25 v25, vRegD_V26 v26, vRegD_V27 v27, vRegD_V28 v28, vRegD_V29 v29, - vRegD_V30 v30, vRegD_V31 v31) %{ - match(Set dst (LoadBarrierSlowReg src dst)); - predicate(!n->as_LoadBarrierSlowReg()->is_weak()); +// Load Pointer +instruct zLoadP(iRegPNoSp dst, memory mem, rFlagsReg cr) +%{ + match(Set dst (LoadP mem)); + predicate(UseZGC && !needs_acquiring_load(n) && (n->as_Load()->barrier_data() == ZLoadBarrierStrong)); + effect(TEMP dst, KILL cr); - effect(KILL cr, - KILL v0, KILL v1, KILL v2, KILL v3, KILL v4, KILL v5, KILL v6, KILL v7, - KILL v8, KILL v9, KILL v10, KILL v11, KILL v12, KILL v13, KILL v14, - KILL v15, KILL v16, KILL v17, KILL v18, KILL v19, KILL v20, KILL v21, - KILL v22, KILL v23, KILL v24, KILL v25, KILL v26, KILL v27, KILL v28, - KILL v29, KILL v30, KILL v31); + ins_cost(4 * INSN_COST); - format %{ "lea $dst, $src\n\t" - "call #ZLoadBarrierSlowPath" %} + format %{ "ldr $dst, $mem" %} ins_encode %{ - z_load_barrier_slow_reg(_masm, $dst$$Register, $src$$base$$Register, - $src$$index, $src$$scale, $src$$disp, false); + const Address ref_addr = mem2address($mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp); + __ ldr($dst$$Register, ref_addr); + if (barrier_data() != ZLoadBarrierElided) { + z_load_barrier(_masm, this, ref_addr, $dst$$Register, rscratch2 /* tmp */, false /* weak */); + } %} - ins_pipe(pipe_slow); + + ins_pipe(iload_reg_mem); %} -// -// Execute ZGC load barrier (weak) slow path -// -instruct loadBarrierWeakSlowReg(iRegP dst, memory src, rFlagsReg cr, - vRegD_V0 v0, vRegD_V1 v1, vRegD_V2 v2, vRegD_V3 v3, vRegD_V4 v4, - vRegD_V5 v5, vRegD_V6 v6, vRegD_V7 v7, vRegD_V8 v8, vRegD_V9 v9, - vRegD_V10 v10, vRegD_V11 v11, vRegD_V12 v12, vRegD_V13 v13, vRegD_V14 v14, - vRegD_V15 v15, vRegD_V16 v16, vRegD_V17 v17, vRegD_V18 v18, vRegD_V19 v19, - vRegD_V20 v20, vRegD_V21 v21, vRegD_V22 v22, vRegD_V23 v23, vRegD_V24 v24, - vRegD_V25 v25, vRegD_V26 v26, vRegD_V27 v27, vRegD_V28 v28, vRegD_V29 v29, - vRegD_V30 v30, vRegD_V31 v31) %{ - match(Set dst (LoadBarrierSlowReg src dst)); - predicate(n->as_LoadBarrierSlowReg()->is_weak()); +// Load Weak Pointer +instruct zLoadWeakP(iRegPNoSp dst, memory mem, rFlagsReg cr) +%{ + match(Set dst (LoadP mem)); + predicate(UseZGC && !needs_acquiring_load(n) && (n->as_Load()->barrier_data() == ZLoadBarrierWeak)); + effect(TEMP dst, KILL cr); - effect(KILL cr, - KILL v0, KILL v1, KILL v2, KILL v3, KILL v4, KILL v5, KILL v6, KILL v7, - KILL v8, KILL v9, KILL v10, KILL v11, KILL v12, KILL v13, KILL v14, - KILL v15, KILL v16, KILL v17, KILL v18, KILL v19, KILL v20, KILL v21, - KILL v22, KILL v23, KILL v24, KILL v25, KILL v26, KILL v27, KILL v28, - KILL v29, KILL v30, KILL v31); + ins_cost(4 * INSN_COST); - format %{ "lea $dst, $src\n\t" - "call #ZLoadBarrierSlowPath" %} + format %{ "ldr $dst, $mem" %} ins_encode %{ - z_load_barrier_slow_reg(_masm, $dst$$Register, $src$$base$$Register, - $src$$index, $src$$scale, $src$$disp, true); + const Address ref_addr = mem2address($mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp); + __ ldr($dst$$Register, ref_addr); + z_load_barrier(_masm, this, ref_addr, $dst$$Register, rscratch2 /* tmp */, true /* weak */); %} - ins_pipe(pipe_slow); + + ins_pipe(iload_reg_mem); %} +// Load Pointer Volatile +instruct zLoadPVolatile(iRegPNoSp dst, indirect mem /* sync_memory */, rFlagsReg cr) +%{ + match(Set dst (LoadP mem)); + predicate(UseZGC && needs_acquiring_load(n) && n->as_Load()->barrier_data() == ZLoadBarrierStrong); + effect(TEMP dst, KILL cr); -// Specialized versions of compareAndExchangeP that adds a keepalive that is consumed -// but doesn't affect output. + ins_cost(VOLATILE_REF_COST); -instruct z_compareAndExchangeP(iRegPNoSp res, indirect mem, - iRegP oldval, iRegP newval, iRegP keepalive, - rFlagsReg cr) %{ - match(Set res (ZCompareAndExchangeP (Binary mem keepalive) (Binary oldval newval))); - ins_cost(2 * VOLATILE_REF_COST); - effect(TEMP_DEF res, KILL cr); - format %{ - "cmpxchg $res = $mem, $oldval, $newval\t# (ptr, weak) if $mem == $oldval then $mem <-- $newval" + format %{ "ldar $dst, $mem\t" %} + + ins_encode %{ + __ ldar($dst$$Register, $mem$$Register); + if (barrier_data() != ZLoadBarrierElided) { + z_load_barrier(_masm, this, Address($mem$$Register), $dst$$Register, rscratch2 /* tmp */, false /* weak */); + } %} - ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ false, /*release*/ true, - /*weak*/ false, $res$$Register); - %} - ins_pipe(pipe_slow); + + ins_pipe(pipe_serial); %} -instruct z_compareAndSwapP(iRegINoSp res, - indirect mem, - iRegP oldval, iRegP newval, iRegP keepalive, - rFlagsReg cr) %{ - - match(Set res (ZCompareAndSwapP (Binary mem keepalive) (Binary oldval newval))); - match(Set res (ZWeakCompareAndSwapP (Binary mem keepalive) (Binary oldval newval))); +instruct zCompareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval, rFlagsReg cr) %{ + match(Set res (CompareAndSwapP mem (Binary oldval newval))); + match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); + predicate(UseZGC && !needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() == ZLoadBarrierStrong); + effect(KILL cr, TEMP_DEF res); ins_cost(2 * VOLATILE_REF_COST); - effect(KILL cr); + format %{ "cmpxchg $mem, $oldval, $newval\n\t" + "cset $res, EQ" %} - format %{ - "cmpxchg $mem, $oldval, $newval\t# (ptr) if $mem == $oldval then $mem <-- $newval" - "cset $res, EQ\t# $res <-- (EQ ? 1 : 0)" - %} - - ins_encode(aarch64_enc_cmpxchg(mem, oldval, newval), - aarch64_enc_cset_eq(res)); + ins_encode %{ + guarantee($mem$$index == -1 && $mem$$disp == 0, "impossible encoding"); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, + false /* acquire */, true /* release */, false /* weak */, rscratch2); + __ cset($res$$Register, Assembler::EQ); + if (barrier_data() != ZLoadBarrierElided) { + Label good; + __ ldr(rscratch1, Address(rthread, ZThreadLocalData::address_bad_mask_offset())); + __ andr(rscratch1, rscratch1, rscratch2); + __ cbz(rscratch1, good); + z_load_barrier_slow_path(_masm, this, Address($mem$$Register), rscratch2 /* ref */, rscratch1 /* tmp */); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, + false /* acquire */, true /* release */, false /* weak */, rscratch2); + __ cset($res$$Register, Assembler::EQ); + __ bind(good); + } + %} ins_pipe(pipe_slow); %} +instruct zCompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval, rFlagsReg cr) %{ + match(Set res (CompareAndSwapP mem (Binary oldval newval))); + match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); + predicate(UseZGC && needs_acquiring_load_exclusive(n) && (n->as_LoadStore()->barrier_data() == ZLoadBarrierStrong)); + effect(KILL cr, TEMP_DEF res); -instruct z_get_and_setP(indirect mem, iRegP newv, iRegPNoSp prev, - iRegP keepalive) %{ - match(Set prev (ZGetAndSetP mem (Binary newv keepalive))); + ins_cost(2 * VOLATILE_REF_COST); + + format %{ "cmpxchg $mem, $oldval, $newval\n\t" + "cset $res, EQ" %} + + ins_encode %{ + guarantee($mem$$index == -1 && $mem$$disp == 0, "impossible encoding"); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, + true /* acquire */, true /* release */, false /* weak */, rscratch2); + __ cset($res$$Register, Assembler::EQ); + if (barrier_data() != ZLoadBarrierElided) { + Label good; + __ ldr(rscratch1, Address(rthread, ZThreadLocalData::address_bad_mask_offset())); + __ andr(rscratch1, rscratch1, rscratch2); + __ cbz(rscratch1, good); + z_load_barrier_slow_path(_masm, this, Address($mem$$Register), rscratch2 /* ref */, rscratch1 /* tmp */ ); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, + true /* acquire */, true /* release */, false /* weak */, rscratch2); + __ cset($res$$Register, Assembler::EQ); + __ bind(good); + } + %} + + ins_pipe(pipe_slow); +%} + +instruct zCompareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, rFlagsReg cr) %{ + match(Set res (CompareAndExchangeP mem (Binary oldval newval))); + predicate(UseZGC && !needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() == ZLoadBarrierStrong); + effect(TEMP_DEF res, KILL cr); ins_cost(2 * VOLATILE_REF_COST); - format %{ "atomic_xchg $prev, $newv, [$mem]" %} + + format %{ "cmpxchg $res = $mem, $oldval, $newval" %} + + ins_encode %{ + guarantee($mem$$index == -1 && $mem$$disp == 0, "impossible encoding"); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, + false /* acquire */, true /* release */, false /* weak */, $res$$Register); + if (barrier_data() != ZLoadBarrierElided) { + Label good; + __ ldr(rscratch1, Address(rthread, ZThreadLocalData::address_bad_mask_offset())); + __ andr(rscratch1, rscratch1, $res$$Register); + __ cbz(rscratch1, good); + z_load_barrier_slow_path(_masm, this, Address($mem$$Register), $res$$Register /* ref */, rscratch1 /* tmp */); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, + false /* acquire */, true /* release */, false /* weak */, $res$$Register); + __ bind(good); + } + %} + + ins_pipe(pipe_slow); +%} + +instruct zCompareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, rFlagsReg cr) %{ + match(Set res (CompareAndExchangeP mem (Binary oldval newval))); + predicate(UseZGC && needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() == ZLoadBarrierStrong); + effect(TEMP_DEF res, KILL cr); + + ins_cost(2 * VOLATILE_REF_COST); + + format %{ "cmpxchg $res = $mem, $oldval, $newval" %} + ins_encode %{ - __ atomic_xchg($prev$$Register, $newv$$Register, as_Register($mem$$base)); + guarantee($mem$$index == -1 && $mem$$disp == 0, "impossible encoding"); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, + true /* acquire */, true /* release */, false /* weak */, $res$$Register); + if (barrier_data() != ZLoadBarrierElided) { + Label good; + __ ldr(rscratch1, Address(rthread, ZThreadLocalData::address_bad_mask_offset())); + __ andr(rscratch1, rscratch1, $res$$Register); + __ cbz(rscratch1, good); + z_load_barrier_slow_path(_masm, this, Address($mem$$Register), $res$$Register /* ref */, rscratch1 /* tmp */); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, + true /* acquire */, true /* release */, false /* weak */, $res$$Register); + __ bind(good); + } + %} + + ins_pipe(pipe_slow); +%} + +instruct zGetAndSetP(indirect mem, iRegP newv, iRegPNoSp prev, rFlagsReg cr) %{ + match(Set prev (GetAndSetP mem newv)); + predicate(UseZGC && !needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() == ZLoadBarrierStrong); + effect(TEMP_DEF prev, KILL cr); + + ins_cost(2 * VOLATILE_REF_COST); + + format %{ "atomic_xchg $prev, $newv, [$mem]" %} + + ins_encode %{ + __ atomic_xchg($prev$$Register, $newv$$Register, $mem$$Register); + if (barrier_data() != ZLoadBarrierElided) { + z_load_barrier(_masm, this, Address(noreg, 0), $prev$$Register, rscratch2 /* tmp */, false /* weak */); + } + %} + + ins_pipe(pipe_serial); +%} + +instruct zGetAndSetPAcq(indirect mem, iRegP newv, iRegPNoSp prev, rFlagsReg cr) %{ + match(Set prev (GetAndSetP mem newv)); + predicate(UseZGC && needs_acquiring_load_exclusive(n) && (n->as_LoadStore()->barrier_data() == ZLoadBarrierStrong)); + effect(TEMP_DEF prev, KILL cr); + + ins_cost(VOLATILE_REF_COST); + + format %{ "atomic_xchg_acq $prev, $newv, [$mem]" %} + + ins_encode %{ + __ atomic_xchgal($prev$$Register, $newv$$Register, $mem$$Register); + if (barrier_data() != ZLoadBarrierElided) { + z_load_barrier(_masm, this, Address(noreg, 0), $prev$$Register, rscratch2 /* tmp */, false /* weak */); + } %} ins_pipe(pipe_serial); %} diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -2132,6 +2132,65 @@ return count; } + +// Push lots of registers in the bit set supplied. Don't push sp. +// Return the number of words pushed +int MacroAssembler::push_fp(unsigned int bitset, Register stack) { + int words_pushed = 0; + + // Scan bitset to accumulate register pairs + unsigned char regs[32]; + int count = 0; + for (int reg = 0; reg <= 31; reg++) { + if (1 & bitset) + regs[count++] = reg; + bitset >>= 1; + } + regs[count++] = zr->encoding_nocheck(); + count &= ~1; // Only push an even number of regs + + // Always pushing full 128 bit registers. + if (count) { + stpq(as_FloatRegister(regs[0]), as_FloatRegister(regs[1]), Address(pre(stack, -count * wordSize * 2))); + words_pushed += 2; + } + for (int i = 2; i < count; i += 2) { + stpq(as_FloatRegister(regs[i]), as_FloatRegister(regs[i+1]), Address(stack, i * wordSize * 2)); + words_pushed += 2; + } + + assert(words_pushed == count, "oops, pushed != count"); + return count; +} + +int MacroAssembler::pop_fp(unsigned int bitset, Register stack) { + int words_pushed = 0; + + // Scan bitset to accumulate register pairs + unsigned char regs[32]; + int count = 0; + for (int reg = 0; reg <= 31; reg++) { + if (1 & bitset) + regs[count++] = reg; + bitset >>= 1; + } + regs[count++] = zr->encoding_nocheck(); + count &= ~1; + + for (int i = 2; i < count; i += 2) { + ldpq(as_FloatRegister(regs[i]), as_FloatRegister(regs[i+1]), Address(stack, i * wordSize * 2)); + words_pushed += 2; + } + if (count) { + ldpq(as_FloatRegister(regs[0]), as_FloatRegister(regs[1]), Address(post(stack, count * wordSize * 2))); + words_pushed += 2; + } + + assert(words_pushed == count, "oops, pushed != count"); + + return count; +} + #ifdef ASSERT void MacroAssembler::verify_heapbase(const char* msg) { #if 0 diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -442,12 +442,18 @@ int push(unsigned int bitset, Register stack); int pop(unsigned int bitset, Register stack); + int push_fp(unsigned int bitset, Register stack); + int pop_fp(unsigned int bitset, Register stack); + void mov(Register dst, Address a); public: void push(RegSet regs, Register stack) { if (regs.bits()) push(regs.bits(), stack); } void pop(RegSet regs, Register stack) { if (regs.bits()) pop(regs.bits(), stack); } + void push_fp(RegSet regs, Register stack) { if (regs.bits()) push_fp(regs.bits(), stack); } + void pop_fp(RegSet regs, Register stack) { if (regs.bits()) pop_fp(regs.bits(), stack); } + // Push and pop everything that might be clobbered by a native // runtime call except rscratch1 and rscratch2. (They are always // scratch, so we don't have to protect them.) Only save the lower diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/cpu/aarch64/register_aarch64.hpp --- a/src/hotspot/cpu/aarch64/register_aarch64.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/cpu/aarch64/register_aarch64.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -230,6 +230,11 @@ return *this; } + RegSet &operator-=(const RegSet aSet) { + *this = *this - aSet; + return *this; + } + static RegSet of(Register r1) { return RegSet(r1); } diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/cpu/arm/compiledIC_arm.cpp --- a/src/hotspot/cpu/arm/compiledIC_arm.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/cpu/arm/compiledIC_arm.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -115,16 +115,7 @@ // Creation also verifies the object. NativeMovConstReg* method_holder = nativeMovConstReg_at(stub); NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); - -#ifdef ASSERT - // read the value once - volatile intptr_t data = method_holder->data(); - volatile address destination = jump->jump_destination(); - assert(data == 0 || data == (intptr_t)callee(), - "a) MT-unsafe modification of inline cache"); - assert(destination == (address)-1 || destination == entry, - "b) MT-unsafe modification of inline cache"); -#endif + verify_mt_safe(callee, entry, method_holder, jump); // Update stub. method_holder->set_data((intptr_t)callee()); diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/cpu/ppc/compiledIC_ppc.cpp --- a/src/hotspot/cpu/ppc/compiledIC_ppc.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/cpu/ppc/compiledIC_ppc.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2015 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -178,15 +178,7 @@ NativeMovConstReg* method_holder = nativeMovConstReg_at(stub + IC_pos_in_java_to_interp_stub); NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); -#ifdef ASSERT - // read the value once - volatile intptr_t data = method_holder->data(); - volatile address destination = jump->jump_destination(); - assert(data == 0 || data == (intptr_t)callee(), - "a) MT-unsafe modification of inline cache"); - assert(destination == (address)-1 || destination == entry, - "b) MT-unsafe modification of inline cache"); -#endif + verify_mt_safe(callee, entry, method_holder, jump); // Update stub. method_holder->set_data((intptr_t)callee()); diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/cpu/s390/compiledIC_s390.cpp --- a/src/hotspot/cpu/s390/compiledIC_s390.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/cpu/s390/compiledIC_s390.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -104,19 +104,7 @@ // Creation also verifies the object. NativeMovConstReg* method_holder = nativeMovConstReg_at(stub + NativeCall::get_IC_pos_in_java_to_interp_stub()); NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); - -#ifdef ASSERT - // A generated lambda form might be deleted from the Lambdaform - // cache in MethodTypeForm. If a jit compiled lambdaform method - // becomes not entrant and the cache access returns null, the new - // resolve will lead to a new generated LambdaForm. - volatile intptr_t data = method_holder->data(); - volatile address destination = jump->jump_destination(); - assert(data == 0 || data == (intptr_t)callee() || callee->is_compiled_lambda_form(), - "a) MT-unsafe modification of inline cache"); - assert(destination == (address)-1 || destination == entry, - "b) MT-unsafe modification of inline cache"); -#endif + verify_mt_safe(callee, entry, method_holder, jump); // Update stub. method_holder->set_data((intptr_t)callee(), relocInfo::metadata_type); diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/cpu/sparc/compiledIC_sparc.cpp --- a/src/hotspot/cpu/sparc/compiledIC_sparc.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/cpu/sparc/compiledIC_sparc.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2019, 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 @@ -104,16 +104,7 @@ // Creation also verifies the object. NativeMovConstReg* method_holder = nativeMovConstReg_at(stub); NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); - -#ifdef ASSERT - // read the value once - volatile intptr_t data = method_holder->data(); - volatile address destination = jump->jump_destination(); - assert(data == 0 || data == (intptr_t)callee(), - "a) MT-unsafe modification of inline cache"); - assert(destination == (address)-1 || destination == entry, - "b) MT-unsafe modification of inline cache"); -#endif + verify_mt_safe(callee, entry, method_holder, jump); // Update stub. method_holder->set_data((intptr_t)callee()); diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/cpu/x86/compiledIC_x86.cpp --- a/src/hotspot/cpu/x86/compiledIC_x86.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/cpu/x86/compiledIC_x86.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2019, 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 @@ -157,16 +157,7 @@ // Creation also verifies the object. NativeMovConstReg* method_holder = nativeMovConstReg_at(stub); NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); - -#ifdef ASSERT - Method* old_method = reinterpret_cast(method_holder->data()); - address destination = jump->jump_destination(); - assert(old_method == NULL || old_method == callee() || - !old_method->method_holder()->is_loader_alive(), - "a) MT-unsafe modification of inline cache"); - assert(destination == (address)-1 || destination == entry, - "b) MT-unsafe modification of inline cache"); -#endif + verify_mt_safe(callee, entry, method_holder, jump); // Update stub. method_holder->set_data((intptr_t)callee()); diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/cpu/x86/gc/z/zArguments_x86.cpp --- a/src/hotspot/cpu/x86/gc/z/zArguments_x86.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/cpu/x86/gc/z/zArguments_x86.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -23,20 +23,7 @@ #include "precompiled.hpp" #include "gc/z/zArguments.hpp" -#include "runtime/globals.hpp" -#include "runtime/globals_extension.hpp" -#include "utilities/debug.hpp" void ZArguments::initialize_platform() { -#ifdef COMPILER2 - // The C2 barrier slow path expects vector registers to be least - // 16 bytes wide, which is the minimum width available on all - // x86-64 systems. However, the user could have speficied a lower - // number on the command-line, in which case we print a warning - // and raise it to 16. - if (MaxVectorSize < 16) { - warning("ZGC requires MaxVectorSize to be at least 16"); - FLAG_SET_DEFAULT(MaxVectorSize, 16); - } -#endif + // Does nothing } diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp --- a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -24,22 +24,22 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" #include "code/codeBlob.hpp" +#include "code/vmreg.inline.hpp" #include "gc/z/zBarrier.inline.hpp" #include "gc/z/zBarrierSet.hpp" #include "gc/z/zBarrierSetAssembler.hpp" #include "gc/z/zBarrierSetRuntime.hpp" #include "memory/resourceArea.hpp" -#include "runtime/stubCodeGenerator.hpp" +#include "runtime/sharedRuntime.hpp" #include "utilities/macros.hpp" #ifdef COMPILER1 #include "c1/c1_LIRAssembler.hpp" #include "c1/c1_MacroAssembler.hpp" #include "gc/z/c1/zBarrierSetC1.hpp" #endif // COMPILER1 - -ZBarrierSetAssembler::ZBarrierSetAssembler() : - _load_barrier_slow_stub(), - _load_barrier_weak_slow_stub() {} +#ifdef COMPILER2 +#include "gc/z/c2/zBarrierSetC2.hpp" +#endif // COMPILER2 #ifdef PRODUCT #define BLOCK_COMMENT(str) /* nothing */ @@ -344,137 +344,327 @@ #endif // COMPILER1 +#ifdef COMPILER2 + +OptoReg::Name ZBarrierSetAssembler::refine_register(const Node* node, OptoReg::Name opto_reg) { + if (!OptoReg::is_reg(opto_reg)) { + return OptoReg::Bad; + } + + const VMReg vm_reg = OptoReg::as_VMReg(opto_reg); + if (vm_reg->is_XMMRegister()) { + opto_reg &= ~15; + switch (node->ideal_reg()) { + case Op_VecX: + opto_reg |= 2; + break; + case Op_VecY: + opto_reg |= 4; + break; + case Op_VecZ: + opto_reg |= 8; + break; + default: + opto_reg |= 1; + break; + } + } + + return opto_reg; +} + +// We use the vec_spill_helper from the x86.ad file to avoid reinventing this wheel +extern int vec_spill_helper(CodeBuffer *cbuf, bool do_size, bool is_load, + int stack_offset, int reg, uint ireg, outputStream* st); + #undef __ -#define __ cgen->assembler()-> +#define __ _masm-> + +class ZSaveLiveRegisters { +private: + struct XMMRegisterData { + XMMRegister _reg; + int _size; + + // Used by GrowableArray::find() + bool operator == (const XMMRegisterData& other) { + return _reg == other._reg; + } + }; + + MacroAssembler* const _masm; + GrowableArray _gp_registers; + GrowableArray _xmm_registers; + int _spill_size; + int _spill_offset; -// Generates a register specific stub for calling -// ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded() or -// ZBarrierSetRuntime::load_barrier_on_weak_oop_field_preloaded(). -// -// The raddr register serves as both input and output for this stub. When the stub is -// called the raddr register contains the object field address (oop*) where the bad oop -// was loaded from, which caused the slow path to be taken. On return from the stub the -// raddr register contains the good/healed oop returned from -// ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded() or -// ZBarrierSetRuntime::load_barrier_on_weak_oop_field_preloaded(). -static address generate_load_barrier_stub(StubCodeGenerator* cgen, Register raddr, DecoratorSet decorators) { - // Don't generate stub for invalid registers - if (raddr == rsp || raddr == r15) { - return NULL; + static int xmm_compare_register_size(XMMRegisterData* left, XMMRegisterData* right) { + if (left->_size == right->_size) { + return 0; + } + + return (left->_size < right->_size) ? -1 : 1; + } + + static int xmm_slot_size(OptoReg::Name opto_reg) { + // The low order 4 bytes denote what size of the XMM register is live + return (opto_reg & 15) << 3; + } + + static uint xmm_ideal_reg_for_size(int reg_size) { + switch (reg_size) { + case 8: + return Op_VecD; + case 16: + return Op_VecX; + case 32: + return Op_VecY; + case 64: + return Op_VecZ; + default: + fatal("Invalid register size %d", reg_size); + return 0; + } + } + + bool xmm_needs_vzeroupper() const { + return _xmm_registers.is_nonempty() && _xmm_registers.at(0)._size > 16; + } + + void xmm_register_save(const XMMRegisterData& reg_data) { + const OptoReg::Name opto_reg = OptoReg::as_OptoReg(reg_data._reg->as_VMReg()); + const uint ideal_reg = xmm_ideal_reg_for_size(reg_data._size); + _spill_offset -= reg_data._size; + vec_spill_helper(__ code(), false /* do_size */, false /* is_load */, _spill_offset, opto_reg, ideal_reg, tty); + } + + void xmm_register_restore(const XMMRegisterData& reg_data) { + const OptoReg::Name opto_reg = OptoReg::as_OptoReg(reg_data._reg->as_VMReg()); + const uint ideal_reg = xmm_ideal_reg_for_size(reg_data._size); + vec_spill_helper(__ code(), false /* do_size */, true /* is_load */, _spill_offset, opto_reg, ideal_reg, tty); + _spill_offset += reg_data._size; + } + + void gp_register_save(Register reg) { + _spill_offset -= 8; + __ movq(Address(rsp, _spill_offset), reg); + } + + void gp_register_restore(Register reg) { + __ movq(reg, Address(rsp, _spill_offset)); + _spill_offset += 8; } - // Create stub name - char name[64]; - const bool weak = (decorators & ON_WEAK_OOP_REF) != 0; - os::snprintf(name, sizeof(name), "zgc_load_barrier%s_stub_%s", weak ? "_weak" : "", raddr->name()); + void initialize(ZLoadBarrierStubC2* stub) { + // Create mask of caller saved registers that need to + // be saved/restored if live + RegMask caller_saved; + caller_saved.Insert(OptoReg::as_OptoReg(rax->as_VMReg())); + caller_saved.Insert(OptoReg::as_OptoReg(rcx->as_VMReg())); + caller_saved.Insert(OptoReg::as_OptoReg(rdx->as_VMReg())); + caller_saved.Insert(OptoReg::as_OptoReg(rsi->as_VMReg())); + caller_saved.Insert(OptoReg::as_OptoReg(rdi->as_VMReg())); + caller_saved.Insert(OptoReg::as_OptoReg(r8->as_VMReg())); + caller_saved.Insert(OptoReg::as_OptoReg(r9->as_VMReg())); + caller_saved.Insert(OptoReg::as_OptoReg(r10->as_VMReg())); + caller_saved.Insert(OptoReg::as_OptoReg(r11->as_VMReg())); + caller_saved.Remove(OptoReg::as_OptoReg(stub->ref()->as_VMReg())); - __ align(CodeEntryAlignment); - StubCodeMark mark(cgen, "StubRoutines", os::strdup(name, mtCode)); - address start = __ pc(); + // Create mask of live registers + RegMask live = stub->live(); + if (stub->tmp() != noreg) { + live.Insert(OptoReg::as_OptoReg(stub->tmp()->as_VMReg())); + } + + int gp_spill_size = 0; + int xmm_spill_size = 0; + + // Record registers that needs to be saved/restored + while (live.is_NotEmpty()) { + const OptoReg::Name opto_reg = live.find_first_elem(); + const VMReg vm_reg = OptoReg::as_VMReg(opto_reg); + + live.Remove(opto_reg); - // Save live registers - if (raddr != rax) { - __ push(rax); - } - if (raddr != rcx) { - __ push(rcx); - } - if (raddr != rdx) { - __ push(rdx); - } - if (raddr != rsi) { - __ push(rsi); - } - if (raddr != rdi) { - __ push(rdi); - } - if (raddr != r8) { - __ push(r8); - } - if (raddr != r9) { - __ push(r9); - } - if (raddr != r10) { - __ push(r10); - } - if (raddr != r11) { - __ push(r11); + if (vm_reg->is_Register()) { + if (caller_saved.Member(opto_reg)) { + _gp_registers.append(vm_reg->as_Register()); + gp_spill_size += 8; + } + } else if (vm_reg->is_XMMRegister()) { + // We encode in the low order 4 bits of the opto_reg, how large part of the register is live + const VMReg vm_reg_base = OptoReg::as_VMReg(opto_reg & ~15); + const int reg_size = xmm_slot_size(opto_reg); + const XMMRegisterData reg_data = { vm_reg_base->as_XMMRegister(), reg_size }; + const int reg_index = _xmm_registers.find(reg_data); + if (reg_index == -1) { + // Not previously appended + _xmm_registers.append(reg_data); + xmm_spill_size += reg_size; + } else { + // Previously appended, update size + const int reg_size_prev = _xmm_registers.at(reg_index)._size; + if (reg_size > reg_size_prev) { + _xmm_registers.at_put(reg_index, reg_data); + xmm_spill_size += reg_size - reg_size_prev; + } + } + } else { + fatal("Unexpected register type"); + } + } + + // Sort by size, largest first + _xmm_registers.sort(xmm_compare_register_size); + + // Stack pointer must be 16 bytes aligned for the call + _spill_offset = _spill_size = align_up(xmm_spill_size + gp_spill_size, 16); } - // Setup arguments - if (raddr != c_rarg1) { - __ movq(c_rarg1, raddr); - } - __ movq(c_rarg0, Address(raddr, 0)); +public: + ZSaveLiveRegisters(MacroAssembler* masm, ZLoadBarrierStubC2* stub) : + _masm(masm), + _gp_registers(), + _xmm_registers(), + _spill_size(0), + _spill_offset(0) { - // Call barrier function - __ call_VM_leaf(ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_addr(decorators), c_rarg0, c_rarg1); + // + // Stack layout after registers have been spilled: + // + // | ... | original rsp, 16 bytes aligned + // ------------------ + // | zmm0 high | + // | ... | + // | zmm0 low | 16 bytes aligned + // | ... | + // | ymm1 high | + // | ... | + // | ymm1 low | 16 bytes aligned + // | ... | + // | xmmN high | + // | ... | + // | xmmN low | 8 bytes aligned + // | reg0 | 8 bytes aligned + // | reg1 | + // | ... | + // | regN | new rsp, if 16 bytes aligned + // | | else new rsp, 16 bytes aligned + // ------------------ + // - // Move result returned in rax to raddr, if needed - if (raddr != rax) { - __ movq(raddr, rax); + // Figure out what registers to save/restore + initialize(stub); + + // Allocate stack space + if (_spill_size > 0) { + __ subptr(rsp, _spill_size); + } + + // Save XMM/YMM/ZMM registers + for (int i = 0; i < _xmm_registers.length(); i++) { + xmm_register_save(_xmm_registers.at(i)); + } + + if (xmm_needs_vzeroupper()) { + __ vzeroupper(); + } + + // Save general purpose registers + for (int i = 0; i < _gp_registers.length(); i++) { + gp_register_save(_gp_registers.at(i)); + } } - // Restore saved registers - if (raddr != r11) { - __ pop(r11); - } - if (raddr != r10) { - __ pop(r10); - } - if (raddr != r9) { - __ pop(r9); - } - if (raddr != r8) { - __ pop(r8); + ~ZSaveLiveRegisters() { + // Restore general purpose registers + for (int i = _gp_registers.length() - 1; i >= 0; i--) { + gp_register_restore(_gp_registers.at(i)); + } + + __ vzeroupper(); + + // Restore XMM/YMM/ZMM registers + for (int i = _xmm_registers.length() - 1; i >= 0; i--) { + xmm_register_restore(_xmm_registers.at(i)); + } + + // Free stack space + if (_spill_size > 0) { + __ addptr(rsp, _spill_size); + } } - if (raddr != rdi) { - __ pop(rdi); - } - if (raddr != rsi) { - __ pop(rsi); - } - if (raddr != rdx) { - __ pop(rdx); - } - if (raddr != rcx) { - __ pop(rcx); - } - if (raddr != rax) { - __ pop(rax); +}; + +class ZSetupArguments { +private: + MacroAssembler* const _masm; + const Register _ref; + const Address _ref_addr; + +public: + ZSetupArguments(MacroAssembler* masm, ZLoadBarrierStubC2* stub) : + _masm(masm), + _ref(stub->ref()), + _ref_addr(stub->ref_addr()) { + + // Setup arguments + if (_ref_addr.base() == noreg) { + // No self healing + if (_ref != c_rarg0) { + __ movq(c_rarg0, _ref); + } + __ xorq(c_rarg1, c_rarg1); + } else { + // Self healing + if (_ref == c_rarg0) { + __ lea(c_rarg1, _ref_addr); + } else if (_ref != c_rarg1) { + __ lea(c_rarg1, _ref_addr); + __ movq(c_rarg0, _ref); + } else if (_ref_addr.base() != c_rarg0 && _ref_addr.index() != c_rarg0) { + __ movq(c_rarg0, _ref); + __ lea(c_rarg1, _ref_addr); + } else { + __ xchgq(c_rarg0, c_rarg1); + if (_ref_addr.base() == c_rarg0) { + __ lea(c_rarg1, Address(c_rarg1, _ref_addr.index(), _ref_addr.scale(), _ref_addr.disp())); + } else if (_ref_addr.index() == c_rarg0) { + __ lea(c_rarg1, Address(_ref_addr.base(), c_rarg1, _ref_addr.scale(), _ref_addr.disp())); + } else { + ShouldNotReachHere(); + } + } + } } - __ ret(0); + ~ZSetupArguments() { + // Transfer result + if (_ref != rax) { + __ movq(_ref, rax); + } + } +}; + +#undef __ +#define __ masm-> - return start; +void ZBarrierSetAssembler::generate_c2_load_barrier_stub(MacroAssembler* masm, ZLoadBarrierStubC2* stub) const { + BLOCK_COMMENT("ZLoadBarrierStubC2"); + + // Stub entry + __ bind(*stub->entry()); + + { + ZSaveLiveRegisters save_live_registers(masm, stub); + ZSetupArguments setup_arguments(masm, stub); + __ call(RuntimeAddress(stub->slow_path())); + } + + // Stub exit + __ jmp(*stub->continuation()); } #undef __ -static void barrier_stubs_init_inner(const char* label, const DecoratorSet decorators, address* stub) { - const int nregs = RegisterImpl::number_of_registers; - const int code_size = nregs * 128; // Rough estimate of code size - - ResourceMark rm; - - CodeBuffer buf(BufferBlob::create(label, code_size)); - StubCodeGenerator cgen(&buf); - - for (int i = 0; i < nregs; i++) { - const Register reg = as_Register(i); - stub[i] = generate_load_barrier_stub(&cgen, reg, decorators); - } -} - -void ZBarrierSetAssembler::barrier_stubs_init() { - barrier_stubs_init_inner("zgc_load_barrier_stubs", ON_STRONG_OOP_REF, _load_barrier_slow_stub); - barrier_stubs_init_inner("zgc_load_barrier_weak_stubs", ON_WEAK_OOP_REF, _load_barrier_weak_slow_stub); -} - -address ZBarrierSetAssembler::load_barrier_slow_stub(Register reg) { - return _load_barrier_slow_stub[reg->encoding()]; -} - -address ZBarrierSetAssembler::load_barrier_weak_slow_stub(Register reg) { - return _load_barrier_weak_slow_stub[reg->encoding()]; -} +#endif // COMPILER2 diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.hpp --- a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -24,6 +24,14 @@ #ifndef CPU_X86_GC_Z_ZBARRIERSETASSEMBLER_X86_HPP #define CPU_X86_GC_Z_ZBARRIERSETASSEMBLER_X86_HPP +#include "code/vmreg.hpp" +#include "oops/accessDecorators.hpp" +#ifdef COMPILER2 +#include "opto/optoreg.hpp" +#endif // COMPILER2 + +class MacroAssembler; + #ifdef COMPILER1 class LIR_Assembler; class LIR_OprDesc; @@ -32,14 +40,13 @@ class ZLoadBarrierStubC1; #endif // COMPILER1 +#ifdef COMPILER2 +class Node; +class ZLoadBarrierStubC2; +#endif // COMPILER2 + class ZBarrierSetAssembler : public ZBarrierSetAssemblerBase { -private: - address _load_barrier_slow_stub[RegisterImpl::number_of_registers]; - address _load_barrier_weak_slow_stub[RegisterImpl::number_of_registers]; - public: - ZBarrierSetAssembler(); - virtual void load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, @@ -82,10 +89,13 @@ DecoratorSet decorators) const; #endif // COMPILER1 - virtual void barrier_stubs_init(); +#ifdef COMPILER2 + OptoReg::Name refine_register(const Node* node, + OptoReg::Name opto_reg); - address load_barrier_slow_stub(Register reg); - address load_barrier_weak_slow_stub(Register reg); + void generate_c2_load_barrier_stub(MacroAssembler* masm, + ZLoadBarrierStubC2* stub) const; +#endif // COMPILER2 }; #endif // CPU_X86_GC_Z_ZBARRIERSETASSEMBLER_X86_HPP diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/cpu/x86/gc/z/z_x86_64.ad --- a/src/hotspot/cpu/x86/gc/z/z_x86_64.ad Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/cpu/x86/gc/z/z_x86_64.ad Wed Oct 09 23:22:56 2019 +0200 @@ -24,190 +24,144 @@ source_hpp %{ #include "gc/z/c2/zBarrierSetC2.hpp" +#include "gc/z/zThreadLocalData.hpp" %} source %{ -#include "gc/z/zBarrierSetAssembler.hpp" +static void z_load_barrier(MacroAssembler& _masm, const MachNode* node, Address ref_addr, Register ref, Register tmp, bool weak) { + ZLoadBarrierStubC2* const stub = ZLoadBarrierStubC2::create(node, ref_addr, ref, tmp, weak); + __ testptr(ref, Address(r15_thread, ZThreadLocalData::address_bad_mask_offset())); + __ jcc(Assembler::notZero, *stub->entry()); + __ bind(*stub->continuation()); +} -static void z_load_barrier_slow_reg(MacroAssembler& _masm, Register dst, Address src, bool weak) { - assert(dst != rsp, "Invalid register"); - assert(dst != r15, "Invalid register"); - - const address stub = weak ? ZBarrierSet::assembler()->load_barrier_weak_slow_stub(dst) - : ZBarrierSet::assembler()->load_barrier_slow_stub(dst); - __ lea(dst, src); - __ call(RuntimeAddress(stub)); +static void z_load_barrier_slow_path(MacroAssembler& _masm, const MachNode* node, Address ref_addr, Register ref, Register tmp) { + ZLoadBarrierStubC2* const stub = ZLoadBarrierStubC2::create(node, ref_addr, ref, tmp, false /* weak */); + __ jmp(*stub->entry()); + __ bind(*stub->continuation()); } %} -// For XMM and YMM enabled processors -instruct zLoadBarrierSlowRegXmmAndYmm(rRegP dst, memory src, rFlagsReg cr, - rxmm0 x0, rxmm1 x1, rxmm2 x2, rxmm3 x3, - rxmm4 x4, rxmm5 x5, rxmm6 x6, rxmm7 x7, - rxmm8 x8, rxmm9 x9, rxmm10 x10, rxmm11 x11, - rxmm12 x12, rxmm13 x13, rxmm14 x14, rxmm15 x15) %{ - match(Set dst (LoadBarrierSlowReg src dst)); - predicate(UseAVX <= 2 && !n->as_LoadBarrierSlowReg()->is_weak()); +// Load Pointer +instruct zLoadP(rRegP dst, memory mem, rFlagsReg cr) +%{ + predicate(UseZGC && n->as_Load()->barrier_data() == ZLoadBarrierStrong); + match(Set dst (LoadP mem)); + effect(KILL cr, TEMP dst); - effect(KILL cr, - KILL x0, KILL x1, KILL x2, KILL x3, - KILL x4, KILL x5, KILL x6, KILL x7, - KILL x8, KILL x9, KILL x10, KILL x11, - KILL x12, KILL x13, KILL x14, KILL x15); + ins_cost(125); - format %{ "lea $dst, $src\n\t" - "call #ZLoadBarrierSlowPath" %} + format %{ "movq $dst, $mem" %} ins_encode %{ - z_load_barrier_slow_reg(_masm, $dst$$Register, $src$$Address, false /* weak */); + __ movptr($dst$$Register, $mem$$Address); + if (barrier_data() != ZLoadBarrierElided) { + z_load_barrier(_masm, this, $mem$$Address, $dst$$Register, noreg /* tmp */, false /* weak */); + } %} - ins_pipe(pipe_slow); + + ins_pipe(ialu_reg_mem); %} -// For ZMM enabled processors -instruct zLoadBarrierSlowRegZmm(rRegP dst, memory src, rFlagsReg cr, - rxmm0 x0, rxmm1 x1, rxmm2 x2, rxmm3 x3, - rxmm4 x4, rxmm5 x5, rxmm6 x6, rxmm7 x7, - rxmm8 x8, rxmm9 x9, rxmm10 x10, rxmm11 x11, - rxmm12 x12, rxmm13 x13, rxmm14 x14, rxmm15 x15, - rxmm16 x16, rxmm17 x17, rxmm18 x18, rxmm19 x19, - rxmm20 x20, rxmm21 x21, rxmm22 x22, rxmm23 x23, - rxmm24 x24, rxmm25 x25, rxmm26 x26, rxmm27 x27, - rxmm28 x28, rxmm29 x29, rxmm30 x30, rxmm31 x31) %{ +// Load Weak Pointer +instruct zLoadWeakP(rRegP dst, memory mem, rFlagsReg cr) +%{ + predicate(UseZGC && n->as_Load()->barrier_data() == ZLoadBarrierWeak); + match(Set dst (LoadP mem)); + effect(KILL cr, TEMP dst); - match(Set dst (LoadBarrierSlowReg src dst)); - predicate(UseAVX == 3 && !n->as_LoadBarrierSlowReg()->is_weak()); + ins_cost(125); - effect(KILL cr, - KILL x0, KILL x1, KILL x2, KILL x3, - KILL x4, KILL x5, KILL x6, KILL x7, - KILL x8, KILL x9, KILL x10, KILL x11, - KILL x12, KILL x13, KILL x14, KILL x15, - KILL x16, KILL x17, KILL x18, KILL x19, - KILL x20, KILL x21, KILL x22, KILL x23, - KILL x24, KILL x25, KILL x26, KILL x27, - KILL x28, KILL x29, KILL x30, KILL x31); - - format %{ "lea $dst, $src\n\t" - "call #ZLoadBarrierSlowPath" %} + format %{ "movq $dst, $mem" %} ins_encode %{ - z_load_barrier_slow_reg(_masm, $dst$$Register, $src$$Address, false /* weak */); + __ movptr($dst$$Register, $mem$$Address); + z_load_barrier(_masm, this, $mem$$Address, $dst$$Register, noreg /* tmp */, true /* weak */); %} - ins_pipe(pipe_slow); -%} - -// For XMM and YMM enabled processors -instruct zLoadBarrierWeakSlowRegXmmAndYmm(rRegP dst, memory src, rFlagsReg cr, - rxmm0 x0, rxmm1 x1, rxmm2 x2, rxmm3 x3, - rxmm4 x4, rxmm5 x5, rxmm6 x6, rxmm7 x7, - rxmm8 x8, rxmm9 x9, rxmm10 x10, rxmm11 x11, - rxmm12 x12, rxmm13 x13, rxmm14 x14, rxmm15 x15) %{ - match(Set dst (LoadBarrierSlowReg src dst)); - predicate(UseAVX <= 2 && n->as_LoadBarrierSlowReg()->is_weak()); - effect(KILL cr, - KILL x0, KILL x1, KILL x2, KILL x3, - KILL x4, KILL x5, KILL x6, KILL x7, - KILL x8, KILL x9, KILL x10, KILL x11, - KILL x12, KILL x13, KILL x14, KILL x15); - - format %{ "lea $dst, $src\n\t" - "call #ZLoadBarrierSlowPath" %} - - ins_encode %{ - z_load_barrier_slow_reg(_masm, $dst$$Register, $src$$Address, true /* weak */); - %} - ins_pipe(pipe_slow); + ins_pipe(ialu_reg_mem); %} -// For ZMM enabled processors -instruct zLoadBarrierWeakSlowRegZmm(rRegP dst, memory src, rFlagsReg cr, - rxmm0 x0, rxmm1 x1, rxmm2 x2, rxmm3 x3, - rxmm4 x4, rxmm5 x5, rxmm6 x6, rxmm7 x7, - rxmm8 x8, rxmm9 x9, rxmm10 x10, rxmm11 x11, - rxmm12 x12, rxmm13 x13, rxmm14 x14, rxmm15 x15, - rxmm16 x16, rxmm17 x17, rxmm18 x18, rxmm19 x19, - rxmm20 x20, rxmm21 x21, rxmm22 x22, rxmm23 x23, - rxmm24 x24, rxmm25 x25, rxmm26 x26, rxmm27 x27, - rxmm28 x28, rxmm29 x29, rxmm30 x30, rxmm31 x31) %{ +instruct zCompareAndExchangeP(memory mem, rax_RegP oldval, rRegP newval, rRegP tmp, rFlagsReg cr) %{ + match(Set oldval (CompareAndExchangeP mem (Binary oldval newval))); + predicate(UseZGC && n->as_LoadStore()->barrier_data() == ZLoadBarrierStrong); + effect(KILL cr, TEMP tmp); - match(Set dst (LoadBarrierSlowReg src dst)); - predicate(UseAVX == 3 && n->as_LoadBarrierSlowReg()->is_weak()); - - effect(KILL cr, - KILL x0, KILL x1, KILL x2, KILL x3, - KILL x4, KILL x5, KILL x6, KILL x7, - KILL x8, KILL x9, KILL x10, KILL x11, - KILL x12, KILL x13, KILL x14, KILL x15, - KILL x16, KILL x17, KILL x18, KILL x19, - KILL x20, KILL x21, KILL x22, KILL x23, - KILL x24, KILL x25, KILL x26, KILL x27, - KILL x28, KILL x29, KILL x30, KILL x31); - - format %{ "lea $dst, $src\n\t" - "call #ZLoadBarrierSlowPath" %} + format %{ "lock\n\t" + "cmpxchgq $newval, $mem" %} ins_encode %{ - z_load_barrier_slow_reg(_masm, $dst$$Register, $src$$Address, true /* weak */); + if (barrier_data() != ZLoadBarrierElided) { + __ movptr($tmp$$Register, $oldval$$Register); + } + __ lock(); + __ cmpxchgptr($newval$$Register, $mem$$Address); + if (barrier_data() != ZLoadBarrierElided) { + Label good; + __ testptr($oldval$$Register, Address(r15_thread, ZThreadLocalData::address_bad_mask_offset())); + __ jcc(Assembler::zero, good); + z_load_barrier_slow_path(_masm, this, $mem$$Address, $oldval$$Register, $tmp$$Register); + __ movptr($oldval$$Register, $tmp$$Register); + __ lock(); + __ cmpxchgptr($newval$$Register, $mem$$Address); + __ bind(good); + } %} - ins_pipe(pipe_slow); + + ins_pipe(pipe_cmpxchg); %} -// Specialized versions of compareAndExchangeP that adds a keepalive that is consumed -// but doesn't affect output. +instruct zCompareAndSwapP(rRegI res, memory mem, rRegP newval, rRegP tmp, rFlagsReg cr, rax_RegP oldval) %{ + match(Set res (CompareAndSwapP mem (Binary oldval newval))); + match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); + predicate(UseZGC && n->as_LoadStore()->barrier_data() == ZLoadBarrierStrong); + effect(KILL cr, KILL oldval, TEMP tmp); + + format %{ "lock\n\t" + "cmpxchgq $newval, $mem\n\t" + "sete $res\n\t" + "movzbl $res, $res" %} -instruct z_compareAndExchangeP( - memory mem_ptr, - rax_RegP oldval, rRegP newval, rRegP keepalive, - rFlagsReg cr) %{ - predicate(VM_Version::supports_cx8()); - match(Set oldval (ZCompareAndExchangeP (Binary mem_ptr keepalive) (Binary oldval newval))); - effect(KILL cr); + ins_encode %{ + if (barrier_data() != ZLoadBarrierElided) { + __ movptr($tmp$$Register, $oldval$$Register); + } + __ lock(); + __ cmpxchgptr($newval$$Register, $mem$$Address); + if (barrier_data() != ZLoadBarrierElided) { + Label good; + __ testptr($oldval$$Register, Address(r15_thread, ZThreadLocalData::address_bad_mask_offset())); + __ jcc(Assembler::zero, good); + z_load_barrier_slow_path(_masm, this, $mem$$Address, $oldval$$Register, $tmp$$Register); + __ movptr($oldval$$Register, $tmp$$Register); + __ lock(); + __ cmpxchgptr($newval$$Register, $mem$$Address); + __ bind(good); + __ cmpptr($tmp$$Register, $oldval$$Register); + } + __ setb(Assembler::equal, $res$$Register); + __ movzbl($res$$Register, $res$$Register); + %} - format %{ "cmpxchgq $mem_ptr,$newval\t# " - "If rax == $mem_ptr then store $newval into $mem_ptr\n\t" %} - opcode(0x0F, 0xB1); - ins_encode(lock_prefix, - REX_reg_mem_wide(newval, mem_ptr), - OpcP, OpcS, - reg_mem(newval, mem_ptr) // lock cmpxchg - ); - ins_pipe( pipe_cmpxchg ); + ins_pipe(pipe_cmpxchg); %} -instruct z_compareAndSwapP(rRegI res, - memory mem_ptr, - rax_RegP oldval, rRegP newval, rRegP keepalive, - rFlagsReg cr) %{ - predicate(VM_Version::supports_cx8()); - match(Set res (ZCompareAndSwapP (Binary mem_ptr keepalive) (Binary oldval newval))); - match(Set res (ZWeakCompareAndSwapP (Binary mem_ptr keepalive) (Binary oldval newval))); - effect(KILL cr, KILL oldval); +instruct zXChgP(memory mem, rRegP newval, rFlagsReg cr) %{ + match(Set newval (GetAndSetP mem newval)); + predicate(UseZGC && n->as_LoadStore()->barrier_data() == ZLoadBarrierStrong); + effect(KILL cr); + + format %{ "xchgq $newval, $mem" %} - format %{ "cmpxchgq $mem_ptr,$newval\t# " - "If rax == $mem_ptr then store $newval into $mem_ptr\n\t" - "sete $res\n\t" - "movzbl $res, $res" %} - opcode(0x0F, 0xB1); - ins_encode(lock_prefix, - REX_reg_mem_wide(newval, mem_ptr), - OpcP, OpcS, - reg_mem(newval, mem_ptr), - REX_breg(res), Opcode(0x0F), Opcode(0x94), reg(res), // sete - REX_reg_breg(res, res), // movzbl - Opcode(0xF), Opcode(0xB6), reg_reg(res, res)); - ins_pipe( pipe_cmpxchg ); + ins_encode %{ + __ xchgptr($newval$$Register, $mem$$Address); + if (barrier_data() != ZLoadBarrierElided) { + z_load_barrier(_masm, this, Address(noreg, 0), $newval$$Register, noreg /* tmp */, false /* weak */); + } + %} + + ins_pipe(pipe_cmpxchg); %} - -instruct z_xchgP( memory mem, rRegP newval, rRegP keepalive) %{ - match(Set newval (ZGetAndSetP mem (Binary newval keepalive))); - format %{ "XCHGQ $newval,[$mem]" %} - ins_encode %{ - __ xchgq($newval$$Register, $mem$$Address); - %} - ins_pipe( pipe_cmpxchg ); -%} diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/cpu/x86/x86.ad --- a/src/hotspot/cpu/x86/x86.ad Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/cpu/x86/x86.ad Wed Oct 09 23:22:56 2019 +0200 @@ -1097,138 +1097,6 @@ reg_class_dynamic vectorz_reg(vectorz_reg_evex, vectorz_reg_legacy, %{ VM_Version::supports_evex() %} ); reg_class_dynamic vectorz_reg_vl(vectorz_reg_evex, vectorz_reg_legacy, %{ VM_Version::supports_evex() && VM_Version::supports_avx512vl() %} ); -reg_class xmm0_reg(XMM0, XMM0b, XMM0c, XMM0d); -reg_class ymm0_reg(XMM0, XMM0b, XMM0c, XMM0d, XMM0e, XMM0f, XMM0g, XMM0h); -reg_class zmm0_reg(XMM0, XMM0b, XMM0c, XMM0d, XMM0e, XMM0f, XMM0g, XMM0h, XMM0i, XMM0j, XMM0k, XMM0l, XMM0m, XMM0n, XMM0o, XMM0p); - -reg_class xmm1_reg(XMM1, XMM1b, XMM1c, XMM1d); -reg_class ymm1_reg(XMM1, XMM1b, XMM1c, XMM1d, XMM1e, XMM1f, XMM1g, XMM1h); -reg_class zmm1_reg(XMM1, XMM1b, XMM1c, XMM1d, XMM1e, XMM1f, XMM1g, XMM1h, XMM1i, XMM1j, XMM1k, XMM1l, XMM1m, XMM1n, XMM1o, XMM1p); - -reg_class xmm2_reg(XMM2, XMM2b, XMM2c, XMM2d); -reg_class ymm2_reg(XMM2, XMM2b, XMM2c, XMM2d, XMM2e, XMM2f, XMM2g, XMM2h); -reg_class zmm2_reg(XMM2, XMM2b, XMM2c, XMM2d, XMM2e, XMM2f, XMM2g, XMM2h, XMM2i, XMM2j, XMM2k, XMM2l, XMM2m, XMM2n, XMM2o, XMM2p); - -reg_class xmm3_reg(XMM3, XMM3b, XMM3c, XMM3d); -reg_class ymm3_reg(XMM3, XMM3b, XMM3c, XMM3d, XMM3e, XMM3f, XMM3g, XMM3h); -reg_class zmm3_reg(XMM3, XMM3b, XMM3c, XMM3d, XMM3e, XMM3f, XMM3g, XMM3h, XMM3i, XMM3j, XMM3k, XMM3l, XMM3m, XMM3n, XMM3o, XMM3p); - -reg_class xmm4_reg(XMM4, XMM4b, XMM4c, XMM4d); -reg_class ymm4_reg(XMM4, XMM4b, XMM4c, XMM4d, XMM4e, XMM4f, XMM4g, XMM4h); -reg_class zmm4_reg(XMM4, XMM4b, XMM4c, XMM4d, XMM4e, XMM4f, XMM4g, XMM4h, XMM4i, XMM4j, XMM4k, XMM4l, XMM4m, XMM4n, XMM4o, XMM4p); - -reg_class xmm5_reg(XMM5, XMM5b, XMM5c, XMM5d); -reg_class ymm5_reg(XMM5, XMM5b, XMM5c, XMM5d, XMM5e, XMM5f, XMM5g, XMM5h); -reg_class zmm5_reg(XMM5, XMM5b, XMM5c, XMM5d, XMM5e, XMM5f, XMM5g, XMM5h, XMM5i, XMM5j, XMM5k, XMM5l, XMM5m, XMM5n, XMM5o, XMM5p); - -reg_class xmm6_reg(XMM6, XMM6b, XMM6c, XMM6d); -reg_class ymm6_reg(XMM6, XMM6b, XMM6c, XMM6d, XMM6e, XMM6f, XMM6g, XMM6h); -reg_class zmm6_reg(XMM6, XMM6b, XMM6c, XMM6d, XMM6e, XMM6f, XMM6g, XMM6h, XMM6i, XMM6j, XMM6k, XMM6l, XMM6m, XMM6n, XMM6o, XMM6p); - -reg_class xmm7_reg(XMM7, XMM7b, XMM7c, XMM7d); -reg_class ymm7_reg(XMM7, XMM7b, XMM7c, XMM7d, XMM7e, XMM7f, XMM7g, XMM7h); -reg_class zmm7_reg(XMM7, XMM7b, XMM7c, XMM7d, XMM7e, XMM7f, XMM7g, XMM7h, XMM7i, XMM7j, XMM7k, XMM7l, XMM7m, XMM7n, XMM7o, XMM7p); - -#ifdef _LP64 - -reg_class xmm8_reg(XMM8, XMM8b, XMM8c, XMM8d); -reg_class ymm8_reg(XMM8, XMM8b, XMM8c, XMM8d, XMM8e, XMM8f, XMM8g, XMM8h); -reg_class zmm8_reg(XMM8, XMM8b, XMM8c, XMM8d, XMM8e, XMM8f, XMM8g, XMM8h, XMM8i, XMM8j, XMM8k, XMM8l, XMM8m, XMM8n, XMM8o, XMM8p); - -reg_class xmm9_reg(XMM9, XMM9b, XMM9c, XMM9d); -reg_class ymm9_reg(XMM9, XMM9b, XMM9c, XMM9d, XMM9e, XMM9f, XMM9g, XMM9h); -reg_class zmm9_reg(XMM9, XMM9b, XMM9c, XMM9d, XMM9e, XMM9f, XMM9g, XMM9h, XMM9i, XMM9j, XMM9k, XMM9l, XMM9m, XMM9n, XMM9o, XMM9p); - -reg_class xmm10_reg(XMM10, XMM10b, XMM10c, XMM10d); -reg_class ymm10_reg(XMM10, XMM10b, XMM10c, XMM10d, XMM10e, XMM10f, XMM10g, XMM10h); -reg_class zmm10_reg(XMM10, XMM10b, XMM10c, XMM10d, XMM10e, XMM10f, XMM10g, XMM10h, XMM10i, XMM10j, XMM10k, XMM10l, XMM10m, XMM10n, XMM10o, XMM10p); - -reg_class xmm11_reg(XMM11, XMM11b, XMM11c, XMM11d); -reg_class ymm11_reg(XMM11, XMM11b, XMM11c, XMM11d, XMM11e, XMM11f, XMM11g, XMM11h); -reg_class zmm11_reg(XMM11, XMM11b, XMM11c, XMM11d, XMM11e, XMM11f, XMM11g, XMM11h, XMM11i, XMM11j, XMM11k, XMM11l, XMM11m, XMM11n, XMM11o, XMM11p); - -reg_class xmm12_reg(XMM12, XMM12b, XMM12c, XMM12d); -reg_class ymm12_reg(XMM12, XMM12b, XMM12c, XMM12d, XMM12e, XMM12f, XMM12g, XMM12h); -reg_class zmm12_reg(XMM12, XMM12b, XMM12c, XMM12d, XMM12e, XMM12f, XMM12g, XMM12h, XMM12i, XMM12j, XMM12k, XMM12l, XMM12m, XMM12n, XMM12o, XMM12p); - -reg_class xmm13_reg(XMM13, XMM13b, XMM13c, XMM13d); -reg_class ymm13_reg(XMM13, XMM13b, XMM13c, XMM13d, XMM13e, XMM13f, XMM13g, XMM13h); -reg_class zmm13_reg(XMM13, XMM13b, XMM13c, XMM13d, XMM13e, XMM13f, XMM13g, XMM13h, XMM13i, XMM13j, XMM13k, XMM13l, XMM13m, XMM13n, XMM13o, XMM13p); - -reg_class xmm14_reg(XMM14, XMM14b, XMM14c, XMM14d); -reg_class ymm14_reg(XMM14, XMM14b, XMM14c, XMM14d, XMM14e, XMM14f, XMM14g, XMM14h); -reg_class zmm14_reg(XMM14, XMM14b, XMM14c, XMM14d, XMM14e, XMM14f, XMM14g, XMM14h, XMM14i, XMM14j, XMM14k, XMM14l, XMM14m, XMM14n, XMM14o, XMM14p); - -reg_class xmm15_reg(XMM15, XMM15b, XMM15c, XMM15d); -reg_class ymm15_reg(XMM15, XMM15b, XMM15c, XMM15d, XMM15e, XMM15f, XMM15g, XMM15h); -reg_class zmm15_reg(XMM15, XMM15b, XMM15c, XMM15d, XMM15e, XMM15f, XMM15g, XMM15h, XMM15i, XMM15j, XMM15k, XMM15l, XMM15m, XMM15n, XMM15o, XMM15p); - -reg_class xmm16_reg(XMM16, XMM16b, XMM16c, XMM16d); -reg_class ymm16_reg(XMM16, XMM16b, XMM16c, XMM16d, XMM16e, XMM16f, XMM16g, XMM16h); -reg_class zmm16_reg(XMM16, XMM16b, XMM16c, XMM16d, XMM16e, XMM16f, XMM16g, XMM16h, XMM16i, XMM16j, XMM16k, XMM16l, XMM16m, XMM16n, XMM16o, XMM16p); - -reg_class xmm17_reg(XMM17, XMM17b, XMM17c, XMM17d); -reg_class ymm17_reg(XMM17, XMM17b, XMM17c, XMM17d, XMM17e, XMM17f, XMM17g, XMM17h); -reg_class zmm17_reg(XMM17, XMM17b, XMM17c, XMM17d, XMM17e, XMM17f, XMM17g, XMM17h, XMM17i, XMM17j, XMM17k, XMM17l, XMM17m, XMM17n, XMM17o, XMM17p); - -reg_class xmm18_reg(XMM18, XMM18b, XMM18c, XMM18d); -reg_class ymm18_reg(XMM18, XMM18b, XMM18c, XMM18d, XMM18e, XMM18f, XMM18g, XMM18h); -reg_class zmm18_reg(XMM18, XMM18b, XMM18c, XMM18d, XMM18e, XMM18f, XMM18g, XMM18h, XMM18i, XMM18j, XMM18k, XMM18l, XMM18m, XMM18n, XMM18o, XMM18p); - -reg_class xmm19_reg(XMM19, XMM19b, XMM19c, XMM19d); -reg_class ymm19_reg(XMM19, XMM19b, XMM19c, XMM19d, XMM19e, XMM19f, XMM19g, XMM19h); -reg_class zmm19_reg(XMM19, XMM19b, XMM19c, XMM19d, XMM19e, XMM19f, XMM19g, XMM19h, XMM19i, XMM19j, XMM19k, XMM19l, XMM19m, XMM19n, XMM19o, XMM19p); - -reg_class xmm20_reg(XMM20, XMM20b, XMM20c, XMM20d); -reg_class ymm20_reg(XMM20, XMM20b, XMM20c, XMM20d, XMM20e, XMM20f, XMM20g, XMM20h); -reg_class zmm20_reg(XMM20, XMM20b, XMM20c, XMM20d, XMM20e, XMM20f, XMM20g, XMM20h, XMM20i, XMM20j, XMM20k, XMM20l, XMM20m, XMM20n, XMM20o, XMM20p); - -reg_class xmm21_reg(XMM21, XMM21b, XMM21c, XMM21d); -reg_class ymm21_reg(XMM21, XMM21b, XMM21c, XMM21d, XMM21e, XMM21f, XMM21g, XMM21h); -reg_class zmm21_reg(XMM21, XMM21b, XMM21c, XMM21d, XMM21e, XMM21f, XMM21g, XMM21h, XMM21i, XMM21j, XMM21k, XMM21l, XMM21m, XMM21n, XMM21o, XMM21p); - -reg_class xmm22_reg(XMM22, XMM22b, XMM22c, XMM22d); -reg_class ymm22_reg(XMM22, XMM22b, XMM22c, XMM22d, XMM22e, XMM22f, XMM22g, XMM22h); -reg_class zmm22_reg(XMM22, XMM22b, XMM22c, XMM22d, XMM22e, XMM22f, XMM22g, XMM22h, XMM22i, XMM22j, XMM22k, XMM22l, XMM22m, XMM22n, XMM22o, XMM22p); - -reg_class xmm23_reg(XMM23, XMM23b, XMM23c, XMM23d); -reg_class ymm23_reg(XMM23, XMM23b, XMM23c, XMM23d, XMM23e, XMM23f, XMM23g, XMM23h); -reg_class zmm23_reg(XMM23, XMM23b, XMM23c, XMM23d, XMM23e, XMM23f, XMM23g, XMM23h, XMM23i, XMM23j, XMM23k, XMM23l, XMM23m, XMM23n, XMM23o, XMM23p); - -reg_class xmm24_reg(XMM24, XMM24b, XMM24c, XMM24d); -reg_class ymm24_reg(XMM24, XMM24b, XMM24c, XMM24d, XMM24e, XMM24f, XMM24g, XMM24h); -reg_class zmm24_reg(XMM24, XMM24b, XMM24c, XMM24d, XMM24e, XMM24f, XMM24g, XMM24h, XMM24i, XMM24j, XMM24k, XMM24l, XMM24m, XMM24n, XMM24o, XMM24p); - -reg_class xmm25_reg(XMM25, XMM25b, XMM25c, XMM25d); -reg_class ymm25_reg(XMM25, XMM25b, XMM25c, XMM25d, XMM25e, XMM25f, XMM25g, XMM25h); -reg_class zmm25_reg(XMM25, XMM25b, XMM25c, XMM25d, XMM25e, XMM25f, XMM25g, XMM25h, XMM25i, XMM25j, XMM25k, XMM25l, XMM25m, XMM25n, XMM25o, XMM25p); - -reg_class xmm26_reg(XMM26, XMM26b, XMM26c, XMM26d); -reg_class ymm26_reg(XMM26, XMM26b, XMM26c, XMM26d, XMM26e, XMM26f, XMM26g, XMM26h); -reg_class zmm26_reg(XMM26, XMM26b, XMM26c, XMM26d, XMM26e, XMM26f, XMM26g, XMM26h, XMM26i, XMM26j, XMM26k, XMM26l, XMM26m, XMM26n, XMM26o, XMM26p); - -reg_class xmm27_reg(XMM27, XMM27b, XMM27c, XMM27d); -reg_class ymm27_reg(XMM27, XMM27b, XMM27c, XMM27d, XMM27e, XMM27f, XMM27g, XMM27h); -reg_class zmm27_reg(XMM27, XMM27b, XMM27c, XMM27d, XMM27e, XMM27f, XMM27g, XMM27h, XMM27i, XMM27j, XMM27k, XMM27l, XMM27m, XMM27n, XMM27o, XMM27p); - -reg_class xmm28_reg(XMM28, XMM28b, XMM28c, XMM28d); -reg_class ymm28_reg(XMM28, XMM28b, XMM28c, XMM28d, XMM28e, XMM28f, XMM28g, XMM28h); -reg_class zmm28_reg(XMM28, XMM28b, XMM28c, XMM28d, XMM28e, XMM28f, XMM28g, XMM28h, XMM28i, XMM28j, XMM28k, XMM28l, XMM28m, XMM28n, XMM28o, XMM28p); - -reg_class xmm29_reg(XMM29, XMM29b, XMM29c, XMM29d); -reg_class ymm29_reg(XMM29, XMM29b, XMM29c, XMM29d, XMM29e, XMM29f, XMM29g, XMM29h); -reg_class zmm29_reg(XMM29, XMM29b, XMM29c, XMM29d, XMM29e, XMM29f, XMM29g, XMM29h, XMM29i, XMM29j, XMM29k, XMM29l, XMM29m, XMM29n, XMM29o, XMM29p); - -reg_class xmm30_reg(XMM30, XMM30b, XMM30c, XMM30d); -reg_class ymm30_reg(XMM30, XMM30b, XMM30c, XMM30d, XMM30e, XMM30f, XMM30g, XMM30h); -reg_class zmm30_reg(XMM30, XMM30b, XMM30c, XMM30d, XMM30e, XMM30f, XMM30g, XMM30h, XMM30i, XMM30j, XMM30k, XMM30l, XMM30m, XMM30n, XMM30o, XMM30p); - -reg_class xmm31_reg(XMM31, XMM31b, XMM31c, XMM31d); -reg_class ymm31_reg(XMM31, XMM31b, XMM31c, XMM31d, XMM31e, XMM31f, XMM31g, XMM31h); -reg_class zmm31_reg(XMM31, XMM31b, XMM31c, XMM31d, XMM31e, XMM31f, XMM31g, XMM31h, XMM31i, XMM31j, XMM31k, XMM31l, XMM31m, XMM31n, XMM31o, XMM31p); - -#endif - %} @@ -1800,8 +1668,8 @@ return (UseAVX > 2) ? 6 : 4; } -static int vec_spill_helper(CodeBuffer *cbuf, bool do_size, bool is_load, - int stack_offset, int reg, uint ireg, outputStream* st) { +int vec_spill_helper(CodeBuffer *cbuf, bool do_size, bool is_load, + int stack_offset, int reg, uint ireg, outputStream* st) { // In 64-bit VM size calculation is very complex. Emitting instructions // into scratch buffer is used to get size in 64-bit VM. LP64_ONLY( assert(!do_size, "this method calculates size only for 32-bit VM"); ) diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/cpu/x86/x86_64.ad --- a/src/hotspot/cpu/x86/x86_64.ad Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/cpu/x86/x86_64.ad Wed Oct 09 23:22:56 2019 +0200 @@ -1058,8 +1058,8 @@ static int vec_mov_helper(CodeBuffer *cbuf, bool do_size, int src_lo, int dst_lo, int src_hi, int dst_hi, uint ireg, outputStream* st); -static int vec_spill_helper(CodeBuffer *cbuf, bool do_size, bool is_load, - int stack_offset, int reg, uint ireg, outputStream* st); +int vec_spill_helper(CodeBuffer *cbuf, bool do_size, bool is_load, + int stack_offset, int reg, uint ireg, outputStream* st); static void vec_stack_to_stack_helper(CodeBuffer *cbuf, int src_offset, int dst_offset, uint ireg, outputStream* st) { @@ -4260,200 +4260,6 @@ %} %} -// Operands for bound floating pointer register arguments -operand rxmm0() %{ - constraint(ALLOC_IN_RC(xmm0_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm1() %{ - constraint(ALLOC_IN_RC(xmm1_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm2() %{ - constraint(ALLOC_IN_RC(xmm2_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm3() %{ - constraint(ALLOC_IN_RC(xmm3_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm4() %{ - constraint(ALLOC_IN_RC(xmm4_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm5() %{ - constraint(ALLOC_IN_RC(xmm5_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm6() %{ - constraint(ALLOC_IN_RC(xmm6_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm7() %{ - constraint(ALLOC_IN_RC(xmm7_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm8() %{ - constraint(ALLOC_IN_RC(xmm8_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm9() %{ - constraint(ALLOC_IN_RC(xmm9_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm10() %{ - constraint(ALLOC_IN_RC(xmm10_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm11() %{ - constraint(ALLOC_IN_RC(xmm11_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm12() %{ - constraint(ALLOC_IN_RC(xmm12_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm13() %{ - constraint(ALLOC_IN_RC(xmm13_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm14() %{ - constraint(ALLOC_IN_RC(xmm14_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm15() %{ - constraint(ALLOC_IN_RC(xmm15_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm16() %{ - constraint(ALLOC_IN_RC(xmm16_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm17() %{ - constraint(ALLOC_IN_RC(xmm17_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm18() %{ - constraint(ALLOC_IN_RC(xmm18_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm19() %{ - constraint(ALLOC_IN_RC(xmm19_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm20() %{ - constraint(ALLOC_IN_RC(xmm20_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm21() %{ - constraint(ALLOC_IN_RC(xmm21_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm22() %{ - constraint(ALLOC_IN_RC(xmm22_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm23() %{ - constraint(ALLOC_IN_RC(xmm23_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm24() %{ - constraint(ALLOC_IN_RC(xmm24_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm25() %{ - constraint(ALLOC_IN_RC(xmm25_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm26() %{ - constraint(ALLOC_IN_RC(xmm26_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm27() %{ - constraint(ALLOC_IN_RC(xmm27_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm28() %{ - constraint(ALLOC_IN_RC(xmm28_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm29() %{ - constraint(ALLOC_IN_RC(xmm29_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm30() %{ - constraint(ALLOC_IN_RC(xmm30_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} -operand rxmm31() %{ - constraint(ALLOC_IN_RC(xmm31_reg)); - match(VecX); - format%{%} - interface(REG_INTER); -%} - //----------OPERAND CLASSES---------------------------------------------------- // Operand Classes are groups of operands that are used as to simplify // instruction definitions by not requiring the AD writer to specify separate @@ -5346,6 +5152,7 @@ instruct loadP(rRegP dst, memory mem) %{ match(Set dst (LoadP mem)); + predicate(n->as_Load()->barrier_data() == 0); ins_cost(125); // XXX format %{ "movq $dst, $mem\t# ptr" %} @@ -7794,6 +7601,7 @@ rax_RegP oldval, rRegP newval, rFlagsReg cr) %{ + predicate(n->as_LoadStore()->barrier_data() == 0); match(Set cr (StorePConditional heap_top_ptr (Binary oldval newval))); format %{ "cmpxchgq $heap_top_ptr, $newval\t# (ptr) " @@ -7845,7 +7653,7 @@ rax_RegP oldval, rRegP newval, rFlagsReg cr) %{ - predicate(VM_Version::supports_cx8()); + predicate(VM_Version::supports_cx8() && n->as_LoadStore()->barrier_data() == 0); match(Set res (CompareAndSwapP mem_ptr (Binary oldval newval))); match(Set res (WeakCompareAndSwapP mem_ptr (Binary oldval newval))); effect(KILL cr, KILL oldval); @@ -8087,7 +7895,7 @@ rax_RegP oldval, rRegP newval, rFlagsReg cr) %{ - predicate(VM_Version::supports_cx8()); + predicate(VM_Version::supports_cx8() && n->as_LoadStore()->barrier_data() == 0); match(Set oldval (CompareAndExchangeP mem_ptr (Binary oldval newval))); effect(KILL cr); @@ -8232,6 +8040,7 @@ instruct xchgP( memory mem, rRegP newval) %{ match(Set newval (GetAndSetP mem newval)); + predicate(n->as_LoadStore()->barrier_data() == 0); format %{ "XCHGQ $newval,[$mem]" %} ins_encode %{ __ xchgq($newval$$Register, $mem$$Address); @@ -11974,6 +11783,7 @@ instruct compP_rReg_mem(rFlagsRegU cr, rRegP op1, memory op2) %{ match(Set cr (CmpP op1 (LoadP op2))); + predicate(n->in(2)->as_Load()->barrier_data() == 0); ins_cost(500); // XXX format %{ "cmpq $op1, $op2\t# ptr" %} @@ -11999,7 +11809,8 @@ // and raw pointers have no anti-dependencies. instruct compP_mem_rReg(rFlagsRegU cr, rRegP op1, memory op2) %{ - predicate(n->in(2)->in(2)->bottom_type()->reloc() == relocInfo::none); + predicate(n->in(2)->in(2)->bottom_type()->reloc() == relocInfo::none && + n->in(2)->as_Load()->barrier_data() == 0); match(Set cr (CmpP op1 (LoadP op2))); format %{ "cmpq $op1, $op2\t# raw ptr" %} @@ -12024,7 +11835,8 @@ // any compare to a zero should be eq/neq. instruct testP_mem(rFlagsReg cr, memory op, immP0 zero) %{ - predicate(!UseCompressedOops || (CompressedOops::base() != NULL)); + predicate((!UseCompressedOops || (CompressedOops::base() != NULL)) && + n->in(1)->as_Load()->barrier_data() == 0); match(Set cr (CmpP (LoadP op) zero)); ins_cost(500); // XXX @@ -12037,7 +11849,9 @@ instruct testP_mem_reg0(rFlagsReg cr, memory mem, immP0 zero) %{ - predicate(UseCompressedOops && (CompressedOops::base() == NULL) && (CompressedKlassPointers::base() == NULL)); + predicate(UseCompressedOops && (CompressedOops::base() == NULL) && + (CompressedKlassPointers::base() == NULL) && + n->in(1)->as_Load()->barrier_data() == 0); match(Set cr (CmpP (LoadP mem) zero)); format %{ "cmpq R12, $mem\t# ptr (R12_heapbase==0)" %} diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/adlc/formssel.cpp --- a/src/hotspot/share/adlc/formssel.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/adlc/formssel.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -773,11 +773,6 @@ !strcmp(_matrule->_rChild->_opType,"CheckCastPP") || !strcmp(_matrule->_rChild->_opType,"GetAndSetP") || !strcmp(_matrule->_rChild->_opType,"GetAndSetN") || -#if INCLUDE_ZGC - !strcmp(_matrule->_rChild->_opType,"ZGetAndSetP") || - !strcmp(_matrule->_rChild->_opType,"ZCompareAndExchangeP") || - !strcmp(_matrule->_rChild->_opType,"LoadBarrierSlowReg") || -#endif #if INCLUDE_SHENANDOAHGC !strcmp(_matrule->_rChild->_opType,"ShenandoahCompareAndExchangeP") || !strcmp(_matrule->_rChild->_opType,"ShenandoahCompareAndExchangeN") || @@ -3510,9 +3505,6 @@ "StoreCM", "GetAndSetB", "GetAndSetS", "GetAndAddI", "GetAndSetI", "GetAndSetP", "GetAndAddB", "GetAndAddS", "GetAndAddL", "GetAndSetL", "GetAndSetN", -#if INCLUDE_ZGC - "ZGetAndSetP", "ZCompareAndSwapP", "ZCompareAndExchangeP", "ZWeakCompareAndSwapP", -#endif "ClearArray" }; int cnt = sizeof(needs_ideal_memory_list)/sizeof(char*); diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/ci/ciEnv.cpp --- a/src/hotspot/share/ci/ciEnv.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/ci/ciEnv.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -238,6 +238,7 @@ _jvmti_can_access_local_variables = JvmtiExport::can_access_local_variables(); _jvmti_can_post_on_exceptions = JvmtiExport::can_post_on_exceptions(); _jvmti_can_pop_frame = JvmtiExport::can_pop_frame(); + _jvmti_can_get_owned_monitor_info = JvmtiExport::can_get_owned_monitor_info(); } bool ciEnv::jvmti_state_changed() const { @@ -262,6 +263,10 @@ JvmtiExport::can_pop_frame()) { return true; } + if (!_jvmti_can_get_owned_monitor_info && + JvmtiExport::can_get_owned_monitor_info()) { + return true; + } return false; } diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/ci/ciEnv.hpp --- a/src/hotspot/share/ci/ciEnv.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/ci/ciEnv.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -73,6 +73,7 @@ bool _jvmti_can_access_local_variables; bool _jvmti_can_post_on_exceptions; bool _jvmti_can_pop_frame; + bool _jvmti_can_get_owned_monitor_info; // includes can_get_owned_monitor_stack_depth_info // Cache DTrace flags bool _dtrace_extended_probes; @@ -347,6 +348,7 @@ } bool jvmti_can_hotswap_or_post_breakpoint() const { return _jvmti_can_hotswap_or_post_breakpoint; } bool jvmti_can_post_on_exceptions() const { return _jvmti_can_post_on_exceptions; } + bool jvmti_can_get_owned_monitor_info() const { return _jvmti_can_get_owned_monitor_info; } // Cache DTrace flags void cache_dtrace_flags(); diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/classfile/systemDictionary.cpp --- a/src/hotspot/share/classfile/systemDictionary.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/classfile/systemDictionary.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -1205,10 +1205,8 @@ TempNewSymbol pkg_name = NULL; PackageEntry* pkg_entry = NULL; ModuleEntry* mod_entry = NULL; - const char* pkg_string = NULL; pkg_name = InstanceKlass::package_from_name(class_name, CHECK_false); if (pkg_name != NULL) { - pkg_string = pkg_name->as_C_string(); if (loader_data != NULL) { pkg_entry = loader_data->packages()->lookup_only(pkg_name); } @@ -1245,7 +1243,7 @@ // 3. or, the class is from an unamed module if (!ent->is_modules_image() && ik->is_shared_boot_class()) { // the class is from the -Xbootclasspath/a - if (pkg_string == NULL || + if (pkg_name == NULL || pkg_entry == NULL || pkg_entry->in_unnamed_module()) { assert(mod_entry == NULL || @@ -1257,8 +1255,7 @@ return false; } else { bool res = SystemDictionaryShared::is_shared_class_visible_for_classloader( - ik, class_loader, pkg_string, pkg_name, - pkg_entry, mod_entry, CHECK_(false)); + ik, class_loader, pkg_name, pkg_entry, mod_entry, CHECK_(false)); return res; } } diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/classfile/systemDictionaryShared.cpp --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -657,7 +657,6 @@ bool SystemDictionaryShared::is_shared_class_visible_for_classloader( InstanceKlass* ik, Handle class_loader, - const char* pkg_string, Symbol* pkg_name, PackageEntry* pkg_entry, ModuleEntry* mod_entry, @@ -684,7 +683,7 @@ } } else if (SystemDictionary::is_system_class_loader(class_loader())) { assert(ent != NULL, "shared class for system loader should have valid SharedClassPathEntry"); - if (pkg_string == NULL) { + if (pkg_name == NULL) { // The archived class is in the unnamed package. Currently, the boot image // does not contain any class in the unnamed package. assert(!ent->is_modules_image(), "Class in the unnamed package must be from the classpath"); diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/classfile/systemDictionaryShared.hpp --- a/src/hotspot/share/classfile/systemDictionaryShared.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -240,7 +240,6 @@ static bool is_sharing_possible(ClassLoaderData* loader_data); static bool is_shared_class_visible_for_classloader(InstanceKlass* ik, Handle class_loader, - const char* pkg_string, Symbol* pkg_name, PackageEntry* pkg_entry, ModuleEntry* mod_entry, diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/code/compiledIC.cpp --- a/src/hotspot/share/code/compiledIC.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/code/compiledIC.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -741,4 +741,22 @@ tty->cr(); } +void CompiledDirectStaticCall::verify_mt_safe(const methodHandle& callee, address entry, + NativeMovConstReg* method_holder, + NativeJump* jump) { + // A generated lambda form might be deleted from the Lambdaform + // cache in MethodTypeForm. If a jit compiled lambdaform method + // becomes not entrant and the cache access returns null, the new + // resolve will lead to a new generated LambdaForm. + Method* old_method = reinterpret_cast(method_holder->data()); + assert(old_method == NULL || old_method == callee() || + callee->is_compiled_lambda_form() || + !old_method->method_holder()->is_loader_alive() || + old_method->is_old(), // may be race patching deoptimized nmethod due to redefinition. + "a) MT-unsafe modification of inline cache"); + + address destination = jump->jump_destination(); + assert(destination == (address)-1 || destination == entry, + "b) MT-unsafe modification of inline cache"); +} #endif // !PRODUCT diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/code/compiledIC.hpp --- a/src/hotspot/share/code/compiledIC.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/code/compiledIC.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -402,6 +402,9 @@ // Also used by CompiledIC void set_to_interpreted(const methodHandle& callee, address entry); + void verify_mt_safe(const methodHandle& callee, address entry, + NativeMovConstReg* method_holder, + NativeJump* jump) PRODUCT_RETURN; #if INCLUDE_AOT void set_to_far(const methodHandle& callee, address entry); #endif diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/compiler/compilerDirectives.hpp --- a/src/hotspot/share/compiler/compilerDirectives.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/compiler/compilerDirectives.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -66,8 +66,7 @@ cflags(VectorizeDebug, uintx, 0, VectorizeDebug) \ cflags(CloneMapDebug, bool, false, CloneMapDebug) \ cflags(IGVPrintLevel, intx, PrintIdealGraphLevel, IGVPrintLevel) \ - cflags(MaxNodeLimit, intx, MaxNodeLimit, MaxNodeLimit) \ -ZGC_ONLY(cflags(ZTraceLoadBarriers, bool, false, ZTraceLoadBarriers)) + cflags(MaxNodeLimit, intx, MaxNodeLimit, MaxNodeLimit) #else #define compilerdirectives_c2_flags(cflags) #endif diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/gc/g1/g1Analytics.cpp --- a/src/hotspot/share/gc/g1/g1Analytics.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/gc/g1/g1Analytics.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -78,6 +78,8 @@ _alloc_rate_ms_seq(new TruncatedSeq(TruncatedSeqLength)), _prev_collection_pause_end_ms(0.0), _rs_length_diff_seq(new TruncatedSeq(TruncatedSeqLength)), + _concurrent_refine_rate_ms_seq(new TruncatedSeq(TruncatedSeqLength)), + _logged_cards_rate_ms_seq(new TruncatedSeq(TruncatedSeqLength)), _cost_per_logged_card_ms_seq(new TruncatedSeq(TruncatedSeqLength)), _cost_scan_hcc_seq(new TruncatedSeq(TruncatedSeqLength)), _young_cards_per_entry_ratio_seq(new TruncatedSeq(TruncatedSeqLength)), @@ -102,6 +104,10 @@ int index = MIN2(ParallelGCThreads - 1, 7u); _rs_length_diff_seq->add(rs_length_diff_defaults[index]); + // Start with inverse of maximum STW cost. + _concurrent_refine_rate_ms_seq->add(1/cost_per_logged_card_ms_defaults[0]); + // Some applications have very low rates for logging cards. + _logged_cards_rate_ms_seq->add(0.0); _cost_per_logged_card_ms_seq->add(cost_per_logged_card_ms_defaults[index]); _cost_scan_hcc_seq->add(0.0); _young_cards_per_entry_ratio_seq->add(young_cards_per_entry_ratio_defaults[index]); @@ -159,6 +165,14 @@ (pause_time_ms * _recent_prev_end_times_for_all_gcs_sec->num()) / interval_ms; } +void G1Analytics::report_concurrent_refine_rate_ms(double cards_per_ms) { + _concurrent_refine_rate_ms_seq->add(cards_per_ms); +} + +void G1Analytics::report_logged_cards_rate_ms(double cards_per_ms) { + _logged_cards_rate_ms_seq->add(cards_per_ms); +} + void G1Analytics::report_cost_per_logged_card_ms(double cost_per_logged_card_ms) { _cost_per_logged_card_ms_seq->add(cost_per_logged_card_ms); } @@ -223,6 +237,14 @@ return get_new_prediction(_alloc_rate_ms_seq); } +double G1Analytics::predict_concurrent_refine_rate_ms() const { + return get_new_prediction(_concurrent_refine_rate_ms_seq); +} + +double G1Analytics::predict_logged_cards_rate_ms() const { + return get_new_prediction(_logged_cards_rate_ms_seq); +} + double G1Analytics::predict_cost_per_logged_card_ms() const { return get_new_prediction(_cost_per_logged_card_ms_seq); } diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/gc/g1/g1Analytics.hpp --- a/src/hotspot/share/gc/g1/g1Analytics.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/gc/g1/g1Analytics.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -46,6 +46,8 @@ double _prev_collection_pause_end_ms; TruncatedSeq* _rs_length_diff_seq; + TruncatedSeq* _concurrent_refine_rate_ms_seq; + TruncatedSeq* _logged_cards_rate_ms_seq; TruncatedSeq* _cost_per_logged_card_ms_seq; TruncatedSeq* _cost_scan_hcc_seq; TruncatedSeq* _young_cards_per_entry_ratio_seq; @@ -99,6 +101,8 @@ void report_concurrent_mark_remark_times_ms(double ms); void report_concurrent_mark_cleanup_times_ms(double ms); void report_alloc_rate_ms(double alloc_rate); + void report_concurrent_refine_rate_ms(double cards_per_ms); + void report_logged_cards_rate_ms(double cards_per_ms); void report_cost_per_logged_card_ms(double cost_per_logged_card_ms); void report_cost_scan_hcc(double cost_scan_hcc); void report_cost_per_remset_card_ms(double cost_per_remset_card_ms, bool for_young_gc); @@ -116,6 +120,8 @@ double predict_alloc_rate_ms() const; int num_alloc_rate_ms() const; + double predict_concurrent_refine_rate_ms() const; + double predict_logged_cards_rate_ms() const; double predict_cost_per_logged_card_ms() const; double predict_scan_hcc_ms() const; diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/gc/g1/g1CollectionSet.cpp --- a/src/hotspot/share/gc/g1/g1CollectionSet.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/gc/g1/g1CollectionSet.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -409,7 +409,7 @@ guarantee(target_pause_time_ms > 0.0, "target_pause_time_ms = %1.6lf should be positive", target_pause_time_ms); - size_t pending_cards = _policy->pending_cards(); + size_t pending_cards = _policy->pending_cards_at_gc_start(); double base_time_ms = _policy->predict_base_elapsed_time_ms(pending_cards); double time_remaining_ms = MAX2(target_pause_time_ms - base_time_ms, 0.0); diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp --- a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -412,6 +412,22 @@ dcqs.notify_if_necessary(); } +G1ConcurrentRefine::RefinementStats G1ConcurrentRefine::total_refinement_stats() const { + struct CollectData : public ThreadClosure { + Tickspan _total_time; + size_t _total_cards; + CollectData() : _total_time(), _total_cards(0) {} + virtual void do_thread(Thread* t) { + G1ConcurrentRefineThread* crt = static_cast(t); + _total_time += crt->total_refinement_time(); + _total_cards += crt->total_refined_cards(); + } + } collector; + // Cast away const so we can call non-modifying closure on threads. + const_cast(this)->threads_do(&collector); + return RefinementStats(collector._total_time, collector._total_cards); +} + size_t G1ConcurrentRefine::activation_threshold(uint worker_id) const { Thresholds thresholds = calc_thresholds(_green_zone, _yellow_zone, worker_id); return activation_level(thresholds); @@ -432,7 +448,8 @@ } } -bool G1ConcurrentRefine::do_refinement_step(uint worker_id) { +bool G1ConcurrentRefine::do_refinement_step(uint worker_id, + size_t* total_refined_cards) { G1DirtyCardQueueSet& dcqs = G1BarrierSet::dirty_card_queue_set(); size_t curr_cards = dcqs.num_cards(); @@ -448,5 +465,6 @@ // Process the next buffer, if there are enough left. return dcqs.refine_completed_buffer_concurrently(worker_id + worker_id_offset(), - deactivation_threshold(worker_id)); + deactivation_threshold(worker_id), + total_refined_cards); } diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/gc/g1/g1ConcurrentRefine.hpp --- a/src/hotspot/share/gc/g1/g1ConcurrentRefine.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefine.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -27,6 +27,7 @@ #include "memory/allocation.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/ticks.hpp" // Forward decl class G1ConcurrentRefine; @@ -118,11 +119,22 @@ // Adjust refinement thresholds based on work done during the pause and the goal time. void adjust(double logged_cards_scan_time, size_t processed_logged_cards, double goal_ms); + struct RefinementStats { + Tickspan _time; + size_t _cards; + RefinementStats(Tickspan time, size_t cards) : _time(time), _cards(cards) {} + }; + + RefinementStats total_refinement_stats() const; + // Cards in the dirty card queue set. size_t activation_threshold(uint worker_id) const; size_t deactivation_threshold(uint worker_id) const; - // Perform a single refinement step. Called by the refinement threads when woken up. - bool do_refinement_step(uint worker_id); + + // Perform a single refinement step; called by the refinement + // threads. Returns true if there was refinement work available. + // Increments *total_refined_cards. + bool do_refinement_step(uint worker_id, size_t* total_refined_cards); // Iterate over all concurrent refinement threads applying the given closure. void threads_do(ThreadClosure *tc); diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp --- a/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -37,6 +37,8 @@ ConcurrentGCThread(), _vtime_start(0.0), _vtime_accum(0.0), + _total_refinement_time(), + _total_refined_cards(0), _worker_id(worker_id), _active(false), _monitor(NULL), @@ -101,11 +103,12 @@ break; } - size_t buffers_processed = 0; log_debug(gc, refine)("Activated worker %d, on threshold: " SIZE_FORMAT ", current: " SIZE_FORMAT, _worker_id, _cr->activation_threshold(_worker_id), G1BarrierSet::dirty_card_queue_set().num_cards()); + size_t start_total_refined_cards = _total_refined_cards; // For logging. + { SuspendibleThreadSetJoiner sts_join; @@ -115,20 +118,22 @@ continue; // Re-check for termination after yield delay. } - if (!_cr->do_refinement_step(_worker_id)) { - break; + Ticks start_time = Ticks::now(); + if (!_cr->do_refinement_step(_worker_id, &_total_refined_cards)) { + break; // No cards to process. } - ++buffers_processed; + _total_refinement_time += (Ticks::now() - start_time); } } deactivate(); log_debug(gc, refine)("Deactivated worker %d, off threshold: " SIZE_FORMAT - ", current: " SIZE_FORMAT ", buffers processed: " - SIZE_FORMAT, + ", current: " SIZE_FORMAT ", refined cards: " + SIZE_FORMAT ", total refined cards: " SIZE_FORMAT, _worker_id, _cr->deactivation_threshold(_worker_id), G1BarrierSet::dirty_card_queue_set().num_cards(), - buffers_processed); + _total_refined_cards - start_total_refined_cards, + _total_refined_cards); if (os::supports_vtime()) { _vtime_accum = (os::elapsedVTime() - _vtime_start); diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/gc/g1/g1ConcurrentRefineThread.hpp --- a/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -26,6 +26,7 @@ #define SHARE_GC_G1_G1CONCURRENTREFINETHREAD_HPP #include "gc/shared/concurrentGCThread.hpp" +#include "utilities/ticks.hpp" // Forward Decl. class G1ConcurrentRefine; @@ -38,6 +39,10 @@ double _vtime_start; // Initial virtual time. double _vtime_accum; // Accumulated virtual time. + + Tickspan _total_refinement_time; + size_t _total_refined_cards; + uint _worker_id; bool _active; @@ -61,6 +66,9 @@ // Activate this thread. void activate(); + Tickspan total_refinement_time() const { return _total_refinement_time; } + size_t total_refined_cards() const { return _total_refined_cards; } + // Total virtual time so far. double vtime_accum() { return _vtime_accum; } }; diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/gc/g1/g1DirtyCardQueue.cpp --- a/src/hotspot/share/gc/g1/g1DirtyCardQueue.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/gc/g1/g1DirtyCardQueue.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -37,6 +37,7 @@ #include "runtime/atomic.hpp" #include "runtime/flags/flagSetting.hpp" #include "runtime/mutexLocker.hpp" +#include "runtime/os.hpp" #include "runtime/safepoint.hpp" #include "runtime/thread.inline.hpp" #include "runtime/threadSMR.hpp" @@ -62,6 +63,9 @@ } } +// Assumed to be zero by concurrent threads. +static uint par_ids_start() { return 0; } + G1DirtyCardQueueSet::G1DirtyCardQueueSet(Monitor* cbl_mon, BufferNode::Allocator* allocator) : PtrQueueSet(allocator), @@ -73,15 +77,16 @@ _process_completed_buffers(false), _max_cards(MaxCardsUnlimited), _max_cards_padding(0), - _free_ids(0, num_par_ids()), - _processed_buffers_mut(0), - _processed_buffers_rs_thread(0) + _free_ids(par_ids_start(), num_par_ids()), + _mutator_refined_cards_counters(NEW_C_HEAP_ARRAY(size_t, num_par_ids(), mtGC)) { + ::memset(_mutator_refined_cards_counters, 0, num_par_ids() * sizeof(size_t)); _all_active = true; } G1DirtyCardQueueSet::~G1DirtyCardQueueSet() { abandon_completed_buffers(); + FREE_C_HEAP_ARRAY(size_t, _mutator_refined_cards_counters); } // Determines how many mutator threads can process the buffers in parallel. @@ -89,6 +94,14 @@ return (uint)os::initial_active_processor_count(); } +size_t G1DirtyCardQueueSet::total_mutator_refined_cards() const { + size_t sum = 0; + for (uint i = 0; i < num_par_ids(); ++i) { + sum += _mutator_refined_cards_counters[i]; + } + return sum; +} + void G1DirtyCardQueueSet::handle_zero_index_for_thread(Thread* t) { G1ThreadLocalData::dirty_card_queue(t).handle_zero_index(); } @@ -213,7 +226,9 @@ return result; } -bool G1DirtyCardQueueSet::refine_buffer(BufferNode* node, uint worker_id) { +bool G1DirtyCardQueueSet::refine_buffer(BufferNode* node, + uint worker_id, + size_t* total_refined_cards) { G1RemSet* rem_set = G1CollectedHeap::heap()->rem_set(); size_t size = buffer_size(); void** buffer = BufferNode::make_buffer_from_node(node); @@ -223,6 +238,7 @@ CardTable::CardValue* cp = static_cast(buffer[i]); rem_set->refine_card_concurrently(cp, worker_id); } + *total_refined_cards += (i - node->index()); node->set_index(i); return i == size; } @@ -260,25 +276,27 @@ bool G1DirtyCardQueueSet::mut_process_buffer(BufferNode* node) { uint worker_id = _free_ids.claim_par_id(); // temporarily claim an id - bool result = refine_buffer(node, worker_id); + uint counter_index = worker_id - par_ids_start(); + size_t* counter = &_mutator_refined_cards_counters[counter_index]; + bool result = refine_buffer(node, worker_id, counter); _free_ids.release_par_id(worker_id); // release the id if (result) { assert_fully_consumed(node, buffer_size()); - Atomic::inc(&_processed_buffers_mut); } return result; } -bool G1DirtyCardQueueSet::refine_completed_buffer_concurrently(uint worker_id, size_t stop_at) { +bool G1DirtyCardQueueSet::refine_completed_buffer_concurrently(uint worker_id, + size_t stop_at, + size_t* total_refined_cards) { BufferNode* node = get_completed_buffer(stop_at); if (node == NULL) { return false; - } else if (refine_buffer(node, worker_id)) { + } else if (refine_buffer(node, worker_id, total_refined_cards)) { assert_fully_consumed(node, buffer_size()); // Done with fully processed buffer. deallocate_buffer(node); - Atomic::inc(&_processed_buffers_rs_thread); return true; } else { // Return partially processed buffer to the queue. diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/gc/g1/g1DirtyCardQueue.hpp --- a/src/hotspot/share/gc/g1/g1DirtyCardQueue.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/gc/g1/g1DirtyCardQueue.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -78,14 +78,15 @@ void abandon_completed_buffers(); - // Refine the cards in "node" from it's index to buffer_size. + // Refine the cards in "node" from its index to buffer_size. // Stops processing if SuspendibleThreadSet::should_yield() is true. // Returns true if the entire buffer was processed, false if there // is a pending yield request. The node's index is updated to exclude // the processed elements, e.g. up to the element before processing // stopped, or one past the last element if the entire buffer was - // processed. - bool refine_buffer(BufferNode* node, uint worker_id); + // processed. Increments *total_refined_cards by the number of cards + // processed and removed from the buffer. + bool refine_buffer(BufferNode* node, uint worker_id, size_t* total_refined_cards); bool mut_process_buffer(BufferNode* node); @@ -97,10 +98,9 @@ G1FreeIdSet _free_ids; - // The number of completed buffers processed by mutator and rs thread, - // respectively. - jint _processed_buffers_mut; - jint _processed_buffers_rs_thread; + // Array of cumulative dirty cards refined by mutator threads. + // Array has an entry per id in _free_ids. + size_t* _mutator_refined_cards_counters; public: G1DirtyCardQueueSet(Monitor* cbl_mon, BufferNode::Allocator* allocator); @@ -158,7 +158,12 @@ // Stops processing a buffer if SuspendibleThreadSet::should_yield(), // returning the incompletely processed buffer to the completed buffer // list, for later processing of the remainder. - bool refine_completed_buffer_concurrently(uint worker_id, size_t stop_at); + // + // Increments *total_refined_cards by the number of cards processed and + // removed from the buffer. + bool refine_completed_buffer_concurrently(uint worker_id, + size_t stop_at, + size_t* total_refined_cards); // If a full collection is happening, reset partial logs, and release // completed ones: the full collection will make them all irrelevant. @@ -181,13 +186,8 @@ return _max_cards_padding; } - jint processed_buffers_mut() { - return _processed_buffers_mut; - } - jint processed_buffers_rs_thread() { - return _processed_buffers_rs_thread; - } - + // Total dirty cards refined by mutator threads. + size_t total_mutator_refined_cards() const; }; inline G1DirtyCardQueueSet* G1DirtyCardQueue::dirty_card_qset() const { diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/gc/g1/g1Policy.cpp --- a/src/hotspot/share/gc/g1/g1Policy.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/gc/g1/g1Policy.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -70,7 +70,11 @@ _free_regions_at_end_of_collection(0), _max_rs_length(0), _rs_length_prediction(0), - _pending_cards(0), + _pending_cards_at_gc_start(0), + _pending_cards_at_prev_gc_end(0), + _total_mutator_refined_cards(0), + _total_concurrent_refined_cards(0), + _total_concurrent_refinement_time(), _bytes_allocated_in_old_since_last_gc(0), _initial_mark_to_mixed(), _collection_set(NULL), @@ -442,6 +446,7 @@ collector_state()->set_in_young_only_phase(false); collector_state()->set_in_full_gc(true); _collection_set->clear_candidates(); + record_concurrent_refinement_data(true /* is_full_collection */); } void G1Policy::record_full_collection_end() { @@ -472,12 +477,67 @@ _survivor_surv_rate_group->reset(); update_young_list_max_and_target_length(); update_rs_length_prediction(); + _pending_cards_at_prev_gc_end = _g1h->pending_card_num(); _bytes_allocated_in_old_since_last_gc = 0; record_pause(FullGC, _full_collection_start_sec, end_sec); } +void G1Policy::record_concurrent_refinement_data(bool is_full_collection) { + _pending_cards_at_gc_start = _g1h->pending_card_num(); + + // Record info about concurrent refinement thread processing. + G1ConcurrentRefine* cr = _g1h->concurrent_refine(); + G1ConcurrentRefine::RefinementStats cr_stats = cr->total_refinement_stats(); + + Tickspan cr_time = cr_stats._time - _total_concurrent_refinement_time; + _total_concurrent_refinement_time = cr_stats._time; + + size_t cr_cards = cr_stats._cards - _total_concurrent_refined_cards; + _total_concurrent_refined_cards = cr_stats._cards; + + // Don't update rate if full collection. We could be in an implicit full + // collection after a non-full collection failure, in which case there + // wasn't any mutator/cr-thread activity since last recording. And if + // we're in an explicit full collection, the time since the last GC can + // be arbitrarily short, so not a very good sample. Similarly, don't + // update the rate if the current sample is empty or time is zero. + if (!is_full_collection && (cr_cards > 0) && (cr_time > Tickspan())) { + double rate = cr_cards / (cr_time.seconds() * MILLIUNITS); + _analytics->report_concurrent_refine_rate_ms(rate); + } + + // Record info about mutator thread processing. + G1DirtyCardQueueSet& dcqs = G1BarrierSet::dirty_card_queue_set(); + size_t mut_total_cards = dcqs.total_mutator_refined_cards(); + size_t mut_cards = mut_total_cards - _total_mutator_refined_cards; + _total_mutator_refined_cards = mut_total_cards; + + // Record mutator's card logging rate. + // Don't update if full collection; see above. + if (!is_full_collection) { + size_t total_cards = _pending_cards_at_gc_start + cr_cards + mut_cards; + assert(_pending_cards_at_prev_gc_end <= total_cards, + "untracked cards: last pending: " SIZE_FORMAT + ", pending: " SIZE_FORMAT ", conc refine: " SIZE_FORMAT + ", mut refine:" SIZE_FORMAT, + _pending_cards_at_prev_gc_end, _pending_cards_at_gc_start, + cr_cards, mut_cards); + size_t logged_cards = total_cards - _pending_cards_at_prev_gc_end; + double logging_start_time = _analytics->prev_collection_pause_end_ms(); + double logging_end_time = Ticks::now().seconds() * MILLIUNITS; + double logging_time = logging_end_time - logging_start_time; + // Unlike above for conc-refine rate, here we should not require a + // non-empty sample, since an application could go some time with only + // young-gen or filtered out writes. But we'll ignore unusually short + // sample periods, as they may just pollute the predictions. + if (logging_time > 1.0) { // Require > 1ms sample time. + _analytics->report_logged_cards_rate_ms(logged_cards / logging_time); + } + } +} + void G1Policy::record_collection_pause_start(double start_time_sec) { // We only need to do this here as the policy will only be applied // to the GC we're about to start. so, no point is calculating this @@ -490,7 +550,8 @@ assert_used_and_recalculate_used_equal(_g1h); phase_times()->record_cur_collection_start_sec(start_time_sec); - _pending_cards = _g1h->pending_card_num(); + + record_concurrent_refinement_data(false /* is_full_collection */); _collection_set->reset_bytes_used_before(); _bytes_copied_during_gc = 0; @@ -744,7 +805,7 @@ // after the mixed gc phase. // During mixed gc we do not use them for young gen sizing. if (this_pause_was_young_only) { - _analytics->report_pending_cards((double) _pending_cards); + _analytics->report_pending_cards((double) _pending_cards_at_gc_start); _analytics->report_rs_length((double) _max_rs_length); } } @@ -798,6 +859,7 @@ scan_logged_cards_time_goal_ms -= scan_hcc_time_ms; } + _pending_cards_at_prev_gc_end = _g1h->pending_card_num(); double const logged_cards_time = logged_cards_processing_time(); log_debug(gc, ergo, refine)("Concurrent refinement times: Logged Cards Scan time goal: %1.2fms Logged Cards Scan time: %1.2fms HCC time: %1.2fms", diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/gc/g1/g1Policy.hpp --- a/src/hotspot/share/gc/g1/g1Policy.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/gc/g1/g1Policy.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -100,7 +100,11 @@ size_t _rs_length_prediction; - size_t _pending_cards; + size_t _pending_cards_at_gc_start; + size_t _pending_cards_at_prev_gc_end; + size_t _total_mutator_refined_cards; + size_t _total_concurrent_refined_cards; + Tickspan _total_concurrent_refinement_time; // The amount of allocated bytes in old gen during the last mutator and the following // young GC phase. @@ -244,7 +248,15 @@ uint base_free_regions, double target_pause_time_ms) const; public: - size_t pending_cards() const { return _pending_cards; } + size_t pending_cards_at_gc_start() const { return _pending_cards_at_gc_start; } + + size_t total_concurrent_refined_cards() const { + return _total_concurrent_refined_cards; + } + + size_t total_mutator_refined_cards() const { + return _total_mutator_refined_cards; + } // Calculate the minimum number of old regions we'll add to the CSet // during a mixed GC. @@ -283,6 +295,9 @@ void record_pause(PauseKind kind, double start, double end); // Indicate that we aborted marking before doing any mixed GCs. void abort_time_to_mixed_tracking(); + + void record_concurrent_refinement_data(bool is_full_collection); + public: G1Policy(STWGCTimer* gc_timer); diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/gc/g1/g1RemSet.cpp --- a/src/hotspot/share/gc/g1/g1RemSet.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -489,7 +489,6 @@ _scan_state(new G1RemSetScanState()), _prev_period_summary(), _g1h(g1h), - _num_conc_refined_cards(0), _ct(ct), _g1p(_g1h->policy()), _hot_card_cache(hot_card_cache) { @@ -1377,7 +1376,6 @@ G1ConcurrentRefineOopClosure conc_refine_cl(_g1h, worker_id); if (r->oops_on_memregion_seq_iterate_careful(dirty_region, &conc_refine_cl) != NULL) { - _num_conc_refined_cards++; // Unsynchronized update, only used for logging. return; } diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/gc/g1/g1RemSet.hpp --- a/src/hotspot/share/gc/g1/g1RemSet.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/gc/g1/g1RemSet.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -61,7 +61,6 @@ G1RemSetSummary _prev_period_summary; G1CollectedHeap* _g1h; - size_t _num_conc_refined_cards; // Number of cards refined concurrently to the mutator. G1CardTable* _ct; G1Policy* _g1p; @@ -125,8 +124,6 @@ // Print accumulated summary info from the last time called. void print_periodic_summary_info(const char* header, uint period_count); - size_t num_conc_refined_cards() const { return _num_conc_refined_cards; } - // Rebuilds the remembered set by scanning from bottom to TARS for all regions // using the given work gang. void rebuild_rem_set(G1ConcurrentMark* cm, WorkGang* workers, uint worker_id_offset); diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/gc/g1/g1RemSetSummary.cpp --- a/src/hotspot/share/gc/g1/g1RemSetSummary.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/gc/g1/g1RemSetSummary.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -27,6 +27,7 @@ #include "gc/g1/g1ConcurrentRefine.hpp" #include "gc/g1/g1ConcurrentRefineThread.hpp" #include "gc/g1/g1DirtyCardQueue.hpp" +#include "gc/g1/g1Policy.hpp" #include "gc/g1/g1RemSet.hpp" #include "gc/g1/g1RemSetSummary.hpp" #include "gc/g1/g1YoungRemSetSamplingThread.hpp" @@ -53,18 +54,17 @@ }; void G1RemSetSummary::update() { - _num_conc_refined_cards = _rem_set->num_conc_refined_cards(); - G1DirtyCardQueueSet& dcqs = G1BarrierSet::dirty_card_queue_set(); - _num_processed_buf_mutator = dcqs.processed_buffers_mut(); - _num_processed_buf_rs_threads = dcqs.processed_buffers_rs_thread(); + G1CollectedHeap* g1h = G1CollectedHeap::heap(); + + const G1Policy* policy = g1h->policy(); + _total_mutator_refined_cards = policy->total_mutator_refined_cards(); + _total_concurrent_refined_cards = policy->total_concurrent_refined_cards(); _num_coarsenings = HeapRegionRemSet::n_coarsenings(); - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - G1ConcurrentRefine* cg1r = g1h->concurrent_refine(); if (_rs_threads_vtimes != NULL) { GetRSThreadVTimeClosure p(this); - cg1r->threads_do(&p); + g1h->concurrent_refine()->threads_do(&p); } set_sampling_thread_vtime(g1h->sampling_thread()->vtime_accum()); } @@ -83,9 +83,8 @@ G1RemSetSummary::G1RemSetSummary() : _rem_set(NULL), - _num_conc_refined_cards(0), - _num_processed_buf_mutator(0), - _num_processed_buf_rs_threads(0), + _total_mutator_refined_cards(0), + _total_concurrent_refined_cards(0), _num_coarsenings(0), _num_vtimes(G1ConcurrentRefine::max_num_threads()), _rs_threads_vtimes(NEW_C_HEAP_ARRAY(double, _num_vtimes, mtGC)), @@ -96,9 +95,8 @@ G1RemSetSummary::G1RemSetSummary(G1RemSet* rem_set) : _rem_set(rem_set), - _num_conc_refined_cards(0), - _num_processed_buf_mutator(0), - _num_processed_buf_rs_threads(0), + _total_mutator_refined_cards(0), + _total_concurrent_refined_cards(0), _num_coarsenings(0), _num_vtimes(G1ConcurrentRefine::max_num_threads()), _rs_threads_vtimes(NEW_C_HEAP_ARRAY(double, _num_vtimes, mtGC)), @@ -114,12 +112,10 @@ assert(other != NULL, "just checking"); assert(_num_vtimes == other->_num_vtimes, "just checking"); - _num_conc_refined_cards = other->num_conc_refined_cards(); + _total_mutator_refined_cards = other->total_mutator_refined_cards(); + _total_concurrent_refined_cards = other->total_concurrent_refined_cards(); - _num_processed_buf_mutator = other->num_processed_buf_mutator(); - _num_processed_buf_rs_threads = other->num_processed_buf_rs_threads(); - - _num_coarsenings = other->_num_coarsenings; + _num_coarsenings = other->num_coarsenings(); memcpy(_rs_threads_vtimes, other->_rs_threads_vtimes, sizeof(double) * _num_vtimes); @@ -130,10 +126,8 @@ assert(other != NULL, "just checking"); assert(_num_vtimes == other->_num_vtimes, "just checking"); - _num_conc_refined_cards = other->num_conc_refined_cards() - _num_conc_refined_cards; - - _num_processed_buf_mutator = other->num_processed_buf_mutator() - _num_processed_buf_mutator; - _num_processed_buf_rs_threads = other->num_processed_buf_rs_threads() - _num_processed_buf_rs_threads; + _total_mutator_refined_cards = other->total_mutator_refined_cards() - _total_mutator_refined_cards; + _total_concurrent_refined_cards = other->total_concurrent_refined_cards() - _total_concurrent_refined_cards; _num_coarsenings = other->num_coarsenings() - _num_coarsenings; @@ -356,16 +350,15 @@ void G1RemSetSummary::print_on(outputStream* out) { out->print_cr(" Recent concurrent refinement statistics"); - out->print_cr(" Processed " SIZE_FORMAT " cards concurrently", num_conc_refined_cards()); - out->print_cr(" Of " SIZE_FORMAT " completed buffers:", num_processed_buf_total()); - out->print_cr(" " SIZE_FORMAT_W(8) " (%5.1f%%) by concurrent RS threads.", - num_processed_buf_total(), - percent_of(num_processed_buf_rs_threads(), num_processed_buf_total())); + out->print_cr(" Of " SIZE_FORMAT " refined cards:", total_refined_cards()); + out->print_cr(" " SIZE_FORMAT_W(8) " (%5.1f%%) by concurrent refinement threads.", + total_concurrent_refined_cards(), + percent_of(total_concurrent_refined_cards(), total_refined_cards())); out->print_cr(" " SIZE_FORMAT_W(8) " (%5.1f%%) by mutator threads.", - num_processed_buf_mutator(), - percent_of(num_processed_buf_mutator(), num_processed_buf_total())); + total_mutator_refined_cards(), + percent_of(total_mutator_refined_cards(), total_refined_cards())); out->print_cr(" Did " SIZE_FORMAT " coarsenings.", num_coarsenings()); - out->print_cr(" Concurrent RS threads times (s)"); + out->print_cr(" Concurrent refinement threads times (s)"); out->print(" "); for (uint i = 0; i < _num_vtimes; i++) { out->print(" %5.2f", rs_thread_vtime(i)); diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/gc/g1/g1RemSetSummary.hpp --- a/src/hotspot/share/gc/g1/g1RemSetSummary.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/gc/g1/g1RemSetSummary.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -38,9 +38,8 @@ G1RemSet* _rem_set; - size_t _num_conc_refined_cards; - size_t _num_processed_buf_mutator; - size_t _num_processed_buf_rs_threads; + size_t _total_mutator_refined_cards; + size_t _total_concurrent_refined_cards; size_t _num_coarsenings; @@ -76,20 +75,16 @@ return _sampling_thread_vtime; } - size_t num_conc_refined_cards() const { - return _num_conc_refined_cards; + size_t total_mutator_refined_cards() const { + return _total_mutator_refined_cards; } - size_t num_processed_buf_mutator() const { - return _num_processed_buf_mutator; + size_t total_concurrent_refined_cards() const { + return _total_concurrent_refined_cards; } - size_t num_processed_buf_rs_threads() const { - return _num_processed_buf_rs_threads; - } - - size_t num_processed_buf_total() const { - return num_processed_buf_mutator() + num_processed_buf_rs_threads(); + size_t total_refined_cards() const { + return total_mutator_refined_cards() + total_concurrent_refined_cards(); } size_t num_coarsenings() const { diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/gc/shared/c2/barrierSetC2.hpp --- a/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -264,7 +264,7 @@ virtual void clone_at_expansion(PhaseMacroExpand* phase, ArrayCopyNode* ac) const; // Support for GC barriers emitted during parsing - virtual bool has_load_barriers() const { return false; } + virtual bool has_load_barrier_nodes() const { return false; } virtual bool is_gc_barrier_node(Node* node) const { return false; } virtual Node* step_over_gc_barrier(Node* c) const { return c; } virtual Node* step_over_gc_barrier_ctrl(Node* c) const { return c; } @@ -287,13 +287,9 @@ virtual bool is_gc_specific_loop_opts_pass(LoopOptsMode mode) const { return false; } virtual bool has_special_unique_user(const Node* node) const { return false; } - virtual bool needs_anti_dependence_check(const Node* node) const { return true; } - - virtual void barrier_insertion_phase(Compile* C, PhaseIterGVN &igvn) const { } enum CompilePhase { BeforeOptimize, - BeforeLateInsertion, BeforeMacroExpand, BeforeCodeGen }; @@ -320,6 +316,10 @@ virtual Node* split_if_pre(PhaseIdealLoop* phase, Node* n) const { return NULL; } virtual bool build_loop_late_post(PhaseIdealLoop* phase, Node* n) const { return false; } virtual bool sink_node(PhaseIdealLoop* phase, Node* n, Node* x, Node* x_ctrl, Node* n_ctrl) const { return false; } + + virtual void late_barrier_analysis() const { } + virtual int estimate_stub_size() const { return 0; } + virtual void emit_stubs(CodeBuffer& cb) const { } }; #endif // SHARE_GC_SHARED_C2_BARRIERSETC2_HPP diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -103,7 +103,7 @@ static const TypeFunc* write_ref_field_pre_entry_Type(); static const TypeFunc* shenandoah_clone_barrier_Type(); static const TypeFunc* shenandoah_load_reference_barrier_Type(); - virtual bool has_load_barriers() const { return true; } + virtual bool has_load_barrier_nodes() const { return true; } // This is the entry-point for the backend to perform accesses through the Access API. virtual void clone_at_expansion(PhaseMacroExpand* phase, ArrayCopyNode* ac) const; diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp --- a/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -22,451 +22,157 @@ */ #include "precompiled.hpp" -#include "opto/castnode.hpp" +#include "classfile/javaClasses.hpp" +#include "gc/z/c2/zBarrierSetC2.hpp" +#include "gc/z/zBarrierSet.hpp" +#include "gc/z/zBarrierSetAssembler.hpp" +#include "gc/z/zBarrierSetRuntime.hpp" +#include "opto/block.hpp" #include "opto/compile.hpp" -#include "opto/escape.hpp" #include "opto/graphKit.hpp" -#include "opto/loopnode.hpp" #include "opto/machnode.hpp" -#include "opto/macro.hpp" #include "opto/memnode.hpp" -#include "opto/movenode.hpp" #include "opto/node.hpp" -#include "opto/phase.hpp" -#include "opto/phaseX.hpp" +#include "opto/regalloc.hpp" #include "opto/rootnode.hpp" -#include "opto/type.hpp" -#include "utilities/copy.hpp" #include "utilities/growableArray.hpp" #include "utilities/macros.hpp" -#include "gc/z/zBarrierSet.hpp" -#include "gc/z/c2/zBarrierSetC2.hpp" -#include "gc/z/zThreadLocalData.hpp" -#include "gc/z/zBarrierSetRuntime.hpp" -ZBarrierSetC2State::ZBarrierSetC2State(Arena* comp_arena) : - _load_barrier_nodes(new (comp_arena) GrowableArray(comp_arena, 8, 0, NULL)) {} +class ZBarrierSetC2State : public ResourceObj { +private: + GrowableArray* _stubs; + Node_Array _live; -int ZBarrierSetC2State::load_barrier_count() const { - return _load_barrier_nodes->length(); -} +public: + ZBarrierSetC2State(Arena* arena) : + _stubs(new (arena) GrowableArray(arena, 8, 0, NULL)), + _live(arena) {} -void ZBarrierSetC2State::add_load_barrier_node(LoadBarrierNode * n) { - assert(!_load_barrier_nodes->contains(n), " duplicate entry in expand list"); - _load_barrier_nodes->append(n); -} + GrowableArray* stubs() { + return _stubs; + } -void ZBarrierSetC2State::remove_load_barrier_node(LoadBarrierNode * n) { - // this function may be called twice for a node so check - // that the node is in the array before attempting to remove it - if (_load_barrier_nodes->contains(n)) { - _load_barrier_nodes->remove(n); - } -} + RegMask* live(const Node* node) { + if (!node->is_Mach()) { + // Don't need liveness for non-MachNodes + return NULL; + } -LoadBarrierNode* ZBarrierSetC2State::load_barrier_node(int idx) const { - return _load_barrier_nodes->at(idx); -} + const MachNode* const mach = node->as_Mach(); + if (mach->barrier_data() != ZLoadBarrierStrong && + mach->barrier_data() != ZLoadBarrierWeak) { + // Don't need liveness data for nodes without barriers + return NULL; + } -void* ZBarrierSetC2::create_barrier_state(Arena* comp_arena) const { - return new(comp_arena) ZBarrierSetC2State(comp_arena); -} + RegMask* live = (RegMask*)_live[node->_idx]; + if (live == NULL) { + live = new (Compile::current()->comp_arena()->Amalloc_D(sizeof(RegMask))) RegMask(); + _live.map(node->_idx, (Node*)live); + } -ZBarrierSetC2State* ZBarrierSetC2::state() const { + return live; + } +}; + +static ZBarrierSetC2State* barrier_set_state() { return reinterpret_cast(Compile::current()->barrier_set_state()); } -bool ZBarrierSetC2::is_gc_barrier_node(Node* node) const { - // 1. This step follows potential oop projections of a load barrier before expansion - if (node->is_Proj()) { - node = node->in(0); - } - - // 2. This step checks for unexpanded load barriers - if (node->is_LoadBarrier()) { - return true; - } - - // 3. This step checks for the phi corresponding to an optimized load barrier expansion - if (node->is_Phi()) { - PhiNode* phi = node->as_Phi(); - Node* n = phi->in(1); - if (n != NULL && n->is_LoadBarrierSlowReg()) { - return true; - } - } - - return false; -} - -void ZBarrierSetC2::register_potential_barrier_node(Node* node) const { - if (node->is_LoadBarrier()) { - state()->add_load_barrier_node(node->as_LoadBarrier()); - } -} - -void ZBarrierSetC2::unregister_potential_barrier_node(Node* node) const { - if (node->is_LoadBarrier()) { - state()->remove_load_barrier_node(node->as_LoadBarrier()); - } -} - -void ZBarrierSetC2::eliminate_useless_gc_barriers(Unique_Node_List &useful, Compile* C) const { - // Remove useless LoadBarrier nodes - ZBarrierSetC2State* s = state(); - for (int i = s->load_barrier_count()-1; i >= 0; i--) { - LoadBarrierNode* n = s->load_barrier_node(i); - if (!useful.member(n)) { - unregister_potential_barrier_node(n); - } - } -} - -void ZBarrierSetC2::enqueue_useful_gc_barrier(PhaseIterGVN* igvn, Node* node) const { - if (node->is_LoadBarrier() && !node->as_LoadBarrier()->has_true_uses()) { - igvn->_worklist.push(node); - } -} - -const uint NoBarrier = 0; -const uint RequireBarrier = 1; -const uint WeakBarrier = 2; -const uint ExpandedBarrier = 4; - -static bool load_require_barrier(LoadNode* load) { return (load->barrier_data() & RequireBarrier) == RequireBarrier; } -static bool load_has_weak_barrier(LoadNode* load) { return (load->barrier_data() & WeakBarrier) == WeakBarrier; } -static bool load_has_expanded_barrier(LoadNode* load) { return (load->barrier_data() & ExpandedBarrier) == ExpandedBarrier; } -static void load_set_expanded_barrier(LoadNode* load) { return load->set_barrier_data(ExpandedBarrier); } - -static void load_set_barrier(LoadNode* load, bool weak) { - if (weak) { - load->set_barrier_data(RequireBarrier | WeakBarrier); - } else { - load->set_barrier_data(RequireBarrier); - } -} - -// == LoadBarrierNode == - -LoadBarrierNode::LoadBarrierNode(Compile* C, - Node* c, - Node* mem, - Node* val, - Node* adr, - bool weak) : - MultiNode(Number_of_Inputs), - _weak(weak) { - init_req(Control, c); - init_req(Memory, mem); - init_req(Oop, val); - init_req(Address, adr); - init_req(Similar, C->top()); - - init_class_id(Class_LoadBarrier); - BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); - bs->register_potential_barrier_node(this); -} - -uint LoadBarrierNode::size_of() const { - return sizeof(*this); -} - -bool LoadBarrierNode::cmp(const Node& n) const { - ShouldNotReachHere(); - return false; -} - -const Type *LoadBarrierNode::bottom_type() const { - const Type** floadbarrier = (const Type **)(Compile::current()->type_arena()->Amalloc_4((Number_of_Outputs)*sizeof(Type*))); - Node* in_oop = in(Oop); - floadbarrier[Control] = Type::CONTROL; - floadbarrier[Memory] = Type::MEMORY; - floadbarrier[Oop] = in_oop == NULL ? Type::TOP : in_oop->bottom_type(); - return TypeTuple::make(Number_of_Outputs, floadbarrier); -} - -const TypePtr* LoadBarrierNode::adr_type() const { - ShouldNotReachHere(); - return NULL; -} - -const Type *LoadBarrierNode::Value(PhaseGVN *phase) const { - const Type** floadbarrier = (const Type **)(phase->C->type_arena()->Amalloc_4((Number_of_Outputs)*sizeof(Type*))); - const Type* val_t = phase->type(in(Oop)); - floadbarrier[Control] = Type::CONTROL; - floadbarrier[Memory] = Type::MEMORY; - floadbarrier[Oop] = val_t; - return TypeTuple::make(Number_of_Outputs, floadbarrier); -} - -bool LoadBarrierNode::is_dominator(PhaseIdealLoop* phase, bool linear_only, Node *d, Node *n) { - if (phase != NULL) { - return phase->is_dominator(d, n); - } - - for (int i = 0; i < 10 && n != NULL; i++) { - n = IfNode::up_one_dom(n, linear_only); - if (n == d) { - return true; - } - } - - return false; -} - -LoadBarrierNode* LoadBarrierNode::has_dominating_barrier(PhaseIdealLoop* phase, bool linear_only, bool look_for_similar) { - if (is_weak()) { - // Weak barriers can't be eliminated - return NULL; - } - - Node* val = in(LoadBarrierNode::Oop); - if (in(Similar)->is_Proj() && in(Similar)->in(0)->is_LoadBarrier()) { - LoadBarrierNode* lb = in(Similar)->in(0)->as_LoadBarrier(); - assert(lb->in(Address) == in(Address), ""); - // Load barrier on Similar edge dominates so if it now has the Oop field it can replace this barrier. - if (lb->in(Oop) == in(Oop)) { - return lb; - } - // Follow chain of load barrier through Similar edges - while (!lb->in(Similar)->is_top()) { - lb = lb->in(Similar)->in(0)->as_LoadBarrier(); - assert(lb->in(Address) == in(Address), ""); - } - if (lb != in(Similar)->in(0)) { - return lb; - } - } - for (DUIterator_Fast imax, i = val->fast_outs(imax); i < imax; i++) { - Node* u = val->fast_out(i); - if (u != this && u->is_LoadBarrier() && u->in(Oop) == val && u->as_LoadBarrier()->has_true_uses()) { - Node* this_ctrl = in(LoadBarrierNode::Control); - Node* other_ctrl = u->in(LoadBarrierNode::Control); - if (is_dominator(phase, linear_only, other_ctrl, this_ctrl)) { - return u->as_LoadBarrier(); - } - } - } - - if (can_be_eliminated()) { - return NULL; - } - - if (!look_for_similar) { - return NULL; +ZLoadBarrierStubC2* ZLoadBarrierStubC2::create(const MachNode* node, Address ref_addr, Register ref, Register tmp, bool weak) { + ZLoadBarrierStubC2* const stub = new (Compile::current()->comp_arena()) ZLoadBarrierStubC2(node, ref_addr, ref, tmp, weak); + if (!Compile::current()->in_scratch_emit_size()) { + barrier_set_state()->stubs()->append(stub); } - Node* addr = in(LoadBarrierNode::Address); - for (DUIterator_Fast imax, i = addr->fast_outs(imax); i < imax; i++) { - Node* u = addr->fast_out(i); - if (u != this && u->is_LoadBarrier() && u->as_LoadBarrier()->has_true_uses()) { - Node* this_ctrl = in(LoadBarrierNode::Control); - Node* other_ctrl = u->in(LoadBarrierNode::Control); - if (is_dominator(phase, linear_only, other_ctrl, this_ctrl)) { - ResourceMark rm; - Unique_Node_List wq; - wq.push(in(LoadBarrierNode::Control)); - bool ok = true; - bool dom_found = false; - for (uint next = 0; next < wq.size(); ++next) { - Node *n = wq.at(next); - if (n->is_top()) { - return NULL; - } - assert(n->is_CFG(), ""); - if (n->is_SafePoint()) { - ok = false; - break; - } - if (n == u) { - dom_found = true; - continue; - } - if (n->is_Region()) { - for (uint i = 1; i < n->req(); i++) { - Node* m = n->in(i); - if (m != NULL) { - wq.push(m); - } - } - } else { - Node* m = n->in(0); - if (m != NULL) { - wq.push(m); - } - } - } - if (ok) { - assert(dom_found, ""); - return u->as_LoadBarrier(); - } - break; - } - } - } + return stub; +} + +ZLoadBarrierStubC2::ZLoadBarrierStubC2(const MachNode* node, Address ref_addr, Register ref, Register tmp, bool weak) : + _node(node), + _ref_addr(ref_addr), + _ref(ref), + _tmp(tmp), + _weak(weak), + _entry(), + _continuation() { + assert_different_registers(ref, ref_addr.base()); + assert_different_registers(ref, ref_addr.index()); +} - return NULL; +Address ZLoadBarrierStubC2::ref_addr() const { + return _ref_addr; +} + +Register ZLoadBarrierStubC2::ref() const { + return _ref; +} + +Register ZLoadBarrierStubC2::tmp() const { + return _tmp; +} + +address ZLoadBarrierStubC2::slow_path() const { + const DecoratorSet decorators = _weak ? ON_WEAK_OOP_REF : ON_STRONG_OOP_REF; + return ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_addr(decorators); } -void LoadBarrierNode::push_dominated_barriers(PhaseIterGVN* igvn) const { - // Change to that barrier may affect a dominated barrier so re-push those - assert(!is_weak(), "sanity"); - Node* val = in(LoadBarrierNode::Oop); +RegMask& ZLoadBarrierStubC2::live() const { + return *barrier_set_state()->live(_node); +} + +Label* ZLoadBarrierStubC2::entry() { + // The _entry will never be bound when in_scratch_emit_size() is true. + // However, we still need to return a label that is not bound now, but + // will eventually be bound. Any lable will do, as it will only act as + // a placeholder, so we return the _continuation label. + return Compile::current()->in_scratch_emit_size() ? &_continuation : &_entry; +} + +Label* ZLoadBarrierStubC2::continuation() { + return &_continuation; +} - for (DUIterator_Fast imax, i = val->fast_outs(imax); i < imax; i++) { - Node* u = val->fast_out(i); - if (u != this && u->is_LoadBarrier() && u->in(Oop) == val) { - Node* this_ctrl = in(Control); - Node* other_ctrl = u->in(Control); - if (is_dominator(NULL, false, this_ctrl, other_ctrl)) { - igvn->_worklist.push(u); - } +void* ZBarrierSetC2::create_barrier_state(Arena* comp_arena) const { + return new (comp_arena) ZBarrierSetC2State(comp_arena); +} + +void ZBarrierSetC2::late_barrier_analysis() const { + analyze_dominating_barriers(); + compute_liveness_at_stubs(); +} + +void ZBarrierSetC2::emit_stubs(CodeBuffer& cb) const { + MacroAssembler masm(&cb); + GrowableArray* const stubs = barrier_set_state()->stubs(); + + for (int i = 0; i < stubs->length(); i++) { + // Make sure there is enough space in the code buffer + if (cb.insts()->maybe_expand_to_ensure_remaining(Compile::MAX_inst_size) && cb.blob() == NULL) { + ciEnv::current()->record_failure("CodeCache is full"); + return; } - Node* addr = in(LoadBarrierNode::Address); - for (DUIterator_Fast imax, i = addr->fast_outs(imax); i < imax; i++) { - Node* u = addr->fast_out(i); - if (u != this && u->is_LoadBarrier() && u->in(Similar)->is_top()) { - Node* this_ctrl = in(Control); - Node* other_ctrl = u->in(Control); - if (is_dominator(NULL, false, this_ctrl, other_ctrl)) { - igvn->_worklist.push(u); - } - } - } - } -} - -Node *LoadBarrierNode::Identity(PhaseGVN *phase) { - LoadBarrierNode* dominating_barrier = has_dominating_barrier(NULL, true, false); - if (dominating_barrier != NULL) { - assert(!is_weak(), "Weak barriers cant be eliminated"); - assert(dominating_barrier->in(Oop) == in(Oop), ""); - return dominating_barrier; - } - - return this; -} - -Node *LoadBarrierNode::Ideal(PhaseGVN *phase, bool can_reshape) { - if (remove_dead_region(phase, can_reshape)) { - return this; + ZBarrierSet::assembler()->generate_c2_load_barrier_stub(&masm, stubs->at(i)); } - Node *val = in(Oop); - Node *mem = in(Memory); - Node *ctrl = in(Control); - - assert(val->Opcode() != Op_LoadN, ""); - assert(val->Opcode() != Op_DecodeN, ""); - - if (mem->is_MergeMem()) { - Node *new_mem = mem->as_MergeMem()->memory_at(Compile::AliasIdxRaw); - set_req(Memory, new_mem); - if (mem->outcnt() == 0 && can_reshape) { - phase->is_IterGVN()->_worklist.push(mem); - } - return this; - } + masm.flush(); +} - LoadBarrierNode *dominating_barrier = NULL; - if (!is_weak()) { - dominating_barrier = has_dominating_barrier(NULL, !can_reshape, !phase->C->major_progress()); - if (dominating_barrier != NULL && dominating_barrier->in(Oop) != in(Oop)) { - assert(in(Address) == dominating_barrier->in(Address), ""); - set_req(Similar, dominating_barrier->proj_out(Oop)); - return this; - } - } - - bool eliminate = can_reshape && (dominating_barrier != NULL || !has_true_uses()); - if (eliminate) { - if (can_reshape) { - PhaseIterGVN* igvn = phase->is_IterGVN(); - Node* out_ctrl = proj_out_or_null(Control); - Node* out_res = proj_out_or_null(Oop); +int ZBarrierSetC2::estimate_stub_size() const { + Compile* const C = Compile::current(); + BufferBlob* const blob = C->scratch_buffer_blob(); + GrowableArray* const stubs = barrier_set_state()->stubs(); + int size = 0; - if (out_ctrl != NULL) { - igvn->replace_node(out_ctrl, ctrl); - } - - // That transformation may cause the Similar edge on the load barrier to be invalid - fix_similar_in_uses(igvn); - if (out_res != NULL) { - if (dominating_barrier != NULL) { - assert(!is_weak(), "Sanity"); - igvn->replace_node(out_res, dominating_barrier->proj_out(Oop)); - } else { - igvn->replace_node(out_res, val); - } - } - } - return new ConINode(TypeInt::ZERO); + for (int i = 0; i < stubs->length(); i++) { + CodeBuffer cb(blob->content_begin(), (address)C->scratch_locs_memory() - blob->content_begin()); + MacroAssembler masm(&cb); + ZBarrierSet::assembler()->generate_c2_load_barrier_stub(&masm, stubs->at(i)); + size += cb.insts_size(); } - // If the Similar edge is no longer a load barrier, clear it - Node* similar = in(Similar); - if (!similar->is_top() && !(similar->is_Proj() && similar->in(0)->is_LoadBarrier())) { - set_req(Similar, phase->C->top()); - return this; - } - - if (can_reshape && !is_weak()) { - // If this barrier is linked through the Similar edge by a - // dominated barrier and both barriers have the same Oop field, - // the dominated barrier can go away, so push it for reprocessing. - // We also want to avoid a barrier to depend on another dominating - // barrier through its Similar edge that itself depend on another - // barrier through its Similar edge and rather have the first - // depend on the third. - PhaseIterGVN* igvn = phase->is_IterGVN(); - Node* out_res = proj_out(Oop); - for (DUIterator_Fast imax, i = out_res->fast_outs(imax); i < imax; i++) { - Node* u = out_res->fast_out(i); - if (u->is_LoadBarrier() && u->in(Similar) == out_res && - (u->in(Oop) == val || !u->in(Similar)->is_top())) { - assert(!u->as_LoadBarrier()->is_weak(), "Sanity"); - igvn->_worklist.push(u); - } - } - push_dominated_barriers(igvn); - } - - return NULL; -} - -uint LoadBarrierNode::match_edge(uint idx) const { - ShouldNotReachHere(); - return 0; -} - -void LoadBarrierNode::fix_similar_in_uses(PhaseIterGVN* igvn) { - Node* out_res = proj_out_or_null(Oop); - if (out_res == NULL) { - return; - } - - for (DUIterator_Fast imax, i = out_res->fast_outs(imax); i < imax; i++) { - Node* u = out_res->fast_out(i); - if (u->is_LoadBarrier() && u->in(Similar) == out_res) { - igvn->replace_input_of(u, Similar, igvn->C->top()); - --i; - --imax; - } - } -} - -bool LoadBarrierNode::has_true_uses() const { - Node* out_res = proj_out_or_null(Oop); - if (out_res != NULL) { - for (DUIterator_Fast imax, i = out_res->fast_outs(imax); i < imax; i++) { - Node *u = out_res->fast_out(i); - if (!u->is_LoadBarrier() || u->in(Similar) != out_res) { - return true; - } - } - } - return false; + return size; } static bool barrier_needed(C2Access& access) { @@ -474,1223 +180,252 @@ } Node* ZBarrierSetC2::load_at_resolved(C2Access& access, const Type* val_type) const { - Node* p = BarrierSetC2::load_at_resolved(access, val_type); - if (!barrier_needed(access)) { - return p; + Node* result = BarrierSetC2::load_at_resolved(access, val_type); + if (barrier_needed(access) && access.raw_access()->is_Mem()) { + if ((access.decorators() & ON_WEAK_OOP_REF) != 0) { + access.raw_access()->as_Load()->set_barrier_data(ZLoadBarrierWeak); + } else { + access.raw_access()->as_Load()->set_barrier_data(ZLoadBarrierStrong); + } } - bool weak = (access.decorators() & ON_WEAK_OOP_REF) != 0; - if (p->isa_Load()) { - load_set_barrier(p->as_Load(), weak); - } - return p; + return result; } Node* ZBarrierSetC2::atomic_cmpxchg_val_at_resolved(C2AtomicParseAccess& access, Node* expected_val, Node* new_val, const Type* val_type) const { Node* result = BarrierSetC2::atomic_cmpxchg_val_at_resolved(access, expected_val, new_val, val_type); - LoadStoreNode* lsn = result->as_LoadStore(); if (barrier_needed(access)) { - lsn->set_has_barrier(); + access.raw_access()->as_LoadStore()->set_barrier_data(ZLoadBarrierStrong); } - return lsn; + return result; } Node* ZBarrierSetC2::atomic_cmpxchg_bool_at_resolved(C2AtomicParseAccess& access, Node* expected_val, Node* new_val, const Type* value_type) const { Node* result = BarrierSetC2::atomic_cmpxchg_bool_at_resolved(access, expected_val, new_val, value_type); - LoadStoreNode* lsn = result->as_LoadStore(); if (barrier_needed(access)) { - lsn->set_has_barrier(); + access.raw_access()->as_LoadStore()->set_barrier_data(ZLoadBarrierStrong); } - return lsn; + return result; } Node* ZBarrierSetC2::atomic_xchg_at_resolved(C2AtomicParseAccess& access, Node* new_val, const Type* val_type) const { Node* result = BarrierSetC2::atomic_xchg_at_resolved(access, new_val, val_type); - LoadStoreNode* lsn = result->as_LoadStore(); if (barrier_needed(access)) { - lsn->set_has_barrier(); + access.raw_access()->as_LoadStore()->set_barrier_data(ZLoadBarrierStrong); } - return lsn; + return result; } -// == Macro Expansion == - -// Optimized, low spill, loadbarrier variant using stub specialized on register used -void ZBarrierSetC2::expand_loadbarrier_node(PhaseMacroExpand* phase, LoadBarrierNode* barrier) const { - PhaseIterGVN &igvn = phase->igvn(); - float unlikely = PROB_UNLIKELY(0.999); - - Node* in_ctrl = barrier->in(LoadBarrierNode::Control); - Node* in_mem = barrier->in(LoadBarrierNode::Memory); - Node* in_val = barrier->in(LoadBarrierNode::Oop); - Node* in_adr = barrier->in(LoadBarrierNode::Address); - - Node* out_ctrl = barrier->proj_out(LoadBarrierNode::Control); - Node* out_res = barrier->proj_out(LoadBarrierNode::Oop); - - assert(barrier->in(LoadBarrierNode::Oop) != NULL, "oop to loadbarrier node cannot be null"); - - Node* jthread = igvn.transform(new ThreadLocalNode()); - Node* adr = phase->basic_plus_adr(jthread, in_bytes(ZThreadLocalData::address_bad_mask_offset())); - Node* bad_mask = igvn.transform(LoadNode::make(igvn, in_ctrl, in_mem, adr, - TypeRawPtr::BOTTOM, TypeX_X, TypeX_X->basic_type(), - MemNode::unordered)); - Node* cast = igvn.transform(new CastP2XNode(in_ctrl, in_val)); - Node* obj_masked = igvn.transform(new AndXNode(cast, bad_mask)); - Node* cmp = igvn.transform(new CmpXNode(obj_masked, igvn.zerocon(TypeX_X->basic_type()))); - Node *bol = igvn.transform(new BoolNode(cmp, BoolTest::ne))->as_Bool(); - IfNode* iff = igvn.transform(new IfNode(in_ctrl, bol, unlikely, COUNT_UNKNOWN))->as_If(); - Node* then = igvn.transform(new IfTrueNode(iff)); - Node* elsen = igvn.transform(new IfFalseNode(iff)); - - Node* new_loadp = igvn.transform(new LoadBarrierSlowRegNode(then, in_adr, in_val, - (const TypePtr*) in_val->bottom_type(), barrier->is_weak())); - - // Create the final region/phi pair to converge cntl/data paths to downstream code - Node* result_region = igvn.transform(new RegionNode(3)); - result_region->set_req(1, then); - result_region->set_req(2, elsen); - - Node* result_phi = igvn.transform(new PhiNode(result_region, TypeInstPtr::BOTTOM)); - result_phi->set_req(1, new_loadp); - result_phi->set_req(2, barrier->in(LoadBarrierNode::Oop)); - - igvn.replace_node(out_ctrl, result_region); - igvn.replace_node(out_res, result_phi); - - assert(barrier->outcnt() == 0,"LoadBarrier macro node has non-null outputs after expansion!"); - - igvn.remove_dead_node(barrier); - igvn.remove_dead_node(out_ctrl); - igvn.remove_dead_node(out_res); - - assert(is_gc_barrier_node(result_phi), "sanity"); - assert(step_over_gc_barrier(result_phi) == in_val, "sanity"); - - phase->C->print_method(PHASE_BARRIER_EXPANSION, 4, barrier->_idx); +bool ZBarrierSetC2::array_copy_requires_gc_barriers(bool tightly_coupled_alloc, BasicType type, + bool is_clone, ArrayCopyPhase phase) const { + return type == T_OBJECT || type == T_ARRAY; } -bool ZBarrierSetC2::expand_barriers(Compile* C, PhaseIterGVN& igvn) const { - ZBarrierSetC2State* s = state(); - if (s->load_barrier_count() > 0) { - PhaseMacroExpand macro(igvn); +// == Dominating barrier elision == - int skipped = 0; - while (s->load_barrier_count() > skipped) { - int load_barrier_count = s->load_barrier_count(); - LoadBarrierNode * n = s->load_barrier_node(load_barrier_count-1-skipped); - if (igvn.type(n) == Type::TOP || (n->in(0) != NULL && n->in(0)->is_top())) { - // Node is unreachable, so don't try to expand it - s->remove_load_barrier_node(n); - continue; - } - if (!n->can_be_eliminated()) { - skipped++; - continue; - } - expand_loadbarrier_node(¯o, n); - assert(s->load_barrier_count() < load_barrier_count, "must have deleted a node from load barrier list"); - if (C->failing()) { - return true; - } - } - while (s->load_barrier_count() > 0) { - int load_barrier_count = s->load_barrier_count(); - LoadBarrierNode* n = s->load_barrier_node(load_barrier_count - 1); - assert(!(igvn.type(n) == Type::TOP || (n->in(0) != NULL && n->in(0)->is_top())), "should have been processed already"); - assert(!n->can_be_eliminated(), "should have been processed already"); - expand_loadbarrier_node(¯o, n); - assert(s->load_barrier_count() < load_barrier_count, "must have deleted a node from load barrier list"); - if (C->failing()) { - return true; - } - } - igvn.set_delay_transform(false); - igvn.optimize(); - if (C->failing()) { +static bool block_has_safepoint(const Block* block, uint from, uint to) { + for (uint i = from; i < to; i++) { + if (block->get_node(i)->is_MachSafePoint()) { + // Safepoint found return true; } } + // Safepoint not found return false; } -Node* ZBarrierSetC2::step_over_gc_barrier(Node* c) const { - Node* node = c; +static bool block_has_safepoint(const Block* block) { + return block_has_safepoint(block, 0, block->number_of_nodes()); +} - // 1. This step follows potential oop projections of a load barrier before expansion - if (node->is_Proj()) { - node = node->in(0); +static uint block_index(const Block* block, const Node* node) { + for (uint j = 0; j < block->number_of_nodes(); ++j) { + if (block->get_node(j) == node) { + return j; + } } + ShouldNotReachHere(); + return 0; +} + +void ZBarrierSetC2::analyze_dominating_barriers() const { + ResourceMark rm; + Compile* const C = Compile::current(); + PhaseCFG* const cfg = C->cfg(); + Block_List worklist; + Node_List mem_ops; + Node_List barrier_loads; - // 2. This step checks for unexpanded load barriers - if (node->is_LoadBarrier()) { - return node->in(LoadBarrierNode::Oop); - } + // Step 1 - Find accesses, and track them in lists + for (uint i = 0; i < cfg->number_of_blocks(); ++i) { + const Block* const block = cfg->get_block(i); + for (uint j = 0; j < block->number_of_nodes(); ++j) { + const Node* const node = block->get_node(j); + if (!node->is_Mach()) { + continue; + } - // 3. This step checks for the phi corresponding to an optimized load barrier expansion - if (node->is_Phi()) { - PhiNode* phi = node->as_Phi(); - Node* n = phi->in(1); - if (n != NULL && n->is_LoadBarrierSlowReg()) { - assert(c == node, "projections from step 1 should only be seen before macro expansion"); - return phi->in(2); + MachNode* const mach = node->as_Mach(); + switch (mach->ideal_Opcode()) { + case Op_LoadP: + case Op_CompareAndExchangeP: + case Op_CompareAndSwapP: + case Op_GetAndSetP: + if (mach->barrier_data() == ZLoadBarrierStrong) { + barrier_loads.push(mach); + } + case Op_StoreP: + mem_ops.push(mach); + break; + + default: + break; + } } } - return c; -} - -Node* ZBarrierSetC2::step_over_gc_barrier_ctrl(Node* c) const { - Node* node = c; - - // 1. This step follows potential ctrl projections of a load barrier before expansion - if (node->is_Proj()) { - node = node->in(0); - } - - // 2. This step checks for unexpanded load barriers - if (node->is_LoadBarrier()) { - return node->in(LoadBarrierNode::Control); - } - - return c; -} - -bool ZBarrierSetC2::array_copy_requires_gc_barriers(bool tightly_coupled_alloc, BasicType type, bool is_clone, ArrayCopyPhase phase) const { - return is_reference_type(type); -} - -bool ZBarrierSetC2::final_graph_reshaping(Compile* compile, Node* n, uint opcode) const { - switch (opcode) { - case Op_LoadBarrier: - assert(0, "There should be no load barriers left"); - case Op_ZGetAndSetP: - case Op_ZCompareAndExchangeP: - case Op_ZCompareAndSwapP: - case Op_ZWeakCompareAndSwapP: -#ifdef ASSERT - if (VerifyOptoOopOffsets) { - MemNode *mem = n->as_Mem(); - // Check to see if address types have grounded out somehow. - const TypeInstPtr *tp = mem->in(MemNode::Address)->bottom_type()->isa_instptr(); - ciInstanceKlass *k = tp->klass()->as_instance_klass(); - bool oop_offset_is_sane = k->contains_field_offset(tp->offset()); - assert(!tp || oop_offset_is_sane, ""); - } -#endif - return true; - default: - return false; - } -} + // Step 2 - Find dominating accesses for each load + for (uint i = 0; i < barrier_loads.size(); i++) { + MachNode* const load = barrier_loads.at(i)->as_Mach(); + const TypePtr* load_adr_type = NULL; + intptr_t load_offset = 0; + const Node* const load_obj = load->get_base_and_disp(load_offset, load_adr_type); + Block* const load_block = cfg->get_block_for_node(load); + const uint load_index = block_index(load_block, load); -bool ZBarrierSetC2::matcher_find_shared_visit(Matcher* matcher, Matcher::MStack& mstack, Node* n, uint opcode, bool& mem_op, int& mem_addr_idx) const { - switch(opcode) { - case Op_CallLeaf: - if (n->as_Call()->entry_point() == ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_addr() || - n->as_Call()->entry_point() == ZBarrierSetRuntime::load_barrier_on_weak_oop_field_preloaded_addr()) { - mem_op = true; - mem_addr_idx = TypeFunc::Parms + 1; - return true; - } - return false; - default: - return false; - } -} - -bool ZBarrierSetC2::matcher_find_shared_post_visit(Matcher* matcher, Node* n, uint opcode) const { - switch(opcode) { - case Op_ZCompareAndExchangeP: - case Op_ZCompareAndSwapP: - case Op_ZWeakCompareAndSwapP: { - Node *mem = n->in(MemNode::Address); - Node *keepalive = n->in(5); - Node *pair1 = new BinaryNode(mem, keepalive); - - Node *newval = n->in(MemNode::ValueIn); - Node *oldval = n->in(LoadStoreConditionalNode::ExpectedIn); - Node *pair2 = new BinaryNode(oldval, newval); + for (uint j = 0; j < mem_ops.size(); j++) { + MachNode* mem = mem_ops.at(j)->as_Mach(); + const TypePtr* mem_adr_type = NULL; + intptr_t mem_offset = 0; + const Node* mem_obj = mem_obj = mem->get_base_and_disp(mem_offset, mem_adr_type); + Block* mem_block = cfg->get_block_for_node(mem); + uint mem_index = block_index(mem_block, mem); - n->set_req(MemNode::Address, pair1); - n->set_req(MemNode::ValueIn, pair2); - n->del_req(5); - n->del_req(LoadStoreConditionalNode::ExpectedIn); - return true; - } - case Op_ZGetAndSetP: { - Node *keepalive = n->in(4); - Node *newval = n->in(MemNode::ValueIn); - Node *pair = new BinaryNode(newval, keepalive); - n->set_req(MemNode::ValueIn, pair); - n->del_req(4); - return true; - } + if (load_obj == NodeSentinel || mem_obj == NodeSentinel || + load_obj == NULL || mem_obj == NULL || + load_offset < 0 || mem_offset < 0) { + continue; + } - default: - return false; - } -} - -// == Verification == - -#ifdef ASSERT - -static void verify_slippery_safepoints_internal(Node* ctrl) { - // Given a CFG node, make sure it does not contain both safepoints and loads - // that have expanded barriers. - bool found_safepoint = false; - bool found_load = false; + if (mem_obj != load_obj || mem_offset != load_offset) { + // Not the same addresses, not a candidate + continue; + } - for (DUIterator_Fast imax, i = ctrl->fast_outs(imax); i < imax; i++) { - Node* node = ctrl->fast_out(i); - if (node->in(0) != ctrl) { - // Skip outgoing precedence edges from ctrl. - continue; - } - if (node->is_SafePoint()) { - found_safepoint = true; - } - if (node->is_Load() && load_require_barrier(node->as_Load()) && - load_has_expanded_barrier(node->as_Load())) { - found_load = true; - } - } - assert(!found_safepoint || !found_load, "found load and safepoint in same block"); -} - -static void verify_slippery_safepoints(Compile* C) { - ResourceArea *area = Thread::current()->resource_area(); - Unique_Node_List visited(area); - Unique_Node_List checked(area); - - // Recursively walk the graph. - visited.push(C->root()); - while (visited.size() > 0) { - Node* node = visited.pop(); - - Node* ctrl = node; - if (!node->is_CFG()) { - ctrl = node->in(0); - } - - if (ctrl != NULL && !checked.member(ctrl)) { - // For each block found in the graph, verify that it does not - // contain both a safepoint and a load requiring barriers. - verify_slippery_safepoints_internal(ctrl); - - checked.push(ctrl); - } - - checked.push(node); - - for (DUIterator_Fast imax, i = node->fast_outs(imax); i < imax; i++) { - Node* use = node->fast_out(i); - if (checked.member(use)) continue; - if (visited.member(use)) continue; - visited.push(use); - } - } -} + if (load_block == mem_block) { + // Earlier accesses in the same block + if (mem_index < load_index && !block_has_safepoint(mem_block, mem_index + 1, load_index)) { + load->set_barrier_data(ZLoadBarrierElided); + } + } else if (mem_block->dominates(load_block)) { + // Dominating block? Look around for safepoints + ResourceMark rm; + Block_List stack; + VectorSet visited(Thread::current()->resource_area()); + stack.push(load_block); + bool safepoint_found = block_has_safepoint(load_block); + while (!safepoint_found && stack.size() > 0) { + Block* block = stack.pop(); + if (visited.test_set(block->_pre_order)) { + continue; + } + if (block_has_safepoint(block)) { + safepoint_found = true; + break; + } + if (block == mem_block) { + continue; + } -void ZBarrierSetC2::verify_gc_barriers(Compile* compile, CompilePhase phase) const { - switch(phase) { - case BarrierSetC2::BeforeOptimize: - case BarrierSetC2::BeforeLateInsertion: - assert(state()->load_barrier_count() == 0, "No barriers inserted yet"); - break; - case BarrierSetC2::BeforeMacroExpand: - // Barrier placement should be set by now. - verify_gc_barriers(false /*post_parse*/); - break; - case BarrierSetC2::BeforeCodeGen: - // Barriers has been fully expanded. - assert(state()->load_barrier_count() == 0, "No more macro barriers"); - verify_slippery_safepoints(compile); - break; - default: - assert(0, "Phase without verification"); - } -} - -// post_parse implies that there might be load barriers without uses after parsing -// That only applies when adding barriers at parse time. -void ZBarrierSetC2::verify_gc_barriers(bool post_parse) const { - ZBarrierSetC2State* s = state(); - Compile* C = Compile::current(); - ResourceMark rm; - VectorSet visited(Thread::current()->resource_area()); - - for (int i = 0; i < s->load_barrier_count(); i++) { - LoadBarrierNode* n = s->load_barrier_node(i); - - // The dominating barrier on the same address if it exists and - // this barrier must not be applied on the value from the same - // load otherwise the value is not reloaded before it's used the - // second time. - assert(n->in(LoadBarrierNode::Similar)->is_top() || - (n->in(LoadBarrierNode::Similar)->in(0)->is_LoadBarrier() && - n->in(LoadBarrierNode::Similar)->in(0)->in(LoadBarrierNode::Address) == n->in(LoadBarrierNode::Address) && - n->in(LoadBarrierNode::Similar)->in(0)->in(LoadBarrierNode::Oop) != n->in(LoadBarrierNode::Oop)), - "broken similar edge"); - - assert(n->as_LoadBarrier()->has_true_uses(), - "found unneeded load barrier"); - - // Several load barrier nodes chained through their Similar edge - // break the code that remove the barriers in final graph reshape. - assert(n->in(LoadBarrierNode::Similar)->is_top() || - (n->in(LoadBarrierNode::Similar)->in(0)->is_LoadBarrier() && - n->in(LoadBarrierNode::Similar)->in(0)->in(LoadBarrierNode::Similar)->is_top()), - "chain of Similar load barriers"); - - if (!n->in(LoadBarrierNode::Similar)->is_top()) { - ResourceMark rm; - Unique_Node_List wq; - Node* other = n->in(LoadBarrierNode::Similar)->in(0); - wq.push(n); - for (uint next = 0; next < wq.size(); ++next) { - Node *nn = wq.at(next); - assert(nn->is_CFG(), ""); - assert(!nn->is_SafePoint(), ""); - - if (nn == other) { - continue; + // Push predecessor blocks + for (uint p = 1; p < block->num_preds(); ++p) { + Block* pred = cfg->get_block_for_node(block->pred(p)); + stack.push(pred); + } } - if (nn->is_Region()) { - for (uint i = 1; i < nn->req(); i++) { - Node* m = nn->in(i); - if (m != NULL) { - wq.push(m); - } - } - } else { - Node* m = nn->in(0); - if (m != NULL) { - wq.push(m); - } + if (!safepoint_found) { + load->set_barrier_data(ZLoadBarrierElided); } } } } } -#endif // end verification code - -// If a call is the control, we actually want its control projection -static Node* normalize_ctrl(Node* node) { - if (node->is_Call()) { - node = node->as_Call()->proj_out(TypeFunc::Control); - } - return node; -} - -static Node* get_ctrl_normalized(PhaseIdealLoop *phase, Node* node) { - return normalize_ctrl(phase->get_ctrl(node)); -} - -static void call_catch_cleanup_one(PhaseIdealLoop* phase, LoadNode* load, Node* ctrl); - -// This code is cloning all uses of a load that is between a call and the catch blocks, -// to each use. - -static bool fixup_uses_in_catch(PhaseIdealLoop *phase, Node *start_ctrl, Node *node) { - - if (!phase->has_ctrl(node)) { - // This node is floating - doesn't need to be cloned. - assert(node != start_ctrl, "check"); - return false; - } - - Node* ctrl = get_ctrl_normalized(phase, node); - if (ctrl != start_ctrl) { - // We are in a successor block - the node is ok. - return false; // Unwind - } - - // Process successor nodes - int outcnt = node->outcnt(); - for (int i = 0; i < outcnt; i++) { - Node* n = node->raw_out(0); - assert(!n->is_LoadBarrier(), "Sanity"); - // Calling recursively, visiting leafs first - fixup_uses_in_catch(phase, start_ctrl, n); - } - - // Now all successors are outside - // - Clone this node to both successors - assert(!node->is_Store(), "Stores not expected here"); - - // In some very rare cases a load that doesn't need a barrier will end up here - // Treat it as a LoadP and the insertion of phis will be done correctly. - if (node->is_Load()) { - call_catch_cleanup_one(phase, node->as_Load(), phase->get_ctrl(node)); - } else { - for (DUIterator_Fast jmax, i = node->fast_outs(jmax); i < jmax; i++) { - Node* use = node->fast_out(i); - Node* clone = node->clone(); - assert(clone->outcnt() == 0, ""); +// == Reduced spilling optimization == - assert(use->find_edge(node) != -1, "check"); - phase->igvn().rehash_node_delayed(use); - use->replace_edge(node, clone); - - Node* new_ctrl; - if (use->is_block_start()) { - new_ctrl = use; - } else if (use->is_CFG()) { - new_ctrl = use->in(0); - assert (new_ctrl != NULL, ""); - } else { - new_ctrl = get_ctrl_normalized(phase, use); - } - - phase->set_ctrl(clone, new_ctrl); - - if (phase->C->directive()->ZTraceLoadBarriersOption) tty->print_cr(" Clone op %i as %i to control %i", node->_idx, clone->_idx, new_ctrl->_idx); - phase->igvn().register_new_node_with_optimizer(clone); - --i, --jmax; - } - assert(node->outcnt() == 0, "must be empty now"); - - // Node node is dead. - phase->igvn().remove_dead_node(node); - } - return true; // unwind - return if a use was processed -} +void ZBarrierSetC2::compute_liveness_at_stubs() const { + ResourceMark rm; + Compile* const C = Compile::current(); + Arena* const A = Thread::current()->resource_area(); + PhaseCFG* const cfg = C->cfg(); + PhaseRegAlloc* const regalloc = C->regalloc(); + RegMask* const live = NEW_ARENA_ARRAY(A, RegMask, cfg->number_of_blocks() * sizeof(RegMask)); + ZBarrierSetAssembler* const bs = ZBarrierSet::assembler(); + Block_List worklist; -// Clone a load to a specific catch_proj -static Node* clone_load_to_catchproj(PhaseIdealLoop* phase, Node* load, Node* catch_proj) { - Node* cloned_load = load->clone(); - cloned_load->set_req(0, catch_proj); // set explicit control - phase->set_ctrl(cloned_load, catch_proj); // update - if (phase->C->directive()->ZTraceLoadBarriersOption) tty->print_cr(" Clone LOAD %i as %i to control %i", load->_idx, cloned_load->_idx, catch_proj->_idx); - phase->igvn().register_new_node_with_optimizer(cloned_load); - return cloned_load; -} - -static Node* get_dominating_region(PhaseIdealLoop* phase, Node* node, Node* stop) { - Node* region = node; - while (!region->isa_Region()) { - Node *up = phase->idom(region); - assert(up != region, "Must not loop"); - assert(up != stop, "Must not find original control"); - region = up; - } - return region; -} - -// Clone this load to each catch block -static void call_catch_cleanup_one(PhaseIdealLoop* phase, LoadNode* load, Node* ctrl) { - bool trace = phase->C->directive()->ZTraceLoadBarriersOption; - phase->igvn().set_delay_transform(true); - - // Verify pre conditions - assert(ctrl->isa_Proj() && ctrl->in(0)->isa_Call(), "Must be a call proj"); - assert(ctrl->raw_out(0)->isa_Catch(), "Must be a catch"); - - if (ctrl->raw_out(0)->isa_Catch()->outcnt() == 1) { - if (trace) tty->print_cr("Cleaning up catch: Skipping load %i, call with single catch", load->_idx); - return; + for (uint i = 0; i < cfg->number_of_blocks(); ++i) { + new ((void*)(live + i)) RegMask(); + worklist.push(cfg->get_block(i)); } - // Process the loads successor nodes - if any is between - // the call and the catch blocks, they need to be cloned to. - // This is done recursively - for (uint i = 0; i < load->outcnt();) { - Node *n = load->raw_out(i); - assert(!n->is_LoadBarrier(), "Sanity"); - if (!fixup_uses_in_catch(phase, ctrl, n)) { - // if no successor was cloned, progress to next out. - i++; - } - } - - // Now all the loads uses has been cloned down - // Only thing left is to clone the loads, but they must end up - // first in the catch blocks. - - // We clone the loads oo the catch blocks only when needed. - // An array is used to map the catch blocks to each lazily cloned load. - // In that way no extra unnecessary loads are cloned. - - // Any use dominated by original block must have an phi and a region added - - Node* catch_node = ctrl->raw_out(0); - int number_of_catch_projs = catch_node->outcnt(); - Node** proj_to_load_mapping = NEW_RESOURCE_ARRAY(Node*, number_of_catch_projs); - Copy::zero_to_bytes(proj_to_load_mapping, sizeof(Node*) * number_of_catch_projs); - - // The phi_map is used to keep track of where phis have already been inserted - int phi_map_len = phase->C->unique(); - Node** phi_map = NEW_RESOURCE_ARRAY(Node*, phi_map_len); - Copy::zero_to_bytes(phi_map, sizeof(Node*) * phi_map_len); + while (worklist.size() > 0) { + const Block* const block = worklist.pop(); + RegMask& old_live = live[block->_pre_order]; + RegMask new_live; - for (unsigned int i = 0; i < load->outcnt(); i++) { - Node* load_use_control = NULL; - Node* load_use = load->raw_out(i); - - if (phase->has_ctrl(load_use)) { - load_use_control = get_ctrl_normalized(phase, load_use); - assert(load_use_control != ctrl, "sanity"); - } else { - load_use_control = load_use->in(0); - } - assert(load_use_control != NULL, "sanity"); - if (trace) tty->print_cr(" Handling use: %i, with control: %i", load_use->_idx, load_use_control->_idx); - - // Some times the loads use is a phi. For them we need to determine from which catch block - // the use is defined. - bool load_use_is_phi = false; - unsigned int load_use_phi_index = 0; - Node* phi_ctrl = NULL; - if (load_use->is_Phi()) { - // Find phi input that matches load - for (unsigned int u = 1; u < load_use->req(); u++) { - if (load_use->in(u) == load) { - load_use_is_phi = true; - load_use_phi_index = u; - assert(load_use->in(0)->is_Region(), "Region or broken"); - phi_ctrl = load_use->in(0)->in(u); - assert(phi_ctrl->is_CFG(), "check"); - assert(phi_ctrl != load, "check"); - break; - } - } - assert(load_use_is_phi, "must find"); - assert(load_use_phi_index > 0, "sanity"); + // Initialize to union of successors + for (uint i = 0; i < block->_num_succs; i++) { + const uint succ_id = block->_succs[i]->_pre_order; + new_live.OR(live[succ_id]); } - // For each load use, see which catch projs dominates, create load clone lazily and reconnect - bool found_dominating_catchproj = false; - for (int c = 0; c < number_of_catch_projs; c++) { - Node* catchproj = catch_node->raw_out(c); - assert(catchproj != NULL && catchproj->isa_CatchProj(), "Sanity"); - - if (!phase->is_dominator(catchproj, load_use_control)) { - if (load_use_is_phi && phase->is_dominator(catchproj, phi_ctrl)) { - // The loads use is local to the catchproj. - // fall out and replace load with catch-local load clone. - } else { - continue; - } - } - assert(!found_dominating_catchproj, "Max one should match"); - - // Clone loads to catch projs - Node* load_clone = proj_to_load_mapping[c]; - if (load_clone == NULL) { - load_clone = clone_load_to_catchproj(phase, load, catchproj); - proj_to_load_mapping[c] = load_clone; - } - phase->igvn().rehash_node_delayed(load_use); + // Walk block backwards, computing liveness + for (int i = block->number_of_nodes() - 1; i >= 0; --i) { + const Node* const node = block->get_node(i); - if (load_use_is_phi) { - // phis are special - the load is defined from a specific control flow - load_use->set_req(load_use_phi_index, load_clone); - } else { - // Multipe edges can be replaced at once - on calls for example - load_use->replace_edge(load, load_clone); + // Remove def bits + const OptoReg::Name first = bs->refine_register(node, regalloc->get_reg_first(node)); + const OptoReg::Name second = bs->refine_register(node, regalloc->get_reg_second(node)); + if (first != OptoReg::Bad) { + new_live.Remove(first); } - --i; // more than one edge can have been removed, but the next is in later iterations - - // We could break the for-loop after finding a dominating match. - // But keep iterating to catch any bad idom early. - found_dominating_catchproj = true; - } + if (second != OptoReg::Bad) { + new_live.Remove(second); + } - // We found no single catchproj that dominated the use - The use is at a point after - // where control flow from multiple catch projs have merged. We will have to create - // phi nodes before the use and tie the output from the cloned loads together. It - // can be a single phi or a number of chained phis, depending on control flow - if (!found_dominating_catchproj) { - - // Use phi-control if use is a phi - if (load_use_is_phi) { - load_use_control = phi_ctrl; - } - assert(phase->is_dominator(ctrl, load_use_control), "Common use but no dominator"); - - // Clone a load on all paths - for (int c = 0; c < number_of_catch_projs; c++) { - Node* catchproj = catch_node->raw_out(c); - Node* load_clone = proj_to_load_mapping[c]; - if (load_clone == NULL) { - load_clone = clone_load_to_catchproj(phase, load, catchproj); - proj_to_load_mapping[c] = load_clone; + // Add use bits + for (uint j = 1; j < node->req(); ++j) { + const Node* const use = node->in(j); + const OptoReg::Name first = bs->refine_register(use, regalloc->get_reg_first(use)); + const OptoReg::Name second = bs->refine_register(use, regalloc->get_reg_second(use)); + if (first != OptoReg::Bad) { + new_live.Insert(first); + } + if (second != OptoReg::Bad) { + new_live.Insert(second); } } - // Move up dominator tree from use until dom front is reached - Node* next_region = get_dominating_region(phase, load_use_control, ctrl); - while (phase->idom(next_region) != catch_node) { - next_region = phase->idom(next_region); - if (trace) tty->print_cr("Moving up idom to region ctrl %i", next_region->_idx); - } - assert(phase->is_dominator(catch_node, next_region), "Sanity"); - - // Create or reuse phi node that collect all cloned loads and feed it to the use. - Node* test_phi = phi_map[next_region->_idx]; - if ((test_phi != NULL) && test_phi->is_Phi()) { - // Reuse an already created phi - if (trace) tty->print_cr(" Using cached Phi %i on load_use %i", test_phi->_idx, load_use->_idx); - phase->igvn().rehash_node_delayed(load_use); - load_use->replace_edge(load, test_phi); - // Now this use is done - } else { - // Otherwise we need to create one or more phis - PhiNode* next_phi = new PhiNode(next_region, load->type()); - phi_map[next_region->_idx] = next_phi; // cache new phi - phase->igvn().rehash_node_delayed(load_use); - load_use->replace_edge(load, next_phi); - - int dominators_of_region = 0; - do { - // New phi, connect to region and add all loads as in. - Node* region = next_region; - assert(region->isa_Region() && region->req() > 2, "Catch dead region nodes"); - PhiNode* new_phi = next_phi; - - if (trace) tty->print_cr("Created Phi %i on load %i with control %i", new_phi->_idx, load->_idx, region->_idx); - - // Need to add all cloned loads to the phi, taking care that the right path is matched - dominators_of_region = 0; // reset for new region - for (unsigned int reg_i = 1; reg_i < region->req(); reg_i++) { - Node* region_pred = region->in(reg_i); - assert(region_pred->is_CFG(), "check"); - bool pred_has_dominator = false; - for (int c = 0; c < number_of_catch_projs; c++) { - Node* catchproj = catch_node->raw_out(c); - if (phase->is_dominator(catchproj, region_pred)) { - new_phi->set_req(reg_i, proj_to_load_mapping[c]); - if (trace) tty->print_cr(" - Phi in(%i) set to load %i", reg_i, proj_to_load_mapping[c]->_idx); - pred_has_dominator = true; - dominators_of_region++; - break; - } - } - - // Sometimes we need to chain several phis. - if (!pred_has_dominator) { - assert(dominators_of_region <= 1, "More than one region can't require extra phi"); - if (trace) tty->print_cr(" - Region %i pred %i not dominated by catch proj", region->_idx, region_pred->_idx); - // Continue search on on this region_pred - // - walk up to next region - // - create a new phi and connect to first new_phi - next_region = get_dominating_region(phase, region_pred, ctrl); - - // Lookup if there already is a phi, create a new otherwise - Node* test_phi = phi_map[next_region->_idx]; - if ((test_phi != NULL) && test_phi->is_Phi()) { - next_phi = test_phi->isa_Phi(); - dominators_of_region++; // record that a match was found and that we are done - if (trace) tty->print_cr(" Using cached phi Phi %i on control %i", next_phi->_idx, next_region->_idx); - } else { - next_phi = new PhiNode(next_region, load->type()); - phi_map[next_region->_idx] = next_phi; - } - new_phi->set_req(reg_i, next_phi); - } - } - - new_phi->set_req(0, region); - phase->igvn().register_new_node_with_optimizer(new_phi); - phase->set_ctrl(new_phi, region); - - assert(dominators_of_region != 0, "Must have found one this iteration"); - } while (dominators_of_region == 1); - } - --i; - } - } // end of loop over uses - - assert(load->outcnt() == 0, "All uses should be handled"); - phase->igvn().remove_dead_node(load); - phase->C->print_method(PHASE_CALL_CATCH_CLEANUP, 4, load->_idx); - - // Now we should be home - phase->igvn().set_delay_transform(false); -} - -// Sort out the loads that are between a call ant its catch blocks -static void process_catch_cleanup_candidate(PhaseIdealLoop* phase, LoadNode* load, bool verify) { - bool trace = phase->C->directive()->ZTraceLoadBarriersOption; - - Node* ctrl = get_ctrl_normalized(phase, load); - if (!ctrl->is_Proj() || (ctrl->in(0) == NULL) || !ctrl->in(0)->isa_Call()) { - return; - } - - Node* catch_node = ctrl->isa_Proj()->raw_out(0); - if (catch_node->is_Catch()) { - if (catch_node->outcnt() > 1) { - assert(!verify, "All loads should already have been moved"); - call_catch_cleanup_one(phase, load, ctrl); - } else { - if (trace) tty->print_cr("Call catch cleanup with only one catch: load %i ", load->_idx); - } - } -} - -void ZBarrierSetC2::barrier_insertion_phase(Compile* C, PhaseIterGVN& igvn) const { - PhaseIdealLoop::optimize(igvn, LoopOptsZBarrierInsertion); - if (C->failing()) return; -} - -bool ZBarrierSetC2::optimize_loops(PhaseIdealLoop* phase, LoopOptsMode mode, VectorSet& visited, Node_Stack& nstack, Node_List& worklist) const { - - if (mode == LoopOptsZBarrierInsertion) { - // First make sure all loads between call and catch are moved to the catch block - clean_catch_blocks(phase); - DEBUG_ONLY(clean_catch_blocks(phase, true /* verify */);) - - // Then expand barriers on all loads - insert_load_barriers(phase); - - // Handle all Unsafe that need barriers. - insert_barriers_on_unsafe(phase); - - phase->C->clear_major_progress(); - return true; - } else { - return false; - } -} - -static bool can_simplify_cas(LoadStoreNode* node) { - if (node->isa_LoadStoreConditional()) { - Node *expected_in = node->as_LoadStoreConditional()->in(LoadStoreConditionalNode::ExpectedIn); - return (expected_in->get_ptr_type() == TypePtr::NULL_PTR); - } else { - return false; - } -} - -static void insert_barrier_before_unsafe(PhaseIdealLoop* phase, LoadStoreNode* old_node) { - - Compile *C = phase->C; - PhaseIterGVN &igvn = phase->igvn(); - LoadStoreNode* zclone = NULL; - - Node *in_ctrl = old_node->in(MemNode::Control); - Node *in_mem = old_node->in(MemNode::Memory); - Node *in_adr = old_node->in(MemNode::Address); - Node *in_val = old_node->in(MemNode::ValueIn); - const TypePtr *adr_type = old_node->adr_type(); - const TypePtr* load_type = TypeOopPtr::BOTTOM; // The type for the load we are adding - - switch (old_node->Opcode()) { - case Op_CompareAndExchangeP: { - zclone = new ZCompareAndExchangePNode(in_ctrl, in_mem, in_adr, in_val, old_node->in(LoadStoreConditionalNode::ExpectedIn), - adr_type, old_node->get_ptr_type(), ((CompareAndExchangeNode*)old_node)->order()); - load_type = old_node->bottom_type()->is_ptr(); - break; - } - case Op_WeakCompareAndSwapP: { - if (can_simplify_cas(old_node)) { - break; - } - zclone = new ZWeakCompareAndSwapPNode(in_ctrl, in_mem, in_adr, in_val, old_node->in(LoadStoreConditionalNode::ExpectedIn), - ((CompareAndSwapNode*)old_node)->order()); - adr_type = TypePtr::BOTTOM; - break; - } - case Op_CompareAndSwapP: { - if (can_simplify_cas(old_node)) { - break; - } - zclone = new ZCompareAndSwapPNode(in_ctrl, in_mem, in_adr, in_val, old_node->in(LoadStoreConditionalNode::ExpectedIn), - ((CompareAndSwapNode*)old_node)->order()); - adr_type = TypePtr::BOTTOM; - break; - } - case Op_GetAndSetP: { - zclone = new ZGetAndSetPNode(in_ctrl, in_mem, in_adr, in_val, old_node->adr_type(), old_node->get_ptr_type()); - load_type = old_node->bottom_type()->is_ptr(); - break; - } - } - if (zclone != NULL) { - igvn.register_new_node_with_optimizer(zclone, old_node); - - // Make load - LoadPNode *load = new LoadPNode(NULL, in_mem, in_adr, adr_type, load_type, MemNode::unordered, - LoadNode::DependsOnlyOnTest); - load_set_expanded_barrier(load); - igvn.register_new_node_with_optimizer(load); - igvn.replace_node(old_node, zclone); - - Node *barrier = new LoadBarrierNode(C, NULL, in_mem, load, in_adr, false /* weak */); - Node *barrier_val = new ProjNode(barrier, LoadBarrierNode::Oop); - Node *barrier_ctrl = new ProjNode(barrier, LoadBarrierNode::Control); - - igvn.register_new_node_with_optimizer(barrier); - igvn.register_new_node_with_optimizer(barrier_val); - igvn.register_new_node_with_optimizer(barrier_ctrl); - - // loop over all of in_ctrl usages and move to barrier_ctrl - for (DUIterator_Last imin, i = in_ctrl->last_outs(imin); i >= imin; --i) { - Node *use = in_ctrl->last_out(i); - uint l; - for (l = 0; use->in(l) != in_ctrl; l++) {} - igvn.replace_input_of(use, l, barrier_ctrl); - } - - load->set_req(MemNode::Control, in_ctrl); - barrier->set_req(LoadBarrierNode::Control, in_ctrl); - zclone->add_req(barrier_val); // add req as keep alive. - - C->print_method(PHASE_ADD_UNSAFE_BARRIER, 4, zclone->_idx); - } -} - -void ZBarrierSetC2::insert_barriers_on_unsafe(PhaseIdealLoop* phase) const { - Compile *C = phase->C; - PhaseIterGVN &igvn = phase->igvn(); - uint new_ids = C->unique(); - VectorSet visited(Thread::current()->resource_area()); - GrowableArray nodeStack(Thread::current()->resource_area(), 0, 0, NULL); - nodeStack.push(C->root()); - visited.test_set(C->root()->_idx); - - // Traverse all nodes, visit all unsafe ops that require a barrier - while (nodeStack.length() > 0) { - Node *n = nodeStack.pop(); - - bool is_old_node = (n->_idx < new_ids); // don't process nodes that were created during cleanup - if (is_old_node) { - if (n->is_LoadStore()) { - LoadStoreNode* lsn = n->as_LoadStore(); - if (lsn->has_barrier()) { - BasicType bt = lsn->in(MemNode::Address)->bottom_type()->basic_type(); - assert (is_reference_type(bt), "Sanity test"); - insert_barrier_before_unsafe(phase, lsn); - } - } - } - for (uint i = 0; i < n->len(); i++) { - if (n->in(i)) { - if (!visited.test_set(n->in(i)->_idx)) { - nodeStack.push(n->in(i)); - } - } - } - } - - igvn.optimize(); - C->print_method(PHASE_ADD_UNSAFE_BARRIER, 2); -} - -// The purpose of ZBarrierSetC2::clean_catch_blocks is to prepare the IR for -// splicing in load barrier nodes. -// -// The problem is that we might have instructions between a call and its catch nodes. -// (This is usually handled in PhaseCFG:call_catch_cleanup, which clones mach nodes in -// already scheduled blocks.) We can't have loads that require barriers there, -// because we need to splice in new control flow, and that would violate the IR. -// -// clean_catch_blocks find all Loads that require a barrier and clone them and any -// dependent instructions to each use. The loads must be in the beginning of the catch block -// before any store. -// -// Sometimes the loads use will be at a place dominated by all catch blocks, then we need -// a load in each catch block, and a Phi at the dominated use. - -void ZBarrierSetC2::clean_catch_blocks(PhaseIdealLoop* phase, bool verify) const { - - Compile *C = phase->C; - uint new_ids = C->unique(); - PhaseIterGVN &igvn = phase->igvn(); - VectorSet visited(Thread::current()->resource_area()); - GrowableArray nodeStack(Thread::current()->resource_area(), 0, 0, NULL); - nodeStack.push(C->root()); - visited.test_set(C->root()->_idx); - - // Traverse all nodes, visit all loads that require a barrier - while(nodeStack.length() > 0) { - Node *n = nodeStack.pop(); - - for (uint i = 0; i < n->len(); i++) { - if (n->in(i)) { - if (!visited.test_set(n->in(i)->_idx)) { - nodeStack.push(n->in(i)); - } + // If this node tracks liveness, update it + RegMask* const regs = barrier_set_state()->live(node); + if (regs != NULL) { + regs->OR(new_live); } } - bool is_old_node = (n->_idx < new_ids); // don't process nodes that were created during cleanup - if (n->is_Load() && is_old_node) { - LoadNode* load = n->isa_Load(); - // only care about loads that will have a barrier - if (load_require_barrier(load)) { - process_catch_cleanup_candidate(phase, load, verify); - } - } - } - - C->print_method(PHASE_CALL_CATCH_CLEANUP, 2); -} - -class DomDepthCompareClosure : public CompareClosure { - PhaseIdealLoop* _phase; - -public: - DomDepthCompareClosure(PhaseIdealLoop* phase) : _phase(phase) { } - - int do_compare(LoadNode* const &n1, LoadNode* const &n2) { - int d1 = _phase->dom_depth(_phase->get_ctrl(n1)); - int d2 = _phase->dom_depth(_phase->get_ctrl(n2)); - if (d1 == d2) { - // Compare index if the depth is the same, ensures all entries are unique. - return n1->_idx - n2->_idx; - } else { - return d2 - d1; - } - } -}; - -// Traverse graph and add all loadPs to list, sorted by dom depth -void gather_loadnodes_sorted(PhaseIdealLoop* phase, GrowableArray* loadList) { - - VectorSet visited(Thread::current()->resource_area()); - GrowableArray nodeStack(Thread::current()->resource_area(), 0, 0, NULL); - DomDepthCompareClosure ddcc(phase); - - nodeStack.push(phase->C->root()); - while(nodeStack.length() > 0) { - Node *n = nodeStack.pop(); - if (visited.test(n->_idx)) { - continue; - } - - if (n->isa_Load()) { - LoadNode *load = n->as_Load(); - if (load_require_barrier(load)) { - assert(phase->get_ctrl(load) != NULL, "sanity"); - assert(phase->dom_depth(phase->get_ctrl(load)) != 0, "sanity"); - loadList->insert_sorted(&ddcc, load); - } - } - - visited.set(n->_idx); - for (uint i = 0; i < n->req(); i++) { - if (n->in(i)) { - if (!visited.test(n->in(i)->_idx)) { - nodeStack.push(n->in(i)); - } + // Now at block top, see if we have any changes + new_live.SUBTRACT(old_live); + if (new_live.is_NotEmpty()) { + // Liveness has refined, update and propagate to prior blocks + old_live.OR(new_live); + for (uint i = 1; i < block->num_preds(); ++i) { + Block* const pred = cfg->get_block_for_node(block->pred(i)); + worklist.push(pred); } } } } - -// Add LoadBarriers to all LoadPs -void ZBarrierSetC2::insert_load_barriers(PhaseIdealLoop* phase) const { - - bool trace = phase->C->directive()->ZTraceLoadBarriersOption; - GrowableArray loadList(Thread::current()->resource_area(), 0, 0, NULL); - gather_loadnodes_sorted(phase, &loadList); - - PhaseIterGVN &igvn = phase->igvn(); - int count = 0; - - for (GrowableArrayIterator loadIter = loadList.begin(); loadIter != loadList.end(); ++loadIter) { - LoadNode *load = *loadIter; - - if (load_has_expanded_barrier(load)) { - continue; - } - - do { - // Insert a barrier on a loadP - // if another load is found that needs to be expanded first, retry on that one - LoadNode* result = insert_one_loadbarrier(phase, load, phase->get_ctrl(load)); - while (result != NULL) { - result = insert_one_loadbarrier(phase, result, phase->get_ctrl(result)); - } - } while (!load_has_expanded_barrier(load)); - } - - phase->C->print_method(PHASE_INSERT_BARRIER, 2); -} - -void push_antidependent_stores(PhaseIdealLoop* phase, Node_Stack& nodestack, LoadNode* start_load) { - // push all stores on the same mem, that can_alias - // Any load found must be handled first - PhaseIterGVN &igvn = phase->igvn(); - int load_alias_idx = igvn.C->get_alias_index(start_load->adr_type()); - - Node *mem = start_load->in(1); - for (DUIterator_Fast imax, u = mem->fast_outs(imax); u < imax; u++) { - Node *mem_use = mem->fast_out(u); - - if (mem_use == start_load) continue; - if (!mem_use->is_Store()) continue; - if (!phase->has_ctrl(mem_use)) continue; - if (phase->get_ctrl(mem_use) != phase->get_ctrl(start_load)) continue; - - // add any aliasing store in this block - StoreNode *store = mem_use->isa_Store(); - const TypePtr *adr_type = store->adr_type(); - if (igvn.C->can_alias(adr_type, load_alias_idx)) { - nodestack.push(store, 0); - } - } -} - -LoadNode* ZBarrierSetC2::insert_one_loadbarrier(PhaseIdealLoop* phase, LoadNode* start_load, Node* ctrl) const { - bool trace = phase->C->directive()->ZTraceLoadBarriersOption; - PhaseIterGVN &igvn = phase->igvn(); - - // Check for other loadPs at the same loop depth that is reachable by a DFS - // - if found - return it. It needs to be inserted first - // - otherwise proceed and insert barrier - - VectorSet visited(Thread::current()->resource_area()); - Node_Stack nodestack(100); - - nodestack.push(start_load, 0); - push_antidependent_stores(phase, nodestack, start_load); - - while(!nodestack.is_empty()) { - Node* n = nodestack.node(); // peek - nodestack.pop(); - if (visited.test(n->_idx)) { - continue; - } - - if (n->is_Load() && n != start_load && load_require_barrier(n->as_Load()) && !load_has_expanded_barrier(n->as_Load())) { - // Found another load that needs a barrier in the same block. Must expand later loads first. - if (trace) tty->print_cr(" * Found LoadP %i on DFS", n->_idx); - return n->as_Load(); // return node that should be expanded first - } - - if (!phase->has_ctrl(n)) continue; - if (phase->get_ctrl(n) != phase->get_ctrl(start_load)) continue; - if (n->is_Phi()) continue; - - visited.set(n->_idx); - // push all children - for (DUIterator_Fast imax, ii = n->fast_outs(imax); ii < imax; ii++) { - Node* c = n->fast_out(ii); - if (c != NULL) { - nodestack.push(c, 0); - } - } - } - - insert_one_loadbarrier_inner(phase, start_load, ctrl, visited); - return NULL; -} - -void ZBarrierSetC2::insert_one_loadbarrier_inner(PhaseIdealLoop* phase, LoadNode* load, Node* ctrl, VectorSet visited2) const { - PhaseIterGVN &igvn = phase->igvn(); - Compile* C = igvn.C; - bool trace = C->directive()->ZTraceLoadBarriersOption; - - // create barrier - Node* barrier = new LoadBarrierNode(C, NULL, load->in(LoadNode::Memory), NULL, load->in(LoadNode::Address), load_has_weak_barrier(load)); - Node* barrier_val = new ProjNode(barrier, LoadBarrierNode::Oop); - Node* barrier_ctrl = new ProjNode(barrier, LoadBarrierNode::Control); - ctrl = normalize_ctrl(ctrl); - - if (trace) tty->print_cr("Insert load %i with barrier: %i and ctrl : %i", load->_idx, barrier->_idx, ctrl->_idx); - - // Splice control - // - insert barrier control diamond between loads ctrl and ctrl successor on path to block end. - // - If control successor is a catch, step over to next. - Node* ctrl_succ = NULL; - for (DUIterator_Fast imax, j = ctrl->fast_outs(imax); j < imax; j++) { - Node* tmp = ctrl->fast_out(j); - - // - CFG nodes is the ones we are going to splice (1 only!) - // - Phi nodes will continue to hang from the region node! - // - self loops should be skipped - if (tmp->is_Phi() || tmp == ctrl) { - continue; - } - - if (tmp->is_CFG()) { - assert(ctrl_succ == NULL, "There can be only one"); - ctrl_succ = tmp; - continue; - } - } - - // Now splice control - assert(ctrl_succ != load, "sanity"); - assert(ctrl_succ != NULL, "Broken IR"); - bool found = false; - for(uint k = 0; k < ctrl_succ->req(); k++) { - if (ctrl_succ->in(k) == ctrl) { - assert(!found, "sanity"); - if (trace) tty->print_cr(" Move CFG ctrl_succ %i to barrier_ctrl", ctrl_succ->_idx); - igvn.replace_input_of(ctrl_succ, k, barrier_ctrl); - found = true; - k--; - } - } - - // For all successors of ctrl - move all visited to become successors of barrier_ctrl instead - for (DUIterator_Fast imax, r = ctrl->fast_outs(imax); r < imax; r++) { - Node* tmp = ctrl->fast_out(r); - if (tmp->is_SafePoint() || (visited2.test(tmp->_idx) && (tmp != load))) { - if (trace) tty->print_cr(" Move ctrl_succ %i to barrier_ctrl", tmp->_idx); - igvn.replace_input_of(tmp, 0, barrier_ctrl); - --r; --imax; - } - } - - // Move the loads user to the barrier - for (DUIterator_Fast imax, i = load->fast_outs(imax); i < imax; i++) { - Node* u = load->fast_out(i); - if (u->isa_LoadBarrier()) { - continue; - } - - // find correct input - replace with iterator? - for(uint j = 0; j < u->req(); j++) { - if (u->in(j) == load) { - igvn.replace_input_of(u, j, barrier_val); - --i; --imax; // Adjust the iterator of the *outer* loop - break; // some nodes (calls) might have several uses from the same node - } - } - } - - // Connect barrier to load and control - barrier->set_req(LoadBarrierNode::Oop, load); - barrier->set_req(LoadBarrierNode::Control, ctrl); - - igvn.replace_input_of(load, MemNode::Control, ctrl); - load->pin(); - - igvn.rehash_node_delayed(load); - igvn.register_new_node_with_optimizer(barrier); - igvn.register_new_node_with_optimizer(barrier_val); - igvn.register_new_node_with_optimizer(barrier_ctrl); - load_set_expanded_barrier(load); - - C->print_method(PHASE_INSERT_BARRIER, 3, load->_idx); -} - -// The bad_mask in the ThreadLocalData shouldn't have an anti-dep-check. -// The bad_mask address if of type TypeRawPtr, but that will alias -// InitializeNodes until the type system is expanded. -bool ZBarrierSetC2::needs_anti_dependence_check(const Node* node) const { - MachNode* mnode = node->as_Mach(); - if (mnode != NULL) { - intptr_t offset = 0; - const TypePtr *adr_type2 = NULL; - const Node* base = mnode->get_base_and_disp(offset, adr_type2); - if ((base != NULL) && - (base->is_Mach() && base->as_Mach()->ideal_Opcode() == Op_ThreadLocal) && - (offset == in_bytes(ZThreadLocalData::address_bad_mask_offset()))) { - return false; - } - } - return true; -} diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp --- a/src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -29,134 +29,38 @@ #include "opto/node.hpp" #include "utilities/growableArray.hpp" -class ZCompareAndSwapPNode : public CompareAndSwapPNode { -public: - ZCompareAndSwapPNode(Node* c, Node *mem, Node *adr, Node *val, Node *ex, MemNode::MemOrd mem_ord) : CompareAndSwapPNode(c, mem, adr, val, ex, mem_ord) { } - virtual int Opcode() const; -}; - -class ZWeakCompareAndSwapPNode : public WeakCompareAndSwapPNode { -public: - ZWeakCompareAndSwapPNode(Node* c, Node *mem, Node *adr, Node *val, Node *ex, MemNode::MemOrd mem_ord) : WeakCompareAndSwapPNode(c, mem, adr, val, ex, mem_ord) { } - virtual int Opcode() const; -}; +const uint8_t ZLoadBarrierStrong = 1; +const uint8_t ZLoadBarrierWeak = 2; +const uint8_t ZLoadBarrierElided = 3; -class ZCompareAndExchangePNode : public CompareAndExchangePNode { -public: - ZCompareAndExchangePNode(Node* c, Node *mem, Node *adr, Node *val, Node *ex, const TypePtr* at, const Type* t, MemNode::MemOrd mem_ord) : CompareAndExchangePNode(c, mem, adr, val, ex, at, t, mem_ord) { } - virtual int Opcode() const; -}; +class ZLoadBarrierStubC2 : public ResourceObj { +private: + const MachNode* _node; + const Address _ref_addr; + const Register _ref; + const Register _tmp; + const bool _weak; + Label _entry; + Label _continuation; -class ZGetAndSetPNode : public GetAndSetPNode { -public: - ZGetAndSetPNode(Node* c, Node *mem, Node *adr, Node *val, const TypePtr* at, const Type* t) : GetAndSetPNode(c, mem, adr, val, at, t) { } - virtual int Opcode() const; -}; - -class LoadBarrierNode : public MultiNode { -private: - bool _weak; // On strong or weak oop reference - static bool is_dominator(PhaseIdealLoop* phase, bool linear_only, Node *d, Node *n); - void push_dominated_barriers(PhaseIterGVN* igvn) const; + ZLoadBarrierStubC2(const MachNode* node, Address ref_addr, Register ref, Register tmp, bool weak); public: - enum { - Control, - Memory, - Oop, - Address, - Number_of_Outputs = Address, - Similar, - Number_of_Inputs - }; - - LoadBarrierNode(Compile* C, - Node* c, - Node* mem, - Node* val, - Node* adr, - bool weak); - - virtual int Opcode() const; - virtual uint size_of() const; - virtual bool cmp(const Node& n) const; - virtual const Type *bottom_type() const; - virtual const TypePtr* adr_type() const; - virtual const Type *Value(PhaseGVN *phase) const; - virtual Node *Identity(PhaseGVN *phase); - virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual uint match_edge(uint idx) const; - - LoadBarrierNode* has_dominating_barrier(PhaseIdealLoop* phase, - bool linear_only, - bool look_for_similar); - - void fix_similar_in_uses(PhaseIterGVN* igvn); - - bool has_true_uses() const; - - bool can_be_eliminated() const { - return !in(Similar)->is_top(); - } - - bool is_weak() const { - return _weak; - } -}; + static ZLoadBarrierStubC2* create(const MachNode* node, Address ref_addr, Register ref, Register tmp, bool weak); -class LoadBarrierSlowRegNode : public TypeNode { -private: - bool _is_weak; -public: - LoadBarrierSlowRegNode(Node *c, - Node *adr, - Node *src, - const TypePtr* t, - bool weak) : - TypeNode(t, 3), _is_weak(weak) { - init_req(1, adr); - init_req(2, src); - init_class_id(Class_LoadBarrierSlowReg); - } - - virtual uint size_of() const { - return sizeof(*this); - } - - virtual const char * name() { - return "LoadBarrierSlowRegNode"; - } - - virtual Node *Ideal(PhaseGVN *phase, bool can_reshape) { - return NULL; - } - - virtual int Opcode() const; - - bool is_weak() { return _is_weak; } -}; - -class ZBarrierSetC2State : public ResourceObj { -private: - // List of load barrier nodes which need to be expanded before matching - GrowableArray* _load_barrier_nodes; - -public: - ZBarrierSetC2State(Arena* comp_arena); - int load_barrier_count() const; - void add_load_barrier_node(LoadBarrierNode* n); - void remove_load_barrier_node(LoadBarrierNode* n); - LoadBarrierNode* load_barrier_node(int idx) const; + Address ref_addr() const; + Register ref() const; + Register tmp() const; + address slow_path() const; + RegMask& live() const; + Label* entry(); + Label* continuation(); }; class ZBarrierSetC2 : public BarrierSetC2 { private: - ZBarrierSetC2State* state() const; - void expand_loadbarrier_node(PhaseMacroExpand* phase, LoadBarrierNode* barrier) const; - -#ifdef ASSERT - void verify_gc_barriers(bool post_parse) const; -#endif + void compute_liveness_at_stubs() const; + void analyze_dominating_barriers() const; protected: virtual Node* load_at_resolved(C2Access& access, const Type* val_type) const; @@ -174,43 +78,14 @@ public: virtual void* create_barrier_state(Arena* comp_arena) const; - - virtual bool has_load_barriers() const { return true; } - virtual bool is_gc_barrier_node(Node* node) const; - virtual Node* step_over_gc_barrier(Node* c) const; - virtual Node* step_over_gc_barrier_ctrl(Node* c) const; - - virtual void register_potential_barrier_node(Node* node) const; - virtual void unregister_potential_barrier_node(Node* node) const; - virtual void eliminate_gc_barrier(PhaseMacroExpand* macro, Node* node) const { } - virtual void enqueue_useful_gc_barrier(PhaseIterGVN* igvn, Node* node) const; - virtual void eliminate_useless_gc_barriers(Unique_Node_List &useful, Compile* C) const; - - virtual bool array_copy_requires_gc_barriers(bool tightly_coupled_alloc, BasicType type, bool is_clone, ArrayCopyPhase phase) const; + virtual bool array_copy_requires_gc_barriers(bool tightly_coupled_alloc, + BasicType type, + bool is_clone, + ArrayCopyPhase phase) const; - virtual bool expand_barriers(Compile* C, PhaseIterGVN& igvn) const; - virtual bool final_graph_reshaping(Compile* compile, Node* n, uint opcode) const; - virtual bool matcher_find_shared_visit(Matcher* matcher, Matcher::MStack& mstack, Node* n, uint opcode, bool& mem_op, int& mem_addr_idx) const; - virtual bool matcher_find_shared_post_visit(Matcher* matcher, Node* n, uint opcode) const; - virtual bool needs_anti_dependence_check(const Node* node) const; - -#ifdef ASSERT - virtual void verify_gc_barriers(Compile* compile, CompilePhase phase) const; -#endif - - // Load barrier insertion and expansion external - virtual void barrier_insertion_phase(Compile* C, PhaseIterGVN &igvn) const; - virtual bool optimize_loops(PhaseIdealLoop* phase, LoopOptsMode mode, VectorSet& visited, Node_Stack& nstack, Node_List& worklist) const; - virtual bool is_gc_specific_loop_opts_pass(LoopOptsMode mode) const { return (mode == LoopOptsZBarrierInsertion); } - virtual bool strip_mined_loops_expanded(LoopOptsMode mode) const { return mode == LoopOptsZBarrierInsertion; } - -private: - // Load barrier insertion and expansion internal - void insert_barriers_on_unsafe(PhaseIdealLoop* phase) const; - void clean_catch_blocks(PhaseIdealLoop* phase, bool verify = false) const; - void insert_load_barriers(PhaseIdealLoop* phase) const; - LoadNode* insert_one_loadbarrier(PhaseIdealLoop* phase, LoadNode* load, Node* ctrl) const; - void insert_one_loadbarrier_inner(PhaseIdealLoop* phase, LoadNode* load, Node* ctrl, VectorSet visited) const; + virtual void late_barrier_analysis() const; + virtual int estimate_stub_size() const; + virtual void emit_stubs(CodeBuffer& cb) const; }; #endif // SHARE_GC_Z_C2_ZBARRIERSETC2_HPP diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/gc/z/zBarrierSetAssembler.hpp --- a/src/hotspot/share/gc/z/zBarrierSetAssembler.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/gc/z/zBarrierSetAssembler.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, 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,10 +24,7 @@ #ifndef SHARE_GC_Z_ZBARRIERSETASSEMBLER_HPP #define SHARE_GC_Z_ZBARRIERSETASSEMBLER_HPP -#include "asm/macroAssembler.hpp" #include "gc/shared/barrierSetAssembler.hpp" -#include "oops/accessDecorators.hpp" -#include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" class ZBarrierSetAssemblerBase : public BarrierSetAssembler { diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp --- a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -390,6 +390,10 @@ Service_lock->unlock(); } + if (UseNotificationThread && Notification_lock->owned_by_self()) { + Notification_lock->unlock(); + } + if (CodeCache_lock->owned_by_self()) { CodeCache_lock->unlock(); } diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/oops/klassVtable.cpp --- a/src/hotspot/share/oops/klassVtable.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/oops/klassVtable.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -128,11 +128,6 @@ *vtable_length_ret = vtable_length; } -int klassVtable::index_of(Method* m, int len) const { - assert(m->has_vtable_index(), "do not ask this of non-vtable methods"); - return m->vtable_index(); -} - // Copy super class's vtable to the first part (prefix) of this class's vtable, // and return the number of entries copied. Expects that 'super' is the Java // super class (arrays can have "array" super classes that must be skipped). @@ -169,7 +164,6 @@ // Note: Arrays can have intermediate array supers. Use java_super to skip them. InstanceKlass* super = _klass->java_super(); - int nofNewEntries = 0; bool is_shared = _klass->is_shared(); @@ -1029,15 +1023,6 @@ } #endif // INCLUDE_JVMTI -// CDS/RedefineClasses support - clear vtables so they can be reinitialized -void klassVtable::clear_vtable() { - for (int i = 0; i < _length; i++) table()[i].clear(); -} - -bool klassVtable::is_initialized() { - return _length == 0 || table()[0].method() != NULL; -} - //----------------------------------------------------------------------------------------- // Itable code @@ -1468,31 +1453,6 @@ #endif } - -// inverse to itable_index -Method* klassItable::method_for_itable_index(InstanceKlass* intf, int itable_index) { - assert(intf->is_interface(), "sanity check"); - assert(intf->verify_itable_index(itable_index), ""); - Array* methods = InstanceKlass::cast(intf)->methods(); - - if (itable_index < 0 || itable_index >= method_count_for_interface(intf)) - return NULL; // help caller defend against bad indices - - int index = itable_index; - Method* m = methods->at(index); - int index2 = -1; - while (!m->has_itable_index() || - (index2 = m->itable_index()) != itable_index) { - assert(index2 < itable_index, "monotonic"); - if (++index == methods->length()) - return NULL; - m = methods->at(index); - } - assert(m->itable_index() == itable_index, "correct inverse"); - - return m; -} - void klassVtable::verify(outputStream* st, bool forced) { // make sure table is initialized if (!Universe::is_fully_initialized()) return; diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/oops/klassVtable.hpp --- a/src/hotspot/share/oops/klassVtable.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/oops/klassVtable.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -48,13 +48,6 @@ int _verify_count; // to make verify faster #endif - // Ordering important, so greater_than (>) can be used as an merge operator. - enum AccessType { - acc_private = 0, - acc_package_private = 1, - acc_publicprotected = 2 - }; - public: klassVtable(Klass* klass, void* base, int length) : _klass(klass) { _tableOffset = (address)base - (address)klass; _length = length; @@ -66,22 +59,12 @@ int length() const { return _length; } inline Method* method_at(int i) const; inline Method* unchecked_method_at(int i) const; - inline Method** adr_method_at(int i) const; // searching; all methods return -1 if not found - int index_of(Method* m) const { return index_of(m, _length); } int index_of_miranda(Symbol* name, Symbol* signature); void initialize_vtable(bool checkconstraints, TRAPS); // initialize vtable of a new klass - // CDS/RedefineClasses support - clear vtables so they can be reinitialized - // at dump time. Clearing gives us an easy way to tell if the vtable has - // already been reinitialized at dump time (see dump.cpp). Vtables can - // be initialized at run time by RedefineClasses so dumping the right order - // is necessary. - void clear_vtable(); - bool is_initialized(); - // computes vtable length (in words) and the number of miranda methods static void compute_vtable_size_and_num_mirandas(int* vtable_length, int* num_new_mirandas, @@ -125,7 +108,6 @@ private: void copy_vtable_to(vtableEntry* start); int initialize_from_super(Klass* super); - int index_of(Method* m, int len) const; // same as index_of, but search only up to len void put_method_at(Method* m, int index); static bool needs_new_vtable_entry(const methodHandle& m, const Klass* super, @@ -223,12 +205,6 @@ return table()[i].method(); } -inline Method** klassVtable::adr_method_at(int i) const { - // Allow one past the last entry to be referenced; useful for loop bounds. - assert(i >= 0 && i <= _length, "index out of bounds"); - return (Method**)(address(table() + i) + vtableEntry::method_offset_in_bytes()); -} - // -------------------------------------------------------------------------------- class klassItable; class itableMethodEntry; @@ -333,9 +309,6 @@ static int compute_itable_size(Array* transitive_interfaces); static void setup_itable_offset_table(InstanceKlass* klass); - // Resolving of method to index - static Method* method_for_itable_index(InstanceKlass* klass, int itable_index); - // Debugging/Statistics static void print_statistics() PRODUCT_RETURN; private: diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/opto/c2compiler.cpp --- a/src/hotspot/share/opto/c2compiler.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/opto/c2compiler.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -102,7 +102,8 @@ assert(is_initialized(), "Compiler thread must be initialized"); bool subsume_loads = SubsumeLoads; - bool do_escape_analysis = DoEscapeAnalysis && !env->should_retain_local_variables(); + bool do_escape_analysis = DoEscapeAnalysis && !env->should_retain_local_variables() + && !env->jvmti_can_get_owned_monitor_info(); bool eliminate_boxing = EliminateAutoBox; while (!env->failing()) { diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/opto/classes.cpp --- a/src/hotspot/share/opto/classes.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/opto/classes.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -48,9 +48,6 @@ #include "opto/subnode.hpp" #include "opto/vectornode.hpp" #include "utilities/macros.hpp" -#if INCLUDE_ZGC -#include "gc/z/c2/zBarrierSetC2.hpp" -#endif #if INCLUDE_SHENANDOAHGC #include "gc/shenandoah/c2/shenandoahBarrierSetC2.hpp" #endif diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/opto/classes.hpp --- a/src/hotspot/share/opto/classes.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/opto/classes.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -193,17 +193,6 @@ macro(LoadN) macro(LoadRange) macro(LoadS) -#if INCLUDE_ZGC -#define zgcmacro(x) macro(x) -#else -#define zgcmacro(x) optionalmacro(x) -#endif -zgcmacro(LoadBarrier) -zgcmacro(LoadBarrierSlowReg) -zgcmacro(ZCompareAndSwapP) -zgcmacro(ZWeakCompareAndSwapP) -zgcmacro(ZCompareAndExchangeP) -zgcmacro(ZGetAndSetP) macro(Lock) macro(Loop) macro(LoopLimit) diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/opto/compile.cpp --- a/src/hotspot/share/opto/compile.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/opto/compile.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -76,9 +76,6 @@ #include "utilities/align.hpp" #include "utilities/copy.hpp" #include "utilities/macros.hpp" -#if INCLUDE_ZGC -#include "gc/z/c2/zBarrierSetC2.hpp" -#endif // -------------------- Compile::mach_constant_base_node ----------------------- @@ -990,6 +987,7 @@ _has_method_handle_invokes(false), _clinit_barrier_on_entry(false), _comp_arena(mtCompiler), + _barrier_set_state(BarrierSet::barrier_set()->barrier_set_c2()->create_barrier_state(comp_arena())), _env(ci_env), _directive(directive), _log(ci_env->log()), @@ -2412,13 +2410,6 @@ print_method(PHASE_MACRO_EXPANSION, 2); } -#ifdef ASSERT - bs->verify_gc_barriers(this, BarrierSetC2::BeforeLateInsertion); -#endif - - bs->barrier_insertion_phase(C, igvn); - if (failing()) return; - { TracePhase tp("barrierExpand", &timers[_t_barrierExpand]); if (bs->expand_barriers(this, igvn)) { diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/opto/compile.hpp --- a/src/hotspot/share/opto/compile.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/opto/compile.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -55,7 +55,6 @@ class IdealGraphPrinter; class InlineTree; class Int_Array; -class LoadBarrierNode; class Matcher; class MachConstantNode; class MachConstantBaseNode; @@ -96,7 +95,6 @@ LoopOptsNone, LoopOptsShenandoahExpand, LoopOptsShenandoahPostExpand, - LoopOptsZBarrierInsertion, LoopOptsSkipSplitIf, LoopOptsVerify }; @@ -1186,11 +1184,7 @@ bool in_scratch_emit_size() const { return _in_scratch_emit_size; } enum ScratchBufferBlob { -#if defined(PPC64) MAX_inst_size = 2048, -#else - MAX_inst_size = 1024, -#endif MAX_locs_size = 128, // number of relocInfo elements MAX_const_size = 128, MAX_stubs_size = 128 @@ -1265,14 +1259,30 @@ // Process an OopMap Element while emitting nodes void Process_OopMap_Node(MachNode *mach, int code_offset); + class BufferSizingData { + public: + int _stub; + int _code; + int _const; + int _reloc; + + BufferSizingData() : + _stub(0), + _code(0), + _const(0), + _reloc(0) + { }; + }; + // Initialize code buffer - CodeBuffer* init_buffer(uint* blk_starts); + void estimate_buffer_size(int& const_req); + CodeBuffer* init_buffer(BufferSizingData& buf_sizes); // Write out basic block data to code buffer void fill_buffer(CodeBuffer* cb, uint* blk_starts); // Determine which variable sized branches can be shortened - void shorten_branches(uint* blk_starts, int& code_size, int& reloc_size, int& stub_size); + void shorten_branches(uint* blk_starts, BufferSizingData& buf_sizes); // Compute the size of first NumberOfLoopInstrToAlign instructions // at the head of a loop. diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/opto/loopnode.cpp --- a/src/hotspot/share/opto/loopnode.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/opto/loopnode.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -993,18 +993,6 @@ } } - if (UseZGC && !inner_out->in(0)->is_CountedLoopEnd()) { - // In some very special cases there can be a load that has no other uses than the - // counted loop safepoint. Then its loadbarrier will be placed between the inner - // loop exit and the safepoint. This is very rare - - Node* ifnode = inner_out->in(1)->in(0); - // Region->IfTrue->If == Region->Iffalse->If - if (ifnode == inner_out->in(2)->in(0)) { - inner_out = ifnode->in(0); - } - } - CountedLoopEndNode* cle = inner_out->in(0)->as_CountedLoopEnd(); assert(cle == inner->loopexit_or_null(), "mismatch"); bool has_skeleton = outer_le->in(1)->bottom_type()->singleton() && outer_le->in(1)->bottom_type()->is_int()->get_con() == 0; diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/opto/loopopts.cpp --- a/src/hotspot/share/opto/loopopts.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/opto/loopopts.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -41,9 +41,6 @@ #include "opto/rootnode.hpp" #include "opto/subnode.hpp" #include "utilities/macros.hpp" -#if INCLUDE_ZGC -#include "gc/z/c2/zBarrierSetC2.hpp" -#endif //============================================================================= //------------------------------split_thru_phi--------------------------------- diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/opto/machnode.hpp --- a/src/hotspot/share/opto/machnode.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/opto/machnode.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -197,7 +197,7 @@ // ADLC inherit from this class. class MachNode : public Node { public: - MachNode() : Node((uint)0), _num_opnds(0), _opnds(NULL) { + MachNode() : Node((uint)0), _barrier(0), _num_opnds(0), _opnds(NULL) { init_class_id(Class_Mach); } // Required boilerplate @@ -211,6 +211,9 @@ // no constant base node input. virtual uint mach_constant_base_node_input() const { return (uint)-1; } + uint8_t barrier_data() const { return _barrier; } + void set_barrier_data(uint data) { _barrier = data; } + // Copy inputs and operands to new node of instruction. // Called from cisc_version() and short_branch_version(). // !!!! The method's body is defined in ad_.cpp file. @@ -255,6 +258,9 @@ // output have choices - but they must use the same choice. virtual uint two_adr( ) const { return 0; } + // The GC might require some barrier metadata for machine code emission. + uint8_t _barrier; + // Array of complex operand pointers. Each corresponds to zero or // more leafs. Must be set by MachNode constructor to point to an // internal array of MachOpers. The MachOper array is sized by diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/opto/matcher.cpp --- a/src/hotspot/share/opto/matcher.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/opto/matcher.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -1751,6 +1751,13 @@ _shared_nodes.map(leaf->_idx, ex); } + // Have mach nodes inherit GC barrier data + if (leaf->is_LoadStore()) { + mach->set_barrier_data(leaf->as_LoadStore()->barrier_data()); + } else if (leaf->is_Mem()) { + mach->set_barrier_data(leaf->as_Mem()->barrier_data()); + } + return ex; } diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/opto/memnode.cpp --- a/src/hotspot/share/opto/memnode.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/opto/memnode.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -49,9 +49,6 @@ #include "utilities/copy.hpp" #include "utilities/macros.hpp" #include "utilities/vmError.hpp" -#if INCLUDE_ZGC -#include "gc/z/c2/zBarrierSetC2.hpp" -#endif // Portions of code courtesy of Clifford Click @@ -2851,7 +2848,7 @@ : Node(required), _type(rt), _adr_type(at), - _has_barrier(false) + _barrier(0) { init_req(MemNode::Control, c ); init_req(MemNode::Memory , mem); diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/opto/memnode.hpp --- a/src/hotspot/share/opto/memnode.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/opto/memnode.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -43,6 +43,8 @@ bool _unaligned_access; // Unaligned access from unsafe bool _mismatched_access; // Mismatched access from unsafe: byte read in integer array for instance bool _unsafe_access; // Access of unsafe origin. + uint8_t _barrier; // Bit field with barrier information + protected: #ifdef ASSERT const TypePtr* _adr_type; // What kind of memory is being addressed? @@ -62,18 +64,30 @@ unset // The memory ordering is not set (used for testing) } MemOrd; protected: - MemNode( Node *c0, Node *c1, Node *c2, const TypePtr* at ) - : Node(c0,c1,c2 ), _unaligned_access(false), _mismatched_access(false), _unsafe_access(false) { + MemNode( Node *c0, Node *c1, Node *c2, const TypePtr* at ) : + Node(c0,c1,c2), + _unaligned_access(false), + _mismatched_access(false), + _unsafe_access(false), + _barrier(0) { init_class_id(Class_Mem); debug_only(_adr_type=at; adr_type();) } - MemNode( Node *c0, Node *c1, Node *c2, const TypePtr* at, Node *c3 ) - : Node(c0,c1,c2,c3), _unaligned_access(false), _mismatched_access(false), _unsafe_access(false) { + MemNode( Node *c0, Node *c1, Node *c2, const TypePtr* at, Node *c3 ) : + Node(c0,c1,c2,c3), + _unaligned_access(false), + _mismatched_access(false), + _unsafe_access(false), + _barrier(0) { init_class_id(Class_Mem); debug_only(_adr_type=at; adr_type();) } - MemNode( Node *c0, Node *c1, Node *c2, const TypePtr* at, Node *c3, Node *c4) - : Node(c0,c1,c2,c3,c4), _unaligned_access(false), _mismatched_access(false), _unsafe_access(false) { + MemNode( Node *c0, Node *c1, Node *c2, const TypePtr* at, Node *c3, Node *c4) : + Node(c0,c1,c2,c3,c4), + _unaligned_access(false), + _mismatched_access(false), + _unsafe_access(false), + _barrier(0) { init_class_id(Class_Mem); debug_only(_adr_type=at; adr_type();) } @@ -125,6 +139,9 @@ #endif } + uint8_t barrier_data() { return _barrier; } + void set_barrier_data(uint8_t barrier_data) { _barrier = barrier_data; } + // Search through memory states which precede this node (load or store). // Look for an exact match for the address, with no intervening // aliased stores. @@ -181,8 +198,6 @@ // this field. const MemOrd _mo; - uint _barrier; // Bit field with barrier information - AllocateNode* is_new_object_mark_load(PhaseGVN *phase) const; protected: @@ -196,7 +211,7 @@ public: LoadNode(Node *c, Node *mem, Node *adr, const TypePtr* at, const Type *rt, MemOrd mo, ControlDependency control_dependency) - : MemNode(c,mem,adr,at), _control_dependency(control_dependency), _mo(mo), _barrier(0), _type(rt) { + : MemNode(c,mem,adr,at), _control_dependency(control_dependency), _mo(mo), _type(rt) { init_class_id(Class_Load); } inline bool is_unordered() const { return !is_acquire(); } @@ -265,10 +280,6 @@ Node* convert_to_unsigned_load(PhaseGVN& gvn); Node* convert_to_signed_load(PhaseGVN& gvn); - void copy_barrier_info(const Node* src) { _barrier = src->as_Load()->_barrier; } - uint barrier_data() { return _barrier; } - void set_barrier_data(uint barrier_data) { _barrier |= barrier_data; } - void pin() { _control_dependency = Pinned; } bool has_unknown_control_dependency() const { return _control_dependency == UnknownControl; } @@ -820,7 +831,7 @@ private: const Type* const _type; // What kind of value is loaded? const TypePtr* _adr_type; // What kind of memory is being addressed? - bool _has_barrier; + uint8_t _barrier; // Bit field with barrier information virtual uint size_of() const; // Size is bigger public: LoadStoreNode( Node *c, Node *mem, Node *adr, Node *val, const TypePtr* at, const Type* rt, uint required ); @@ -833,8 +844,9 @@ bool result_not_used() const; MemBarNode* trailing_membar() const; - void set_has_barrier() { _has_barrier = true; }; - bool has_barrier() const { return _has_barrier; }; + + uint8_t barrier_data() { return _barrier; } + void set_barrier_data(uint8_t barrier_data) { _barrier = barrier_data; } }; class LoadStoreConditionalNode : public LoadStoreNode { @@ -886,6 +898,7 @@ MemNode::MemOrd order() const { return _mem_ord; } + virtual uint size_of() const { return sizeof(*this); } }; class CompareAndExchangeNode : public LoadStoreNode { @@ -903,6 +916,7 @@ MemNode::MemOrd order() const { return _mem_ord; } + virtual uint size_of() const { return sizeof(*this); } }; //------------------------------CompareAndSwapBNode--------------------------- diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/opto/node.cpp --- a/src/hotspot/share/opto/node.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/opto/node.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -546,9 +546,6 @@ if (n->is_SafePoint()) { n->as_SafePoint()->clone_replaced_nodes(); } - if (n->is_Load()) { - n->as_Load()->copy_barrier_info(this); - } return n; // Return the clone } @@ -1473,10 +1470,6 @@ if (req() < 2 || (_flags & Flag_needs_anti_dependence_check) == 0) { return false; } - BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); - if (!bs->needs_anti_dependence_check(this)) { - return false; - } return in(1)->bottom_type()->has_memory(); } diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/opto/node.hpp --- a/src/hotspot/share/opto/node.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/opto/node.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -83,8 +83,6 @@ class JumpNode; class JumpProjNode; class LoadNode; -class LoadBarrierNode; -class LoadBarrierSlowRegNode; class LoadStoreNode; class LoadStoreConditionalNode; class LockNode; @@ -642,7 +640,6 @@ DEFINE_CLASS_ID(MemBar, Multi, 3) DEFINE_CLASS_ID(Initialize, MemBar, 0) DEFINE_CLASS_ID(MemBarStoreStore, MemBar, 1) - DEFINE_CLASS_ID(LoadBarrier, Multi, 4) DEFINE_CLASS_ID(Mach, Node, 1) DEFINE_CLASS_ID(MachReturn, Mach, 0) @@ -679,7 +676,6 @@ DEFINE_CLASS_ID(EncodeNarrowPtr, Type, 6) DEFINE_CLASS_ID(EncodeP, EncodeNarrowPtr, 0) DEFINE_CLASS_ID(EncodePKlass, EncodeNarrowPtr, 1) - DEFINE_CLASS_ID(LoadBarrierSlowReg, Type, 7) DEFINE_CLASS_ID(Proj, Node, 3) DEFINE_CLASS_ID(CatchProj, Proj, 0) @@ -836,8 +832,6 @@ DEFINE_CLASS_QUERY(Load) DEFINE_CLASS_QUERY(LoadStore) DEFINE_CLASS_QUERY(LoadStoreConditional) - DEFINE_CLASS_QUERY(LoadBarrier) - DEFINE_CLASS_QUERY(LoadBarrierSlowReg) DEFINE_CLASS_QUERY(Lock) DEFINE_CLASS_QUERY(Loop) DEFINE_CLASS_QUERY(Mach) diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/opto/output.cpp --- a/src/hotspot/share/opto/output.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/opto/output.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -31,6 +31,8 @@ #include "compiler/compileBroker.hpp" #include "compiler/compilerDirectives.hpp" #include "compiler/oopMap.hpp" +#include "gc/shared/barrierSet.hpp" +#include "gc/shared/c2/barrierSetC2.hpp" #include "memory/allocation.inline.hpp" #include "opto/ad.hpp" #include "opto/callnode.hpp" @@ -114,35 +116,33 @@ } } + // Keeper of sizing aspects + BufferSizingData buf_sizes = BufferSizingData(); + + // Initialize code buffer + estimate_buffer_size(buf_sizes._const); + if (failing()) return; + + // Pre-compute the length of blocks and replace + // long branches with short if machine supports it. + // Must be done before ScheduleAndBundle due to SPARC delay slots uint* blk_starts = NEW_RESOURCE_ARRAY(uint, _cfg->number_of_blocks() + 1); blk_starts[0] = 0; - - // Initialize code buffer and process short branches. - CodeBuffer* cb = init_buffer(blk_starts); - - if (cb == NULL || failing()) { - return; - } + shorten_branches(blk_starts, buf_sizes); ScheduleAndBundle(); - -#ifndef PRODUCT - if (trace_opto_output()) { - tty->print("\n---- After ScheduleAndBundle ----\n"); - for (uint i = 0; i < _cfg->number_of_blocks(); i++) { - tty->print("\nBB#%03d:\n", i); - Block* block = _cfg->get_block(i); - for (uint j = 0; j < block->number_of_nodes(); j++) { - Node* n = block->get_node(j); - OptoReg::Name reg = _regalloc->get_reg_first(n); - tty->print(" %-6s ", reg >= 0 && reg < REG_COUNT ? Matcher::regName[reg] : ""); - n->dump(); - } - } + if (failing()) { + return; } -#endif - - if (failing()) { + + // Late barrier analysis must be done after schedule and bundle + // Otherwise liveness based spilling will fail + BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); + bs->late_barrier_analysis(); + + // Complete sizing of codebuffer + CodeBuffer* cb = init_buffer(buf_sizes); + if (cb == NULL || failing()) { return; } @@ -223,7 +223,7 @@ // The architecture description provides short branch variants for some long // branch instructions. Replace eligible long branches with short branches. -void Compile::shorten_branches(uint* blk_starts, int& code_size, int& reloc_size, int& stub_size) { +void Compile::shorten_branches(uint* blk_starts, BufferSizingData& buf_sizes) { // Compute size of each block, method size, and relocation information size uint nblocks = _cfg->number_of_blocks(); @@ -241,11 +241,11 @@ bool has_short_branch_candidate = false; // Initialize the sizes to 0 - code_size = 0; // Size in bytes of generated code - stub_size = 0; // Size in bytes of all stub entries + int code_size = 0; // Size in bytes of generated code + int stub_size = 0; // Size in bytes of all stub entries // Size in bytes of all relocation entries, including those in local stubs. // Start with 2-bytes of reloc info for the unvalidated entry point - reloc_size = 1; // Number of relocation entries + int reloc_size = 1; // Number of relocation entries // Make three passes. The first computes pessimistic blk_starts, // relative jmp_offset and reloc_size information. The second performs @@ -479,6 +479,10 @@ // a relocation index. // The CodeBuffer will expand the locs array if this estimate is too low. reloc_size *= 10 / sizeof(relocInfo); + + buf_sizes._reloc = reloc_size; + buf_sizes._code = code_size; + buf_sizes._stub = stub_size; } //------------------------------FillLocArray----------------------------------- @@ -490,8 +494,8 @@ // This should never have accepted Bad before assert(OptoReg::is_valid(regnum), "location must be valid"); return (OptoReg::is_reg(regnum)) - ? new LocationValue(Location::new_reg_loc(l_type, OptoReg::as_VMReg(regnum)) ) - : new LocationValue(Location::new_stk_loc(l_type, ra->reg2offset(regnum))); + ? new LocationValue(Location::new_reg_loc(l_type, OptoReg::as_VMReg(regnum)) ) + : new LocationValue(Location::new_stk_loc(l_type, ra->reg2offset(regnum))); } @@ -610,12 +614,12 @@ } #endif //_LP64 else if( (t->base() == Type::FloatBot || t->base() == Type::FloatCon) && - OptoReg::is_reg(regnum) ) { + OptoReg::is_reg(regnum) ) { array->append(new_loc_value( _regalloc, regnum, Matcher::float_in_double() - ? Location::float_in_dbl : Location::normal )); + ? Location::float_in_dbl : Location::normal )); } else if( t->base() == Type::Int && OptoReg::is_reg(regnum) ) { array->append(new_loc_value( _regalloc, regnum, Matcher::int_in_long - ? Location::int_in_long : Location::normal )); + ? Location::int_in_long : Location::normal )); } else if( t->base() == Type::NarrowOop ) { array->append(new_loc_value( _regalloc, regnum, Location::narrowoop )); } else { @@ -626,48 +630,48 @@ // No register. It must be constant data. switch (t->base()) { - case Type::Half: // Second half of a double - ShouldNotReachHere(); // Caller should skip 2nd halves - break; - case Type::AnyPtr: - array->append(new ConstantOopWriteValue(NULL)); - break; - case Type::AryPtr: - case Type::InstPtr: // fall through - array->append(new ConstantOopWriteValue(t->isa_oopptr()->const_oop()->constant_encoding())); - break; - case Type::NarrowOop: - if (t == TypeNarrowOop::NULL_PTR) { + case Type::Half: // Second half of a double + ShouldNotReachHere(); // Caller should skip 2nd halves + break; + case Type::AnyPtr: array->append(new ConstantOopWriteValue(NULL)); - } else { - array->append(new ConstantOopWriteValue(t->make_ptr()->isa_oopptr()->const_oop()->constant_encoding())); + break; + case Type::AryPtr: + case Type::InstPtr: // fall through + array->append(new ConstantOopWriteValue(t->isa_oopptr()->const_oop()->constant_encoding())); + break; + case Type::NarrowOop: + if (t == TypeNarrowOop::NULL_PTR) { + array->append(new ConstantOopWriteValue(NULL)); + } else { + array->append(new ConstantOopWriteValue(t->make_ptr()->isa_oopptr()->const_oop()->constant_encoding())); + } + break; + case Type::Int: + array->append(new ConstantIntValue(t->is_int()->get_con())); + break; + case Type::RawPtr: + // A return address (T_ADDRESS). + assert((intptr_t)t->is_ptr()->get_con() < (intptr_t)0x10000, "must be a valid BCI"); +#ifdef _LP64 + // Must be restored to the full-width 64-bit stack slot. + array->append(new ConstantLongValue(t->is_ptr()->get_con())); +#else + array->append(new ConstantIntValue(t->is_ptr()->get_con())); +#endif + break; + case Type::FloatCon: { + float f = t->is_float_constant()->getf(); + array->append(new ConstantIntValue(jint_cast(f))); + break; } - break; - case Type::Int: - array->append(new ConstantIntValue(t->is_int()->get_con())); - break; - case Type::RawPtr: - // A return address (T_ADDRESS). - assert((intptr_t)t->is_ptr()->get_con() < (intptr_t)0x10000, "must be a valid BCI"); + case Type::DoubleCon: { + jdouble d = t->is_double_constant()->getd(); #ifdef _LP64 - // Must be restored to the full-width 64-bit stack slot. - array->append(new ConstantLongValue(t->is_ptr()->get_con())); + array->append(new ConstantIntValue((jint)0)); + array->append(new ConstantDoubleValue(d)); #else - array->append(new ConstantIntValue(t->is_ptr()->get_con())); -#endif - break; - case Type::FloatCon: { - float f = t->is_float_constant()->getf(); - array->append(new ConstantIntValue(jint_cast(f))); - break; - } - case Type::DoubleCon: { - jdouble d = t->is_double_constant()->getd(); -#ifdef _LP64 - array->append(new ConstantIntValue((jint)0)); - array->append(new ConstantDoubleValue(d)); -#else - // Repack the double as two jints. + // Repack the double as two jints. // The convention the interpreter uses is that the second local // holds the first raw word of the native double representation. // This is actually reasonable, since locals and stack arrays @@ -679,15 +683,15 @@ array->append(new ConstantIntValue(acc.words[1])); array->append(new ConstantIntValue(acc.words[0])); #endif - break; - } - case Type::Long: { - jlong d = t->is_long()->get_con(); + break; + } + case Type::Long: { + jlong d = t->is_long()->get_con(); #ifdef _LP64 - array->append(new ConstantIntValue((jint)0)); - array->append(new ConstantLongValue(d)); + array->append(new ConstantIntValue((jint)0)); + array->append(new ConstantLongValue(d)); #else - // Repack the long as two jints. + // Repack the long as two jints. // The convention the interpreter uses is that the second local // holds the first raw word of the native double representation. // This is actually reasonable, since locals and stack arrays @@ -699,14 +703,14 @@ array->append(new ConstantIntValue(acc.words[1])); array->append(new ConstantIntValue(acc.words[0])); #endif - break; - } - case Type::Top: // Add an illegal value here - array->append(new LocationValue(Location())); - break; - default: - ShouldNotReachHere(); - break; + break; + } + case Type::Top: // Add an illegal value here + array->append(new LocationValue(Location())); + break; + default: + ShouldNotReachHere(); + break; } } @@ -871,58 +875,58 @@ // A simplified version of Process_OopMap_Node, to handle non-safepoints. class NonSafepointEmitter { - Compile* C; - JVMState* _pending_jvms; - int _pending_offset; - - void emit_non_safepoint(); + Compile* C; + JVMState* _pending_jvms; + int _pending_offset; + + void emit_non_safepoint(); public: - NonSafepointEmitter(Compile* compile) { - this->C = compile; - _pending_jvms = NULL; - _pending_offset = 0; - } - - void observe_instruction(Node* n, int pc_offset) { - if (!C->debug_info()->recording_non_safepoints()) return; - - Node_Notes* nn = C->node_notes_at(n->_idx); - if (nn == NULL || nn->jvms() == NULL) return; - if (_pending_jvms != NULL && - _pending_jvms->same_calls_as(nn->jvms())) { - // Repeated JVMS? Stretch it up here. - _pending_offset = pc_offset; - } else { + NonSafepointEmitter(Compile* compile) { + this->C = compile; + _pending_jvms = NULL; + _pending_offset = 0; + } + + void observe_instruction(Node* n, int pc_offset) { + if (!C->debug_info()->recording_non_safepoints()) return; + + Node_Notes* nn = C->node_notes_at(n->_idx); + if (nn == NULL || nn->jvms() == NULL) return; if (_pending_jvms != NULL && + _pending_jvms->same_calls_as(nn->jvms())) { + // Repeated JVMS? Stretch it up here. + _pending_offset = pc_offset; + } else { + if (_pending_jvms != NULL && + _pending_offset < pc_offset) { + emit_non_safepoint(); + } + _pending_jvms = NULL; + if (pc_offset > C->debug_info()->last_pc_offset()) { + // This is the only way _pending_jvms can become non-NULL: + _pending_jvms = nn->jvms(); + _pending_offset = pc_offset; + } + } + } + + // Stay out of the way of real safepoints: + void observe_safepoint(JVMState* jvms, int pc_offset) { + if (_pending_jvms != NULL && + !_pending_jvms->same_calls_as(jvms) && _pending_offset < pc_offset) { emit_non_safepoint(); } _pending_jvms = NULL; - if (pc_offset > C->debug_info()->last_pc_offset()) { - // This is the only way _pending_jvms can become non-NULL: - _pending_jvms = nn->jvms(); - _pending_offset = pc_offset; - } } - } - - // Stay out of the way of real safepoints: - void observe_safepoint(JVMState* jvms, int pc_offset) { - if (_pending_jvms != NULL && - !_pending_jvms->same_calls_as(jvms) && - _pending_offset < pc_offset) { - emit_non_safepoint(); + + void flush_at_end() { + if (_pending_jvms != NULL) { + emit_non_safepoint(); + } + _pending_jvms = NULL; } - _pending_jvms = NULL; - } - - void flush_at_end() { - if (_pending_jvms != NULL) { - emit_non_safepoint(); - } - _pending_jvms = NULL; - } }; void NonSafepointEmitter::emit_non_safepoint() { @@ -952,15 +956,11 @@ } //------------------------------init_buffer------------------------------------ -CodeBuffer* Compile::init_buffer(uint* blk_starts) { +void Compile::estimate_buffer_size(int& const_req) { // Set the initially allocated size - int code_req = initial_code_capacity; - int locs_req = initial_locs_capacity; - int stub_req = initial_stub_capacity; - int const_req = initial_const_capacity; - - int pad_req = NativeCall::instruction_size; + const_req = initial_const_capacity; + // The extra spacing after the code is necessary on some platforms. // Sometimes we need to patch in a jump after the last instruction, // if the nmethod has been deoptimized. (See 4932387, 4894843.) @@ -972,7 +972,7 @@ // Compute prolog code size _method_size = 0; - _frame_slots = OptoReg::reg2stack(_matcher->_old_SP)+_regalloc->_framesize; + _frame_slots = OptoReg::reg2stack(_matcher->_old_SP) + _regalloc->_framesize; #if defined(IA64) && !defined(AIX) if (save_argument_registers()) { // 4815101: this is a stub with implicit and unknown precision fp args. @@ -1021,11 +1021,18 @@ // Initialize the space for the BufferBlob used to find and verify // instruction size in MachNode::emit_size() init_scratch_buffer_blob(const_req); - if (failing()) return NULL; // Out of memory - - // Pre-compute the length of blocks and replace - // long branches with short if machine supports it. - shorten_branches(blk_starts, code_req, locs_req, stub_req); +} + +CodeBuffer* Compile::init_buffer(BufferSizingData& buf_sizes) { + + int stub_req = buf_sizes._stub; + int code_req = buf_sizes._code; + int const_req = buf_sizes._const; + + int pad_req = NativeCall::instruction_size; + + BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); + stub_req += bs->estimate_stub_size(); // nmethod and CodeBuffer count stubs & constants as part of method's code. // class HandlerImpl is platform-specific and defined in the *.ad files. @@ -1038,18 +1045,18 @@ code_req = const_req = stub_req = exception_handler_req = deopt_handler_req = 0x10; // force expansion int total_req = - const_req + - code_req + - pad_req + - stub_req + - exception_handler_req + - deopt_handler_req; // deopt handler + const_req + + code_req + + pad_req + + stub_req + + exception_handler_req + + deopt_handler_req; // deopt handler if (has_method_handle_invokes()) total_req += deopt_handler_req; // deopt MH handler CodeBuffer* cb = code_buffer(); - cb->initialize(total_req, locs_req); + cb->initialize(total_req, buf_sizes._reloc); // Have we run out of code space? if ((cb->blob() == NULL) || (!CompileBroker::should_compile_new_jobs())) { @@ -1268,12 +1275,12 @@ Process_OopMap_Node(mach, current_offset); } // End if safepoint - // If this is a null check, then add the start of the previous instruction to the list + // If this is a null check, then add the start of the previous instruction to the list else if( mach->is_MachNullCheck() ) { inct_starts[inct_cnt++] = previous_offset; } - // If this is a branch, then fill in the label with the target BB's label + // If this is a branch, then fill in the label with the target BB's label else if (mach->is_MachBranch()) { // This requires the TRUE branch target be in succs[0] uint block_num = block->non_connector_successor(0)->_pre_order; @@ -1284,8 +1291,8 @@ bool delay_slot_is_used = valid_bundle_info(n) && node_bundling(n)->use_unconditional_delay(); if (!delay_slot_is_used && mach->may_be_short_branch()) { - assert(delay_slot == NULL, "not expecting delay slot node"); - int br_size = n->size(_regalloc); + assert(delay_slot == NULL, "not expecting delay slot node"); + int br_size = n->size(_regalloc); int offset = blk_starts[block_num] - current_offset; if (block_num >= i) { // Current and following block's offset are not @@ -1343,7 +1350,7 @@ } } #ifdef ASSERT - // Check that oop-store precedes the card-mark + // Check that oop-store precedes the card-mark else if (mach->ideal_Opcode() == Op_StoreCM) { uint storeCM_idx = j; int count = 0; @@ -1514,6 +1521,10 @@ } #endif + BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); + bs->emit_stubs(*cb); + if (failing()) return; + #ifndef PRODUCT // Information on the size of the method, without the extraneous code Scheduling::increment_method_size(cb->insts_size()); @@ -1688,20 +1699,20 @@ // Initializer for class Scheduling Scheduling::Scheduling(Arena *arena, Compile &compile) - : _arena(arena), - _cfg(compile.cfg()), - _regalloc(compile.regalloc()), - _scheduled(arena), - _available(arena), - _reg_node(arena), - _pinch_free_list(arena), - _next_node(NULL), - _bundle_instr_count(0), - _bundle_cycle_number(0), - _bundle_use(0, 0, resource_count, &_bundle_use_elements[0]) + : _arena(arena), + _cfg(compile.cfg()), + _regalloc(compile.regalloc()), + _scheduled(arena), + _available(arena), + _reg_node(arena), + _pinch_free_list(arena), + _next_node(NULL), + _bundle_instr_count(0), + _bundle_cycle_number(0), + _bundle_use(0, 0, resource_count, &_bundle_use_elements[0]) #ifndef PRODUCT - , _branches(0) - , _unconditional_delays(0) + , _branches(0) + , _unconditional_delays(0) #endif { // Create a MachNopNode @@ -1782,8 +1793,8 @@ _bundle_use.reset(); memcpy(_bundle_use_elements, - Pipeline_Use::elaborated_elements, - sizeof(Pipeline_Use::elaborated_elements)); + Pipeline_Use::elaborated_elements, + sizeof(Pipeline_Use::elaborated_elements)); } // Perform instruction scheduling and bundling over the sequence of @@ -1810,6 +1821,22 @@ // Walk backwards over each basic block, computing the needed alignment // Walk over all the basic blocks scheduling.DoScheduling(); + +#ifndef PRODUCT + if (trace_opto_output()) { + tty->print("\n---- After ScheduleAndBundle ----\n"); + for (uint i = 0; i < _cfg->number_of_blocks(); i++) { + tty->print("\nBB#%03d:\n", i); + Block* block = _cfg->get_block(i); + for (uint j = 0; j < block->number_of_nodes(); j++) { + Node* n = block->get_node(j); + OptoReg::Name reg = _regalloc->get_reg_first(n); + tty->print(" %-6s ", reg >= 0 && reg < REG_COUNT ? Matcher::regName[reg] : ""); + n->dump(); + } + } + } +#endif } // Compute the latency of all the instructions. This is fairly simple, @@ -1878,7 +1905,7 @@ #ifndef PRODUCT if (_cfg->C->trace_opto_output()) tty->print("# NodeFitsInBundle [%4d]: FALSE; latency %4d > %d\n", - n->_idx, _current_latency[n_idx], _bundle_cycle_number); + n->_idx, _current_latency[n_idx], _bundle_cycle_number); #endif return (false); } @@ -1895,7 +1922,7 @@ #ifndef PRODUCT if (_cfg->C->trace_opto_output()) tty->print("# NodeFitsInBundle [%4d]: FALSE; too many instructions: %d > %d\n", - n->_idx, _bundle_instr_count + instruction_count, Pipeline::_max_instrs_per_cycle); + n->_idx, _bundle_instr_count + instruction_count, Pipeline::_max_instrs_per_cycle); #endif return (false); } @@ -2103,12 +2130,12 @@ // Don't allow safepoints in the branch shadow, that will // cause a number of difficulties if ( avail_pipeline->instructionCount() == 1 && - !avail_pipeline->hasMultipleBundles() && - !avail_pipeline->hasBranchDelay() && - Pipeline::instr_has_unit_size() && - d->size(_regalloc) == Pipeline::instr_unit_size() && - NodeFitsInBundle(d) && - !node_bundling(d)->used_in_delay()) { + !avail_pipeline->hasMultipleBundles() && + !avail_pipeline->hasBranchDelay() && + Pipeline::instr_has_unit_size() && + d->size(_regalloc) == Pipeline::instr_unit_size() && + NodeFitsInBundle(d) && + !node_bundling(d)->used_in_delay()) { if (d->is_Mach() && !d->is_MachSafePoint()) { // A node that fits in the delay slot was found, so we need to @@ -2153,13 +2180,13 @@ // step of the bundles if (!NodeFitsInBundle(n)) { #ifndef PRODUCT - if (_cfg->C->trace_opto_output()) - tty->print("# *** STEP(branch won't fit) ***\n"); + if (_cfg->C->trace_opto_output()) + tty->print("# *** STEP(branch won't fit) ***\n"); #endif - // Update the state information - _bundle_instr_count = 0; - _bundle_cycle_number += 1; - _bundle_use.step(1); + // Update the state information + _bundle_instr_count = 0; + _bundle_cycle_number += 1; + _bundle_use.step(1); } } @@ -2205,8 +2232,8 @@ #ifndef PRODUCT if (_cfg->C->trace_opto_output()) tty->print("# *** STEP(%d >= %d instructions) ***\n", - instruction_count + _bundle_instr_count, - Pipeline::_max_instrs_per_cycle); + instruction_count + _bundle_instr_count, + Pipeline::_max_instrs_per_cycle); #endif step(1); } @@ -2412,7 +2439,7 @@ } assert(!last->is_Mach() || last->as_Mach()->ideal_Opcode() != Op_Con, ""); if( last->is_Catch() || - (last->is_Mach() && last->as_Mach()->ideal_Opcode() == Op_Halt) ) { + (last->is_Mach() && last->as_Mach()->ideal_Opcode() == Op_Halt) ) { // There might be a prior call. Skip it. while (_bb_start < _bb_end && bb->get_node(--_bb_end)->is_MachProj()); } else if( last->is_MachNullCheck() ) { @@ -2482,7 +2509,7 @@ } #endif #ifdef ASSERT - verify_good_schedule(bb,"after block local scheduling"); + verify_good_schedule(bb,"after block local scheduling"); #endif } @@ -2830,31 +2857,31 @@ // void Scheduling::garbage_collect_pinch_nodes() { #ifndef PRODUCT - if (_cfg->C->trace_opto_output()) tty->print("Reclaimed pinch nodes:"); + if (_cfg->C->trace_opto_output()) tty->print("Reclaimed pinch nodes:"); #endif - int trace_cnt = 0; - for (uint k = 0; k < _reg_node.Size(); k++) { - Node* pinch = _reg_node[k]; - if ((pinch != NULL) && pinch->Opcode() == Op_Node && - // no predecence input edges - (pinch->req() == pinch->len() || pinch->in(pinch->req()) == NULL) ) { - cleanup_pinch(pinch); - _pinch_free_list.push(pinch); - _reg_node.map(k, NULL); + int trace_cnt = 0; + for (uint k = 0; k < _reg_node.Size(); k++) { + Node* pinch = _reg_node[k]; + if ((pinch != NULL) && pinch->Opcode() == Op_Node && + // no predecence input edges + (pinch->req() == pinch->len() || pinch->in(pinch->req()) == NULL) ) { + cleanup_pinch(pinch); + _pinch_free_list.push(pinch); + _reg_node.map(k, NULL); #ifndef PRODUCT - if (_cfg->C->trace_opto_output()) { - trace_cnt++; - if (trace_cnt > 40) { - tty->print("\n"); - trace_cnt = 0; - } - tty->print(" %d", pinch->_idx); + if (_cfg->C->trace_opto_output()) { + trace_cnt++; + if (trace_cnt > 40) { + tty->print("\n"); + trace_cnt = 0; } + tty->print(" %d", pinch->_idx); + } #endif - } } + } #ifndef PRODUCT - if (_cfg->C->trace_opto_output()) tty->print("\n"); + if (_cfg->C->trace_opto_output()) tty->print("\n"); #endif } @@ -2891,19 +2918,19 @@ void Scheduling::print_statistics() { // Print the size added by nops for bundling tty->print("Nops added %d bytes to total of %d bytes", - _total_nop_size, _total_method_size); + _total_nop_size, _total_method_size); if (_total_method_size > 0) tty->print(", for %.2f%%", - ((double)_total_nop_size) / ((double) _total_method_size) * 100.0); + ((double)_total_nop_size) / ((double) _total_method_size) * 100.0); tty->print("\n"); // Print the number of branch shadows filled if (Pipeline::_branch_has_delay_slot) { tty->print("Of %d branches, %d had unconditional delay slots filled", - _total_branches, _total_unconditional_delays); + _total_branches, _total_unconditional_delays); if (_total_branches > 0) tty->print(", for %.2f%%", - ((double)_total_unconditional_delays) / ((double)_total_branches) * 100.0); + ((double)_total_unconditional_delays) / ((double)_total_branches) * 100.0); tty->print("\n"); } @@ -2917,6 +2944,6 @@ if (total_bundles > 0) tty->print("Average ILP (excluding nops) is %.2f\n", - ((double)total_instructions) / ((double)total_bundles)); + ((double)total_instructions) / ((double)total_bundles)); } #endif diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/opto/output.hpp --- a/src/hotspot/share/opto/output.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/opto/output.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -40,7 +40,6 @@ class PhaseChaitin; class Pipeline_Use_Element; class Pipeline_Use; - #ifndef PRODUCT #define DEBUG_ARG(x) , x #else @@ -49,10 +48,7 @@ // Define the initial sizes for allocation of the resizable code buffer enum { - initial_code_capacity = 16 * 1024, - initial_stub_capacity = 4 * 1024, - initial_const_capacity = 4 * 1024, - initial_locs_capacity = 3 * 1024 + initial_const_capacity = 4 * 1024 }; //------------------------------Scheduling---------------------------------- diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/opto/phaseX.cpp --- a/src/hotspot/share/opto/phaseX.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/opto/phaseX.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -1648,14 +1648,14 @@ // of the mirror load depends on the type of 'n'. See LoadNode::Value(). // LoadBarrier?(LoadP(LoadP(AddP(foo:Klass, #java_mirror)))) BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); - bool has_load_barriers = bs->has_load_barriers(); + bool has_load_barrier_nodes = bs->has_load_barrier_nodes(); if (use_op == Op_LoadP && use->bottom_type()->isa_rawptr()) { for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { Node* u = use->fast_out(i2); const Type* ut = u->bottom_type(); if (u->Opcode() == Op_LoadP && ut->isa_instptr()) { - if (has_load_barriers) { + if (has_load_barrier_nodes) { // Search for load barriers behind the load for (DUIterator_Fast i3max, i3 = u->fast_outs(i3max); i3 < i3max; i3++) { Node* b = u->fast_out(i3); @@ -1808,14 +1808,14 @@ // Loading the java mirror from a Klass requires two loads and the type // of the mirror load depends on the type of 'n'. See LoadNode::Value(). BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); - bool has_load_barriers = bs->has_load_barriers(); + bool has_load_barrier_nodes = bs->has_load_barrier_nodes(); if (m_op == Op_LoadP && m->bottom_type()->isa_rawptr()) { for (DUIterator_Fast i2max, i2 = m->fast_outs(i2max); i2 < i2max; i2++) { Node* u = m->fast_out(i2); const Type* ut = u->bottom_type(); if (u->Opcode() == Op_LoadP && ut->isa_instptr() && ut != type(u)) { - if (has_load_barriers) { + if (has_load_barrier_nodes) { // Search for load barriers behind the load for (DUIterator_Fast i3max, i3 = u->fast_outs(i3max); i3 < i3max; i3++) { Node* b = u->fast_out(i3); diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/prims/jvmtiExport.cpp --- a/src/hotspot/share/prims/jvmtiExport.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/prims/jvmtiExport.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -1202,6 +1202,7 @@ bool JvmtiExport::_can_post_method_exit = false; bool JvmtiExport::_can_pop_frame = false; bool JvmtiExport::_can_force_early_return = false; +bool JvmtiExport::_can_get_owned_monitor_info = false; bool JvmtiExport::_early_vmstart_recorded = false; diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/prims/jvmtiExport.hpp --- a/src/hotspot/share/prims/jvmtiExport.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/prims/jvmtiExport.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -91,6 +91,7 @@ JVMTI_SUPPORT_FLAG(can_force_early_return) JVMTI_SUPPORT_FLAG(early_vmstart_recorded) + JVMTI_SUPPORT_FLAG(can_get_owned_monitor_info) // includes can_get_owned_monitor_stack_depth_info friend class JvmtiEventControllerPrivate; // should only modify these flags JVMTI_SUPPORT_FLAG(should_post_single_step) diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/prims/jvmtiManageCapabilities.cpp --- a/src/hotspot/share/prims/jvmtiManageCapabilities.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/prims/jvmtiManageCapabilities.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2019, 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 @@ -367,6 +367,8 @@ JvmtiExport::set_can_pop_frame(avail.can_pop_frame); JvmtiExport::set_can_force_early_return(avail.can_force_early_return); JvmtiExport::set_should_clean_up_heap_objects(avail.can_generate_breakpoint_events); + JvmtiExport::set_can_get_owned_monitor_info(avail.can_get_owned_monitor_info || + avail.can_get_owned_monitor_stack_depth_info); } #ifndef PRODUCT diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/prims/jvmtiRawMonitor.cpp --- a/src/hotspot/share/prims/jvmtiRawMonitor.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/prims/jvmtiRawMonitor.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -32,20 +32,20 @@ JvmtiRawMonitor::QNode::QNode(Thread* thread) : _next(NULL), _prev(NULL), _event(thread->_ParkEvent), - _notified(0), TState(TS_RUN) { + _notified(0), _t_state(TS_RUN) { } -GrowableArray *JvmtiPendingMonitors::_monitors = +GrowableArray* JvmtiPendingMonitors::_monitors = new (ResourceObj::C_HEAP, mtInternal) GrowableArray(1, true); void JvmtiPendingMonitors::transition_raw_monitors() { assert((Threads::number_of_threads()==1), - "Java thread has not been created yet or more than one java thread \ -is running. Raw monitor transition will not work"); - JavaThread *current_java_thread = JavaThread::current(); + "Java thread has not been created yet or more than one java thread " + "is running. Raw monitor transition will not work"); + JavaThread* current_java_thread = JavaThread::current(); assert(current_java_thread->thread_state() == _thread_in_vm, "Must be in vm"); - for(int i=0; i< count(); i++) { - JvmtiRawMonitor *rmonitor = monitors()->at(i); + for (int i = 0; i < count(); i++) { + JvmtiRawMonitor* rmonitor = monitors()->at(i); rmonitor->raw_enter(current_java_thread); } // pending monitors are converted to real monitor so delete them all. @@ -56,10 +56,10 @@ // class JvmtiRawMonitor // -JvmtiRawMonitor::JvmtiRawMonitor(const char *name) : _owner(NULL), +JvmtiRawMonitor::JvmtiRawMonitor(const char* name) : _owner(NULL), _recursions(0), - _EntryList(NULL), - _WaitSet(NULL), + _entry_list(NULL), + _wait_set(NULL), _waiters(0), _magic(JVMTI_RM_MAGIC), _name(NULL) { @@ -119,155 +119,165 @@ // // ------------------------------------------------------------------------- -void JvmtiRawMonitor::SimpleEnter (Thread * Self) { +void JvmtiRawMonitor::simple_enter(Thread* self) { for (;;) { - if (Atomic::replace_if_null(Self, &_owner)) { - return ; + if (Atomic::replace_if_null(self, &_owner)) { + return; } - QNode Node (Self) ; - Self->_ParkEvent->reset() ; // strictly optional - Node.TState = QNode::TS_ENTER ; + QNode node(self); + self->_ParkEvent->reset(); // strictly optional + node._t_state = QNode::TS_ENTER; - RawMonitor_lock->lock_without_safepoint_check() ; - Node._next = _EntryList ; - _EntryList = &Node ; - OrderAccess::fence() ; - if (_owner == NULL && Atomic::replace_if_null(Self, &_owner)) { - _EntryList = Node._next ; - RawMonitor_lock->unlock() ; - return ; + RawMonitor_lock->lock_without_safepoint_check(); + node._next = _entry_list; + _entry_list = &node; + OrderAccess::fence(); + if (_owner == NULL && Atomic::replace_if_null(self, &_owner)) { + _entry_list = node._next; + RawMonitor_lock->unlock(); + return; } - RawMonitor_lock->unlock() ; - while (Node.TState == QNode::TS_ENTER) { - Self->_ParkEvent->park() ; + RawMonitor_lock->unlock(); + while (node._t_state == QNode::TS_ENTER) { + self->_ParkEvent->park(); } } } -void JvmtiRawMonitor::SimpleExit (Thread * Self) { - guarantee (_owner == Self, "invariant") ; - OrderAccess::release_store(&_owner, (Thread*)NULL) ; - OrderAccess::fence() ; - if (_EntryList == NULL) return ; - QNode * w ; +void JvmtiRawMonitor::simple_exit(Thread* self) { + guarantee(_owner == self, "invariant"); + OrderAccess::release_store(&_owner, (Thread*)NULL); + OrderAccess::fence(); + if (_entry_list == NULL) { + return; + } - RawMonitor_lock->lock_without_safepoint_check() ; - w = _EntryList ; + RawMonitor_lock->lock_without_safepoint_check(); + QNode* w = _entry_list; if (w != NULL) { - _EntryList = w->_next ; + _entry_list = w->_next; } - RawMonitor_lock->unlock() ; + RawMonitor_lock->unlock(); if (w != NULL) { - guarantee (w ->TState == QNode::TS_ENTER, "invariant") ; - // Once we set TState to TS_RUN the waiting thread can complete - // SimpleEnter and 'w' is pointing into random stack space. So we have - // to ensure we extract the ParkEvent (which is in type-stable memory) - // before we set the state, and then don't access 'w'. - ParkEvent * ev = w->_event ; - OrderAccess::loadstore(); - w->TState = QNode::TS_RUN ; - OrderAccess::fence() ; - ev->unpark() ; + guarantee(w ->_t_state == QNode::TS_ENTER, "invariant"); + // Once we set _t_state to TS_RUN the waiting thread can complete + // simple_enter and 'w' is pointing into random stack space. So we have + // to ensure we extract the ParkEvent (which is in type-stable memory) + // before we set the state, and then don't access 'w'. + ParkEvent* ev = w->_event; + OrderAccess::loadstore(); + w->_t_state = QNode::TS_RUN; + OrderAccess::fence(); + ev->unpark(); } - return ; + return; } -int JvmtiRawMonitor::SimpleWait (Thread * Self, jlong millis) { - guarantee (_owner == Self , "invariant") ; - guarantee (_recursions == 0, "invariant") ; +int JvmtiRawMonitor::simple_wait(Thread* self, jlong millis) { + guarantee(_owner == self , "invariant"); + guarantee(_recursions == 0, "invariant"); - QNode Node (Self) ; - Node._notified = 0 ; - Node.TState = QNode::TS_WAIT ; + QNode node(self); + node._notified = 0; + node._t_state = QNode::TS_WAIT; - RawMonitor_lock->lock_without_safepoint_check() ; - Node._next = _WaitSet ; - _WaitSet = &Node ; - RawMonitor_lock->unlock() ; + RawMonitor_lock->lock_without_safepoint_check(); + node._next = _wait_set; + _wait_set = &node; + RawMonitor_lock->unlock(); - SimpleExit (Self) ; - guarantee (_owner != Self, "invariant") ; + simple_exit(self); + guarantee(_owner != self, "invariant"); - int ret = OS_OK ; + int ret = OS_OK; if (millis <= 0) { - Self->_ParkEvent->park(); + self->_ParkEvent->park(); } else { - ret = Self->_ParkEvent->park(millis); + ret = self->_ParkEvent->park(millis); } // If thread still resides on the waitset then unlink it. // Double-checked locking -- the usage is safe in this context - // as TState is volatile and the lock-unlock operators are + // as _t_state is volatile and the lock-unlock operators are // serializing (barrier-equivalent). - if (Node.TState == QNode::TS_WAIT) { - RawMonitor_lock->lock_without_safepoint_check() ; - if (Node.TState == QNode::TS_WAIT) { + if (node._t_state == QNode::TS_WAIT) { + RawMonitor_lock->lock_without_safepoint_check(); + if (node._t_state == QNode::TS_WAIT) { // Simple O(n) unlink, but performance isn't critical here. - QNode * p ; - QNode * q = NULL ; - for (p = _WaitSet ; p != &Node; p = p->_next) { - q = p ; + QNode* p; + QNode* q = NULL; + for (p = _wait_set; p != &node; p = p->_next) { + q = p; } - guarantee (p == &Node, "invariant") ; + guarantee(p == &node, "invariant"); if (q == NULL) { - guarantee (p == _WaitSet, "invariant") ; - _WaitSet = p->_next ; + guarantee (p == _wait_set, "invariant"); + _wait_set = p->_next; } else { - guarantee (p == q->_next, "invariant") ; - q->_next = p->_next ; + guarantee(p == q->_next, "invariant"); + q->_next = p->_next; } - Node.TState = QNode::TS_RUN ; + node._t_state = QNode::TS_RUN; } - RawMonitor_lock->unlock() ; + RawMonitor_lock->unlock(); } - guarantee (Node.TState == QNode::TS_RUN, "invariant") ; - SimpleEnter (Self) ; + guarantee(node._t_state == QNode::TS_RUN, "invariant"); + simple_enter(self); - guarantee (_owner == Self, "invariant") ; - guarantee (_recursions == 0, "invariant") ; - return ret ; + guarantee(_owner == self, "invariant"); + guarantee(_recursions == 0, "invariant"); + return ret; } -void JvmtiRawMonitor::SimpleNotify (Thread * Self, bool All) { - guarantee (_owner == Self, "invariant") ; - if (_WaitSet == NULL) return ; +void JvmtiRawMonitor::simple_notify(Thread* self, bool all) { + guarantee(_owner == self, "invariant"); + if (_wait_set == NULL) { + return; + } // We have two options: - // A. Transfer the threads from the WaitSet to the EntryList - // B. Remove the thread from the WaitSet and unpark() it. + // A. Transfer the threads from the _wait_set to the _entry_list + // B. Remove the thread from the _wait_set and unpark() it. // // We use (B), which is crude and results in lots of futile // context switching. In particular (B) induces lots of contention. - ParkEvent * ev = NULL ; // consider using a small auto array ... - RawMonitor_lock->lock_without_safepoint_check() ; + ParkEvent* ev = NULL; // consider using a small auto array ... + RawMonitor_lock->lock_without_safepoint_check(); for (;;) { - QNode * w = _WaitSet ; - if (w == NULL) break ; - _WaitSet = w->_next ; - if (ev != NULL) { ev->unpark(); ev = NULL; } - ev = w->_event ; - OrderAccess::loadstore() ; - w->TState = QNode::TS_RUN ; - OrderAccess::storeload(); - if (!All) break ; + QNode* w = _wait_set; + if (w == NULL) break; + _wait_set = w->_next; + if (ev != NULL) { + ev->unpark(); + ev = NULL; + } + ev = w->_event; + OrderAccess::loadstore(); + w->_t_state = QNode::TS_RUN; + OrderAccess::storeload(); + if (!all) { + break; + } } - RawMonitor_lock->unlock() ; - if (ev != NULL) ev->unpark(); - return ; + RawMonitor_lock->unlock(); + if (ev != NULL) { + ev->unpark(); + } + return; } // Any JavaThread will enter here with state _thread_blocked -void JvmtiRawMonitor::raw_enter(Thread * Self) { - void * Contended ; - JavaThread * jt = NULL; +void JvmtiRawMonitor::raw_enter(Thread* self) { + void* contended; + JavaThread* jt = NULL; // don't enter raw monitor if thread is being externally suspended, it will // surprise the suspender if a "suspended" thread can still enter monitor - if (Self->is_Java_thread()) { - jt = (JavaThread*) Self; + if (self->is_Java_thread()) { + jt = (JavaThread*)self; jt->SR_lock()->lock_without_safepoint_check(); while (jt->is_external_suspend()) { jt->SR_lock()->unlock(); @@ -275,37 +285,39 @@ jt->SR_lock()->lock_without_safepoint_check(); } // guarded by SR_lock to avoid racing with new external suspend requests. - Contended = Atomic::cmpxchg(jt, &_owner, (Thread*)NULL); + contended = Atomic::cmpxchg(jt, &_owner, (Thread*)NULL); jt->SR_lock()->unlock(); } else { - Contended = Atomic::cmpxchg(Self, &_owner, (Thread*)NULL); + contended = Atomic::cmpxchg(self, &_owner, (Thread*)NULL); } - if (Contended == Self) { - _recursions ++ ; - return ; + if (contended == self) { + _recursions++; + return; } - if (Contended == NULL) { - guarantee (_owner == Self, "invariant") ; - guarantee (_recursions == 0, "invariant") ; - return ; + if (contended == NULL) { + guarantee(_owner == self, "invariant"); + guarantee(_recursions == 0, "invariant"); + return; } - Self->set_current_pending_raw_monitor(this); + self->set_current_pending_raw_monitor(this); - if (!Self->is_Java_thread()) { - SimpleEnter (Self) ; + if (!self->is_Java_thread()) { + simple_enter(self); } else { - guarantee (jt->thread_state() == _thread_blocked, "invariant") ; + guarantee(jt->thread_state() == _thread_blocked, "invariant"); for (;;) { jt->set_suspend_equivalent(); // cleared by handle_special_suspend_equivalent_condition() or // java_suspend_self() - SimpleEnter (jt) ; + simple_enter(jt); // were we externally suspended while we were waiting? - if (!jt->handle_special_suspend_equivalent_condition()) break ; + if (!jt->handle_special_suspend_equivalent_condition()) { + break; + } // This thread was externally suspended // We have reentered the contended monitor, but while we were @@ -314,26 +326,26 @@ // thread that suspended us. // // Drop the lock - SimpleExit (jt) ; + simple_exit(jt); jt->java_suspend_self(); } } - Self->set_current_pending_raw_monitor(NULL); + self->set_current_pending_raw_monitor(NULL); - guarantee (_owner == Self, "invariant") ; - guarantee (_recursions == 0, "invariant") ; + guarantee(_owner == self, "invariant"); + guarantee(_recursions == 0, "invariant"); } -int JvmtiRawMonitor::raw_exit(Thread * Self) { - if (Self != _owner) { +int JvmtiRawMonitor::raw_exit(Thread* self) { + if (self != _owner) { return M_ILLEGAL_MONITOR_STATE; } if (_recursions > 0) { - --_recursions ; + _recursions--; } else { - SimpleExit (Self) ; + simple_exit(self); } return M_OK; @@ -341,70 +353,72 @@ // All JavaThreads will enter here with state _thread_blocked -int JvmtiRawMonitor::raw_wait(jlong millis, bool interruptible, Thread * Self) { - if (Self != _owner) { +int JvmtiRawMonitor::raw_wait(jlong millis, bool interruptible, Thread* self) { + if (self != _owner) { return M_ILLEGAL_MONITOR_STATE; } - // To avoid spurious wakeups we reset the parkevent -- This is strictly optional. + // To avoid spurious wakeups we reset the parkevent. This is strictly optional. // The caller must be able to tolerate spurious returns from raw_wait(). - Self->_ParkEvent->reset() ; - OrderAccess::fence() ; + self->_ParkEvent->reset(); + OrderAccess::fence(); - JavaThread * jt = NULL; + JavaThread* jt = NULL; // check interrupt event if (interruptible) { - assert(Self->is_Java_thread(), "Only JavaThreads can be interruptible"); - jt = (JavaThread*) Self; + assert(self->is_Java_thread(), "Only JavaThreads can be interruptible"); + jt = (JavaThread*)self; if (jt->is_interrupted(true)) { return M_INTERRUPTED; } } else { - assert(!Self->is_Java_thread(), "JavaThreads must be interuptible"); + assert(!self->is_Java_thread(), "JavaThreads must be interuptible"); } - intptr_t save = _recursions ; - _recursions = 0 ; - _waiters ++ ; - if (Self->is_Java_thread()) { - guarantee (jt->thread_state() == _thread_blocked, "invariant") ; + intptr_t save = _recursions; + _recursions = 0; + _waiters++; + if (self->is_Java_thread()) { + guarantee(jt->thread_state() == _thread_blocked, "invariant"); jt->set_suspend_equivalent(); } - int rv = SimpleWait (Self, millis) ; - _recursions = save ; - _waiters -- ; + int rv = simple_wait(self, millis); + _recursions = save; + _waiters--; - guarantee (Self == _owner, "invariant") ; - if (Self->is_Java_thread()) { - for (;;) { - if (!jt->handle_special_suspend_equivalent_condition()) break ; - SimpleExit (jt) ; - jt->java_suspend_self(); - SimpleEnter (jt) ; - jt->set_suspend_equivalent() ; - } - guarantee (jt == _owner, "invariant") ; + guarantee(self == _owner, "invariant"); + if (self->is_Java_thread()) { + for (;;) { + if (!jt->handle_special_suspend_equivalent_condition()) { + break; + } + simple_exit(jt); + jt->java_suspend_self(); + simple_enter(jt); + jt->set_suspend_equivalent(); + } + guarantee(jt == _owner, "invariant"); } if (interruptible && jt->is_interrupted(true)) { return M_INTERRUPTED; } - return M_OK ; -} - -int JvmtiRawMonitor::raw_notify(Thread * Self) { - if (Self != _owner) { - return M_ILLEGAL_MONITOR_STATE; - } - SimpleNotify (Self, false) ; return M_OK; } -int JvmtiRawMonitor::raw_notifyAll(Thread * Self) { - if (Self != _owner) { +int JvmtiRawMonitor::raw_notify(Thread* self) { + if (self != _owner) { return M_ILLEGAL_MONITOR_STATE; } - SimpleNotify (Self, true) ; + simple_notify(self, false); return M_OK; } + +int JvmtiRawMonitor::raw_notifyAll(Thread* self) { + if (self != _owner) { + return M_ILLEGAL_MONITOR_STATE; + } + simple_notify(self, true); + return M_OK; +} diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/prims/jvmtiRawMonitor.hpp --- a/src/hotspot/share/prims/jvmtiRawMonitor.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/prims/jvmtiRawMonitor.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -46,31 +46,31 @@ enum TStates { TS_READY, TS_RUN, TS_WAIT, TS_ENTER }; QNode* volatile _next; QNode* volatile _prev; - ParkEvent * _event; - volatile int _notified; - volatile TStates TState; + ParkEvent* _event; + volatile int _notified; + volatile TStates _t_state; QNode(Thread* thread); }; - Thread* volatile _owner; // pointer to owning thread - volatile int _recursions; // recursion count, 0 for first entry - QNode* volatile _EntryList; // Threads blocked on entry or reentry. - // The list is actually composed of nodes, - // acting as proxies for Threads. - QNode* volatile _WaitSet; // Threads wait()ing on the monitor - volatile jint _waiters; // number of waiting threads - int _magic; - char * _name; + Thread* volatile _owner; // pointer to owning thread + volatile int _recursions; // recursion count, 0 for first entry + QNode* volatile _entry_list; // Threads blocked on entry or reentry. + // The list is actually composed of nodes, + // acting as proxies for Threads. + QNode* volatile _wait_set; // Threads wait()ing on the monitor + volatile jint _waiters; // number of waiting threads + int _magic; + char* _name; // JVMTI_RM_MAGIC is set in contructor and unset in destructor. enum { JVMTI_RM_MAGIC = (int)(('T' << 24) | ('I' << 16) | ('R' << 8) | 'M') }; - void SimpleEnter (Thread * Self) ; - void SimpleExit (Thread * Self) ; - int SimpleWait (Thread * Self, jlong millis) ; - void SimpleNotify(Thread * Self, bool All) ; + void simple_enter(Thread* self); + void simple_exit(Thread* self); + int simple_wait(Thread* self, jlong millis); + void simple_notify(Thread* self, bool all); -public: + public: // return codes enum { @@ -84,20 +84,20 @@ return CHeapObj::operator new(size, std::nothrow); } - JvmtiRawMonitor(const char *name); + JvmtiRawMonitor(const char* name); ~JvmtiRawMonitor(); - Thread * owner() const { return _owner; } - void set_owner(Thread * owner) { _owner = owner; } - int recursions() const { return _recursions; } - void raw_enter(Thread * Self); - int raw_exit(Thread * Self); - int raw_wait(jlong millis, bool interruptable, Thread * Self); - int raw_notify(Thread * Self); - int raw_notifyAll(Thread * Self); - int magic() const { return _magic; } - const char *get_name() const { return _name; } - bool is_valid(); + Thread* owner() const { return _owner; } + void set_owner(Thread* owner) { _owner = owner; } + int recursions() const { return _recursions; } + void raw_enter(Thread* self); + int raw_exit(Thread* self); + int raw_wait(jlong millis, bool interruptible, Thread* self); + int raw_notify(Thread* self); + int raw_notifyAll(Thread* self); + int magic() const { return _magic; } + const char* get_name() const { return _name; } + bool is_valid(); }; // Onload pending raw monitors @@ -106,8 +106,8 @@ // VM is fully initialized. class JvmtiPendingMonitors : public AllStatic { -private: - static GrowableArray *_monitors; // Cache raw monitor enter + private: + static GrowableArray* _monitors; // Cache raw monitor enter inline static GrowableArray* monitors() { return _monitors; } @@ -115,8 +115,8 @@ delete monitors(); } -public: - static void enter(JvmtiRawMonitor *monitor) { + public: + static void enter(JvmtiRawMonitor* monitor) { monitors()->append(monitor); } @@ -124,14 +124,14 @@ return monitors()->length(); } - static void destroy(JvmtiRawMonitor *monitor) { + static void destroy(JvmtiRawMonitor* monitor) { while (monitors()->contains(monitor)) { monitors()->remove(monitor); } } // Return false if monitor is not found in the list. - static bool exit(JvmtiRawMonitor *monitor) { + static bool exit(JvmtiRawMonitor* monitor) { if (monitors()->contains(monitor)) { monitors()->remove(monitor); return true; diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/runtime/globals.hpp --- a/src/hotspot/share/runtime/globals.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/runtime/globals.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -1047,6 +1047,9 @@ diagnostic(bool, EnableThreadSMRStatistics, trueInDebug, \ "Enable Thread SMR Statistics") \ \ + product(bool, UseNotificationThread, true, \ + "Use Notification Thread") \ + \ product(bool, Inline, true, \ "Enable inlining") \ \ diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/runtime/mutexLocker.cpp --- a/src/hotspot/share/runtime/mutexLocker.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/runtime/mutexLocker.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -115,6 +115,7 @@ Mutex* Management_lock = NULL; Monitor* Service_lock = NULL; +Monitor* Notification_lock = NULL; Monitor* PeriodicTask_lock = NULL; Monitor* RedefineClasses_lock = NULL; Mutex* Verify_lock = NULL; @@ -236,6 +237,13 @@ def(Patching_lock , PaddedMutex , special, true, _safepoint_check_never); // used for safepointing and code patching. def(CompiledMethod_lock , PaddedMutex , special-1, true, _safepoint_check_never); def(Service_lock , PaddedMonitor, special, true, _safepoint_check_never); // used for service thread operations + + if (UseNotificationThread) { + def(Notification_lock , PaddedMonitor, special, true, _safepoint_check_never); // used for notification thread operations + } else { + Notification_lock = Service_lock; + } + def(JmethodIdCreation_lock , PaddedMutex , leaf, true, _safepoint_check_always); // used for creating jmethodIDs. def(SystemDictionary_lock , PaddedMonitor, leaf, true, _safepoint_check_always); diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/runtime/mutexLocker.hpp --- a/src/hotspot/share/runtime/mutexLocker.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/runtime/mutexLocker.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -111,6 +111,7 @@ extern Mutex* Management_lock; // a lock used to serialize JVM management extern Monitor* Service_lock; // a lock used for service thread operation +extern Monitor* Notification_lock; // a lock used for notification thread operation extern Monitor* PeriodicTask_lock; // protects the periodic task structure extern Monitor* RedefineClasses_lock; // locks classes from parallel redefinition extern Mutex* Verify_lock; // synchronize initialization of verify library diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/runtime/notificationThread.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/share/runtime/notificationThread.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "memory/universe.hpp" +#include "runtime/interfaceSupport.inline.hpp" +#include "runtime/javaCalls.hpp" +#include "runtime/notificationThread.hpp" +#include "services/diagnosticArgument.hpp" +#include "services/diagnosticFramework.hpp" +#include "services/gcNotifier.hpp" +#include "services/lowMemoryDetector.hpp" + +NotificationThread* NotificationThread::_instance = NULL; + +void NotificationThread::initialize() { + EXCEPTION_MARK; + + const char* name = "Notification Thread"; + Handle string = java_lang_String::create_from_str(name, CHECK); + + // Initialize thread_oop to put it into the system threadGroup + Handle thread_group (THREAD, Universe::system_thread_group()); + Handle thread_oop = JavaCalls::construct_new_instance( + SystemDictionary::Thread_klass(), + vmSymbols::threadgroup_string_void_signature(), + thread_group, + string, + CHECK); + + Klass* group = SystemDictionary::ThreadGroup_klass(); + JavaValue result(T_VOID); + JavaCalls::call_special(&result, + thread_group, + group, + vmSymbols::add_method_name(), + vmSymbols::thread_void_signature(), + thread_oop, + THREAD); + { + MutexLocker mu(Threads_lock); + NotificationThread* thread = new NotificationThread(¬ification_thread_entry); + + // At this point it may be possible that no osthread was created for the + // JavaThread due to lack of memory. We would have to throw an exception + // in that case. However, since this must work and we do not allow + // exceptions anyway, check and abort if this fails. + if (thread == NULL || thread->osthread() == NULL) { + vm_exit_during_initialization("java.lang.OutOfMemoryError", + os::native_thread_creation_failed_msg()); + } + + java_lang_Thread::set_thread(thread_oop(), thread); + java_lang_Thread::set_priority(thread_oop(), NearMaxPriority); + java_lang_Thread::set_daemon(thread_oop()); + thread->set_threadObj(thread_oop()); + _instance = thread; + + Threads::add(thread); + Thread::start(thread); + } +} + + + +void NotificationThread::notification_thread_entry(JavaThread* jt, TRAPS) { + while (true) { + bool sensors_changed = false; + bool has_dcmd_notification_event = false; + bool has_gc_notification_event = false; + { + // Need state transition ThreadBlockInVM so that this thread + // will be handled by safepoint correctly when this thread is + // notified at a safepoint. + + ThreadBlockInVM tbivm(jt); + + MonitorLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag); + // Process all available work on each (outer) iteration, rather than + // only the first recognized bit of work, to avoid frequently true early + // tests from potentially starving later work. Hence the use of + // arithmetic-or to combine results; we don't want short-circuiting. + while (((sensors_changed = LowMemoryDetector::has_pending_requests()) | + (has_dcmd_notification_event = DCmdFactory::has_pending_jmx_notification()) | + (has_gc_notification_event = GCNotifier::has_event())) + == 0) { + // Wait as a suspend equalent until notified that there is some work to do. + ml.wait(0, true); + } + + } + + if (sensors_changed) { + LowMemoryDetector::process_sensor_changes(jt); + } + + if(has_gc_notification_event) { + GCNotifier::sendNotification(CHECK); + } + + if(has_dcmd_notification_event) { + DCmdFactory::send_notification(CHECK); + } + + } +} + diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/runtime/notificationThread.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/share/runtime/notificationThread.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_RUNTIME_NOTIFICATIONTHREAD_HPP +#define SHARE_RUNTIME_NOTIFICATIONTHREAD_HPP + +#include "runtime/thread.hpp" + +// A JavaThread for low memory detection support, GC and +// diagnostic framework notifications. This thread is not hidden +// from the external view to allow the debugger to stop at the +// breakpoints inside registred MXBean notification listeners. + +class NotificationThread : public JavaThread { + friend class VMStructs; + private: + + static NotificationThread* _instance; + + static void notification_thread_entry(JavaThread* thread, TRAPS); + NotificationThread(ThreadFunction entry_point) : JavaThread(entry_point) {}; + + public: + static void initialize(); + +}; + +#endif // SHARE_RUNTIME_NOTIFICATIONTHREAD_HPP diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/runtime/serviceThread.cpp --- a/src/hotspot/share/runtime/serviceThread.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/runtime/serviceThread.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -43,6 +43,7 @@ #include "services/diagnosticFramework.hpp" #include "services/gcNotifier.hpp" #include "services/lowMemoryDetector.hpp" +#include "services/threadIdTable.hpp" ServiceThread* ServiceThread::_instance = NULL; @@ -101,6 +102,7 @@ bool stringtable_work = false; bool symboltable_work = false; bool resolved_method_table_work = false; + bool thread_id_table_work = false; bool protection_domain_table_work = false; bool oopstorage_work = false; JvmtiDeferredEvent jvmti_event; @@ -120,13 +122,14 @@ // only the first recognized bit of work, to avoid frequently true early // tests from potentially starving later work. Hence the use of // arithmetic-or to combine results; we don't want short-circuiting. - while (((sensors_changed = LowMemoryDetector::has_pending_requests()) | + while (((sensors_changed = (!UseNotificationThread && LowMemoryDetector::has_pending_requests())) | (has_jvmti_events = JvmtiDeferredEventQueue::has_events()) | - (has_gc_notification_event = GCNotifier::has_event()) | - (has_dcmd_notification_event = DCmdFactory::has_pending_jmx_notification()) | + (has_gc_notification_event = (!UseNotificationThread && GCNotifier::has_event())) | + (has_dcmd_notification_event = (!UseNotificationThread && DCmdFactory::has_pending_jmx_notification())) | (stringtable_work = StringTable::has_work()) | (symboltable_work = SymbolTable::has_work()) | (resolved_method_table_work = ResolvedMethodTable::has_work()) | + (thread_id_table_work = ThreadIdTable::has_work()) | (protection_domain_table_work = SystemDictionary::pd_cache_table()->has_work()) | (oopstorage_work = OopStorage::has_cleanup_work_and_reset()) ) == 0) { @@ -151,22 +154,28 @@ jvmti_event.post(); } - if (sensors_changed) { - LowMemoryDetector::process_sensor_changes(jt); - } + if (!UseNotificationThread) { + if (sensors_changed) { + LowMemoryDetector::process_sensor_changes(jt); + } - if(has_gc_notification_event) { - GCNotifier::sendNotification(CHECK); - } + if(has_gc_notification_event) { + GCNotifier::sendNotification(CHECK); + } - if(has_dcmd_notification_event) { - DCmdFactory::send_notification(CHECK); + if(has_dcmd_notification_event) { + DCmdFactory::send_notification(CHECK); + } } if (resolved_method_table_work) { ResolvedMethodTable::do_concurrent_work(jt); } + if (thread_id_table_work) { + ThreadIdTable::do_concurrent_work(jt); + } + if (protection_domain_table_work) { SystemDictionary::pd_cache_table()->unlink(); } diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/runtime/serviceThread.hpp --- a/src/hotspot/share/runtime/serviceThread.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/runtime/serviceThread.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -27,8 +27,10 @@ #include "runtime/thread.hpp" -// A JavaThread for low memory detection support and JVMTI -// compiled-method-load events. +// A hidden from external view JavaThread for JVMTI compiled-method-load +// events, oop storage cleanup, and the maintainance of string, symbol, +// protection domain, and resolved method tables. + class ServiceThread : public JavaThread { friend class VMStructs; private: diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/runtime/vmStructs.cpp --- a/src/hotspot/share/runtime/vmStructs.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/runtime/vmStructs.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -88,6 +88,7 @@ #include "runtime/globals.hpp" #include "runtime/java.hpp" #include "runtime/javaCalls.hpp" +#include "runtime/notificationThread.hpp" #include "runtime/os.hpp" #include "runtime/perfMemory.hpp" #include "runtime/serviceThread.hpp" @@ -1366,6 +1367,7 @@ declare_type(JavaThread, Thread) \ declare_type(JvmtiAgentThread, JavaThread) \ declare_type(ServiceThread, JavaThread) \ + declare_type(NotificationThread, JavaThread) \ declare_type(CompilerThread, JavaThread) \ declare_type(CodeCacheSweeperThread, JavaThread) \ declare_toplevel_type(OSThread) \ diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/services/diagnosticFramework.cpp --- a/src/hotspot/share/services/diagnosticFramework.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/services/diagnosticFramework.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -437,9 +437,9 @@ } void DCmdFactory::push_jmx_notification_request() { - MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag); + MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag); _has_pending_jmx_notification = true; - Service_lock->notify_all(); + Notification_lock->notify_all(); } void DCmdFactory::send_notification(TRAPS) { @@ -455,7 +455,7 @@ HandleMark hm(THREAD); bool notif = false; { - MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag); + MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag); notif = _has_pending_jmx_notification; _has_pending_jmx_notification = false; } diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/services/gcNotifier.cpp --- a/src/hotspot/share/services/gcNotifier.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/services/gcNotifier.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -54,18 +54,18 @@ } void GCNotifier::addRequest(GCNotificationRequest *request) { - MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag); + MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag); if(first_request == NULL) { first_request = request; } else { last_request->next = request; } last_request = request; - Service_lock->notify_all(); + Notification_lock->notify_all(); } GCNotificationRequest *GCNotifier::getRequest() { - MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag); + MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag); GCNotificationRequest *request = first_request; if(first_request != NULL) { first_request = first_request->next; diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/services/lowMemoryDetector.cpp --- a/src/hotspot/share/services/lowMemoryDetector.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/services/lowMemoryDetector.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -40,7 +40,7 @@ volatile jint LowMemoryDetector::_disabled_count = 0; bool LowMemoryDetector::has_pending_requests() { - assert(Service_lock->owned_by_self(), "Must own Service_lock"); + assert(Notification_lock->owned_by_self(), "Must own Notification_lock"); bool has_requests = false; int num_memory_pools = MemoryService::num_memory_pools(); for (int i = 0; i < num_memory_pools; i++) { @@ -62,7 +62,7 @@ ResourceMark rm(THREAD); HandleMark hm(THREAD); - // No need to hold Service_lock to call out to Java + // No need to hold Notification_lock to call out to Java int num_memory_pools = MemoryService::num_memory_pools(); for (int i = 0; i < num_memory_pools; i++) { MemoryPool* pool = MemoryService::get_memory_pool(i); @@ -80,7 +80,7 @@ // This method could be called from any Java threads // and also VMThread. void LowMemoryDetector::detect_low_memory() { - MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag); + MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag); bool has_pending_requests = false; int num_memory_pools = MemoryService::num_memory_pools(); @@ -98,7 +98,7 @@ } if (has_pending_requests) { - Service_lock->notify_all(); + Notification_lock->notify_all(); } } @@ -113,14 +113,14 @@ } { - MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag); + MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag); MemoryUsage usage = pool->get_memory_usage(); sensor->set_gauge_sensor_level(usage, pool->usage_threshold()); if (sensor->has_pending_requests()) { // notify sensor state update - Service_lock->notify_all(); + Notification_lock->notify_all(); } } } @@ -135,14 +135,14 @@ } { - MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag); + MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag); MemoryUsage usage = pool->get_last_collection_usage(); sensor->set_counter_sensor_level(usage, pool->gc_usage_threshold()); if (sensor->has_pending_requests()) { // notify sensor state update - Service_lock->notify_all(); + Notification_lock->notify_all(); } } } @@ -205,7 +205,7 @@ // If the current level is between high and low threshold, no change. // void SensorInfo::set_gauge_sensor_level(MemoryUsage usage, ThresholdSupport* high_low_threshold) { - assert(Service_lock->owned_by_self(), "Must own Service_lock"); + assert(Notification_lock->owned_by_self(), "Must own Notification_lock"); assert(high_low_threshold->is_high_threshold_supported(), "just checking"); bool is_over_high = high_low_threshold->is_high_threshold_crossed(usage); @@ -260,7 +260,7 @@ // the sensor will be on (i.e. sensor is currently off // and has pending trigger requests). void SensorInfo::set_counter_sensor_level(MemoryUsage usage, ThresholdSupport* counter_threshold) { - assert(Service_lock->owned_by_self(), "Must own Service_lock"); + assert(Notification_lock->owned_by_self(), "Must own Notification_lock"); assert(counter_threshold->is_high_threshold_supported(), "just checking"); bool is_over_high = counter_threshold->is_high_threshold_crossed(usage); @@ -334,8 +334,8 @@ } { - // Holds Service_lock and update the sensor state - MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag); + // Holds Notification_lock and update the sensor state + MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag); assert(_pending_trigger_count > 0, "Must have pending trigger"); _sensor_on = true; _sensor_count += count; @@ -345,8 +345,8 @@ void SensorInfo::clear(int count, TRAPS) { { - // Holds Service_lock and update the sensor state - MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag); + // Holds Notification_lock and update the sensor state + MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag); if (_pending_clear_count == 0) { // Bail out if we lost a race to set_*_sensor_level() which may have // reactivated the sensor in the meantime because it was triggered again. diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/services/lowMemoryDetector.hpp --- a/src/hotspot/share/services/lowMemoryDetector.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/services/lowMemoryDetector.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -59,7 +59,8 @@ // // May need to deal with hysteresis effect. // -// Memory detection code runs in the Service thread (serviceThread.hpp). +// Memory detection code runs in the Notification thread or +// ServiceThread depending on UseNotificationThread flag. class OopClosure; class MemoryPool; @@ -214,6 +215,7 @@ class LowMemoryDetector : public AllStatic { friend class LowMemoryDetectorDisabler; friend class ServiceThread; + friend class NotificationThread; private: // true if any collected heap has low memory detection enabled static volatile bool _enabled_for_collected_pools; diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/services/management.cpp --- a/src/hotspot/share/services/management.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/services/management.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -44,6 +44,7 @@ #include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaCalls.hpp" #include "runtime/jniHandles.inline.hpp" +#include "runtime/notificationThread.hpp" #include "runtime/os.hpp" #include "runtime/serviceThread.hpp" #include "runtime/thread.inline.hpp" @@ -148,7 +149,9 @@ void Management::initialize(TRAPS) { // Start the service thread ServiceThread::initialize(); - + if (UseNotificationThread) { + NotificationThread::initialize(); + } if (ManagementServer) { ResourceMark rm(THREAD); HandleMark hm(THREAD); diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/services/threadIdTable.cpp --- a/src/hotspot/share/services/threadIdTable.cpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/services/threadIdTable.cpp Wed Oct 09 23:22:56 2019 +0200 @@ -46,6 +46,7 @@ static volatile size_t _items_count = 0; volatile bool ThreadIdTable::_is_initialized = false; +volatile bool ThreadIdTable::_has_work = false; class ThreadIdTableEntry : public CHeapObj { private: @@ -141,6 +142,26 @@ return (size_t)1 << _local_table->get_size_log2(Thread::current()); } +void ThreadIdTable::check_concurrent_work() { + if (_has_work) { + return; + } + + double load_factor = get_load_factor(); + // Resize if we have more items than preferred load factor + if ( load_factor > PREF_AVG_LIST_LEN && !_local_table->is_max_size_reached()) { + log_debug(thread, table)("Concurrent work triggered, load factor: %g", + load_factor); + trigger_concurrent_work(); + } +} + +void ThreadIdTable::trigger_concurrent_work() { + MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag); + _has_work = true; + Service_lock->notify_all(); +} + void ThreadIdTable::grow(JavaThread* jt) { ThreadIdTableHash::GrowTask gt(_local_table); if (!gt.prepare(jt)) { @@ -192,13 +213,13 @@ } }; -void ThreadIdTable::grow_if_required() { - assert(Thread::current()->is_Java_thread(),"Must be Java thread"); +void ThreadIdTable::do_concurrent_work(JavaThread* jt) { assert(_is_initialized, "Thread table is not initialized"); + _has_work = false; double load_factor = get_load_factor(); log_debug(thread, table)("Concurrent work, load factor: %g", load_factor); if (load_factor > PREF_AVG_LIST_LEN && !_local_table->is_max_size_reached()) { - grow(JavaThread::current()); + grow(jt); } } @@ -215,7 +236,7 @@ // The hash table takes ownership of the ThreadTableEntry, // even if it's not inserted. if (_local_table->insert(thread, lookup, entry)) { - grow_if_required(); + check_concurrent_work(); return java_thread; } } diff -r 2a49d43aaa6a -r ec9da3c0eaec src/hotspot/share/services/threadIdTable.hpp --- a/src/hotspot/share/services/threadIdTable.hpp Wed Oct 09 23:21:07 2019 +0200 +++ b/src/hotspot/share/services/threadIdTable.hpp Wed Oct 09 23:22:56 2019 +0200 @@ -36,6 +36,7 @@ friend class ThreadIdTableConfig; static volatile bool _is_initialized; + static volatile bool _has_work; public: // Initialization @@ -47,12 +48,17 @@ static JavaThread* add_thread(jlong tid, JavaThread* thread); static bool remove_thread(jlong tid); + // Growing + static bool has_work() { return _has_work; } + static void do_concurrent_work(JavaThread* jt); + private: static void create_table(size_t size); static size_t table_size(); static double get_load_factor(); - static void grow_if_required(); + static void check_concurrent_work(); + static void trigger_concurrent_work(); static void grow(JavaThread* jt); static void item_added(); diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/java/lang/Throwable.java --- a/src/java.base/share/classes/java/lang/Throwable.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/java/lang/Throwable.java Wed Oct 09 23:22:56 2019 +0200 @@ -230,6 +230,7 @@ * @serial * @since 1.7 */ + @SuppressWarnings("serial") // Not statically typed as Serializable private List suppressedExceptions = SUPPRESSED_SENTINEL; /** Message for trying to suppress a null exception. */ diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/java/lang/invoke/SerializedLambda.java --- a/src/java.base/share/classes/java/lang/invoke/SerializedLambda.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/java/lang/invoke/SerializedLambda.java Wed Oct 09 23:22:56 2019 +0200 @@ -76,6 +76,7 @@ private final String implMethodSignature; private final int implMethodKind; private final String instantiatedMethodType; + @SuppressWarnings("serial") // Not statically typed as Serializable private final Object[] capturedArgs; /** diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/java/lang/reflect/Proxy.java --- a/src/java.base/share/classes/java/lang/reflect/Proxy.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/java/lang/reflect/Proxy.java Wed Oct 09 23:22:56 2019 +0200 @@ -308,6 +308,7 @@ * the invocation handler for this proxy instance. * @serial */ + @SuppressWarnings("serial") // Not statically typed as Serializable protected InvocationHandler h; /** diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/java/net/DatagramSocket.java --- a/src/java.base/share/classes/java/net/DatagramSocket.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/java/net/DatagramSocket.java Wed Oct 09 23:22:56 2019 +0200 @@ -434,14 +434,15 @@ * verify that datagrams are permitted to be sent and received * respectively. * - *

When a socket is connected, {@link #receive receive} and - * {@link #send send} will not perform any security checks - * on incoming and outgoing packets, other than matching the packet's - * and the socket's address and port. On a send operation, if the - * packet's address is set and the packet's address and the socket's - * address do not match, an {@code IllegalArgumentException} will be - * thrown. A socket connected to a multicast address may only be used - * to send packets. + *

Care should be taken to ensure that a connected datagram socket + * is not shared with untrusted code. When a socket is connected, + * {@link #receive receive} and {@link #send send} will not perform + * any security checks on incoming and outgoing packets, other than + * matching the packet's and the socket's address and port. On a send + * operation, if the packet's address is set and the packet's address + * and the socket's address do not match, an {@code IllegalArgumentException} + * will be thrown. A socket connected to a multicast address may only + * be used to send packets. * * @param address the remote address for the socket * @@ -708,9 +709,11 @@ * the length of the received message. If the message is longer than * the packet's length, the message is truncated. *

- * If there is a security manager, a packet cannot be received if the - * security manager's {@code checkAccept} method - * does not allow it. + * If there is a security manager, and the socket is not currently + * connected to a remote address, a packet cannot be received if the + * security manager's {@code checkAccept} method does not allow it. + * Datagrams that are not permitted by the security manager are silently + * discarded. * * @param p the {@code DatagramPacket} into which to place * the incoming data. @@ -896,12 +899,15 @@ * * @param timeout the specified timeout in milliseconds. * @throws SocketException if there is an error in the underlying protocol, such as an UDP error. + * @throws IllegalArgumentException if {@code timeout} is negative * @since 1.1 * @see #getSoTimeout() */ public synchronized void setSoTimeout(int timeout) throws SocketException { if (isClosed()) throw new SocketException("Socket is closed"); + if (timeout < 0) + throw new IllegalArgumentException("timeout < 0"); getImpl().setOption(SocketOptions.SO_TIMEOUT, timeout); } diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/java/nio/channels/DatagramChannel.java --- a/src/java.base/share/classes/java/nio/channels/DatagramChannel.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/java/nio/channels/DatagramChannel.java Wed Oct 09 23:22:56 2019 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2019, 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 @@ -265,7 +265,10 @@ * java.lang.SecurityManager#checkAccept checkAccept} and {@link * java.lang.SecurityManager#checkConnect checkConnect} methods permit * datagrams to be received from and sent to, respectively, the given - * remote address. + * remote address. Once connected, no further security checks are performed + * for datagrams received from, or sent to, the given remote address. Care + * should be taken to ensure that a connected datagram channel is not shared + * with untrusted code. * *

This method may be invoked at any time. It will not have any effect * on read or write operations that are already in progress at the moment @@ -325,6 +328,10 @@ *

If this channel's socket is not connected, or if the channel is * closed, then invoking this method has no effect.

* + * @apiNote If this method throws an IOException, the channel's socket + * may be left in an unspecified state. It is strongly recommended that + * the channel be closed when disconnect fails. + * * @return This datagram channel * * @throws IOException @@ -369,9 +376,10 @@ * to a specific remote address and a security manager has been installed * then for each datagram received this method verifies that the source's * address and port number are permitted by the security manager's {@link - * java.lang.SecurityManager#checkAccept checkAccept} method. The overhead - * of this security check can be avoided by first connecting the socket via - * the {@link #connect connect} method. + * java.lang.SecurityManager#checkAccept checkAccept} method. Datagrams + * that are not permitted by the security manager are silently discarded. + * The overhead of this security check can be avoided by first connecting + * the socket via the {@link #connect connect} method. * *

This method may be invoked at any time. If another thread has * already initiated a read operation upon this channel, however, then an @@ -401,11 +409,6 @@ * closing the channel and setting the current thread's * interrupt status * - * @throws SecurityException - * If a security manager has been installed - * and it does not permit datagrams to be accepted - * from the datagram's sender - * * @throws IOException * If some other I/O error occurs */ diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/java/nio/channels/SelectionKey.java --- a/src/java.base/share/classes/java/nio/channels/SelectionKey.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/java/nio/channels/SelectionKey.java Wed Oct 09 23:22:56 2019 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,8 @@ package java.nio.channels; -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; /** * A token representing the registration of a {@link SelectableChannel} with a @@ -428,13 +429,17 @@ // -- Attachments -- + private static final VarHandle ATTACHMENT; + static { + try { + MethodHandles.Lookup l = MethodHandles.lookup(); + ATTACHMENT = l.findVarHandle(SelectionKey.class, "attachment", Object.class); + } catch (Exception e) { + throw new InternalError(e); + } + } private volatile Object attachment; - private static final AtomicReferenceFieldUpdater - attachmentUpdater = AtomicReferenceFieldUpdater.newUpdater( - SelectionKey.class, Object.class, "attachment" - ); - /** * Attaches the given object to this key. * @@ -450,7 +455,7 @@ * otherwise {@code null} */ public final Object attach(Object ob) { - return attachmentUpdater.getAndSet(this, ob); + return ATTACHMENT.getAndSet(this, ob); } /** diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/java/nio/file/Files.java --- a/src/java.base/share/classes/java/nio/file/Files.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/java/nio/file/Files.java Wed Oct 09 23:22:56 2019 +0200 @@ -3550,8 +3550,8 @@ // ensure lines is not null before opening file Objects.requireNonNull(lines); CharsetEncoder encoder = cs.newEncoder(); - OutputStream out = newOutputStream(path, options); - try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, encoder))) { + try (OutputStream out = newOutputStream(path, options); + BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, encoder))) { for (CharSequence line: lines) { writer.append(line); writer.newLine(); diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/java/security/GuardedObject.java --- a/src/java.base/share/classes/java/security/GuardedObject.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/java/security/GuardedObject.java Wed Oct 09 23:22:56 2019 +0200 @@ -52,7 +52,9 @@ @java.io.Serial private static final long serialVersionUID = -5240450096227834308L; + @SuppressWarnings("serial") // Not statically typed as Serializable private Object object; // the object we are guarding + @SuppressWarnings("serial") // Not statically typed as Serializable private Guard guard; // the guard /** diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/java/security/SecureRandom.java --- a/src/java.base/share/classes/java/security/SecureRandom.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/java/security/SecureRandom.java Wed Oct 09 23:22:56 2019 +0200 @@ -1043,6 +1043,7 @@ /** * @serial */ + @SuppressWarnings("serial") // Not statically typed as Serializable private MessageDigest digest = null; /** * @serial diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/java/time/Clock.java --- a/src/java.base/share/classes/java/time/Clock.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/java/time/Clock.java Wed Oct 09 23:22:56 2019 +0200 @@ -641,6 +641,7 @@ static final class OffsetClock extends Clock implements Serializable { @java.io.Serial private static final long serialVersionUID = 2007484719125426256L; + @SuppressWarnings("serial") // Not statically typed as Serializable private final Clock baseClock; private final Duration offset; @@ -692,6 +693,7 @@ static final class TickClock extends Clock implements Serializable { @java.io.Serial private static final long serialVersionUID = 6504659149906368850L; + @SuppressWarnings("serial") // Not statically typed as Serializable private final Clock baseClock; private final long tickNanos; diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/java/time/chrono/ChronoPeriodImpl.java --- a/src/java.base/share/classes/java/time/chrono/ChronoPeriodImpl.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/java/time/chrono/ChronoPeriodImpl.java Wed Oct 09 23:22:56 2019 +0200 @@ -109,6 +109,7 @@ /** * The chronology. */ + @SuppressWarnings("serial") // Not statically typed as Serializable private final Chronology chrono; /** * The number of years. diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/java/util/AbstractMap.java --- a/src/java.base/share/classes/java/util/AbstractMap.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/java/util/AbstractMap.java Wed Oct 09 23:22:56 2019 +0200 @@ -607,7 +607,9 @@ @java.io.Serial private static final long serialVersionUID = -8499721149061103585L; + @SuppressWarnings("serial") // Conditionally serializable private final K key; + @SuppressWarnings("serial") // Conditionally serializable private V value; /** @@ -738,7 +740,9 @@ @java.io.Serial private static final long serialVersionUID = 7138329143949025153L; + @SuppressWarnings("serial") // Not statically typed as Serializable private final K key; + @SuppressWarnings("serial") // Not statically typed as Serializable private final V value; /** diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/java/util/ArrayPrefixHelpers.java --- a/src/java.base/share/classes/java/util/ArrayPrefixHelpers.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/java/util/ArrayPrefixHelpers.java Wed Oct 09 23:22:56 2019 +0200 @@ -103,10 +103,15 @@ static final int MIN_PARTITION = 16; static final class CumulateTask extends CountedCompleter { + @SuppressWarnings("serial") // Not statically typed as Serializable final T[] array; + @SuppressWarnings("serial") // Not statically typed as Serializable final BinaryOperator function; CumulateTask left, right; - T in, out; + @SuppressWarnings("serial") // Not statically typed as Serializable + T in; + @SuppressWarnings("serial") // Not statically typed as Serializable + T out; final int lo, hi, origin, fence, threshold; /** Root task constructor */ @@ -257,6 +262,7 @@ static final class LongCumulateTask extends CountedCompleter { final long[] array; + @SuppressWarnings("serial") // Not statically typed as Serializable final LongBinaryOperator function; LongCumulateTask left, right; long in, out; @@ -408,6 +414,7 @@ static final class DoubleCumulateTask extends CountedCompleter { final double[] array; + @SuppressWarnings("serial") // Not statically typed as Serializable final DoubleBinaryOperator function; DoubleCumulateTask left, right; double in, out; @@ -559,6 +566,7 @@ static final class IntCumulateTask extends CountedCompleter { final int[] array; + @SuppressWarnings("serial") // Not statically typed as Serializable final IntBinaryOperator function; IntCumulateTask left, right; int in, out; diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/java/util/Arrays.java --- a/src/java.base/share/classes/java/util/Arrays.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/java/util/Arrays.java Wed Oct 09 23:22:56 2019 +0200 @@ -4339,6 +4339,7 @@ { @java.io.Serial private static final long serialVersionUID = -2764017481108945198L; + @SuppressWarnings("serial") // Conditionally serializable private final E[] a; ArrayList(E[] array) { diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/java/util/ArraysParallelSortHelpers.java --- a/src/java.base/share/classes/java/util/ArraysParallelSortHelpers.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/java/util/ArraysParallelSortHelpers.java Wed Oct 09 23:22:56 2019 +0200 @@ -115,8 +115,12 @@ static final class Sorter extends CountedCompleter { @java.io.Serial static final long serialVersionUID = 2446542900576103244L; - final T[] a, w; + @SuppressWarnings("serial") // Not statically typed as Serializable + final T[] a; + @SuppressWarnings("serial") // Not statically typed as Serializable + final T[] w; final int base, size, wbase, gran; + @SuppressWarnings("serial") // Not statically typed as Serializable Comparator comparator; Sorter(CountedCompleter par, T[] a, T[] w, int base, int size, int wbase, int gran, @@ -153,8 +157,13 @@ static final class Merger extends CountedCompleter { @java.io.Serial static final long serialVersionUID = 2446542900576103244L; - final T[] a, w; // main and workspace arrays + // main and workspace arrays + @SuppressWarnings("serial") // Not statically typed as Serializable + final T[] a; + @SuppressWarnings("serial") // Not statically typed as Serializable + final T[] w; final int lbase, lsize, rbase, rsize, wbase, gran; + @SuppressWarnings("serial") // Not statically typed as Serializable Comparator comparator; Merger(CountedCompleter par, T[] a, T[] w, int lbase, int lsize, int rbase, diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/java/util/Collections.java --- a/src/java.base/share/classes/java/util/Collections.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/java/util/Collections.java Wed Oct 09 23:22:56 2019 +0200 @@ -1024,6 +1024,7 @@ @java.io.Serial private static final long serialVersionUID = 1820017752578914078L; + @SuppressWarnings("serial") // Conditionally serializable final Collection c; UnmodifiableCollection(Collection c) { @@ -1164,6 +1165,7 @@ implements SortedSet, Serializable { @java.io.Serial private static final long serialVersionUID = -4929149591599911165L; + @SuppressWarnings("serial") // Conditionally serializable private final SortedSet ss; UnmodifiableSortedSet(SortedSet s) {super(s); ss = s;} @@ -1244,6 +1246,7 @@ /** * The instance we are protecting. */ + @SuppressWarnings("serial") // Conditionally serializable private final NavigableSet ns; UnmodifiableNavigableSet(NavigableSet s) {super(s); ns = s;} @@ -1304,6 +1307,7 @@ @java.io.Serial private static final long serialVersionUID = -283967356065247728L; + @SuppressWarnings("serial") // Conditionally serializable final List list; UnmodifiableList(List list) { @@ -1450,6 +1454,7 @@ @java.io.Serial private static final long serialVersionUID = -1034234728574286014L; + @SuppressWarnings("serial") // Conditionally serializable private final Map m; UnmodifiableMap(Map m) { @@ -1809,6 +1814,7 @@ @java.io.Serial private static final long serialVersionUID = -8806743815996713206L; + @SuppressWarnings("serial") // Conditionally serializable private final SortedMap sm; UnmodifiableSortedMap(SortedMap m) {super(m); sm = m; } @@ -1886,6 +1892,7 @@ /** * The instance we wrap and protect. */ + @SuppressWarnings("serial") // Conditionally serializable private final NavigableMap nm; UnmodifiableNavigableMap(NavigableMap m) @@ -2017,7 +2024,9 @@ @java.io.Serial private static final long serialVersionUID = 3053995032091335093L; + @SuppressWarnings("serial") // Conditionally serializable final Collection c; // Backing Collection + @SuppressWarnings("serial") // Conditionally serializable final Object mutex; // Object on which to synchronize SynchronizedCollection(Collection c) { @@ -2219,6 +2228,7 @@ @java.io.Serial private static final long serialVersionUID = 8695801310862127406L; + @SuppressWarnings("serial") // Conditionally serializable private final SortedSet ss; SynchronizedSortedSet(SortedSet s) { @@ -2314,6 +2324,7 @@ @java.io.Serial private static final long serialVersionUID = -5505529816273629798L; + @SuppressWarnings("serial") // Conditionally serializable private final NavigableSet ns; SynchronizedNavigableSet(NavigableSet s) { @@ -2424,6 +2435,7 @@ @java.io.Serial private static final long serialVersionUID = -7754090372962971524L; + @SuppressWarnings("serial") // Conditionally serializable final List list; SynchronizedList(List list) { @@ -2591,7 +2603,9 @@ @java.io.Serial private static final long serialVersionUID = 1978198479659022715L; + @SuppressWarnings("serial") // Conditionally serializable private final Map m; // Backing Map + @SuppressWarnings("serial") // Conditionally serializable final Object mutex; // Object on which to synchronize SynchronizedMap(Map m) { @@ -2788,6 +2802,7 @@ @java.io.Serial private static final long serialVersionUID = -8798146769416483793L; + @SuppressWarnings("serial") // Conditionally serializable private final SortedMap sm; SynchronizedSortedMap(SortedMap m) { @@ -2891,6 +2906,7 @@ @java.io.Serial private static final long serialVersionUID = 699392247599746807L; + @SuppressWarnings("serial") // Conditionally serializable private final NavigableMap nm; SynchronizedNavigableMap(NavigableMap m) { @@ -3070,7 +3086,9 @@ @java.io.Serial private static final long serialVersionUID = 1578914078182001775L; + @SuppressWarnings("serial") // Conditionally serializable final Collection c; + @SuppressWarnings("serial") // Conditionally serializable final Class type; @SuppressWarnings("unchecked") @@ -3126,6 +3144,7 @@ public boolean add(E e) { return c.add(typeCheck(e)); } + @SuppressWarnings("serial") // Conditionally serializable private E[] zeroLengthElementArray; // Lazily initialized private E[] zeroLengthElementArray() { @@ -3219,6 +3238,7 @@ { @java.io.Serial private static final long serialVersionUID = 1433151992604707767L; + @SuppressWarnings("serial") // Conditionally serializable final Queue queue; CheckedQueue(Queue queue, Class elementType) { @@ -3323,6 +3343,7 @@ @java.io.Serial private static final long serialVersionUID = 1599911165492914959L; + @SuppressWarnings("serial") // Conditionally serializable private final SortedSet ss; CheckedSortedSet(SortedSet s, Class type) { @@ -3387,6 +3408,7 @@ @java.io.Serial private static final long serialVersionUID = -5429120189805438922L; + @SuppressWarnings("serial") // Conditionally serializable private final NavigableSet ns; CheckedNavigableSet(NavigableSet s, Class type) { @@ -3470,6 +3492,7 @@ { @java.io.Serial private static final long serialVersionUID = 65247728283967356L; + @SuppressWarnings("serial") // Conditionally serializable final List list; CheckedList(List list, Class type) { @@ -3619,8 +3642,11 @@ @java.io.Serial private static final long serialVersionUID = 5742860141034234728L; + @SuppressWarnings("serial") // Conditionally serializable private final Map m; + @SuppressWarnings("serial") // Conditionally serializable final Class keyType; + @SuppressWarnings("serial") // Conditionally serializable final Class valueType; private void typeCheck(Object key, Object value) { @@ -4019,6 +4045,7 @@ @java.io.Serial private static final long serialVersionUID = 1599671320688067438L; + @SuppressWarnings("serial") // Conditionally serializable private final SortedMap sm; CheckedSortedMap(SortedMap m, @@ -4094,6 +4121,7 @@ @java.io.Serial private static final long serialVersionUID = -4852462692372534096L; + @SuppressWarnings("serial") // Conditionally serializable private final NavigableMap nm; CheckedNavigableMap(NavigableMap m, @@ -4825,6 +4853,7 @@ @java.io.Serial private static final long serialVersionUID = 3193687207550431679L; + @SuppressWarnings("serial") // Conditionally serializable private final E element; SingletonSet(E e) {element = e;} @@ -4879,6 +4908,7 @@ @java.io.Serial private static final long serialVersionUID = 3093736618740652951L; + @SuppressWarnings("serial") // Conditionally serializable private final E element; SingletonList(E obj) {element = obj;} @@ -4948,7 +4978,9 @@ @java.io.Serial private static final long serialVersionUID = -6979724477215052911L; + @SuppressWarnings("serial") // Conditionally serializable private final K k; + @SuppressWarnings("serial") // Conditionally serializable private final V v; SingletonMap(K key, V value) { @@ -5087,6 +5119,7 @@ private static final long serialVersionUID = 2739099268398711800L; final int n; + @SuppressWarnings("serial") // Conditionally serializable final E element; CopiesList(int n, E e) { @@ -5320,6 +5353,7 @@ * * @serial */ + @SuppressWarnings("serial") // Conditionally serializable final Comparator cmp; ReverseComparator2(Comparator cmp) { @@ -5601,6 +5635,7 @@ private static class SetFromMap extends AbstractSet implements Set, Serializable { + @SuppressWarnings("serial") // Conditionally serializable private final Map m; // The backing map private transient Set s; // Its keySet @@ -5686,6 +5721,7 @@ implements Queue, Serializable { @java.io.Serial private static final long serialVersionUID = 1802017725587941708L; + @SuppressWarnings("serial") // Conditionally serializable private final Deque q; AsLIFOQueue(Deque q) { this.q = q; } public boolean add(E e) { q.addFirst(e); return true; } diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/java/util/Comparators.java --- a/src/java.base/share/classes/java/util/Comparators.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/java/util/Comparators.java Wed Oct 09 23:22:56 2019 +0200 @@ -66,6 +66,7 @@ private static final long serialVersionUID = -7569533591570686392L; private final boolean nullFirst; // if null, non-null Ts are considered equal + @SuppressWarnings("serial") // Not statically typed as Serializable private final Comparator real; @SuppressWarnings("unchecked") diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/java/util/PriorityQueue.java --- a/src/java.base/share/classes/java/util/PriorityQueue.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/java/util/PriorityQueue.java Wed Oct 09 23:22:56 2019 +0200 @@ -111,6 +111,7 @@ * The comparator, or null if priority queue uses elements' * natural ordering. */ + @SuppressWarnings("serial") // Conditionally serializable private final Comparator comparator; /** diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/java/util/TreeMap.java --- a/src/java.base/share/classes/java/util/TreeMap.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/java/util/TreeMap.java Wed Oct 09 23:22:56 2019 +0200 @@ -118,6 +118,7 @@ * * @serial */ + @SuppressWarnings("serial") // Conditionally serializable private final Comparator comparator; private transient Entry root; @@ -1353,7 +1354,10 @@ * if loInclusive is true, lo is the inclusive bound, else lo * is the exclusive bound. Similarly for the upper bound. */ - final K lo, hi; + @SuppressWarnings("serial") // Conditionally serializable + final K lo; + @SuppressWarnings("serial") // Conditionally serializable + final K hi; final boolean fromStart, toEnd; final boolean loInclusive, hiInclusive; @@ -1936,6 +1940,7 @@ super(m, fromStart, lo, loInclusive, toEnd, hi, hiInclusive); } + @SuppressWarnings("serial") // Conditionally serializable private final Comparator reverseComparator = Collections.reverseOrder(m.comparator); @@ -2024,7 +2029,10 @@ @java.io.Serial private static final long serialVersionUID = -6520786458950516097L; private boolean fromStart = false, toEnd = false; - private K fromKey, toKey; + @SuppressWarnings("serial") // Conditionally serializable + private K fromKey; + @SuppressWarnings("serial") // Conditionally serializable + private K toKey; @java.io.Serial private Object readResolve() { return new AscendingSubMap<>(TreeMap.this, diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/java/util/Vector.java --- a/src/java.base/share/classes/java/util/Vector.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/java/util/Vector.java Wed Oct 09 23:22:56 2019 +0200 @@ -102,6 +102,7 @@ * * @serial */ + @SuppressWarnings("serial") // Conditionally serializable protected Object[] elementData; /** diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/java/util/jar/JarVerifier.java --- a/src/java.base/share/classes/java/util/jar/JarVerifier.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/java/util/jar/JarVerifier.java Wed Oct 09 23:22:56 2019 +0200 @@ -590,6 +590,7 @@ URL vlocation; CodeSigner[] vsigners; java.security.cert.Certificate[] vcerts; + @SuppressWarnings("serial") // Not statically typed as Serializable Object csdomain; VerifierCodeSource(Object csdomain, URL location, CodeSigner[] signers) { diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/javax/crypto/CryptoPermission.java --- a/src/java.base/share/classes/javax/crypto/CryptoPermission.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/javax/crypto/CryptoPermission.java Wed Oct 09 23:22:56 2019 +0200 @@ -55,6 +55,7 @@ private String alg; private int maxKeySize = Integer.MAX_VALUE; // no restriction on maxKeySize private String exemptionMechanism = null; + @SuppressWarnings("serial") // Not statically typed as Serializable private AlgorithmParameterSpec algParamSpec = null; private boolean checkParam = false; // no restriction on param diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/javax/security/auth/PrivateCredentialPermission.java --- a/src/java.base/share/classes/javax/security/auth/PrivateCredentialPermission.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/javax/security/auth/PrivateCredentialPermission.java Wed Oct 09 23:22:56 2019 +0200 @@ -119,6 +119,7 @@ * The set contains elements of type, * {@code PrivateCredentialPermission.CredOwner}. */ + @SuppressWarnings("serial") // Not statically typed as Serializable private Set principals; // ignored - kept around for compatibility private transient CredOwner[] credOwners; diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/javax/security/auth/Subject.java --- a/src/java.base/share/classes/javax/security/auth/Subject.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/javax/security/auth/Subject.java Wed Oct 09 23:22:56 2019 +0200 @@ -111,6 +111,7 @@ * {@code java.security.Principal}. * The set is a {@code Subject.SecureSet}. */ + @SuppressWarnings("serial") // Not statically typed as Serializable Set principals; /** diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/javax/security/auth/callback/UnsupportedCallbackException.java --- a/src/java.base/share/classes/javax/security/auth/callback/UnsupportedCallbackException.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/javax/security/auth/callback/UnsupportedCallbackException.java Wed Oct 09 23:22:56 2019 +0200 @@ -39,6 +39,7 @@ /** * @serial */ + @SuppressWarnings("serial") // Not statically typed as Serializable private Callback callback; /** diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java --- a/src/java.base/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java Wed Oct 09 23:22:56 2019 +0200 @@ -49,6 +49,7 @@ private static final long serialVersionUID = 100L; private static final PlatformLogger logger = HttpURLConnection.getHttpLogger(); + @SuppressWarnings("serial") // Not statically typed as Serializable private final HttpCallerInfo hci; // These maps are used to manage the GSS availability for diffrent @@ -67,6 +68,7 @@ } // The HTTP Negotiate Helper + @SuppressWarnings("serial") // Not statically typed as Serializable private Negotiator negotiator = null; /** diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java --- a/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java Wed Oct 09 23:22:56 2019 +0200 @@ -875,6 +875,11 @@ if (state == ST_CONNECTED) throw new AlreadyConnectedException(); + // ensure that the socket is bound + if (localAddress == null) { + bindInternal(null); + } + int n = Net.connect(family, fd, isa.getAddress(), @@ -932,8 +937,21 @@ remoteAddress = null; state = ST_UNCONNECTED; - // refresh local address - localAddress = Net.localAddress(fd); + // check whether rebind is needed + InetSocketAddress isa = Net.localAddress(fd); + if (isa.getPort() == 0) { + // On Linux, if bound to ephemeral port, + // disconnect does not preserve that port. + // In this case, try to rebind to the previous port. + int port = localAddress.getPort(); + localAddress = isa; // in case Net.bind fails + Net.bind(family, fd, isa.getAddress(), port); + isa = Net.localAddress(fd); // refresh address + assert isa.getPort() == port; + } + + // refresh localAddress + localAddress = isa; } } finally { writeLock.unlock(); diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/sun/nio/ch/SelectorImpl.java --- a/src/java.base/share/classes/sun/nio/ch/SelectorImpl.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/sun/nio/ch/SelectorImpl.java Wed Oct 09 23:22:56 2019 +0200 @@ -208,7 +208,8 @@ if (!(ch instanceof SelChImpl)) throw new IllegalSelectorException(); SelectionKeyImpl k = new SelectionKeyImpl((SelChImpl)ch, this); - k.attach(attachment); + if (attachment != null) + k.attach(attachment); // register (if needed) before adding to key set implRegister(k); diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java --- a/src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java Wed Oct 09 23:22:56 2019 +0200 @@ -44,6 +44,7 @@ @java.io.Serial private static final long serialVersionUID = 6182022883658399397L; private final Class type; + @SuppressWarnings("serial") // Not statically typed as Serializable private final Map memberValues; AnnotationInvocationHandler(Class type, Map memberValues) { diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/sun/reflect/annotation/AnnotationTypeMismatchExceptionProxy.java --- a/src/java.base/share/classes/sun/reflect/annotation/AnnotationTypeMismatchExceptionProxy.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/sun/reflect/annotation/AnnotationTypeMismatchExceptionProxy.java Wed Oct 09 23:22:56 2019 +0200 @@ -36,7 +36,8 @@ class AnnotationTypeMismatchExceptionProxy extends ExceptionProxy { @java.io.Serial private static final long serialVersionUID = 7844069490309503934L; - private Method member; + @SuppressWarnings("serial") // Not statically typed as Serializable + private Method member; // Would be more robust to null-out in a writeObject method. private final String foundType; /** diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/sun/security/internal/spec/TlsKeyMaterialSpec.java --- a/src/java.base/share/classes/sun/security/internal/spec/TlsKeyMaterialSpec.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/sun/security/internal/spec/TlsKeyMaterialSpec.java Wed Oct 09 23:22:56 2019 +0200 @@ -50,7 +50,11 @@ private final SecretKey clientMacKey, serverMacKey; private final SecretKey clientCipherKey, serverCipherKey; - private final IvParameterSpec clientIv, serverIv; + + @SuppressWarnings("serial") // Not statically typed as Serializable + private final IvParameterSpec clientIv; + @SuppressWarnings("serial") // Not statically typed as Serializable + private final IvParameterSpec serverIv; /** * Constructs a new TlsKeymaterialSpec from the client and server MAC diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/sun/security/provider/PolicyParser.java --- a/src/java.base/share/classes/sun/security/provider/PolicyParser.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/sun/security/provider/PolicyParser.java Wed Oct 09 23:22:56 2019 +0200 @@ -1315,7 +1315,9 @@ private static final long serialVersionUID = -4330692689482574072L; private String i18nMessage; + @SuppressWarnings("serial") // Not statically typed as Serializable private LocalizedMessage localizedMsg; + @SuppressWarnings("serial") // Not statically typed as Serializable private Object[] source; /** diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/sun/security/provider/SubjectCodeSource.java --- a/src/java.base/share/classes/sun/security/provider/SubjectCodeSource.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/sun/security/provider/SubjectCodeSource.java Wed Oct 09 23:22:56 2019 +0200 @@ -54,6 +54,7 @@ private static final Class[] PARAMS = { String.class }; private static final sun.security.util.Debug debug = sun.security.util.Debug.getInstance("auth", "\t[Auth Access]"); + @SuppressWarnings("serial") // Not statically typed as Serializable private ClassLoader sysClassLoader; /** diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/sun/security/provider/certpath/X509CertPath.java --- a/src/java.base/share/classes/sun/security/provider/certpath/X509CertPath.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/sun/security/provider/certpath/X509CertPath.java Wed Oct 09 23:22:56 2019 +0200 @@ -69,6 +69,7 @@ /** * List of certificates in this chain */ + @SuppressWarnings("serial") // Not statically typed as Serializable private List certs; /** diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java --- a/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java Wed Oct 09 23:22:56 2019 +0200 @@ -70,6 +70,7 @@ // Optional parameters associated with this RSA key // specified in the encoding of its AlgorithmId. // Must be null for "RSA" keys. + @SuppressWarnings("serial") // Not statically typed as Serializable private AlgorithmParameterSpec keyParams; /** diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/sun/security/rsa/RSAPrivateKeyImpl.java --- a/src/java.base/share/classes/sun/security/rsa/RSAPrivateKeyImpl.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/sun/security/rsa/RSAPrivateKeyImpl.java Wed Oct 09 23:22:56 2019 +0200 @@ -61,6 +61,7 @@ // optional parameters associated with this RSA key // specified in the encoding of its AlgorithmId. // must be null for "RSA" keys. + @SuppressWarnings("serial") // Not statically typed as Serializable private final AlgorithmParameterSpec keyParams; /** diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/sun/security/rsa/RSAPublicKeyImpl.java --- a/src/java.base/share/classes/sun/security/rsa/RSAPublicKeyImpl.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/sun/security/rsa/RSAPublicKeyImpl.java Wed Oct 09 23:22:56 2019 +0200 @@ -62,6 +62,7 @@ // optional parameters associated with this RSA key // specified in the encoding of its AlgorithmId // must be null for "RSA" keys. + @SuppressWarnings("serial") // Not statically typed as Serializable private AlgorithmParameterSpec keyParams; /** diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/sun/security/util/ObjectIdentifier.java --- a/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java Wed Oct 09 23:22:56 2019 +0200 @@ -98,6 +98,7 @@ * Changed to Object * @serial */ + @SuppressWarnings("serial") // Not statically typed as Serializable private Object components = null; // path from root /** * @serial diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/sun/security/validator/ValidatorException.java --- a/src/java.base/share/classes/sun/security/validator/ValidatorException.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/sun/security/validator/ValidatorException.java Wed Oct 09 23:22:56 2019 +0200 @@ -62,6 +62,7 @@ public static final Object T_UNTRUSTED_CERT = "Untrusted certificate"; + @SuppressWarnings("serial") // Not statically typed as Serializable private Object type; private X509Certificate cert; diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/sun/security/x509/AlgorithmId.java --- a/src/java.base/share/classes/sun/security/x509/AlgorithmId.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/sun/security/x509/AlgorithmId.java Wed Oct 09 23:22:56 2019 +0200 @@ -72,6 +72,7 @@ private ObjectIdentifier algid; // The (parsed) parameters + @SuppressWarnings("serial") // Not statically typed as Serializable private AlgorithmParameters algParams; private boolean constructedFromDer = true; @@ -80,6 +81,7 @@ * DER-encoded form; subclasses can be made to automaticaly parse * them so there is fast access to these parameters. */ + @SuppressWarnings("serial") // Not statically typed as Serializable protected DerValue params; diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/sun/security/x509/X509CertImpl.java --- a/src/java.base/share/classes/sun/security/x509/X509CertImpl.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/sun/security/x509/X509CertImpl.java Wed Oct 09 23:22:56 2019 +0200 @@ -70,6 +70,7 @@ * @author Hemma Prafullchandra * @see X509CertInfo */ +@SuppressWarnings("serial") // See writeReplace method in Certificate public class X509CertImpl extends X509Certificate implements DerEncoder { @java.io.Serial diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/classes/sun/security/x509/X509Key.java --- a/src/java.base/share/classes/sun/security/x509/X509Key.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/classes/sun/security/x509/X509Key.java Wed Oct 09 23:22:56 2019 +0200 @@ -84,7 +84,7 @@ private int unusedBits = 0; /* BitArray form of key */ - private BitArray bitStringKey = null; + private transient BitArray bitStringKey = null; /* The encoding for the key. */ protected byte[] encodedKey; diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.base/share/native/libzip/Deflater.c --- a/src/java.base/share/native/libzip/Deflater.c Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.base/share/native/libzip/Deflater.c Wed Oct 09 23:22:56 2019 +0200 @@ -257,7 +257,7 @@ res = doDeflate(env, addr, input, inputLen, output + outputOff, outputLen, flush, params); - (*env)->ReleasePrimitiveArrayCritical(env, outputArray, input, 0); + (*env)->ReleasePrimitiveArrayCritical(env, outputArray, output, 0); retVal = checkDeflateStatus(env, addr, inputLen, outputLen, params, res); return retVal; diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java Wed Oct 09 23:22:56 2019 +0200 @@ -1391,6 +1391,7 @@ static class KerberosSessionKey implements Key { private static final long serialVersionUID = 699307378954123869L; + @SuppressWarnings("serial") // Not statically typed as Serializable private final EncryptionKey key; KerberosSessionKey(EncryptionKey key) { diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java Wed Oct 09 23:22:56 2019 +0200 @@ -53,7 +53,9 @@ private static final long serialVersionUID = 7723415700837898232L; + @SuppressWarnings("serial") // Not statically typed as Serializable private Krb5NameElement name; + @SuppressWarnings("serial") // Not statically typed as Serializable private Credentials krb5Credentials; private Krb5InitCredential(Krb5NameElement name, diff -r 2a49d43aaa6a -r ec9da3c0eaec src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java --- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java Wed Oct 09 23:22:56 2019 +0200 @@ -79,7 +79,9 @@ * * http://www.ietf.org/rfc/rfc4120.txt. */ - +// The instance fields not statically typed as Serializable are ASN.1 +// encoded and written by the writeObject method. +@SuppressWarnings("serial") public class KRBError implements java.io.Serializable { static final long serialVersionUID = 3643809337475284503L; diff -r 2a49d43aaa6a -r ec9da3c0eaec src/jdk.compiler/share/classes/com/sun/tools/javac/code/Scope.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Scope.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Scope.java Wed Oct 09 23:22:56 2019 +0200 @@ -389,7 +389,7 @@ Entry e = table[hash]; Assert.check(e == elems, elems.sym); table[hash] = elems.shadowed; - elems = elems.sibling; + elems = elems.nextSibling; } Assert.check(next.shared > 0); next.shared--; @@ -466,15 +466,15 @@ } // remove e from elems and sibling list - te = elems; - if (te == e) - elems = e.sibling; - else while (true) { - if (te.sibling == e) { - te.sibling = e.sibling; - break; - } - te = te.sibling; + if (elems == e) { + elems = e.nextSibling; + if (elems != null) + elems.prevSibling = null; + } else { + Assert.check(e.prevSibling != null, e.sym); + e.prevSibling.nextSibling = e.nextSibling; + if (e.nextSibling != null) + e.nextSibling.prevSibling = e.prevSibling; } removeCount++; @@ -597,7 +597,7 @@ private Symbol doNext() { Symbol sym = (currEntry == null ? null : currEntry.sym); if (currEntry != null) { - currEntry = currEntry.sibling; + currEntry = currEntry.nextSibling; } update(); return sym; @@ -617,7 +617,7 @@ void skipToNextMatchingEntry() { while (currEntry != null && sf != null && !sf.accepts(currEntry.sym)) { - currEntry = currEntry.sibling; + currEntry = currEntry.nextSibling; } } }; @@ -677,7 +677,7 @@ result.append("Scope["); for (ScopeImpl s = this; s != null ; s = s.next) { if (s != this) result.append(" | "); - for (Entry e = s.elems; e != null; e = e.sibling) { + for (Entry e = s.elems; e != null; e = e.nextSibling) { if (e != s.elems) result.append(", "); result.append(e.sym); } @@ -702,18 +702,24 @@ /** Next entry in same scope. */ - public Entry sibling; + public Entry nextSibling; + + /** Prev entry in same scope. + */ + public Entry prevSibling; /** The entry's scope. * scope == null iff this == sentinel */ public ScopeImpl scope; - public Entry(Symbol sym, Entry shadowed, Entry sibling, ScopeImpl scope) { + public Entry(Symbol sym, Entry shadowed, Entry nextSibling, ScopeImpl scope) { this.sym = sym; this.shadowed = shadowed; - this.sibling = sibling; + this.nextSibling = nextSibling; this.scope = scope; + if (nextSibling != null) + nextSibling.prevSibling = this; } /** Return next entry with the same name as this entry, proceeding diff -r 2a49d43aaa6a -r ec9da3c0eaec src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java Wed Oct 09 23:22:56 2019 +0200 @@ -3745,12 +3745,9 @@ return cl1; } else if (shouldSkip.test(cl1.head, cl2.head)) { return union(cl1.tail, cl2.tail, shouldSkip).prepend(cl1.head); - } else if (cl1.head.tsym.precedes(cl2.head.tsym, this)) { - return union(cl1.tail, cl2, shouldSkip).prepend(cl1.head); } else if (cl2.head.tsym.precedes(cl1.head.tsym, this)) { return union(cl1, cl2.tail, shouldSkip).prepend(cl2.head); } else { - // unrelated types return union(cl1.tail, cl2, shouldSkip).prepend(cl1.head); } } diff -r 2a49d43aaa6a -r ec9da3c0eaec src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/NotificationThread.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/NotificationThread.java Wed Oct 09 23:22:56 2019 +0200 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package sun.jvm.hotspot.runtime; + + +import sun.jvm.hotspot.debugger.Address; + +public class NotificationThread extends JavaThread { + public NotificationThread(Address addr) { + super(addr); + } + + public boolean isJavaThread() { return false; } + +} diff -r 2a49d43aaa6a -r ec9da3c0eaec src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java Wed Oct 09 23:22:56 2019 +0200 @@ -158,6 +158,7 @@ } virtualConstructor.addMapping("JvmtiAgentThread", JvmtiAgentThread.class); virtualConstructor.addMapping("ServiceThread", ServiceThread.class); + virtualConstructor.addMapping("NotificationThread", NotificationThread.class); } public Threads() { @@ -165,14 +166,14 @@ } /** NOTE: this returns objects of type JavaThread, CompilerThread, - JvmtiAgentThread, and ServiceThread. + JvmtiAgentThread, NotificationThread, and ServiceThread. The latter four are subclasses of the former. Most operations (fetching the top frame, etc.) are only allowed to be performed on a "pure" JavaThread. For this reason, {@link sun.jvm.hotspot.runtime.JavaThread#isJavaThread} has been changed from the definition in the VM (which returns true for all of these thread types) to return true for JavaThreads and - false for the three subclasses. FIXME: should reconsider the + false for the four subclasses. FIXME: should reconsider the inheritance hierarchy; see {@link sun.jvm.hotspot.runtime.JavaThread#isJavaThread}. */ public JavaThread getJavaThreadAt(int i) { diff -r 2a49d43aaa6a -r ec9da3c0eaec src/jdk.httpserver/share/classes/com/sun/net/httpserver/spi/HttpServerProvider.java --- a/src/jdk.httpserver/share/classes/com/sun/net/httpserver/spi/HttpServerProvider.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/jdk.httpserver/share/classes/com/sun/net/httpserver/spi/HttpServerProvider.java Wed Oct 09 23:22:56 2019 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2019, 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 @@ -133,8 +133,9 @@ *

    * *
  1. If the system property - * {@code com.sun.net.httpserver.HttpServerProvider} is defined then it - * is taken to be the fully-qualified name of a concrete provider class. + * {@systemProperty com.sun.net.httpserver.HttpServerProvider} + * is defined then it is taken to be the fully-qualified name + * of a concrete provider class. * The class is loaded and instantiated; if this process fails then an * unspecified unchecked error or exception is thrown.

  2. * diff -r 2a49d43aaa6a -r ec9da3c0eaec src/jdk.jcmd/share/classes/sun/tools/common/ProcessArgumentMatcher.java --- a/src/jdk.jcmd/share/classes/sun/tools/common/ProcessArgumentMatcher.java Wed Oct 09 23:21:07 2019 +0200 +++ b/src/jdk.jcmd/share/classes/sun/tools/common/ProcessArgumentMatcher.java Wed Oct 09 23:22:56 2019 +0200 @@ -140,18 +140,14 @@ return vids; } - public Collection getVirtualMachineDescriptors(Class excludeClass) { + public Collection getVirtualMachineDescriptors() { if (singlePid != null) { return getSingleVMD(singlePid); } else { - return getVMDs(excludeClass, matchClass); + return getVMDs(null, matchClass); } } - public Collection getVirtualMachineDescriptors() { - return this.getVirtualMachineDescriptors(null); - } - public Collection getVirtualMachinePids(Class excludeClass) { if (singlePid != null) { // There is a bug in AttachProvider, when VM is debuggee-suspended it's not listed by the AttachProvider. @@ -162,7 +158,4 @@ } } - public Collection getVirtualMachinePids() { - return this.getVirtualMachinePids(null); - } } diff -r 2a49d43aaa6a -r ec9da3c0eaec test/hotspot/jtreg/ProblemList-graal.txt --- a/test/hotspot/jtreg/ProblemList-graal.txt Wed Oct 09 23:21:07 2019 +0200 +++ b/test/hotspot/jtreg/ProblemList-graal.txt Wed Oct 09 23:22:56 2019 +0200 @@ -236,7 +236,6 @@ runtime/exceptionMsgs/AbstractMethodError/AbstractMethodErrorTest.java 8222582 generic-all # Graal unit tests -org.graalvm.compiler.core.test.CheckGraalInvariants 8205081 org.graalvm.compiler.core.test.OptionsVerifierTest 8205081 org.graalvm.compiler.hotspot.test.CompilationWrapperTest 8205081 org.graalvm.compiler.replacements.test.classfile.ClassfileBytecodeProviderTest 8205081 diff -r 2a49d43aaa6a -r ec9da3c0eaec test/hotspot/jtreg/compiler/graalunit/CoreAarch64Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/compiler/graalunit/CoreAarch64Test.java Wed Oct 09 23:22:56 2019 +0200 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary + * @requires vm.opt.final.EnableJVMCI == true + * + * @modules jdk.internal.vm.compiler + * + * @library /test/lib /compiler/graalunit / + * + * @build compiler.graalunit.common.GraalUnitTestLauncher + * + * @run driver jdk.test.lib.FileInstaller ../../ProblemList-graal.txt ExcludeList.txt + * + * @run main compiler.graalunit.common.GraalUnitTestLauncher -prefix org.graalvm.compiler.core.aarch64.test -exclude ExcludeList.txt + */ + +/* DO NOT MODIFY THIS FILE. GENERATED BY generateTests.sh */ diff -r 2a49d43aaa6a -r ec9da3c0eaec test/hotspot/jtreg/compiler/graalunit/CoreJdk9Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/compiler/graalunit/CoreJdk9Test.java Wed Oct 09 23:22:56 2019 +0200 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary + * @requires vm.opt.final.EnableJVMCI == true + * + * @modules jdk.internal.vm.compiler + * + * @library /test/lib /compiler/graalunit / + * + * @build compiler.graalunit.common.GraalUnitTestLauncher + * + * @run driver jdk.test.lib.FileInstaller ../../ProblemList-graal.txt ExcludeList.txt + * + * @run main compiler.graalunit.common.GraalUnitTestLauncher -prefix org.graalvm.compiler.core.jdk9.test -exclude ExcludeList.txt + */ + +/* DO NOT MODIFY THIS FILE. GENERATED BY generateTests.sh */ diff -r 2a49d43aaa6a -r ec9da3c0eaec test/hotspot/jtreg/compiler/graalunit/EA9Test.java --- a/test/hotspot/jtreg/compiler/graalunit/EA9Test.java Wed Oct 09 23:21:07 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary - * @requires vm.opt.final.EnableJVMCI == true - * - * @modules jdk.internal.vm.compiler - * - * @library /test/lib /compiler/graalunit / - * - * @build compiler.graalunit.common.GraalUnitTestLauncher - * - * @run driver jdk.test.lib.FileInstaller ../../ProblemList-graal.txt ExcludeList.txt - * - * @run main compiler.graalunit.common.GraalUnitTestLauncher -prefix org.graalvm.compiler.core.jdk9.test.ea -exclude ExcludeList.txt - */ - -/* DO NOT MODIFY THIS FILE. GENERATED BY generateTests.sh */ diff -r 2a49d43aaa6a -r ec9da3c0eaec test/hotspot/jtreg/compiler/graalunit/TestPackages.txt --- a/test/hotspot/jtreg/compiler/graalunit/TestPackages.txt Wed Oct 09 23:21:07 2019 +0200 +++ b/test/hotspot/jtreg/compiler/graalunit/TestPackages.txt Wed Oct 09 23:22:56 2019 +0200 @@ -4,18 +4,19 @@ AsmAarch64 org.graalvm.compiler.asm.aarch64.test AsmAmd64 org.graalvm.compiler.asm.amd64.test AsmSparc org.graalvm.compiler.asm.sparc.test -CoreAmd64 org.graalvm.compiler.core.amd64.test Core org.graalvm.compiler.core.test @requires !vm.graal.enabled EA org.graalvm.compiler.core.test.ea -EA9 org.graalvm.compiler.core.jdk9.test.ea +CoreAmd64 org.graalvm.compiler.core.amd64.test +CoreAarch64 org.graalvm.compiler.core.aarch64.test +CoreJdk9 org.graalvm.compiler.core.jdk9.test Debug org.graalvm.compiler.debug.test Graph org.graalvm.compiler.graph.test @requires vm.graal.enabled +Hotspot org.graalvm.compiler.hotspot.test HotspotAarch64 org.graalvm.compiler.hotspot.aarch64.test HotspotAmd64 org.graalvm.compiler.hotspot.amd64.test HotspotJdk9 org.graalvm.compiler.hotspot.jdk9.test HotspotSparc org.graalvm.compiler.hotspot.sparc.test @requires vm.simpleArch == "sparcv9" HotspotLir org.graalvm.compiler.hotspot.lir.test -Hotspot org.graalvm.compiler.hotspot.test Loop org.graalvm.compiler.loop.test Nodes org.graalvm.compiler.nodes.test @requires vm.graal.enabled Options org.graalvm.compiler.options.test diff -r 2a49d43aaa6a -r ec9da3c0eaec test/hotspot/jtreg/compiler/graalunit/com.oracle.mxtool.junit/com/oracle/mxtool/junit/FindClassesByAnnotatedMethods.java --- a/test/hotspot/jtreg/compiler/graalunit/com.oracle.mxtool.junit/com/oracle/mxtool/junit/FindClassesByAnnotatedMethods.java Wed Oct 09 23:21:07 2019 +0200 +++ b/test/hotspot/jtreg/compiler/graalunit/com.oracle.mxtool.junit/com/oracle/mxtool/junit/FindClassesByAnnotatedMethods.java Wed Oct 09 23:22:56 2019 +0200 @@ -73,7 +73,7 @@ System.out.print(jarFilePath); while (e.hasMoreElements()) { JarEntry je = e.nextElement(); - if (je.isDirectory() || !je.getName().endsWith(".class")) { + if (je.isDirectory() || !je.getName().endsWith(".class") || je.getName().equals("module-info.class")) { continue; } Set methodAnnotationTypes = new HashSet<>(); @@ -84,6 +84,8 @@ } catch (UnsupportedClassVersionError ucve) { isSupported = false; unsupportedClasses++; + } catch (Throwable t) { + throw new InternalError("Error while parsing class from " + je + " in " + jarFilePath, t); } String className = je.getName().substring(0, je.getName().length() - ".class".length()).replaceAll("/", "."); if (!isSupported) { @@ -129,7 +131,7 @@ /* * Small bytecode parser that extract annotations. */ - private static final int MAJOR_VERSION_JAVA7 = 51; + private static final int MAJOR_VERSION_JAVA6 = 50; private static final int MAJOR_VERSION_OFFSET = 44; private static final byte CONSTANT_Utf8 = 1; private static final byte CONSTANT_Integer = 3; @@ -146,6 +148,8 @@ private static final byte CONSTANT_MethodType = 16; private static final byte CONSTANT_Dynamic = 17; private static final byte CONSTANT_InvokeDynamic = 18; + private static final byte CONSTANT_Module = 19; + private static final byte CONSTANT_Package = 20; private static void readClassfile(DataInputStream stream, Collection methodAnnotationTypes) throws IOException { // magic @@ -154,7 +158,7 @@ int minor = stream.readUnsignedShort(); int major = stream.readUnsignedShort(); - if (major < MAJOR_VERSION_JAVA7) { + if (major < MAJOR_VERSION_JAVA6) { throw new UnsupportedClassVersionError("Unsupported class file version: " + major + "." + minor); } // Starting with JDK8, ignore a classfile that has a newer format than the current JDK. @@ -210,7 +214,9 @@ switch (tag) { case CONSTANT_Class: case CONSTANT_String: - case CONSTANT_MethodType: { + case CONSTANT_MethodType: + case CONSTANT_Module: + case CONSTANT_Package: { skipFully(stream, 2); break; } diff -r 2a49d43aaa6a -r ec9da3c0eaec test/hotspot/jtreg/compiler/graalunit/com.oracle.mxtool.junit/com/oracle/mxtool/junit/JLModule.java --- a/test/hotspot/jtreg/compiler/graalunit/com.oracle.mxtool.junit/com/oracle/mxtool/junit/JLModule.java Wed Oct 09 23:21:07 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,211 +0,0 @@ -/* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.mxtool.junit; - -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Set; - -/** - * Facade for the {@code java.lang.Module} class introduced in JDK9 that allows tests to be - * developed against JDK8 but use module logic if deployed on JDK9. - */ -class JLModule { - - private final Object realModule; - - JLModule(Object module) { - this.realModule = module; - } - - private static final Class moduleClass; - private static final Class layerClass; - - private static final Method bootMethod; - private static final Method modulesMethod; - private static final Method getModuleMethod; - private static final Method getUnnamedModuleMethod; - private static final Method getNameMethod; - private static final Method getPackagesMethod; - private static final Method isExportedMethod; - private static final Method isExported2Method; - private static final Method addExportsMethod; - private static final Method addOpensMethod; - static { - try { - moduleClass = findModuleClass(); - Class modulesClass = Class.forName("jdk.internal.module.Modules"); - layerClass = findModuleLayerClass(); - bootMethod = layerClass.getMethod("boot"); - modulesMethod = layerClass.getMethod("modules"); - getModuleMethod = Class.class.getMethod("getModule"); - getUnnamedModuleMethod = ClassLoader.class.getMethod("getUnnamedModule"); - getNameMethod = moduleClass.getMethod("getName"); - getPackagesMethod = moduleClass.getMethod("getPackages"); - isExportedMethod = moduleClass.getMethod("isExported", String.class); - isExported2Method = moduleClass.getMethod("isExported", String.class, moduleClass); - addExportsMethod = modulesClass.getDeclaredMethod("addExports", moduleClass, String.class, moduleClass); - addOpensMethod = getDeclaredMethodOptional(modulesClass, "addOpens", moduleClass, String.class, moduleClass); - } catch (Exception e) { - throw new AssertionError(e); - } - } - - // API change http://hg.openjdk.java.net/jdk9/dev/hotspot/rev/afedee84773e. - protected static Class findModuleClass() throws ClassNotFoundException { - try { - return Class.forName("java.lang.Module"); - } catch (ClassNotFoundException e) { - return Class.forName("java.lang.reflect.Module"); - } - } - - // API change http://hg.openjdk.java.net/jdk9/dev/hotspot/rev/afedee84773e. - protected static Class findModuleLayerClass() throws ClassNotFoundException { - try { - return Class.forName("java.lang.ModuleLayer"); - } catch (ClassNotFoundException e) { - return Class.forName("java.lang.reflect.Layer"); - } - } - - private static Method getDeclaredMethodOptional(Class declaringClass, String name, Class... parameterTypes) { - try { - return declaringClass.getDeclaredMethod(name, parameterTypes); - } catch (NoSuchMethodException e) { - return null; - } - } - - public static JLModule fromClass(Class cls) { - try { - return new JLModule(getModuleMethod.invoke(cls)); - } catch (Exception e) { - throw new AssertionError(e); - } - } - - @SuppressWarnings("unchecked") - public static JLModule find(String name) { - try { - Object bootLayer = bootMethod.invoke(null); - Set modules = (Set) modulesMethod.invoke(bootLayer); - for (Object m : modules) { - JLModule module = new JLModule(m); - String mname = module.getName(); - if (mname.equals(name)) { - return module; - } - } - } catch (Exception e) { - throw new InternalError(e); - } - return null; - } - - public static JLModule getUnnamedModuleFor(ClassLoader cl) { - try { - return new JLModule(getUnnamedModuleMethod.invoke(cl)); - } catch (Exception e) { - throw new AssertionError(e); - } - } - - public String getName() { - try { - return (String) getNameMethod.invoke(realModule); - } catch (Exception e) { - throw new AssertionError(e); - } - } - - /** - * Exports all packages in this module to a given module. - */ - public void exportAllPackagesTo(JLModule module) { - if (this != module) { - for (String pkg : getPackages()) { - // Export all JVMCI packages dynamically instead - // of requiring a long list of -XaddExports - // options on the JVM command line. - if (!isExported(pkg, module)) { - addExports(pkg, module); - addOpens(pkg, module); - } - } - } - } - - @SuppressWarnings("unchecked") - public Iterable getPackages() { - try { - // API change http://hg.openjdk.java.net/jdk9/dev/hotspot/rev/afedee84773e#l1.15 - Object res = getPackagesMethod.invoke(realModule); - if (res instanceof String[]) { - return Arrays.asList((String[]) res); - } - return (Set) res; - } catch (Exception e) { - throw new AssertionError(e); - } - } - - public boolean isExported(String pn) { - try { - return (Boolean) isExportedMethod.invoke(realModule, pn); - } catch (Exception e) { - throw new AssertionError(e); - } - } - - public boolean isExported(String pn, JLModule other) { - try { - return (Boolean) isExported2Method.invoke(realModule, pn, other.realModule); - } catch (Exception e) { - throw new AssertionError(e); - } - } - - public void addExports(String pn, JLModule other) { - try { - addExportsMethod.invoke(null, realModule, pn, other.realModule); - } catch (Exception e) { - throw new AssertionError(e); - } - } - - public void addOpens(String pn, JLModule other) { - if (addOpensMethod != null) { - try { - addOpensMethod.invoke(null, realModule, pn, other.realModule); - } catch (Exception e) { - throw new AssertionError(e); - } - } - } - - @Override - public String toString() { - return realModule.toString(); - } -} diff -r 2a49d43aaa6a -r ec9da3c0eaec test/hotspot/jtreg/compiler/graalunit/com.oracle.mxtool.junit/com/oracle/mxtool/junit/ModuleSupport.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/compiler/graalunit/com.oracle.mxtool.junit/com/oracle/mxtool/junit/ModuleSupport.java Wed Oct 09 23:22:56 2019 +0200 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.mxtool.junit; + +import java.io.PrintStream; +import java.util.Set; + +/** + * Interface to {@code java.lang.Module} related functionality. + */ +class ModuleSupport { + + /** + * @param out stream to use for printing warnings + */ + ModuleSupport(PrintStream out) { + } + + /** + * Exports and opens packages based on {@code spec}. See further documentation in + * {@code mx_unittest.py}. + * + * @param spec + * @param context + * @param opened the set of opens performed are added to this set in the format + * {@code /=(,)*} (e.g. + * {@code "com.foo/com.foo.util=ALL-NAMED,com.bar"}) + * @param exported the set of exports performed are added to this set in the format + * {@code /=(,)*} (e.g. + * {@code "com.foo/com.foo.util=ALL-NAMED,com.bar"}) + */ + void openPackages(String spec, Object context, Set opened, Set exported) { + // Nop on JDK 8 + } + + /** + * Updates modules specified in {@code AddExport} annotations on {@code classes} to export and + * open packages to the annotation classes' declaring modules. + * + * @param classes + * @param opened the set of opens performed are added to this set in the format + * {@code /=(,)*} (e.g. + * {@code "com.foo/com.foo.util=ALL-NAMED,com.bar"}) + * @param exported the set of exports performed are added to this set in the format + * {@code /=(,)*} (e.g. + * {@code "com.foo/com.foo.util=ALL-NAMED,com.bar"}) + */ + void processAddExportsAnnotations(Set> classes, Set opened, Set exported) { + // Nop on JDK 8 + } +} diff -r 2a49d43aaa6a -r ec9da3c0eaec test/hotspot/jtreg/compiler/graalunit/com.oracle.mxtool.junit/com/oracle/mxtool/junit/MxJUnitWrapper.java --- a/test/hotspot/jtreg/compiler/graalunit/com.oracle.mxtool.junit/com/oracle/mxtool/junit/MxJUnitWrapper.java Wed Oct 09 23:21:07 2019 +0200 +++ b/test/hotspot/jtreg/compiler/graalunit/com.oracle.mxtool.junit/com/oracle/mxtool/junit/MxJUnitWrapper.java Wed Oct 09 23:22:56 2019 +0200 @@ -29,18 +29,13 @@ import java.io.FileReader; import java.io.IOException; import java.io.PrintStream; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.ServiceLoader; import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.TreeSet; import org.junit.internal.JUnitSystem; import org.junit.internal.RealSystem; @@ -59,6 +54,14 @@ public class MxJUnitWrapper { + // Unit tests that start a JVM subprocess can use these system properties to + // add --add-exports and --add-opens as necessary to the JVM command line. + // + // Known usages: + // org.graalvm.compiler.test.SubprocessUtil.getPackageOpeningOptions() + public static final String OPENED_PACKAGES_PROPERTY_NAME = "com.oracle.mxtool.junit.opens"; + public static final String EXPORTED_PACKAGES_PROPERTY_NAME = "com.oracle.mxtool.junit.exports"; + public static class MxJUnitConfig { public boolean verbose = false; @@ -136,14 +139,25 @@ String[] expandedArgs = expandArgs(args); int i = 0; + List testSpecs = new ArrayList<>(); + List openPackagesSpecs = new ArrayList<>(); while (i < expandedArgs.length) { String each = expandedArgs[i]; if (each.charAt(0) == '-') { // command line arguments if (each.contentEquals("-JUnitVerbose")) { config.verbose = true; + config.enableTiming = true; + } else if (each.contentEquals("-JUnitOpenPackages")) { + if (i + 1 >= expandedArgs.length) { + system.out().println("Must include argument for -JUnitAddExports"); + System.exit(1); + } + openPackagesSpecs.add(expandedArgs[++i]); } else if (each.contentEquals("-JUnitVeryVerbose")) { + config.verbose = true; config.veryVerbose = true; + config.enableTiming = true; } else if (each.contentEquals("-JUnitFailFast")) { config.failFast = true; } else if (each.contentEquals("-JUnitEnableTiming")) { @@ -172,21 +186,35 @@ } } else { - - try { - builder.addTestSpec(each); - } catch (MxJUnitRequest.BuilderException ex) { - system.out().println(ex.getMessage()); - System.exit(1); - } + testSpecs.add(each); } i++; } - MxJUnitRequest request = builder.build(); + ModuleSupport moduleSupport = new ModuleSupport(system.out()); + Set opened = new TreeSet<>(); + Set exported = new TreeSet<>(); + for (String spec : openPackagesSpecs) { + moduleSupport.openPackages(spec, "-JUnitOpenPackages", opened, exported); + } - if (System.getProperty("java.specification.version").compareTo("1.9") >= 0) { - addExports(request.classes, system.out()); + for (String spec : testSpecs) { + try { + builder.addTestSpec(spec); + } catch (MxJUnitRequest.BuilderException ex) { + system.out().println(ex.getMessage()); + System.exit(1); + } + } + + MxJUnitRequest request = builder.build(); + moduleSupport.processAddExportsAnnotations(request.classes, opened, exported); + + if (!opened.isEmpty()) { + System.setProperty(OPENED_PACKAGES_PROPERTY_NAME, String.join(System.lineSeparator(), opened)); + } + if (!exported.isEmpty()) { + System.setProperty(EXPORTED_PACKAGES_PROPERTY_NAME, String.join(System.lineSeparator(), exported)); } for (RunListener p : ServiceLoader.load(RunListener.class)) { @@ -285,8 +313,6 @@ return result; } - private static final Pattern MODULE_PACKAGE_RE = Pattern.compile("([^/]+)/(.+)"); - private static class Timing implements Comparable> { final T subject; final long value; @@ -344,93 +370,6 @@ } /** - * Adds the super types of {@code cls} to {@code supertypes}. - */ - private static void gatherSupertypes(Class cls, Set> supertypes) { - if (!supertypes.contains(cls)) { - supertypes.add(cls); - Class superclass = cls.getSuperclass(); - if (superclass != null) { - gatherSupertypes(superclass, supertypes); - } - for (Class iface : cls.getInterfaces()) { - gatherSupertypes(iface, supertypes); - } - } - } - - /** - * Updates modules specified in {@code AddExport} annotations on {@code classes} to export - * concealed packages to the annotation classes' declaring modules. - */ - private static void addExports(Set> classes, PrintStream out) { - Set> types = new HashSet<>(); - for (Class cls : classes) { - gatherSupertypes(cls, types); - } - for (Class cls : types) { - Annotation[] annos = cls.getAnnotations(); - for (Annotation a : annos) { - Class annotationType = a.annotationType(); - if (annotationType.getSimpleName().equals("AddExports")) { - Optional value = getElement("value", String[].class, a); - if (value.isPresent()) { - for (String export : value.get()) { - Matcher m = MODULE_PACKAGE_RE.matcher(export); - if (m.matches()) { - String moduleName = m.group(1); - String packageName = m.group(2); - JLModule module = JLModule.find(moduleName); - if (module == null) { - out.printf("%s: Cannot find module named %s specified in \"AddExports\" annotation: %s%n", cls.getName(), moduleName, a); - } else { - if (packageName.equals("*")) { - module.exportAllPackagesTo(JLModule.fromClass(cls)); - } else { - module.addExports(packageName, JLModule.fromClass(cls)); - module.addOpens(packageName, JLModule.fromClass(cls)); - } - } - } else { - out.printf("%s: Ignoring \"AddExports\" annotation with value not matching / pattern: %s%n", cls.getName(), a); - } - } - } else { - out.printf("%s: Ignoring \"AddExports\" annotation without `String value` element: %s%n", cls.getName(), a); - } - } - } - } - } - - /** - * Gets the value of the element named {@code name} of type {@code type} from {@code annotation} - * if present. - * - * @return the requested element value wrapped in an {@link Optional} or - * {@link Optional#empty()} if {@code annotation} has no element named {@code name} - * @throws AssertionError if {@code annotation} has an element of the given name but whose type - * is not {@code type} or if there's some problem reading the value via reflection - */ - private static Optional getElement(String name, Class type, Annotation annotation) { - Class annotationType = annotation.annotationType(); - Method valueAccessor; - try { - valueAccessor = annotationType.getMethod(name); - if (!valueAccessor.getReturnType().equals(type)) { - throw new AssertionError(String.format("Element %s of %s is of type %s, not %s ", name, annotationType.getName(), valueAccessor.getReturnType().getName(), type.getName())); - } - } catch (NoSuchMethodException e) { - return Optional.empty(); - } - try { - return Optional.of(type.cast(valueAccessor.invoke(annotation))); - } catch (Exception e) { - throw new AssertionError(String.format("Could not read %s element from %s", name, annotation), e); - } - } - - /** * Expand any arguments starting with @ and return the resulting argument array. * * @return the expanded argument array diff -r 2a49d43aaa6a -r ec9da3c0eaec test/hotspot/jtreg/compiler/graalunit/com.oracle.mxtool.junit/com/oracle/mxtool/junit/TextRunListener.java --- a/test/hotspot/jtreg/compiler/graalunit/com.oracle.mxtool.junit/com/oracle/mxtool/junit/TextRunListener.java Wed Oct 09 23:21:07 2019 +0200 +++ b/test/hotspot/jtreg/compiler/graalunit/com.oracle.mxtool.junit/com/oracle/mxtool/junit/TextRunListener.java Wed Oct 09 23:22:56 2019 +0200 @@ -74,7 +74,8 @@ } public static RunListener createRunListener(MxRunListener l) { - return new TextListener(l.getWriter()) { + PrintStream theWriter = l.getWriter(); + return new TextListener(theWriter) { private Class lastClass; private int passedInLastClass; private int failedInLastClass; @@ -143,6 +144,15 @@ public void testAssumptionFailure(Failure failure) { l.testAssumptionFailure(failure); } + + @Override + protected void printFailure(Failure each, String prefix) { + // Print out the test message in the same format used to run a single test: + // my.package.MyClass#methodName + String header = each.getDescription().getClassName() + "#" + each.getDescription().getMethodName(); + theWriter.println(prefix + ") " + header); + theWriter.print(each.getTrace()); + } }; } } diff -r 2a49d43aaa6a -r ec9da3c0eaec test/hotspot/jtreg/compiler/graalunit/com.oracle.mxtool.junit/com/oracle/mxtool/junit/TimingDecorator.java --- a/test/hotspot/jtreg/compiler/graalunit/com.oracle.mxtool.junit/com/oracle/mxtool/junit/TimingDecorator.java Wed Oct 09 23:21:07 2019 +0200 +++ b/test/hotspot/jtreg/compiler/graalunit/com.oracle.mxtool.junit/com/oracle/mxtool/junit/TimingDecorator.java Wed Oct 09 23:22:56 2019 +0200 @@ -22,8 +22,8 @@ */ package com.oracle.mxtool.junit; -import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import org.junit.runner.Description; @@ -40,8 +40,8 @@ TimingDecorator(MxRunListener l) { super(l); - this.classTimes = new HashMap<>(); - this.testTimes = new HashMap<>(); + this.classTimes = new ConcurrentHashMap<>(); + this.testTimes = new ConcurrentHashMap<>(); } @Override diff -r 2a49d43aaa6a -r ec9da3c0eaec test/hotspot/jtreg/compiler/graalunit/common/GraalUnitTestLauncher.java --- a/test/hotspot/jtreg/compiler/graalunit/common/GraalUnitTestLauncher.java Wed Oct 09 23:21:07 2019 +0200 +++ b/test/hotspot/jtreg/compiler/graalunit/common/GraalUnitTestLauncher.java Wed Oct 09 23:22:56 2019 +0200 @@ -229,6 +229,8 @@ javaFlags.add("jdk.internal.vm.compiler,jdk.internal.vm.ci"); javaFlags.add("--add-exports"); javaFlags.add("java.base/jdk.internal.module=ALL-UNNAMED"); + javaFlags.add("--add-exports"); + javaFlags.add("java.base/jdk.internal.misc=ALL-UNNAMED"); javaFlags.addAll(getModuleExports("jdk.internal.vm.compiler", "ALL-UNNAMED")); javaFlags.addAll(getModuleExports("jdk.internal.vm.ci", "ALL-UNNAMED,jdk.internal.vm.compiler")); diff -r 2a49d43aaa6a -r ec9da3c0eaec test/hotspot/jtreg/gc/g1/TestRemsetLoggingThreads.java --- a/test/hotspot/jtreg/gc/g1/TestRemsetLoggingThreads.java Wed Oct 09 23:21:07 2019 +0200 +++ b/test/hotspot/jtreg/gc/g1/TestRemsetLoggingThreads.java Wed Oct 09 23:22:56 2019 +0200 @@ -54,7 +54,7 @@ OutputAnalyzer output = new OutputAnalyzer(pb.start()); - String pattern = "Concurrent RS threads times \\(s\\)$"; + String pattern = "Concurrent refinement threads times \\(s\\)$"; Matcher m = Pattern.compile(pattern, Pattern.MULTILINE).matcher(output.getStdout()); if (!m.find()) { diff -r 2a49d43aaa6a -r ec9da3c0eaec test/hotspot/jtreg/gc/g1/TestRemsetLoggingTools.java --- a/test/hotspot/jtreg/gc/g1/TestRemsetLoggingTools.java Wed Oct 09 23:21:07 2019 +0200 +++ b/test/hotspot/jtreg/gc/g1/TestRemsetLoggingTools.java Wed Oct 09 23:22:56 2019 +0200 @@ -110,7 +110,7 @@ } public static void expectRSetSummaries(String result, int expectedCumulative, int expectedPeriodic) throws Exception { - int actualTotal = result.split("concurrent refinement").length - 1; + int actualTotal = result.split("concurrent refinement statistics").length - 1; int actualCumulative = result.split("Cumulative RS summary").length - 1; if (expectedCumulative != actualCumulative) { diff -r 2a49d43aaa6a -r ec9da3c0eaec test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorInfo/GetOwnedMonitorInfoWithEATest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorInfo/GetOwnedMonitorInfoWithEATest.java Wed Oct 09 23:22:56 2019 +0200 @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2019 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8230677 + * @summary Test JVMTI's GetOwnedMonitorInfo with scalar replaced objects and eliminated locks on stack (optimizations based on escape analysis). + * @comment Without RFE 8227745 escape analysis needs to be switched off to pass the test. For the implementation of RFE 8227745 it serves as a regression test. + * @requires (vm.compMode != "Xcomp" & vm.compiler2.enabled) + * @library /test/lib + * @compile GetOwnedMonitorInfoWithEATest.java + * @run main/othervm/native + * -agentlib:GetOwnedMonitorInfoWithEATest + * -XX:+UnlockDiagnosticVMOptions + * -Xms128m -Xmx128m + * -XX:CompileCommand=dontinline,*::dontinline_* + * -XX:+PrintCompilation + * -XX:+PrintInlining + * -XX:-TieredCompilation + * -Xbatch + * -XX:CICompilerCount=1 + * -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:+UseBiasedLocking + * GetOwnedMonitorInfoWithEATest + * @run main/othervm/native + * -agentlib:GetOwnedMonitorInfoWithEATest + * -XX:+UnlockDiagnosticVMOptions + * -Xms128m -Xmx128m + * -XX:CompileCommand=dontinline,*::dontinline_* + * -XX:+PrintCompilation + * -XX:+PrintInlining + * -XX:-TieredCompilation + * -Xbatch + * -XX:CICompilerCount=1 + * -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:-EliminateLocks -XX:+EliminateNestedLocks -XX:+UseBiasedLocking -XX:-UseOptoBiasInlining + * GetOwnedMonitorInfoWithEATest + * @run main/othervm/native + * -agentlib:GetOwnedMonitorInfoWithEATest + * -XX:+UnlockDiagnosticVMOptions + * -Xms128m -Xmx128m + * -XX:CompileCommand=dontinline,*::dontinline_* + * -XX:+PrintCompilation + * -XX:+PrintInlining + * -XX:-TieredCompilation + * -Xbatch + * -XX:CICompilerCount=1 + * -XX:+DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:+UseBiasedLocking + * GetOwnedMonitorInfoWithEATest + * @run main/othervm/native + * -agentlib:GetOwnedMonitorInfoWithEATest + * -XX:+UnlockDiagnosticVMOptions + * -Xms128m -Xmx128m + * -XX:CompileCommand=dontinline,*::dontinline_* + * -XX:+PrintCompilation + * -XX:+PrintInlining + * -XX:-TieredCompilation + * -Xbatch + * -XX:CICompilerCount=1 + * -XX:-DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:+UseBiasedLocking + * GetOwnedMonitorInfoWithEATest + * @run main/othervm/native + * -agentlib:GetOwnedMonitorInfoWithEATest + * -XX:+UnlockDiagnosticVMOptions + * -Xms128m -Xmx128m + * -XX:CompileCommand=dontinline,*::dontinline_* + * -XX:+PrintCompilation + * -XX:+PrintInlining + * -XX:-TieredCompilation + * -Xbatch + * -XX:CICompilerCount=1 + * -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:-UseBiasedLocking + * GetOwnedMonitorInfoWithEATest + * @run main/othervm/native + * -agentlib:GetOwnedMonitorInfoWithEATest + * -XX:+UnlockDiagnosticVMOptions + * -Xms128m -Xmx128m + * -XX:CompileCommand=dontinline,*::dontinline_* + * -XX:+PrintCompilation + * -XX:+PrintInlining + * -XX:-TieredCompilation + * -Xbatch + * -XX:CICompilerCount=1 + * -XX:+DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:-UseBiasedLocking + * GetOwnedMonitorInfoWithEATest + * @run main/othervm/native + * -agentlib:GetOwnedMonitorInfoWithEATest + * -XX:+UnlockDiagnosticVMOptions + * -Xms128m -Xmx128m + * -XX:CompileCommand=dontinline,*::dontinline_* + * -XX:+PrintCompilation + * -XX:+PrintInlining + * -XX:-TieredCompilation + * -Xbatch + * -XX:CICompilerCount=1 + * -XX:-DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:-UseBiasedLocking + * GetOwnedMonitorInfoWithEATest + */ + +import jdk.test.lib.Asserts; + +public class GetOwnedMonitorInfoWithEATest { + + public static final int COMPILE_THRESHOLD = 20000; + + /** + * Native wrapper arround JVMTI's GetOwnedMonitorInfo(). + * @param t The thread for which the owned monitors information should be retrieved. + * @param ownedMonitors Array filled in by the call with the objects associated + * with the monitors owned by the given thread. + * @param depths Per owned monitor the depth of the frame were it was locked. + * Filled in by the call + * @return Number of monitors owned by the given thread. + */ + public static native int getOwnedMonitorInfo(Thread t, Object[] ownedMonitors); + + public static void main(String[] args) throws Exception { + new GetOwnedMonitorInfoWithEATest().runTest(); + } + + public void runTest() throws Exception { + new TestCase_1().run(); + new TestCase_2().run(); + } + + public static abstract class TestCaseBase implements Runnable { + + public long checkSum; + public boolean doLoop; + public volatile long loopCount; + public volatile boolean targetIsInLoop; + + public void run() { + try { + msgHL("Executing test case " + getClass().getName()); + warmUp(); + runTest(); + } catch (Exception e) { + Asserts.fail("Unexpected Exception", e); + } + } + + public void warmUp() { + int callCount = COMPILE_THRESHOLD + 1000; + doLoop = true; + while (callCount-- > 0) { + dontinline_testMethod(); + } + } + + public abstract void runTest() throws Exception; + public abstract void dontinline_testMethod(); + + public long dontinline_endlessLoop() { + long cs = checkSum; + while (doLoop && loopCount-- > 0) { + targetIsInLoop = true; + checkSum += checkSum % ++cs; + } + loopCount = 3; + targetIsInLoop = false; + return checkSum; + } + + public void waitUntilTargetThreadHasEnteredEndlessLoop() throws Exception { + while(!targetIsInLoop) { + msg("Target has not yet entered the loop. Sleep 200ms."); + try { Thread.sleep(200); } catch (InterruptedException e) { /*ignore */ } + } + msg("Target has entered the loop."); + } + + public void terminateEndlessLoop() throws Exception { + msg("Terminate endless loop"); + do { + doLoop = false; + } while(targetIsInLoop); + } + + public void msg(String m) { + System.out.println(); + System.out.println("### " + m); + System.out.println(); + } + + public void msgHL(String m) { + System.out.println(); + System.out.println("#####################################################"); + System.out.println("### " + m); + System.out.println("###"); + System.out.println(); + } + } + + /** + * Starts target thread T and then queries monitor information for T using JVMTI's GetOwnedMonitorInfo(). + * With escape analysis enabled the jit compiled method {@link #dontinline_testMethod()} has + * scalar replaced objects with eliminated (nested) locking in scope when the monitor + * information is retrieved. Effectively the objects escape through the JVMTI call. This works + * only with RFE 8227745. Without it escape analysis needs to be disabled. + */ + public static class TestCase_1 extends TestCaseBase { + + public void runTest() throws Exception { + loopCount = 1L << 62; // endless loop + Thread t1 = new Thread(() -> dontinline_testMethod(), "Target Thread"); + t1.start(); + try { + waitUntilTargetThreadHasEnteredEndlessLoop(); + int expectedMonitorCount = 1; + int resultSize = expectedMonitorCount + 3; + Object[] ownedMonitors = new Object[resultSize]; + msg("Get monitor info"); + int monitorCount = getOwnedMonitorInfo(t1, ownedMonitors); + terminateEndlessLoop(); + t1.join(); + Asserts.assertGreaterThanOrEqual(monitorCount, 0, "getOwnedMonitorsFor() call failed"); + msg("Monitor info:"); + for (int i = 0; i < monitorCount; i++) { + System.out.println(i + ": cls=" + (ownedMonitors[i] != null ? ownedMonitors[i].getClass() : null)); + } + Asserts.assertEQ(monitorCount, expectedMonitorCount, "unexpected monitor count returned by getOwnedMonitorsFor()"); + Asserts.assertNotNull(ownedMonitors[0]); + Asserts.assertSame(ownedMonitors[0].getClass(), LockCls.class); + } finally { + terminateEndlessLoop(); + t1.join(); + } + } + + public void dontinline_testMethod() { + LockCls l1 = new LockCls(); // to be scalar replaced + synchronized (l1) { + inlinedTestMethodWithNestedLocking(l1); + } + } + + public void inlinedTestMethodWithNestedLocking(LockCls l1) { + synchronized (l1) { // nested + dontinline_endlessLoop(); + } + } + } + + /** + * Similar to {@link TestCase_1}. Additionally the target thread T has got eliminated locking + * for a synchronized method of a different type {@linkplain LockCls2}. + */ + public static class TestCase_2 extends TestCaseBase { + + public void runTest() throws Exception { + loopCount = 1L << 62; // endless loop + Thread t1 = new Thread(() -> dontinline_testMethod(), "Target Thread"); + t1.start(); + try { + waitUntilTargetThreadHasEnteredEndlessLoop(); + int expectedMonitorCount = 2; + int resultSize = expectedMonitorCount + 3; + Object[] ownedMonitors = new Object[resultSize]; + msg("Get monitor info"); + int monitorCount = getOwnedMonitorInfo(t1, ownedMonitors); + terminateEndlessLoop(); + t1.join(); + Asserts.assertGreaterThanOrEqual(monitorCount, 0, "getOwnedMonitorsFor() call failed"); + msg("Monitor info:"); + for (int i = 0; i < monitorCount; i++) { + System.out.println(i + ": cls=" + (ownedMonitors[i] != null ? ownedMonitors[i].getClass() : null)); + } + Asserts.assertEQ(monitorCount, expectedMonitorCount, "unexpected monitor count returned by getOwnedMonitorsFor()"); + Asserts.assertNotNull(ownedMonitors[0]); + Asserts.assertSame(ownedMonitors[0].getClass(), LockCls2.class); + + Asserts.assertNotNull(ownedMonitors[1]); + Asserts.assertSame(ownedMonitors[1].getClass(), LockCls.class); + } finally { + terminateEndlessLoop(); + t1.join(); + } + } + + public void dontinline_testMethod() { + LockCls l1 = new LockCls(); + synchronized (l1) { + inlinedTestMethodWithNestedLocking(l1); + } + } + + public void inlinedTestMethodWithNestedLocking(LockCls l1) { + synchronized (l1) { + dontinline_testMethod2(); + } + } + + public void dontinline_testMethod2() { + // Call synchronized method. Receiver of the call will be scalar replaced, + // and locking will be eliminated. Here we use a different type. + new LockCls2().inline_synchronized_testMethod(this); + } + } + + public static class LockCls { + } + + public static class LockCls2 { + public synchronized void inline_synchronized_testMethod(TestCaseBase testCase) { + testCase.dontinline_endlessLoop(); + } + } +} diff -r 2a49d43aaa6a -r ec9da3c0eaec test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorInfo/libGetOwnedMonitorInfoWithEATest.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorInfo/libGetOwnedMonitorInfoWithEATest.c Wed Oct 09 23:22:56 2019 +0200 @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2019 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include "jvmti.h" +#include "jni.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef JNI_ENV_ARG + +#ifdef __cplusplus +#define JNI_ENV_ARG(x, y) y +#define JNI_ENV_PTR(x) x +#else +#define JNI_ENV_ARG(x,y) x, y +#define JNI_ENV_PTR(x) (*x) +#endif + +#endif + +#define FAILED -1 + +static jvmtiEnv *jvmti; + +static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved); + +static void ShowErrorMessage(jvmtiEnv *jvmti, jvmtiError errCode, const char *message) { + char *errMsg; + jvmtiError result; + + result = (*jvmti)->GetErrorName(jvmti, errCode, &errMsg); + if (result == JVMTI_ERROR_NONE) { + fprintf(stderr, "%s: %s (%d)\n", message, errMsg, errCode); + (*jvmti)->Deallocate(jvmti, (unsigned char *)errMsg); + } else { + fprintf(stderr, "%s (%d)\n", message, errCode); + } +} + +JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { + return Agent_Initialize(jvm, options, reserved); +} + +JNIEXPORT jint JNICALL +Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) { + return Agent_Initialize(jvm, options, reserved); +} + +JNIEXPORT jint JNICALL +JNI_OnLoad(JavaVM *jvm, void *reserved) { + jint res; + JNIEnv *env; + + res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &env), + JNI_VERSION_9); + if (res != JNI_OK || env == NULL) { + fprintf(stderr, "Error: GetEnv call failed(%d)!\n", res); + return JNI_ERR; + } + + return JNI_VERSION_9; +} + +static +jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { + jint res; + jvmtiError err; + jvmtiCapabilities caps; + + printf("Agent_OnLoad started\n"); + + memset(&caps, 0, sizeof(caps)); + + res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti), + JVMTI_VERSION_9); + if (res != JNI_OK || jvmti == NULL) { + fprintf(stderr, "Error: wrong result of a valid call to GetEnv!\n"); + return JNI_ERR; + } + + caps.can_get_owned_monitor_info = 1; + + err = (*jvmti)->AddCapabilities(jvmti, &caps); + if (err != JVMTI_ERROR_NONE) { + ShowErrorMessage(jvmti, err, + "Agent_OnLoad: error in JVMTI AddCapabilities"); + return JNI_ERR; + } + + err = (*jvmti)->GetCapabilities(jvmti, &caps); + if (err != JVMTI_ERROR_NONE) { + ShowErrorMessage(jvmti, err, + "Agent_OnLoad: error in JVMTI GetCapabilities"); + return JNI_ERR; + } + + if (!caps.can_get_owned_monitor_info) { + fprintf(stderr, "Warning: GetOwnedMonitorInfo is not implemented\n"); + return JNI_ERR; + } + + printf("Agent_OnLoad finished\n"); + return JNI_OK; +} + +JNIEXPORT jint JNICALL +Java_GetOwnedMonitorInfoWithEATest_getOwnedMonitorInfo(JNIEnv *env, jclass cls, jobject targetThread, jobjectArray resOwnedMonitors) { + jvmtiError err; + jvmtiThreadInfo threadInfo; + jint monitorCount; + jobject* monitors; + jint idx; + + err = (*jvmti)->GetThreadInfo(jvmti, targetThread, &threadInfo); + if (err != JVMTI_ERROR_NONE) { + ShowErrorMessage(jvmti, err, + "getOwnedMonitorsFor: error in JVMTI GetThreadInfo"); + return FAILED; + } + + err = (*jvmti)->GetOwnedMonitorInfo(jvmti, targetThread, &monitorCount, &monitors); + if (err != JVMTI_ERROR_NONE) { + ShowErrorMessage(jvmti, err, + "getOwnedMonitorsFor: error in JVMTI GetOwnedMonitorInfo"); + return FAILED; + } + + printf("getOwnedMonitorsFor: %s owns %d monitor(s)\n", threadInfo.name, monitorCount); + + for (idx = 0; idx < monitorCount; idx++) { + (*env)->SetObjectArrayElement(env, resOwnedMonitors, idx, monitors[idx]); + } + + (*jvmti)->Deallocate(jvmti, (unsigned char *) monitors); + (*jvmti)->Deallocate(jvmti, (unsigned char *) threadInfo.name); + return monitorCount; +} + +#ifdef __cplusplus +} +#endif diff -r 2a49d43aaa6a -r ec9da3c0eaec test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorStackDepthInfo/GetOwnedMonitorStackDepthInfoWithEATest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorStackDepthInfo/GetOwnedMonitorStackDepthInfoWithEATest.java Wed Oct 09 23:22:56 2019 +0200 @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2019 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8230677 + * @summary Test JVMTI's GetOwnedMonitorStackDepthInfo with scalar replaced objects and eliminated locks on stack (optimizations based on escape analysis). + * @comment Without RFE 8227745 escape analysis needs to be switched off to pass the test. For the implementation of RFE 8227745 it serves as a regression test. + * @requires (vm.compMode != "Xcomp" & vm.compiler2.enabled) + * @library /test/lib + * @compile GetOwnedMonitorStackDepthInfoWithEATest.java + * @run main/othervm/native + * -agentlib:GetOwnedMonitorStackDepthInfoWithEATest + * -XX:+UnlockDiagnosticVMOptions + * -Xms128m -Xmx128m + * -XX:CompileCommand=dontinline,*::dontinline_* + * -XX:+PrintCompilation + * -XX:+PrintInlining + * -XX:-TieredCompilation + * -Xbatch + * -XX:CICompilerCount=1 + * -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:+UseBiasedLocking + * GetOwnedMonitorStackDepthInfoWithEATest + * @run main/othervm/native + * -agentlib:GetOwnedMonitorStackDepthInfoWithEATest + * -XX:+UnlockDiagnosticVMOptions + * -Xms128m -Xmx128m + * -XX:CompileCommand=dontinline,*::dontinline_* + * -XX:+PrintCompilation + * -XX:+PrintInlining + * -XX:-TieredCompilation + * -Xbatch + * -XX:CICompilerCount=1 + * -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:-EliminateLocks -XX:+EliminateNestedLocks -XX:+UseBiasedLocking -XX:-UseOptoBiasInlining + * GetOwnedMonitorStackDepthInfoWithEATest + * @run main/othervm/native + * -agentlib:GetOwnedMonitorStackDepthInfoWithEATest + * -XX:+UnlockDiagnosticVMOptions + * -Xms128m -Xmx128m + * -XX:CompileCommand=dontinline,*::dontinline_* + * -XX:+PrintCompilation + * -XX:+PrintInlining + * -XX:-TieredCompilation + * -Xbatch + * -XX:CICompilerCount=1 + * -XX:+DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:+UseBiasedLocking + * GetOwnedMonitorStackDepthInfoWithEATest + * @run main/othervm/native + * -agentlib:GetOwnedMonitorStackDepthInfoWithEATest + * -XX:+UnlockDiagnosticVMOptions + * -Xms128m -Xmx128m + * -XX:CompileCommand=dontinline,*::dontinline_* + * -XX:+PrintCompilation + * -XX:+PrintInlining + * -XX:-TieredCompilation + * -Xbatch + * -XX:CICompilerCount=1 + * -XX:-DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:+UseBiasedLocking + * GetOwnedMonitorStackDepthInfoWithEATest + * @run main/othervm/native + * -agentlib:GetOwnedMonitorStackDepthInfoWithEATest + * -XX:+UnlockDiagnosticVMOptions + * -Xms128m -Xmx128m + * -XX:CompileCommand=dontinline,*::dontinline_* + * -XX:+PrintCompilation + * -XX:+PrintInlining + * -XX:-TieredCompilation + * -Xbatch + * -XX:CICompilerCount=1 + * -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:-UseBiasedLocking + * GetOwnedMonitorStackDepthInfoWithEATest + * @run main/othervm/native + * -agentlib:GetOwnedMonitorStackDepthInfoWithEATest + * -XX:+UnlockDiagnosticVMOptions + * -Xms128m -Xmx128m + * -XX:CompileCommand=dontinline,*::dontinline_* + * -XX:+PrintCompilation + * -XX:+PrintInlining + * -XX:-TieredCompilation + * -Xbatch + * -XX:CICompilerCount=1 + * -XX:+DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:-UseBiasedLocking + * GetOwnedMonitorStackDepthInfoWithEATest + * @run main/othervm/native + * -agentlib:GetOwnedMonitorStackDepthInfoWithEATest + * -XX:+UnlockDiagnosticVMOptions + * -Xms128m -Xmx128m + * -XX:CompileCommand=dontinline,*::dontinline_* + * -XX:+PrintCompilation + * -XX:+PrintInlining + * -XX:-TieredCompilation + * -Xbatch + * -XX:CICompilerCount=1 + * -XX:-DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:-UseBiasedLocking + * GetOwnedMonitorStackDepthInfoWithEATest + */ + +import jdk.test.lib.Asserts; + +public class GetOwnedMonitorStackDepthInfoWithEATest { + + public static final int COMPILE_THRESHOLD = 20000; + + /** + * Native wrapper arround JVMTI's GetOwnedMonitorStackDepthInfo(). + * @param t The thread for which the owned monitors information should be retrieved. + * @param ownedMonitors Array filled in by the call with the objects associated + * with the monitors owned by the given thread. + * @param depths Per owned monitor the depth of the frame were it was locked. + * Filled in by the call + * @return Number of monitors owned by the given thread. + */ + public static native int getOwnedMonitorStackDepthInfo(Thread t, Object[] ownedMonitors, int[] depths); + + public static void main(String[] args) throws Exception { + new GetOwnedMonitorStackDepthInfoWithEATest().runTest(); + } + + public void runTest() throws Exception { + new TestCase_1().run(); + new TestCase_2().run(); + } + + public static abstract class TestCaseBase implements Runnable { + + public long checkSum; + public boolean doLoop; + public volatile long loopCount; + public volatile boolean targetIsInLoop; + + public void run() { + try { + msgHL("Executing test case " + getClass().getName()); + warmUp(); + runTest(); + } catch (Exception e) { + Asserts.fail("Unexpected Exception", e); + } + } + + public void warmUp() { + int callCount = COMPILE_THRESHOLD + 1000; + doLoop = true; + while (callCount-- > 0) { + dontinline_testMethod(); + } + } + + public abstract void runTest() throws Exception; + public abstract void dontinline_testMethod(); + + public long dontinline_endlessLoop() { + long cs = checkSum; + while (doLoop && loopCount-- > 0) { + targetIsInLoop = true; + checkSum += checkSum % ++cs; + } + loopCount = 3; + targetIsInLoop = false; + return checkSum; + } + + public void waitUntilTargetThreadHasEnteredEndlessLoop() throws Exception { + while(!targetIsInLoop) { + msg("Target has not yet entered the loop. Sleep 200ms."); + try { Thread.sleep(200); } catch (InterruptedException e) { /*ignore */ } + } + msg("Target has entered the loop."); + } + + public void terminateEndlessLoop() throws Exception { + msg("Terminate endless loop"); + do { + doLoop = false; + } while(targetIsInLoop); + } + + public void msg(String m) { + System.out.println(); + System.out.println("### " + m); + System.out.println(); + } + + public void msgHL(String m) { + System.out.println(); + System.out.println("#####################################################"); + System.out.println("### " + m); + System.out.println("###"); + System.out.println(); + } + } + + /** + * Starts target thread T and then queries monitor information for T using JVMTI's GetOwnedMonitorStackDepthInfo(). + * With escape analysis enabled the jit compiled method {@link #dontinline_testMethod()} has + * scalar replaced objects with eliminated (nested) locking in scope when the monitor + * information is retrieved. Effectively the objects escape through the JVMTI call. This works + * only with RFE 8227745. Without it escape analysis needs to be disabled. + */ + public static class TestCase_1 extends TestCaseBase { + + public void runTest() throws Exception { + loopCount = 1L << 62; // endless loop + Thread t1 = new Thread(() -> dontinline_testMethod(), "Target Thread"); + try { + t1.start(); + waitUntilTargetThreadHasEnteredEndlessLoop(); + int expectedMonitorCount = 1; + int resultSize = expectedMonitorCount + 3; + Object[] ownedMonitors = new Object[resultSize]; + int[] depths = new int[resultSize]; + msg("Get monitor info"); + int monitorCount = getOwnedMonitorStackDepthInfo(t1, ownedMonitors, depths); + Asserts.assertGreaterThanOrEqual(monitorCount, 0, "getOwnedMonitorsFor() call failed"); + msg("Monitor info:"); + for (int i = 0; i < monitorCount; i++) { + System.out.println(i + ": cls=" + (ownedMonitors[i] != null ? ownedMonitors[i].getClass() : null) + " depth=" + depths[i]); + } + Asserts.assertEQ(monitorCount, expectedMonitorCount, "unexpected monitor count returned by getOwnedMonitorsFor()"); + Asserts.assertNotNull(ownedMonitors[0]); + Asserts.assertSame(ownedMonitors[0].getClass(), LockCls.class); + Asserts.assertEQ(depths[0], 1, "unexpected depth for owned monitor at index 0"); + } finally { + terminateEndlessLoop(); + t1.join(); + } + } + + public void dontinline_testMethod() { + LockCls l1 = new LockCls(); // to be scalar replaced + synchronized (l1) { + inlinedTestMethodWithNestedLocking(l1); + } + } + + public void inlinedTestMethodWithNestedLocking(LockCls l1) { + synchronized (l1) { // nested + dontinline_endlessLoop(); + } + } + } + + /** + * Similar to {@link TestCase_1}. Additionally the target thread T has got eliminated locking + * for a synchronized method of a different type {@linkplain LockCls2}. + */ + public static class TestCase_2 extends TestCaseBase { + + public void runTest() throws Exception { + loopCount = 1L << 62; // endless loop + Thread t1 = new Thread(() -> dontinline_testMethod(), "Target Thread"); + t1.start(); + try { + waitUntilTargetThreadHasEnteredEndlessLoop(); + int expectedMonitorCount = 2; + int resultSize = expectedMonitorCount + 3; + Object[] ownedMonitors = new Object[resultSize]; + int[] depths = new int[resultSize]; + msg("Get monitor info"); + int monitorCount = getOwnedMonitorStackDepthInfo(t1, ownedMonitors, depths); + terminateEndlessLoop(); + t1.join(); + Asserts.assertGreaterThanOrEqual(monitorCount, 0, "getOwnedMonitorsFor() call failed"); + msg("Monitor info:"); + for (int i = 0; i < monitorCount; i++) { + System.out.println(i + ": cls=" + (ownedMonitors[i] != null ? ownedMonitors[i].getClass() : null) + " depth=" + depths[i]); + } + Asserts.assertEQ(monitorCount, expectedMonitorCount, "unexpected monitor count returned by getOwnedMonitorsFor()"); + Asserts.assertNotNull(ownedMonitors[0]); + Asserts.assertSame(ownedMonitors[0].getClass(), LockCls2.class); + Asserts.assertEQ(depths[0], 1, "unexpected depth for owned monitor at index 0"); + + Asserts.assertNotNull(ownedMonitors[1]); + Asserts.assertSame(ownedMonitors[1].getClass(), LockCls.class); + Asserts.assertEQ(depths[1], 3, "unexpected depth for owned monitor at index 1"); + } finally { + terminateEndlessLoop(); + t1.join(); + } + } + + public void dontinline_testMethod() { + LockCls l1 = new LockCls(); + synchronized (l1) { + inlinedTestMethodWithNestedLocking(l1); + } + } + + public void inlinedTestMethodWithNestedLocking(LockCls l1) { + synchronized (l1) { + dontinline_testMethod2(); + } + } + + public void dontinline_testMethod2() { + // Call synchronized method. Receiver of the call will be scalar replaced, + // and locking will be eliminated. Here we use a different type. + new LockCls2().inline_synchronized_testMethod(this); + } + } + + public static class LockCls { + } + + public static class LockCls2 { + public synchronized void inline_synchronized_testMethod(TestCaseBase testCase) { + testCase.dontinline_endlessLoop(); + } + } +} diff -r 2a49d43aaa6a -r ec9da3c0eaec test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorStackDepthInfo/libGetOwnedMonitorStackDepthInfoWithEATest.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorStackDepthInfo/libGetOwnedMonitorStackDepthInfoWithEATest.c Wed Oct 09 23:22:56 2019 +0200 @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2019 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include "jvmti.h" +#include "jni.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef JNI_ENV_ARG + +#ifdef __cplusplus +#define JNI_ENV_ARG(x, y) y +#define JNI_ENV_PTR(x) x +#else +#define JNI_ENV_ARG(x,y) x, y +#define JNI_ENV_PTR(x) (*x) +#endif + +#endif + +#define FAILED -1 + +static jvmtiEnv *jvmti; + +static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved); + +static void ShowErrorMessage(jvmtiEnv *jvmti, jvmtiError errCode, const char *message) { + char *errMsg; + jvmtiError result; + + result = (*jvmti)->GetErrorName(jvmti, errCode, &errMsg); + if (result == JVMTI_ERROR_NONE) { + fprintf(stderr, "%s: %s (%d)\n", message, errMsg, errCode); + (*jvmti)->Deallocate(jvmti, (unsigned char *)errMsg); + } else { + fprintf(stderr, "%s (%d)\n", message, errCode); + } +} + +JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { + return Agent_Initialize(jvm, options, reserved); +} + +JNIEXPORT jint JNICALL +Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) { + return Agent_Initialize(jvm, options, reserved); +} + +JNIEXPORT jint JNICALL +JNI_OnLoad(JavaVM *jvm, void *reserved) { + jint res; + JNIEnv *env; + + res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &env), + JNI_VERSION_9); + if (res != JNI_OK || env == NULL) { + fprintf(stderr, "Error: GetEnv call failed(%d)!\n", res); + return JNI_ERR; + } + + return JNI_VERSION_9; +} + +static +jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { + jint res; + jvmtiError err; + jvmtiCapabilities caps; + + printf("Agent_OnLoad started\n"); + + memset(&caps, 0, sizeof(caps)); + + res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti), + JVMTI_VERSION_9); + if (res != JNI_OK || jvmti == NULL) { + fprintf(stderr, "Error: wrong result of a valid call to GetEnv!\n"); + return JNI_ERR; + } + + caps.can_get_owned_monitor_stack_depth_info = 1; + + err = (*jvmti)->AddCapabilities(jvmti, &caps); + if (err != JVMTI_ERROR_NONE) { + ShowErrorMessage(jvmti, err, + "Agent_OnLoad: error in JVMTI AddCapabilities"); + return JNI_ERR; + } + + err = (*jvmti)->GetCapabilities(jvmti, &caps); + if (err != JVMTI_ERROR_NONE) { + ShowErrorMessage(jvmti, err, + "Agent_OnLoad: error in JVMTI GetCapabilities"); + return JNI_ERR; + } + + if (!caps.can_get_owned_monitor_stack_depth_info) { + fprintf(stderr, "Warning: GetOwnedMonitorStackDepthInfo is not implemented\n"); + return JNI_ERR; + } + + printf("Agent_OnLoad finished\n"); + return JNI_OK; +} + +JNIEXPORT jint JNICALL +Java_GetOwnedMonitorStackDepthInfoWithEATest_getOwnedMonitorStackDepthInfo(JNIEnv *env, jclass cls, jobject targetThread, jobjectArray ownedMonitors, jintArray depths) { + jvmtiError err; + jvmtiThreadInfo threadInfo; + jint monitorCount; + jvmtiMonitorStackDepthInfo* stackDepthInfo; + jint* depthsPtr; + jint idx = 0; + + err = (*jvmti)->GetThreadInfo(jvmti, targetThread, &threadInfo); + if (err != JVMTI_ERROR_NONE) { + ShowErrorMessage(jvmti, err, + "getOwnedMonitorsFor: error in JVMTI GetThreadInfo"); + return FAILED; + } + + err = (*jvmti)->GetOwnedMonitorStackDepthInfo(jvmti, targetThread, &monitorCount, &stackDepthInfo); + if (err != JVMTI_ERROR_NONE) { + ShowErrorMessage(jvmti, err, + "getOwnedMonitorsFor: error in JVMTI GetOwnedMonitorStackDepthInfo"); + return FAILED; + } + + printf("getOwnedMonitorsFor: %s owns %d monitor(s)\n", threadInfo.name, monitorCount); + + depthsPtr = (*env)->GetIntArrayElements(env, depths, NULL); + for (idx = 0; idx < monitorCount; idx++) { + (*env)->SetObjectArrayElement(env, ownedMonitors, idx, stackDepthInfo[idx].monitor); + depthsPtr[idx] = stackDepthInfo[idx].stack_depth; + } + (*env)->ReleaseIntArrayElements(env, depths, depthsPtr, 0); + + (*jvmti)->Deallocate(jvmti, (unsigned char *) stackDepthInfo); + (*jvmti)->Deallocate(jvmti, (unsigned char *) threadInfo.name); + return monitorCount; +} + +#ifdef __cplusplus +} +#endif diff -r 2a49d43aaa6a -r ec9da3c0eaec test/hotspot/jtreg/serviceability/sa/TestUniverse.java --- a/test/hotspot/jtreg/serviceability/sa/TestUniverse.java Wed Oct 09 23:21:07 2019 +0200 +++ b/test/hotspot/jtreg/serviceability/sa/TestUniverse.java Wed Oct 09 23:22:56 2019 +0200 @@ -86,7 +86,7 @@ break; case Shenandoah: - expStrings.add("Shenandoah Heap"); + expStrings.add("Shenandoah heap"); break; } diff -r 2a49d43aaa6a -r ec9da3c0eaec test/hotspot/jtreg/vmTestbase/metaspace/gc/HighWaterMarkTest.java --- a/test/hotspot/jtreg/vmTestbase/metaspace/gc/HighWaterMarkTest.java Wed Oct 09 23:21:07 2019 +0200 +++ b/test/hotspot/jtreg/vmTestbase/metaspace/gc/HighWaterMarkTest.java Wed Oct 09 23:22:56 2019 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019, 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 @@ -42,8 +42,6 @@ * Test verifies that MinMetaspaceFreeRatio/MaxMetaspaceFreeRatio settings * affect the frequency of GC. (High-water mark) * - * Note: The test doesn't check the GC count if CMS is used. - * * Quoting: Java SE 8 HotSpot[tm] Virtual Machine Garbage Collection Tuning *
      * Class metadata is deallocated when the corresponding Java class is unloaded.
    @@ -175,11 +173,6 @@
                 throw new Fault("Committed amount hasn't achieved " + bytes2k(committedLevel));
             }
     
    -        if (VMRuntimeEnvUtils.isVMOptionEnabled("UseConcMarkSweepGC")) {
    -            System.out.println("ConcMarkSweep is used, cannot count GC");
    -            return;
    -        }
    -
             int gcCount = getMetaspaceGCCount();
             if (gcCount < 0) {
                 // perhpas, it's better to silently pass here... Let's see.
    diff -r 2a49d43aaa6a -r ec9da3c0eaec test/hotspot/jtreg/vmTestbase/metaspace/gc/watermark_0_1/TestDescription.java
    --- a/test/hotspot/jtreg/vmTestbase/metaspace/gc/watermark_0_1/TestDescription.java	Wed Oct 09 23:21:07 2019 +0200
    +++ b/test/hotspot/jtreg/vmTestbase/metaspace/gc/watermark_0_1/TestDescription.java	Wed Oct 09 23:22:56 2019 +0200
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -29,6 +29,11 @@
      * @summary converted from VM Testbase metaspace/gc/watermark_0_1.
      * VM Testbase keywords: [nonconcurrent, no_cds]
      *
    + * @comment Don't run test in configurations where we can't reliably count number of metaspace triggered GCs
    + * @requires vm.gc != null | !vm.opt.final.ClassUnloadingWithConcurrentMark
    + * @requires vm.gc != "G1" | !vm.opt.final.ClassUnloadingWithConcurrentMark
    + * @requires vm.gc != "ConcMarkSweep"
    + * @requires vm.gc != "Z"
      * @library /vmTestbase /test/lib
      * @run driver jdk.test.lib.FileInstaller . .
      * @run main/othervm
    diff -r 2a49d43aaa6a -r ec9da3c0eaec test/hotspot/jtreg/vmTestbase/metaspace/gc/watermark_10_20/TestDescription.java
    --- a/test/hotspot/jtreg/vmTestbase/metaspace/gc/watermark_10_20/TestDescription.java	Wed Oct 09 23:21:07 2019 +0200
    +++ b/test/hotspot/jtreg/vmTestbase/metaspace/gc/watermark_10_20/TestDescription.java	Wed Oct 09 23:22:56 2019 +0200
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -29,6 +29,11 @@
      * @summary converted from VM Testbase metaspace/gc/watermark_10_20.
      * VM Testbase keywords: [nonconcurrent, no_cds]
      *
    + * @comment Don't run test in configurations where we can't reliably count number of metaspace triggered GCs
    + * @requires vm.gc != null | !vm.opt.final.ClassUnloadingWithConcurrentMark
    + * @requires vm.gc != "G1" | !vm.opt.final.ClassUnloadingWithConcurrentMark
    + * @requires vm.gc != "ConcMarkSweep"
    + * @requires vm.gc != "Z"
      * @library /vmTestbase /test/lib
      * @run driver jdk.test.lib.FileInstaller . .
      * @run main/othervm
    diff -r 2a49d43aaa6a -r ec9da3c0eaec test/hotspot/jtreg/vmTestbase/metaspace/gc/watermark_70_80/TestDescription.java
    --- a/test/hotspot/jtreg/vmTestbase/metaspace/gc/watermark_70_80/TestDescription.java	Wed Oct 09 23:21:07 2019 +0200
    +++ b/test/hotspot/jtreg/vmTestbase/metaspace/gc/watermark_70_80/TestDescription.java	Wed Oct 09 23:22:56 2019 +0200
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -29,6 +29,11 @@
      * @summary converted from VM Testbase metaspace/gc/watermark_70_80.
      * VM Testbase keywords: [nonconcurrent, no_cds]
      *
    + * @comment Don't run test in configurations where we can't reliably count number of metaspace triggered GCs
    + * @requires vm.gc != null | !vm.opt.final.ClassUnloadingWithConcurrentMark
    + * @requires vm.gc != "G1" | !vm.opt.final.ClassUnloadingWithConcurrentMark
    + * @requires vm.gc != "ConcMarkSweep"
    + * @requires vm.gc != "Z"
      * @library /vmTestbase /test/lib
      * @run driver jdk.test.lib.FileInstaller . .
      * @run main/othervm
    diff -r 2a49d43aaa6a -r ec9da3c0eaec test/hotspot/jtreg/vmTestbase/metaspace/gc/watermark_99_100/TestDescription.java
    --- a/test/hotspot/jtreg/vmTestbase/metaspace/gc/watermark_99_100/TestDescription.java	Wed Oct 09 23:21:07 2019 +0200
    +++ b/test/hotspot/jtreg/vmTestbase/metaspace/gc/watermark_99_100/TestDescription.java	Wed Oct 09 23:22:56 2019 +0200
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -29,6 +29,11 @@
      * @summary converted from VM Testbase metaspace/gc/watermark_99_100.
      * VM Testbase keywords: [nonconcurrent, no_cds]
      *
    + * @comment Don't run test in configurations where we can't reliably count number of metaspace triggered GCs
    + * @requires vm.gc != null | !vm.opt.final.ClassUnloadingWithConcurrentMark
    + * @requires vm.gc != "G1" | !vm.opt.final.ClassUnloadingWithConcurrentMark
    + * @requires vm.gc != "ConcMarkSweep"
    + * @requires vm.gc != "Z"
      * @library /vmTestbase /test/lib
      * @run driver jdk.test.lib.FileInstaller . .
      * @run main/othervm
    diff -r 2a49d43aaa6a -r ec9da3c0eaec test/jdk/com/sun/jdi/JdbStopInNotificationThreadTest.java
    --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    +++ b/test/jdk/com/sun/jdi/JdbStopInNotificationThreadTest.java	Wed Oct 09 23:22:56 2019 +0200
    @@ -0,0 +1,148 @@
    +/*
    + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
    + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    + *
    + * This code is free software; you can redistribute it and/or modify it
    + * under the terms of the GNU General Public License version 2 only, as
    + * published by the Free Software Foundation.
    + *
    + * This code is distributed in the hope that it will be useful, but WITHOUT
    + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    + * version 2 for more details (a copy is included in the LICENSE file that
    + * accompanied this code).
    + *
    + * You should have received a copy of the GNU General Public License version
    + * 2 along with this work; if not, write to the Free Software Foundation,
    + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    + *
    + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    + * or visit www.oracle.com if you need additional information or have any
    + * questions.
    + */
    +
    +/*
    + * @test
    + * @summary Tests that the breakpoint in the notification listener is hit when the
    + * notification thread is enabled and is not hit when the notification thread is disabled
    + * (the service thread delivers the notifications in this case).
    + *
    + * @library /test/lib
    + * @run compile -g JdbStopInNotificationThreadTest.java
    + * @run main/othervm JdbStopInNotificationThreadTest
    + */
    +
    +import jdk.test.lib.process.OutputAnalyzer;
    +import lib.jdb.JdbCommand;
    +import lib.jdb.JdbTest;
    +
    +import javax.management.Notification;
    +import javax.management.NotificationEmitter;
    +import javax.management.NotificationListener;
    +import java.lang.management.*;
    +import java.util.Collection;
    +import java.util.LinkedList;
    +
    +class JdbStopInNotificationThreadTestTarg {
    +
    +    private static volatile boolean done = false;
    +
    +    private static final MemoryPoolMXBean tenuredGenPool =
    +            findTenuredGenPool();
    +
    +    public static void main(String[] args) throws Exception {
    +        test(); // @1 breakpoint
    +    }
    +
    +    private static void test() throws Exception {
    +        setPercentageUsageThreshold(0.1);
    +        MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();
    +        NotificationEmitter emitter = (NotificationEmitter) mbean;
    +        emitter.addNotificationListener(new NotificationListener() {
    +            public void handleNotification(Notification n, Object hb) {
    +                System.out.println("Notification received:" + n.getType());
    +                if (n.getType().equals(
    +                        MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED)) {
    +                    done = true;
    +                    System.out.println("Notification MEMORY_THRESHOLD_EXCEEDED received:");
    +                    long maxMemory = tenuredGenPool.getUsage().getMax();
    +                    long usedMemory = tenuredGenPool.getUsage().getUsed();
    +                    System.out.println("Memory usage low!!!"); // @2 breakpoint
    +                    double percentageUsed = ((double) usedMemory) / maxMemory;
    +                    System.out.println("percentageUsed = " + percentageUsed);
    +                }
    +            }
    +        }, null, null);
    +
    +        Collection numbers = new LinkedList();
    +        long counter = 0;
    +        while (!done) {
    +            numbers.add(new Object[1000]);
    +            counter++;
    +            if (counter % 1000 == 0) {
    +                Thread.sleep(100);
    +            }
    +        }
    +        System.out.println("Done");
    +    }
    +
    +    private static MemoryPoolMXBean findTenuredGenPool() {
    +        for (MemoryPoolMXBean pool :
    +                ManagementFactory.getMemoryPoolMXBeans()) {
    +            if (pool.getType() == MemoryType.HEAP &&
    +                    pool.isUsageThresholdSupported()) {
    +                return pool;
    +            }
    +        }
    +        throw new RuntimeException("Could not find tenured space");
    +    }
    +
    +    public static void setPercentageUsageThreshold(double percentage) {
    +        if (percentage <= 0.0 || percentage > 1.0) {
    +            throw new IllegalArgumentException("Percentage not in range");
    +        }
    +        System.out.println("Setting threashold for pool " + tenuredGenPool.getName() + " percentage:" + percentage);
    +        long maxMemory = tenuredGenPool.getUsage().getMax();
    +        long warningThreshold = (long) (maxMemory * percentage);
    +        tenuredGenPool.setUsageThreshold(warningThreshold);
    +    }
    +}
    +
    +public class JdbStopInNotificationThreadTest extends JdbTest {
    +
    +    private static final String DEBUGGEE_CLASS = JdbStopInNotificationThreadTestTarg.class.getName();
    +    private static final String PATTERN1_TEMPLATE = "^Breakpoint hit: \"thread=Notification Thread\", " +
    +            "JdbStopInNotificationThreadTestTarg\\$1\\.handleNotification\\(\\), line=%LINE_NUMBER.*\\R%LINE_NUMBER\\s+System\\.out\\.println\\(\"Memory usage low!!!\"\\);.*";
    +
    +    private JdbStopInNotificationThreadTest() {
    +        super(DEBUGGEE_CLASS);
    +    }
    +
    +    public static void main(String argv[]) {
    +        new JdbStopInNotificationThreadTest().run();
    +    }
    +
    +    @Override
    +    protected void runCases() {
    +        if (isNotificationThreadDisabled()) {
    +            System.out.println("Notification Thread is disabled. Skipping the test");
    +            return;
    +        }
    +        int bpLine2 = parseBreakpoints(getTestSourcePath("JdbStopInNotificationThreadTest.java"), 2).get(0);
    +        jdb.command(JdbCommand.stopAt(DEBUGGEE_CLASS + "$1", bpLine2));
    +        String pattern = PATTERN1_TEMPLATE.replaceAll("%LINE_NUMBER", String.valueOf(bpLine2));
    +        jdb.command(JdbCommand.cont());
    +        new OutputAnalyzer(jdb.getJdbOutput()).shouldMatch(pattern);
    +    }
    +
    +    private boolean isNotificationThreadDisabled() {
    +        int bpLine1 = parseBreakpoints(getTestSourcePath("JdbStopInNotificationThreadTest.java"), 1).get(0);
    +        jdb.command(JdbCommand.stopAt(DEBUGGEE_CLASS, bpLine1));
    +        jdb.command(JdbCommand.run());
    +        jdb.command(JdbCommand.threads());
    +        if (new OutputAnalyzer(jdb.getJdbOutput()).getOutput().contains("Notification Thread")) {
    +            return false;
    +        }
    +        return true;
    +    }
    +}
    diff -r 2a49d43aaa6a -r ec9da3c0eaec test/jdk/java/net/DatagramSocket/DatagramTimeout.java
    --- a/test/jdk/java/net/DatagramSocket/DatagramTimeout.java	Wed Oct 09 23:21:07 2019 +0200
    +++ b/test/jdk/java/net/DatagramSocket/DatagramTimeout.java	Wed Oct 09 23:22:56 2019 +0200
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -23,32 +23,56 @@
     
     /**
      * @test
    - * @bug 4163126
    - * @summary  test to see if timeout hangs
    - * @run main/timeout=15 DatagramTimeout
    + * @bug 4163126 8222829
    + * @summary Test to see if timeout hangs. Also checks that
    + * negative timeout value fails as expected.
    + * @run testng DatagramTimeout
      */
     import java.net.DatagramPacket;
     import java.net.DatagramSocket;
     import java.net.SocketTimeoutException;
    +import java.nio.channels.DatagramChannel;
    +
    +import org.testng.annotations.Test;
    +
    +import static org.testng.Assert.expectThrows;
    +
     
     public class DatagramTimeout {
    -    public static void main(String[] args) throws Exception {
    -        boolean success = false;
    -        DatagramSocket sock = new DatagramSocket();
    +    private static final Class IAE = IllegalArgumentException.class;
    +    private static final Class STE = SocketTimeoutException.class;
     
    -        try {
    -            DatagramPacket p;
    +    /**
    +     * Test DatagramSocket setSoTimeout with a valid timeout value.
    +     */
    +    @Test
    +    public void testSetTimeout() throws Exception {
    +        try (DatagramSocket s = new DatagramSocket()) {
                 byte[] buffer = new byte[50];
    -            p = new DatagramPacket(buffer, buffer.length);
    -            sock.setSoTimeout(2);
    -            sock.receive(p);
    -        } catch (SocketTimeoutException e) {
    -            success = true;
    -        } finally {
    -            sock.close();
    +            DatagramPacket p = new DatagramPacket(buffer, buffer.length);
    +            s.setSoTimeout(2);
    +            expectThrows(STE, () -> s.receive(p));
             }
    -        if (!success)
    -            throw new RuntimeException("Socket timeout failure.");
         }
     
    +    /**
    +     * Test DatagramSocket setSoTimeout with a negative timeout.
    +     */
    +    @Test
    +    public void testSetNegativeTimeout() throws Exception {
    +        try (DatagramSocket s = new DatagramSocket()) {
    +            expectThrows(IAE, () -> s.setSoTimeout(-1));
    +        }
    +    }
    +
    +    /**
    +     * Test DatagramSocketAdaptor setSoTimeout with a negative timeout.
    +     */
    +    @Test
    +    public void testNegativeTimeout() throws Exception {
    +        try (DatagramChannel dc = DatagramChannel.open()) {
    +            var s = dc.socket();
    +            expectThrows(IAE, () -> s.setSoTimeout(-1));
    +        }
    +    }
     }
    diff -r 2a49d43aaa6a -r ec9da3c0eaec test/jdk/java/nio/channels/DatagramChannel/AddressesAfterDisconnect.java
    --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    +++ b/test/jdk/java/nio/channels/DatagramChannel/AddressesAfterDisconnect.java	Wed Oct 09 23:22:56 2019 +0200
    @@ -0,0 +1,109 @@
    +/*
    + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
    + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    + *
    + * This code is free software; you can redistribute it and/or modify it
    + * under the terms of the GNU General Public License version 2 only, as
    + * published by the Free Software Foundation.
    + *
    + * This code is distributed in the hope that it will be useful, but WITHOUT
    + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    + * version 2 for more details (a copy is included in the LICENSE file that
    + * accompanied this code).
    + *
    + * You should have received a copy of the GNU General Public License version
    + * 2 along with this work; if not, write to the Free Software Foundation,
    + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    + *
    + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    + * or visit www.oracle.com if you need additional information or have any
    + * questions.
    + */
    +
    +/* @test
    + * @library /test/lib
    + * @summary Test DatagramChannel local address after disconnect.
    + * @requires (os.family != "mac")
    + * @run testng/othervm AddressesAfterDisconnect
    + * @run testng/othervm -Djava.net.preferIPv6Addresses=true AddressesAfterDisconnect
    + * @run testng/othervm -Djava.net.preferIPv4Stack=true AddressesAfterDisconnect
    + */
    +
    +import jdk.test.lib.net.IPSupport;
    +
    +import java.io.IOException;
    +import java.net.InetAddress;
    +import java.net.InetSocketAddress;
    +import java.net.SocketAddress;
    +import java.net.StandardProtocolFamily;
    +import java.nio.channels.DatagramChannel;
    +
    +import org.testng.annotations.Test;
    +import static org.testng.Assert.assertEquals;
    +import static org.testng.Assert.assertTrue;
    +import static org.testng.Assert.assertFalse;
    +
    +public class AddressesAfterDisconnect {
    +
    +    public static void main(String[] args) throws IOException {
    +        new AddressesAfterDisconnect().execute();
    +    }
    +
    +    @Test
    +    public void execute() throws IOException {
    +        IPSupport.throwSkippedExceptionIfNonOperational();
    +        boolean preferIPv6 = Boolean.getBoolean("java.net.preferIPv6Addresses");
    +
    +        // test with default protocol family
    +        try (DatagramChannel dc = DatagramChannel.open()) {
    +            System.out.println("Test with default");
    +            dc.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
    +            test(dc);
    +            test(dc);
    +        }
    +
    +        if (IPSupport.hasIPv6()) {
    +            // test with IPv6 only
    +            System.out.println("Test with IPv6 only");
    +            try (DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET6)) {
    +                dc.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
    +                test(dc);
    +                test(dc);
    +            }
    +        }
    +
    +        if (IPSupport.hasIPv4() && !preferIPv6) {
    +            // test with IPv4 only
    +            System.out.println("Test with IPv4 only");
    +            try (DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET)) {
    +                dc.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
    +                test(dc);
    +                test(dc);
    +            }
    +        }
    +    }
    +
    +    /**
    +     * Connect DatagramChannel to a server, write a datagram and disconnect. Invoke
    +     * a second or subsequent time with the same DatagramChannel instance to check
    +     * that disconnect works as expected.
    +     */
    +    static void test(DatagramChannel dc) throws IOException {
    +        SocketAddress local = dc.getLocalAddress();
    +        try (DatagramChannel server = DatagramChannel.open()) {
    +            server.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
    +            SocketAddress remote = server.getLocalAddress();
    +            dc.connect(remote);
    +            assertTrue(dc.isConnected());
    +            // comment the following two lines on OS X to see JDK-8231259
    +            assertEquals(dc.getLocalAddress(), local, "local address after connect");
    +            assertEquals(dc.getRemoteAddress(), remote, "remote address after connect");
    +            dc.disconnect();
    +            assertFalse(dc.isConnected());
    +            assertEquals(dc.getLocalAddress(), local, "local address after disconnect");
    +            assertEquals(dc.getRemoteAddress(), null, "remote address after disconnect");
    +        }
    +    }
    +
    +}
    diff -r 2a49d43aaa6a -r ec9da3c0eaec test/jdk/java/util/zip/FlaterTest.java
    --- a/test/jdk/java/util/zip/FlaterTest.java	Wed Oct 09 23:21:07 2019 +0200
    +++ b/test/jdk/java/util/zip/FlaterTest.java	Wed Oct 09 23:22:56 2019 +0200
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -23,10 +23,11 @@
     
     /**
      * @test
    - * @bug 6348045 6341887
    + * @bug 6348045 6341887 8231770
      * @summary GZipOutputStream/InputStream goes critical(calls JNI_Get*Critical)
    - * and causes slowness.  This test uses Deflater and Inflater directly.
    + * and causes slowness. This test uses Deflater and Inflater directly.
      * @key randomness
    + * @run main/othervm -Xcheck:jni FlaterTest
      */
     
     import java.nio.*;
    diff -r 2a49d43aaa6a -r ec9da3c0eaec test/jdk/javax/management/mxbean/ThreadStartTest.java
    --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    +++ b/test/jdk/javax/management/mxbean/ThreadStartTest.java	Wed Oct 09 23:22:56 2019 +0200
    @@ -0,0 +1,56 @@
    +/*
    + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
    + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    + *
    + * This code is free software; you can redistribute it and/or modify it
    + * under the terms of the GNU General Public License version 2 only, as
    + * published by the Free Software Foundation.
    + *
    + * This code is distributed in the hope that it will be useful, but WITHOUT
    + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    + * version 2 for more details (a copy is included in the LICENSE file that
    + * accompanied this code).
    + *
    + * You should have received a copy of the GNU General Public License version
    + * 2 along with this work; if not, write to the Free Software Foundation,
    + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    + *
    + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    + * or visit www.oracle.com if you need additional information or have any
    + * questions.
    + */
    +
    +/*
    + * @test
    + * @bug 8231666
    + * @summary Test checks that new threads could be successfully started when the thread
    + * table introduced in 8185005 is growing. The test enables the thread table by calling
    + * ThreadMXBean.getThreadInfo() and then creates a number of threads to force the thread
    + * table to grow.
    + *
    + * @run main ThreadStartTest
    + */
    +
    +import java.lang.management.ManagementFactory;
    +import java.lang.management.ThreadMXBean;
    +
    +public class ThreadStartTest {
    +    public static void main(String[] args) {
    +
    +        ThreadMXBean mbean = ManagementFactory.getThreadMXBean();
    +        // Enable thread table
    +        mbean.getThreadInfo(Thread.currentThread().getId());
    +
    +        // Create a large number of threads to make the thread table grow
    +        for (int i = 0; i < 1000; i++) {
    +            Thread t = new Thread(() -> {
    +                try {
    +                    Thread.sleep(1000);
    +                } catch (InterruptedException ex) {
    +                }
    +            });
    +            t.start();
    +        }
    +    }
    +}
    diff -r 2a49d43aaa6a -r ec9da3c0eaec test/jdk/security/infra/java/security/cert/CertPathValidator/certification/ComodoCA.java
    --- a/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/ComodoCA.java	Wed Oct 09 23:21:07 2019 +0200
    +++ b/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/ComodoCA.java	Wed Oct 09 23:22:56 2019 +0200
    @@ -23,7 +23,7 @@
     
      /*
      * @test
    - * @bug 8189131
    + * @bug 8189131 8231887
      * @summary Interoperability tests with Comodo RSA, ECC, userTrust RSA, and
      *          userTrust ECC CAs
      * @build ValidatePathWithParams
    @@ -112,13 +112,66 @@
     
         // Owner: CN=comodorsacertificationauthority-ev.comodoca.com, OU=COMODO EV SGC SSL, O=Sectigo Limited,
         // STREET="3rd Floor, 26 Office Village", STREET=Exchange Quay, STREET=Trafford Road, L=Salford,
    +    // OID.2.5.4.17=M5 3EQ, C=GB, OID.2.5.4.15=Private Organization, OID.1.3.6.1.4.1.311.60.2.1.3=GB,
    +    // SERIALNUMBER=04058690
    +    // Issuer: CN=COMODO RSA Extended Validation Secure Server CA, O=COMODO CA Limited, L=Salford,
    +    // ST=Greater Manchester, C=GB
    +    // Serial number: a0c7cabcc25ed9358ded02cc1d485545
    +    // Valid from: Sun Sep 29 17:00:00 PDT 2019 until: Tue Dec 28 15:59:59 PST 2021
    +    private static final String VALID = "-----BEGIN CERTIFICATE-----\n" +
    +            "MIIH0TCCBrmgAwIBAgIRAKDHyrzCXtk1je0CzB1IVUUwDQYJKoZIhvcNAQELBQAw\n" +
    +            "gZIxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO\n" +
    +            "BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMTgwNgYD\n" +
    +            "VQQDEy9DT01PRE8gUlNBIEV4dGVuZGVkIFZhbGlkYXRpb24gU2VjdXJlIFNlcnZl\n" +
    +            "ciBDQTAeFw0xOTA5MzAwMDAwMDBaFw0yMTEyMjgyMzU5NTlaMIIBPjERMA8GA1UE\n" +
    +            "BRMIMDQwNTg2OTAxEzARBgsrBgEEAYI3PAIBAxMCR0IxHTAbBgNVBA8TFFByaXZh\n" +
    +            "dGUgT3JnYW5pemF0aW9uMQswCQYDVQQGEwJHQjEPMA0GA1UEERMGTTUgM0VRMRAw\n" +
    +            "DgYDVQQHEwdTYWxmb3JkMRYwFAYDVQQJEw1UcmFmZm9yZCBSb2FkMRYwFAYDVQQJ\n" +
    +            "Ew1FeGNoYW5nZSBRdWF5MSUwIwYDVQQJExwzcmQgRmxvb3IsIDI2IE9mZmljZSBW\n" +
    +            "aWxsYWdlMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxGjAYBgNVBAsTEUNPTU9E\n" +
    +            "TyBFViBTR0MgU1NMMTgwNgYDVQQDEy9jb21vZG9yc2FjZXJ0aWZpY2F0aW9uYXV0\n" +
    +            "aG9yaXR5LWV2LmNvbW9kb2NhLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\n" +
    +            "AQoCggEBAND/eZQBTjpBDsuteKwl+zpTitF8tJzwHAhcQHC2AaLF/GJl1rnjx4Of\n" +
    +            "elMhKhN1Od9KU6onHGOd2w4mD4EiYK9TpXwuwTyzfkCmnkqxZjYK3KAJN013o4L+\n" +
    +            "8y1zsGVUulpN/GfMaxTb4XdmeSekTP91Phw3xezijBq3sa++1rO5RBaT1IHeHhHv\n" +
    +            "iC9WNrG8CIg/j5MyC9i43LZHiRXLER1LzT/MCIRsiG5AEbiYXV5BNd5SiiHtBJ1q\n" +
    +            "0ZJH+AxL2ERaT41VCppboZwThmJGGoky9FWjp6z8U6Enx0fAMJIZNEzW6LAJFKPE\n" +
    +            "ynEU004jFFCEumPUqqCC4ogxulphY80CAwEAAaOCA3EwggNtMB8GA1UdIwQYMBaA\n" +
    +            "FDna/8ooFIqodBMIueQOqdL6fp1pMB0GA1UdDgQWBBQ+S4ZhIrwOoeGs9BBT4uXq\n" +
    +            "89Ux/jAOBgNVHQ8BAf8EBAMCBaAwDAYDVR0TAQH/BAIwADAdBgNVHSUEFjAUBggr\n" +
    +            "BgEFBQcDAQYIKwYBBQUHAwIwTwYDVR0gBEgwRjA7BgwrBgEEAbIxAQIBBQEwKzAp\n" +
    +            "BggrBgEFBQcCARYdaHR0cHM6Ly9zZWN1cmUuY29tb2RvLmNvbS9DUFMwBwYFZ4EM\n" +
    +            "AQEwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5jb21vZG9jYS5jb20vQ09N\n" +
    +            "T0RPUlNBRXh0ZW5kZWRWYWxpZGF0aW9uU2VjdXJlU2VydmVyQ0EuY3JsMIGHBggr\n" +
    +            "BgEFBQcBAQR7MHkwUQYIKwYBBQUHMAKGRWh0dHA6Ly9jcnQuY29tb2RvY2EuY29t\n" +
    +            "L0NPTU9ET1JTQUV4dGVuZGVkVmFsaWRhdGlvblNlY3VyZVNlcnZlckNBLmNydDAk\n" +
    +            "BggrBgEFBQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMDoGA1UdEQQzMDGC\n" +
    +            "L2NvbW9kb3JzYWNlcnRpZmljYXRpb25hdXRob3JpdHktZXYuY29tb2RvY2EuY29t\n" +
    +            "MIIBfQYKKwYBBAHWeQIEAgSCAW0EggFpAWcAdQDuS723dc5guuFCaR+r4Z5mow9+\n" +
    +            "X7By2IMAxHuJeqj9ywAAAW2DAXefAAAEAwBGMEQCIDqP1einOiPHnaG1fOZMDrEc\n" +
    +            "RAxjq3vEl94fp4pkmke7AiBsJOvPE6irgcOO1/lnP7NRuln7iPJjU7T20PEK5/rm\n" +
    +            "KwB2AFWB1MIWkDYBSuoLm1c8U/DA5Dh4cCUIFy+jqh0HE9MMAAABbYMBd0kAAAQD\n" +
    +            "AEcwRQIhALgUI5XxM1NHbJDdr19h2pe3LhzK4tpuB/OQ9BgCyrGXAiBdr6mNCB/G\n" +
    +            "rbdVx0u7iezwC7mq7iaWugR3rrWlSA8fWQB2ALvZ37wfinG1k5Qjl6qSe0c4V5UK\n" +
    +            "q1LoGpCWZDaOHtGFAAABbYMBd1oAAAQDAEcwRQIgXbG32dagMeLhuZb+LSpJO1vI\n" +
    +            "BmxmRnNdiz5FbG9cCbwCIQCr1X9f+ebT5fhlDUNBURUorTtM8QQciBiueBqvHk7+\n" +
    +            "1DANBgkqhkiG9w0BAQsFAAOCAQEAM/A/1dgoc5NP1n+w3SX9qWcN7QT7ExdrnZSl\n" +
    +            "Ygn0PF2fx4gz7cvNKucbpQJNA4C9awGydyYK8/o5KDUXt3K7eb1OAZ/NZBjygsJs\n" +
    +            "ikXvxlBh8oEoqBOfOtr24l0NGUWnP8Qeu/VPcIMER4V8qX+in0pCXkSd67nkp6Bs\n" +
    +            "EcqhDPgmzdSC1gQHsZuBdotG14OfdH1cG1bRK6GadISLG1h8BFukVem42B149v8F\n" +
    +            "MCIUQAYprAVv2WlTZKBx9XzuK6IK3+klHZ07Jfvjvt7PPG5HKSMWBMnMaTHKcyQI\n" +
    +            "G3t91yw7BnNNInZlBSsFtqjbHhDcr7uruZdbi0rerSsi2qDr0w==\n" +
    +            "-----END CERTIFICATE-----";
    +
    +    // Owner: CN=comodorsacertificationauthority-ev.comodoca.com, OU=COMODO EV SGC SSL, O=Sectigo Limited,
    +    // STREET="3rd Floor, 26 Office Village", STREET=Exchange Quay, STREET=Trafford Road, L=Salford,
         // ST=Greater Manchester, OID.2.5.4.17=M5 3EQ, C=GB, OID.2.5.4.15=Private Organization,
         // OID.1.3.6.1.4.1.311.60.2.1.3=GB, SERIALNUMBER=04058690
         // Issuer: CN=COMODO RSA Extended Validation Secure Server CA, O=COMODO CA Limited, L=Salford,
         // ST=Greater Manchester, C=GB
         // Serial number: d3df2597cbed1ab6e02ee82021771614
         // Valid from: Wed Nov 28 16:00:00 PST 2018 until: Fri Feb 26 15:59:59 PST 2021
    -    private static final String VALID = "-----BEGIN CERTIFICATE-----\n" +
    +    private static final String REVOKED = "-----BEGIN CERTIFICATE-----\n" +
                 "MIIH7jCCBtagAwIBAgIRANPfJZfL7Rq24C7oICF3FhQwDQYJKoZIhvcNAQELBQAw\n" +
                 "gZIxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO\n" +
                 "BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMTgwNgYD\n" +
    @@ -164,60 +217,6 @@
                 "YrTYerPngjPbZB0bfLOja0vb\n" +
                 "-----END CERTIFICATE-----";
     
    -    // Owner: CN=comodorsacertificationauthority-ev.comodoca.com, OU=COMODO EV SGC SSL, O=COMODO CA Limited,
    -    // STREET="3rd Floor, 26 Office Village", STREET=Exchange Quay, STREET=Trafford Road, L=Salford,
    -    // ST=Greater Manchester, OID.2.5.4.17=M5 3EQ, C=GB, OID.2.5.4.15=Private Organization,
    -    // OID.1.3.6.1.4.1.311.60.2.1.3=GB, SERIALNUMBER=04058690
    -    // Issuer: CN=COMODO RSA Extended Validation Secure Server CA, O=COMODO CA Limited, L=Salford,
    -    // ST=Greater Manchester, C=GB
    -    // Serial number: 720aa2cfa40094521224f901a984b167
    -    // Valid from: Thu Jun 29 17:00:00 PDT 2017 until: Sun Sep 29 16:59:59 PDT 2019
    -    private static final String REVOKED = "-----BEGIN CERTIFICATE-----\n" +
    -            "MIIH8jCCBtqgAwIBAgIQcgqiz6QAlFISJPkBqYSxZzANBgkqhkiG9w0BAQsFADCB\n" +
    -            "kjELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G\n" +
    -            "A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxODA2BgNV\n" +
    -            "BAMTL0NPTU9ETyBSU0EgRXh0ZW5kZWQgVmFsaWRhdGlvbiBTZWN1cmUgU2VydmVy\n" +
    -            "IENBMB4XDTE3MDYzMDAwMDAwMFoXDTE5MDkyOTIzNTk1OVowggFdMREwDwYDVQQF\n" +
    -            "EwgwNDA1ODY5MDETMBEGCysGAQQBgjc8AgEDEwJHQjEdMBsGA1UEDxMUUHJpdmF0\n" +
    -            "ZSBPcmdhbml6YXRpb24xCzAJBgNVBAYTAkdCMQ8wDQYDVQQREwZNNSAzRVExGzAZ\n" +
    -            "BgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEWMBQG\n" +
    -            "A1UECRMNVHJhZmZvcmQgUm9hZDEWMBQGA1UECRMNRXhjaGFuZ2UgUXVheTElMCMG\n" +
    -            "A1UECRMcM3JkIEZsb29yLCAyNiBPZmZpY2UgVmlsbGFnZTEaMBgGA1UEChMRQ09N\n" +
    -            "T0RPIENBIExpbWl0ZWQxGjAYBgNVBAsTEUNPTU9ETyBFViBTR0MgU1NMMTgwNgYD\n" +
    -            "VQQDEy9jb21vZG9yc2FjZXJ0aWZpY2F0aW9uYXV0aG9yaXR5LWV2LmNvbW9kb2Nh\n" +
    -            "LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAND/eZQBTjpBDsut\n" +
    -            "eKwl+zpTitF8tJzwHAhcQHC2AaLF/GJl1rnjx4OfelMhKhN1Od9KU6onHGOd2w4m\n" +
    -            "D4EiYK9TpXwuwTyzfkCmnkqxZjYK3KAJN013o4L+8y1zsGVUulpN/GfMaxTb4Xdm\n" +
    -            "eSekTP91Phw3xezijBq3sa++1rO5RBaT1IHeHhHviC9WNrG8CIg/j5MyC9i43LZH\n" +
    -            "iRXLER1LzT/MCIRsiG5AEbiYXV5BNd5SiiHtBJ1q0ZJH+AxL2ERaT41VCppboZwT\n" +
    -            "hmJGGoky9FWjp6z8U6Enx0fAMJIZNEzW6LAJFKPEynEU004jFFCEumPUqqCC4ogx\n" +
    -            "ulphY80CAwEAAaOCA3QwggNwMB8GA1UdIwQYMBaAFDna/8ooFIqodBMIueQOqdL6\n" +
    -            "fp1pMB0GA1UdDgQWBBQ+S4ZhIrwOoeGs9BBT4uXq89Ux/jAOBgNVHQ8BAf8EBAMC\n" +
    -            "BaAwDAYDVR0TAQH/BAIwADAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw\n" +
    -            "TwYDVR0gBEgwRjA7BgwrBgEEAbIxAQIBBQEwKzApBggrBgEFBQcCARYdaHR0cHM6\n" +
    -            "Ly9zZWN1cmUuY29tb2RvLmNvbS9DUFMwBwYFZ4EMAQEwVgYDVR0fBE8wTTBLoEmg\n" +
    -            "R4ZFaHR0cDovL2NybC5jb21vZG9jYS5jb20vQ09NT0RPUlNBRXh0ZW5kZWRWYWxp\n" +
    -            "ZGF0aW9uU2VjdXJlU2VydmVyQ0EuY3JsMIGHBggrBgEFBQcBAQR7MHkwUQYIKwYB\n" +
    -            "BQUHMAKGRWh0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NPTU9ET1JTQUV4dGVuZGVk\n" +
    -            "VmFsaWRhdGlvblNlY3VyZVNlcnZlckNBLmNydDAkBggrBgEFBQcwAYYYaHR0cDov\n" +
    -            "L29jc3AuY29tb2RvY2EuY29tMDoGA1UdEQQzMDGCL2NvbW9kb3JzYWNlcnRpZmlj\n" +
    -            "YXRpb25hdXRob3JpdHktZXYuY29tb2RvY2EuY29tMIIBgAYKKwYBBAHWeQIEAgSC\n" +
    -            "AXAEggFsAWoAdgCkuQmQtBhYFIe7E6LMZ3AKPDWYBPkb37jjd80OyA3cEAAAAVz5\n" +
    -            "cV7GAAAEAwBHMEUCIQCpgc0Eqw3g4pr+oX88h5xgL1VEAiDpqAhbRtilgYwBbgIg\n" +
    -            "UaIm+n8AHi55nB//Sb4Nz18GYVcfELfpIzRh1vW9HbYAdwBWFAaaL9fC7NP14b1E\n" +
    -            "sj7HRna5vJkRXMDvlJhV1onQ3QAAAVz5cVybAAAEAwBIMEYCIQDdsgC4KZ++OP44\n" +
    -            "X7LbUcNaxe0kFzbctF2L3bnmhp9nXQIhAM0/g+PrZBIBpYlOtzidePi8bBHrLWn2\n" +
    -            "uBiP3pYIntl4AHcA7ku9t3XOYLrhQmkfq+GeZqMPfl+wctiDAMR7iXqo/csAAAFc\n" +
    -            "+XFeoQAABAMASDBGAiEAoySTb/QKw7JwtZtPHnECEMzgENQSFy58Kl+Mvcd3SmcC\n" +
    -            "IQD8cU66Ih3ejvt0OTX+lfxQPKyggQfm4Uk/lwn5LEJXbDANBgkqhkiG9w0BAQsF\n" +
    -            "AAOCAQEAKEaSYWn3Hi8rfJS4cMTJoMkVp2vpPH2dGXySBEy67TEGRw9+f75w3q95\n" +
    -            "r1m3P+xsR6dBoidTq/6wqUYI51lB4Fq9ylh1Stp5Gj54CuyT+S31l7lD7sl0KMsn\n" +
    -            "HDUDQHId7hKeORYpiIZOcrKOglKdi1uiGwDgoiLKh98lUrZA6durrhH+sl69wqp2\n" +
    -            "0XAu+3hurXzCoZFJfyngTO1kt9qcFUAxc5LofIa9QvC6VR7dI4aAh7dUpIRlnjG3\n" +
    -            "jJ1mUMTqWO6TFTtddb+uQjDqNgkYYYNuSax1WMEIZWbIi13EjXK1GPQUXJe6gQin\n" +
    -            "NUq9JH9NPK6m8A1YKT+wgzfTDeaV2Q==\n" +
    -            "-----END CERTIFICATE-----";
    -
         public void runTest(ValidatePathWithParams pathValidator) throws Exception {
             // Validate valid
             pathValidator.validate(new String[]{VALID, INT},
    @@ -226,7 +225,7 @@
             // Validate Revoked
             pathValidator.validate(new String[]{REVOKED, INT},
                     ValidatePathWithParams.Status.REVOKED,
    -                "Thu Nov 29 08:41:09 PST 2018", System.out);
    +                "Wed Oct 02 06:06:24 PDT 2019", System.out);
         }
     }
     
    @@ -263,13 +262,58 @@
     
         // Owner: CN=comodoecccertificationauthority-ev.comodoca.com, OU=COMODO EV SSL, O=Sectigo Limited,
         // STREET="3rd Floor, 26 Office Village", STREET=Exchange Quay, STREET=Trafford Road, L=Salford,
    +    // OID.2.5.4.17=M5 3EQ, C=GB, OID.2.5.4.15=Private Organization, OID.1.3.6.1.4.1.311.60.2.1.3=GB,
    +    // SERIALNUMBER=04058690
    +    // Issuer: CN=COMODO ECC Extended Validation Secure Server CA, O=COMODO CA Limited, L=Salford,
    +    // ST=Greater Manchester, C=GB
    +    // Serial number: 7972d9d8472a2d52ad1ee6edfb16cbe1
    +    // Valid from: Sun Sep 29 17:00:00 PDT 2019 until: Tue Dec 28 15:59:59 PST 2021
    +    private static final String VALID = "-----BEGIN CERTIFICATE-----\n" +
    +            "MIIGPzCCBeWgAwIBAgIQeXLZ2EcqLVKtHubt+xbL4TAKBggqhkjOPQQDAjCBkjEL\n" +
    +            "MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE\n" +
    +            "BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxODA2BgNVBAMT\n" +
    +            "L0NPTU9ETyBFQ0MgRXh0ZW5kZWQgVmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENB\n" +
    +            "MB4XDTE5MDkzMDAwMDAwMFoXDTIxMTIyODIzNTk1OVowggE6MREwDwYDVQQFEwgw\n" +
    +            "NDA1ODY5MDETMBEGCysGAQQBgjc8AgEDEwJHQjEdMBsGA1UEDxMUUHJpdmF0ZSBP\n" +
    +            "cmdhbml6YXRpb24xCzAJBgNVBAYTAkdCMQ8wDQYDVQQREwZNNSAzRVExEDAOBgNV\n" +
    +            "BAcTB1NhbGZvcmQxFjAUBgNVBAkTDVRyYWZmb3JkIFJvYWQxFjAUBgNVBAkTDUV4\n" +
    +            "Y2hhbmdlIFF1YXkxJTAjBgNVBAkTHDNyZCBGbG9vciwgMjYgT2ZmaWNlIFZpbGxh\n" +
    +            "Z2UxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDEWMBQGA1UECxMNQ09NT0RPIEVW\n" +
    +            "IFNTTDE4MDYGA1UEAxMvY29tb2RvZWNjY2VydGlmaWNhdGlvbmF1dGhvcml0eS1l\n" +
    +            "di5jb21vZG9jYS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS3bqoFLtNG\n" +
    +            "7/J9H5GKosDNbYL5SykVmU5FzgSEt81gyAWShkqMSfAnO50fpr65E+o86E+BR3o8\n" +
    +            "V9FAU5wuOaGBo4IDcDCCA2wwHwYDVR0jBBgwFoAU007DGbpYWdEcYLdhU0c7p3eP\n" +
    +            "+IowHQYDVR0OBBYEFOlnS3MqxwXDpne8IQMXMZHlVKRXMA4GA1UdDwEB/wQEAwIF\n" +
    +            "gDAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBP\n" +
    +            "BgNVHSAESDBGMDsGDCsGAQQBsjEBAgEFATArMCkGCCsGAQUFBwIBFh1odHRwczov\n" +
    +            "L3NlY3VyZS5jb21vZG8uY29tL0NQUzAHBgVngQwBATBWBgNVHR8ETzBNMEugSaBH\n" +
    +            "hkVodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9FQ0NFeHRlbmRlZFZhbGlk\n" +
    +            "YXRpb25TZWN1cmVTZXJ2ZXJDQS5jcmwwgYcGCCsGAQUFBwEBBHsweTBRBggrBgEF\n" +
    +            "BQcwAoZFaHR0cDovL2NydC5jb21vZG9jYS5jb20vQ09NT0RPRUNDRXh0ZW5kZWRW\n" +
    +            "YWxpZGF0aW9uU2VjdXJlU2VydmVyQ0EuY3J0MCQGCCsGAQUFBzABhhhodHRwOi8v\n" +
    +            "b2NzcC5jb21vZG9jYS5jb20wOgYDVR0RBDMwMYIvY29tb2RvZWNjY2VydGlmaWNh\n" +
    +            "dGlvbmF1dGhvcml0eS1ldi5jb21vZG9jYS5jb20wggF8BgorBgEEAdZ5AgQCBIIB\n" +
    +            "bASCAWgBZgB1AO5Lvbd1zmC64UJpH6vhnmajD35fsHLYgwDEe4l6qP3LAAABbYME\n" +
    +            "EzgAAAQDAEYwRAIgbdo71lBleuJiq+D0ZLp51oVUyWD9EyrtgBSCNwIW4cMCIAqg\n" +
    +            "0VFTWHEmAVjaV23fGj3Ybu3mpSiHr6viGlgA2lYaAHUAVYHUwhaQNgFK6gubVzxT\n" +
    +            "8MDkOHhwJQgXL6OqHQcT0wwAAAFtgwQTKAAABAMARjBEAiBb/gW1RU7kgFBiNpHx\n" +
    +            "LStujKIocyENUTXsMbsac+LktwIgXbEr8vOOCEdBdXQ2F/FKec8ft6gz57mHNmwl\n" +
    +            "pp7phbQAdgC72d+8H4pxtZOUI5eqkntHOFeVCqtS6BqQlmQ2jh7RhQAAAW2DBBM6\n" +
    +            "AAAEAwBHMEUCIQDjKN3h86ofR94+JxLFoYuoA+DRtxEY8XGg+NQXlZfUrgIgEoO2\n" +
    +            "ZzKbGfohdwj/WtDwJDRX5pjXF4M0nECiwtYXDIwwCgYIKoZIzj0EAwIDSAAwRQIg\n" +
    +            "AkIRVQBwrElFjrnqk5XPvnlnwkIm1A70ayqOf1FexoQCIQC8tBTn//RCfrhcgTjd\n" +
    +            "ER4wRjFfFoc6lC68OHGVg9CZZg==\n" +
    +            "-----END CERTIFICATE-----";
    +
    +    // Owner: CN=comodoecccertificationauthority-ev.comodoca.com, OU=COMODO EV SSL, O=Sectigo Limited,
    +    // STREET="3rd Floor, 26 Office Village", STREET=Exchange Quay, STREET=Trafford Road, L=Salford,
         // ST=Greater Manchester, OID.2.5.4.17=M5 3EQ, C=GB, OID.2.5.4.15=Private Organization,
         // OID.1.3.6.1.4.1.311.60.2.1.3=GB, SERIALNUMBER=04058690
         // Issuer: CN=COMODO ECC Extended Validation Secure Server CA, O=COMODO CA Limited, L=Salford,
         // ST=Greater Manchester, C=GB
         // Serial number: 603a5c2f85b63e00ba46ce8c3f6000b0
         // Valid from: Wed Nov 28 16:00:00 PST 2018 until: Fri Feb 26 15:59:59 PST 2021
    -    private static final String VALID = "-----BEGIN CERTIFICATE-----\n" +
    +    private static final String REVOKED = "-----BEGIN CERTIFICATE-----\n" +
                 "MIIGXzCCBgWgAwIBAgIQYDpcL4W2PgC6Rs6MP2AAsDAKBggqhkjOPQQDAjCBkjEL\n" +
                 "MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE\n" +
                 "BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxODA2BgNVBAMT\n" +
    @@ -307,52 +351,6 @@
                 "KOC7\n" +
                 "-----END CERTIFICATE-----";
     
    -    // Owner: CN=comodoecccertificationauthority-ev.comodoca.com, OU=COMODO EV SSL, O=COMODO CA Limited,
    -    // STREET="3rd Floor, 26 Office Village", STREET=Exchange Quay, STREET=Trafford Road, L=Salford,
    -    // ST=Greater Manchester, OID.2.5.4.17=M5 3EQ, C=GB, OID.2.5.4.15=Private Organization,
    -    // OID.1.3.6.1.4.1.311.60.2.1.3=GB, SERIALNUMBER=04058690
    -    // Issuer: CN=COMODO ECC Extended Validation Secure Server CA, O=COMODO CA Limited, L=Salford,
    -    // ST=Greater Manchester, C=GB
    -    // Serial number: 414e5d66ec7d15ca504213f2811d57af
    -    // Valid from: Mon Jul 03 17:00:00 PDT 2017 until: Thu Oct 03 16:59:59 PDT 2019
    -    private static final String REVOKED = "-----BEGIN CERTIFICATE-----\n" +
    -            "MIIGYDCCBgWgAwIBAgIQQU5dZux9FcpQQhPygR1XrzAKBggqhkjOPQQDAjCBkjEL\n" +
    -            "MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE\n" +
    -            "BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxODA2BgNVBAMT\n" +
    -            "L0NPTU9ETyBFQ0MgRXh0ZW5kZWQgVmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENB\n" +
    -            "MB4XDTE3MDcwNDAwMDAwMFoXDTE5MTAwMzIzNTk1OVowggFZMREwDwYDVQQFEwgw\n" +
    -            "NDA1ODY5MDETMBEGCysGAQQBgjc8AgEDEwJHQjEdMBsGA1UEDxMUUHJpdmF0ZSBP\n" +
    -            "cmdhbml6YXRpb24xCzAJBgNVBAYTAkdCMQ8wDQYDVQQREwZNNSAzRVExGzAZBgNV\n" +
    -            "BAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEWMBQGA1UE\n" +
    -            "CRMNVHJhZmZvcmQgUm9hZDEWMBQGA1UECRMNRXhjaGFuZ2UgUXVheTElMCMGA1UE\n" +
    -            "CRMcM3JkIEZsb29yLCAyNiBPZmZpY2UgVmlsbGFnZTEaMBgGA1UEChMRQ09NT0RP\n" +
    -            "IENBIExpbWl0ZWQxFjAUBgNVBAsTDUNPTU9ETyBFViBTU0wxODA2BgNVBAMTL2Nv\n" +
    -            "bW9kb2VjY2NlcnRpZmljYXRpb25hdXRob3JpdHktZXYuY29tb2RvY2EuY29tMFkw\n" +
    -            "EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEt26qBS7TRu/yfR+RiqLAzW2C+UspFZlO\n" +
    -            "Rc4EhLfNYMgFkoZKjEnwJzudH6a+uRPqPOhPgUd6PFfRQFOcLjmhgaOCA3EwggNt\n" +
    -            "MB8GA1UdIwQYMBaAFNNOwxm6WFnRHGC3YVNHO6d3j/iKMB0GA1UdDgQWBBTpZ0tz\n" +
    -            "KscFw6Z3vCEDFzGR5VSkVzAOBgNVHQ8BAf8EBAMCBYAwDAYDVR0TAQH/BAIwADAd\n" +
    -            "BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwTwYDVR0gBEgwRjA7BgwrBgEE\n" +
    -            "AbIxAQIBBQEwKzApBggrBgEFBQcCARYdaHR0cHM6Ly9zZWN1cmUuY29tb2RvLmNv\n" +
    -            "bS9DUFMwBwYFZ4EMAQEwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5jb21v\n" +
    -            "ZG9jYS5jb20vQ09NT0RPRUNDRXh0ZW5kZWRWYWxpZGF0aW9uU2VjdXJlU2VydmVy\n" +
    -            "Q0EuY3JsMIGHBggrBgEFBQcBAQR7MHkwUQYIKwYBBQUHMAKGRWh0dHA6Ly9jcnQu\n" +
    -            "Y29tb2RvY2EuY29tL0NPTU9ET0VDQ0V4dGVuZGVkVmFsaWRhdGlvblNlY3VyZVNl\n" +
    -            "cnZlckNBLmNydDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29t\n" +
    -            "MDoGA1UdEQQzMDGCL2NvbW9kb2VjY2NlcnRpZmljYXRpb25hdXRob3JpdHktZXYu\n" +
    -            "Y29tb2RvY2EuY29tMIIBfQYKKwYBBAHWeQIEAgSCAW0EggFpAWcAdgCkuQmQtBhY\n" +
    -            "FIe7E6LMZ3AKPDWYBPkb37jjd80OyA3cEAAAAV0NLqsqAAAEAwBHMEUCIAz9Jjq3\n" +
    -            "qLUd/a2PYZnLGsEG/MrL7vab5rmGBg8RGAJxAiEA7JJnar07NIjCLLO77xJ3UFcu\n" +
    -            "UMM3M8JgGC8wbuRwxbUAdgBWFAaaL9fC7NP14b1Esj7HRna5vJkRXMDvlJhV1onQ\n" +
    -            "3QAAAV0NLqjmAAAEAwBHMEUCIHRvPWKr7vPMBWx1gLPkt8inPINWPNSoax178e5A\n" +
    -            "D0cPAiEAvRL/VP4DLiyHvcU9AOqTzQXGuWCzswWKG59hSm7gS4kAdQDuS723dc5g\n" +
    -            "uuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAV0NLqsDAAAEAwBGMEQCIFALT043\n" +
    -            "X5IffLsxIAGXTrWgkZHf12QKgrYKXVB629eOAiAIeci2xi3fUW6mU8tT4LwyjowV\n" +
    -            "DkrSCw1ZMo0JApsfzTAKBggqhkjOPQQDAgNJADBGAiEA7HUxjwx0MBC+4PuPx4Z1\n" +
    -            "WpKz7jdHOMTh1sdaoVV5hNoCIQDrnjBFUopXHTvm/rj+aMFIeYejggPqv14KJOqT\n" +
    -            "gym+uA==\n" +
    -            "-----END CERTIFICATE-----";
    -
         public void runTest(ValidatePathWithParams pathValidator) throws Exception {
             // Validate valid
             pathValidator.validate(new String[]{VALID, INT},
    @@ -361,19 +359,61 @@
             // Validate Revoked
             pathValidator.validate(new String[]{REVOKED, INT},
                     ValidatePathWithParams.Status.REVOKED,
    -                "Thu Nov 29 08:12:02 PST 2018", System.out);
    +                "Wed Oct 02 06:05:57 PDT 2019", System.out);
         }
     }
     
     class ComodoUserTrustRSA {
     
    +    // Owner: CN=Sectigo RSA Extended Validation Secure Server CA, O=Sectigo Limited, L=Salford,
    +    // ST=Greater Manchester, C=GB
    +    // Issuer: CN=USERTrust RSA Certification Authority, O=The USERTRUST Network, L=Jersey City, ST=New Jersey, C=US
    +    // Serial number: 284e39c14b386d889c7299e58cd05a57
    +    // Valid from: Thu Nov 01 17:00:00 PDT 2018 until: Tue Dec 31 15:59:59 PST 2030
    +    private static final String INT_VALID = "-----BEGIN CERTIFICATE-----\n" +
    +            "MIIGNDCCBBygAwIBAgIQKE45wUs4bYiccpnljNBaVzANBgkqhkiG9w0BAQwFADCB\n" +
    +            "iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl\n" +
    +            "cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV\n" +
    +            "BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTgx\n" +
    +            "MTAyMDAwMDAwWhcNMzAxMjMxMjM1OTU5WjCBkTELMAkGA1UEBhMCR0IxGzAZBgNV\n" +
    +            "BAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEYMBYGA1UE\n" +
    +            "ChMPU2VjdGlnbyBMaW1pdGVkMTkwNwYDVQQDEzBTZWN0aWdvIFJTQSBFeHRlbmRl\n" +
    +            "ZCBWYWxpZGF0aW9uIFNlY3VyZSBTZXJ2ZXIgQ0EwggEiMA0GCSqGSIb3DQEBAQUA\n" +
    +            "A4IBDwAwggEKAoIBAQCaoslYBiqFev0Yc4TXPa0s9oliMcn9VaENfTUK4GVT7niB\n" +
    +            "QXxC6Mt8kTtvyr5lU92hDQDh2WDPQsZ7oibh75t2kowT3z1S+Sy1GsUDM4NbdOde\n" +
    +            "orcmzFm/b4bwD4G/G+pB4EX1HSfjN9eT0Hje+AGvCrd2MmnxJ+Yymv9BH9OB65jK\n" +
    +            "rUO9Na4iHr48XWBDFvzsPCJ11Uioof6dRBVp+Lauj88Z7k2X8d606HeXn43h6acp\n" +
    +            "LLURWyqXM0CrzedVWBzuXKuBEaqD6w/1VpLJvSU+wl3ScvXSLFp82DSRJVJONXWl\n" +
    +            "dp9gjJioPGRByeZw11k3galbbF5gFK9xSnbDx29LAgMBAAGjggGNMIIBiTAfBgNV\n" +
    +            "HSMEGDAWgBRTeb9aqitKz1SA4dibwJ3ysgNmyzAdBgNVHQ4EFgQULGn/gMmHkK40\n" +
    +            "4bTnTJOFmUDpp7IwDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAw\n" +
    +            "HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMDoGA1UdIAQzMDEwLwYEVR0g\n" +
    +            "ADAnMCUGCCsGAQUFBwIBFhlodHRwczovL2Nwcy51c2VydHJ1c3QuY29tMFAGA1Ud\n" +
    +            "HwRJMEcwRaBDoEGGP2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VU0VSVHJ1c3RS\n" +
    +            "U0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDB2BggrBgEFBQcBAQRqMGgwPwYI\n" +
    +            "KwYBBQUHMAKGM2h0dHA6Ly9jcnQudXNlcnRydXN0LmNvbS9VU0VSVHJ1c3RSU0FB\n" +
    +            "ZGRUcnVzdENBLmNydDAlBggrBgEFBQcwAYYZaHR0cDovL29jc3AudXNlcnRydXN0\n" +
    +            "LmNvbTANBgkqhkiG9w0BAQwFAAOCAgEAQ4AzPxVypLyy3IjUUmVl7FaxrHsXQq2z\n" +
    +            "Zt2gKnHQShuA+5xpRPNndjvhHk4D08PZXUe6Im7E5knqxtyl5aYdldb+HI/7f+zd\n" +
    +            "W/1ub2N4Vq4ZYUjcZ1ECOFK7Z2zoNicDmU+Fe/TreXPuPsDicTG/tMcWEVM558OQ\n" +
    +            "TJkB2LK3ZhGukWM/RTMRcRdXaXOX8Lh0ylzRO1O0ObXytvOFpkkkD92HGsfS06i7\n" +
    +            "NLDPJEeZXqzHE5Tqj7VSAj+2luwfaXaPLD8lQEVci8xmsPGOn0mXE1ZzsChEPhVq\n" +
    +            "FYQUsbiRJRhidKauhd+G2CkRTcR5fpsuz+iStB9s5Fks9lKoXnn0hv78VYjvR78C\n" +
    +            "Cvj5FW/ounHjWTWMb3il9S5ngbFGcelB1l/MQkR63+1ybdi2OpjNWJCftxOWUpkC\n" +
    +            "xaRdnOnSj7GQY0NLn8Gtq9FcSZydtkVgXpouSFZkXNS/MYwbcCCcRKBbrk8ss0SI\n" +
    +            "Xg1gTURjh9VP1OHm0OktYcUw9e90wHIDn7h0qA+bWOsZquSRzT4s2crF3ZSA3tuV\n" +
    +            "/UJ33mjdVO8wBD8aI5y10QreSPJvZHHNDyCmoyjXvNhR+u3arXUoHWxO+MZBeXbi\n" +
    +            "iF7Nwn/IEmQvWBW8l6D26CXIavcY1kAJcfyzHkrPbLo+fAOa/KFl3lIU+0biEVNk\n" +
    +            "Q9zXE6hC6X4=\n" +
    +            "-----END CERTIFICATE-----";
    +
         // Owner: CN=USERTrust RSA Extended Validation Secure Server CA,
         // O=The USERTRUST Network, L=Jersey City, ST=New Jersey, C=US
         // Issuer: CN=USERTrust RSA Certification Authority, O=The USERTRUST Network,
         // L=Jersey City, ST=New Jersey, C=US
         // Serial number: f6bb751efa7d2e8368e606407334f83
         // Valid from: Sat Feb 11 16:00:00 PST 2012 until: Thu Feb 11 15:59:59 PST 2027
    -    private static final String INT = "-----BEGIN CERTIFICATE-----\n"
    +    private static final String INT_REVOKED = "-----BEGIN CERTIFICATE-----\n"
                 + "MIIGGTCCBAGgAwIBAgIQD2u3Ue+n0ug2jmBkBzNPgzANBgkqhkiG9w0BAQwFADCB\n"
                 + "iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl\n"
                 + "cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV\n"
    @@ -409,15 +449,69 @@
                 + "4fokbdNREXoShKClNIPbB5iY+WdSzb9CKLyb96g=\n"
                 + "-----END CERTIFICATE-----";
     
    -    // Owner: CN=usertrustrsacertificationauthority-ev.comodoca.com, OU=COMODO EV SGC SSL,
    -    // O=Sectigo Limited, STREET="3rd Floor, 26 Office Village", STREET=Exchange Quay, STREET=Trafford Road,
    -    // L=Salford, ST=Greater Manchester, OID.2.5.4.17=M5 3EQ, C=GB, OID.2.5.4.15=Private Organization,
    +    // Owner: CN=usertrustrsacertificationauthority-ev.comodoca.com, OU=COMODO EV SGC SSL, O=Sectigo Limited,
    +    // STREET="3rd Floor, 26 Office Village", STREET=Exchange Quay, STREET=Trafford Road, L=Salford, ST=Manchester,
    +    // OID.2.5.4.17=M5 3EQ, C=GB, OID.2.5.4.15=Private Organization, OID.1.3.6.1.4.1.311.60.2.1.3=GB,
    +    // SERIALNUMBER=04058690
    +    // Issuer: CN=Sectigo RSA Extended Validation Secure Server CA, O=Sectigo Limited, L=Salford,
    +    // ST=Greater Manchester, C=GB
    +    // Serial number: b07fd164b5790c9d5d1fddff5819cdb2
    +    // Valid from: Sun Sep 29 17:00:00 PDT 2019 until: Tue Dec 28 15:59:59 PST 2021
    +    private static final String VALID = "-----BEGIN CERTIFICATE-----\n" +
    +            "MIIH5TCCBs2gAwIBAgIRALB/0WS1eQydXR/d/1gZzbIwDQYJKoZIhvcNAQELBQAw\n" +
    +            "gZExCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO\n" +
    +            "BgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDE5MDcGA1UE\n" +
    +            "AxMwU2VjdGlnbyBSU0EgRXh0ZW5kZWQgVmFsaWRhdGlvbiBTZWN1cmUgU2VydmVy\n" +
    +            "IENBMB4XDTE5MDkzMDAwMDAwMFoXDTIxMTIyODIzNTk1OVowggFWMREwDwYDVQQF\n" +
    +            "EwgwNDA1ODY5MDETMBEGCysGAQQBgjc8AgEDEwJHQjEdMBsGA1UEDxMUUHJpdmF0\n" +
    +            "ZSBPcmdhbml6YXRpb24xCzAJBgNVBAYTAkdCMQ8wDQYDVQQREwZNNSAzRVExEzAR\n" +
    +            "BgNVBAgTCk1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxFjAUBgNVBAkTDVRy\n" +
    +            "YWZmb3JkIFJvYWQxFjAUBgNVBAkTDUV4Y2hhbmdlIFF1YXkxJTAjBgNVBAkTHDNy\n" +
    +            "ZCBGbG9vciwgMjYgT2ZmaWNlIFZpbGxhZ2UxGDAWBgNVBAoTD1NlY3RpZ28gTGlt\n" +
    +            "aXRlZDEaMBgGA1UECxMRQ09NT0RPIEVWIFNHQyBTU0wxOzA5BgNVBAMTMnVzZXJ0\n" +
    +            "cnVzdHJzYWNlcnRpZmljYXRpb25hdXRob3JpdHktZXYuY29tb2RvY2EuY29tMIIB\n" +
    +            "IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnh/rxeiYwpLa651eLvGnR+RE\n" +
    +            "rhDWkTZtqZcHw9Oy7JL2uELyEPbM+v0az40cBHS0bQZJZbWmXNukMUMSwIb4z7t8\n" +
    +            "OXlxz9uvxEufvlqBl4qeC/z3LpFBRRHEero3yGKVwkoe1aP2Pq7Udi+7i7eVZZdA\n" +
    +            "1ticxZWo/UBU9mwbIOYqf/4xzZ6G891hKb+NAuuEfxG52vXZl8odMThfHuDlkfS7\n" +
    +            "nZMQBaO40KJeSEBhr+5TIS7d7tWWye/F6oEQ0+dHBiF9PyZ1dXoO8aue/80mP+0F\n" +
    +            "MYTmRFsKHge6ZjojfH9cLlR5kTqtP5Tqh5GBQ4zp3uyIBBU6ylKp9PNHkewGUQID\n" +
    +            "AQABo4IDbjCCA2owHwYDVR0jBBgwFoAULGn/gMmHkK404bTnTJOFmUDpp7IwHQYD\n" +
    +            "VR0OBBYEFHz7cvDn1LYe2M+z4plwQn7rt938MA4GA1UdDwEB/wQEAwIFoDAMBgNV\n" +
    +            "HRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBJBgNVHSAE\n" +
    +            "QjBAMDUGDCsGAQQBsjEBAgEFATAlMCMGCCsGAQUFBwIBFhdodHRwczovL3NlY3Rp\n" +
    +            "Z28uY29tL0NQUzAHBgVngQwBATBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3Js\n" +
    +            "LnNlY3RpZ28uY29tL1NlY3RpZ29SU0FFeHRlbmRlZFZhbGlkYXRpb25TZWN1cmVT\n" +
    +            "ZXJ2ZXJDQS5jcmwwgYYGCCsGAQUFBwEBBHoweDBRBggrBgEFBQcwAoZFaHR0cDov\n" +
    +            "L2NydC5zZWN0aWdvLmNvbS9TZWN0aWdvUlNBRXh0ZW5kZWRWYWxpZGF0aW9uU2Vj\n" +
    +            "dXJlU2VydmVyQ0EuY3J0MCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5zZWN0aWdv\n" +
    +            "LmNvbTA9BgNVHREENjA0gjJ1c2VydHJ1c3Ryc2FjZXJ0aWZpY2F0aW9uYXV0aG9y\n" +
    +            "aXR5LWV2LmNvbW9kb2NhLmNvbTCCAX4GCisGAQQB1nkCBAIEggFuBIIBagFoAHYA\n" +
    +            "7ku9t3XOYLrhQmkfq+GeZqMPfl+wctiDAMR7iXqo/csAAAFtgzv54wAABAMARzBF\n" +
    +            "AiB5PmhsK3zU3XdKvyxw/wWHMmLI7apHLa1yKdjkA8H+ggIhALdUx7Tl8aeWhK6z\n" +
    +            "lh+PHvMAdCcAJK6w9qBJGQtSrYO5AHUAVYHUwhaQNgFK6gubVzxT8MDkOHhwJQgX\n" +
    +            "L6OqHQcT0wwAAAFtgzv5zgAABAMARjBEAiBumSwAUamibqJXTN2cf/H3mjd0T35/\n" +
    +            "UK9w2hu9gFobxgIgSXTLndHyqFUmcmquu3It0WC1yl6YMceGixbQL1e8BQcAdwC7\n" +
    +            "2d+8H4pxtZOUI5eqkntHOFeVCqtS6BqQlmQ2jh7RhQAAAW2DO/nXAAAEAwBIMEYC\n" +
    +            "IQDHRs10oYoXE5yq6WsiksjdQsUWZNpbSsrmz0u+KlxTVQIhAJ4rvHItKSeJLkaN\n" +
    +            "S3YpVZnkN8tOwuxPsYeyVx/BtaNpMA0GCSqGSIb3DQEBCwUAA4IBAQAPFIsUFymo\n" +
    +            "VTp0vntHrZpBApBQzDeriQv7Bi7tmou/Ng47RtXW3DjGdrePGSfOdl7h62k8qprU\n" +
    +            "JeLyloDqhvmT/CG/hdwrfZ3Sv3N2xpetGcnW5S3oEi3m+/M1ls9eD+x1vybqV9Kd\n" +
    +            "lcjuV7SYDlbvAS9w7TcygudhdW0cI8XTCvesGKohBkAlqaQ/MWYpt4WvsxHjbWgn\n" +
    +            "5ZlIYR6A1ZFEjADifViH/5AA79lgGhAskkIWPjvRFalEVKTKtjhRK76eCfZs4Frr\n" +
    +            "CEOpon+BeNKk+x/K/r10dSoWe0SV2uGVxTD83zkP++eREwo1hTgn8bXn7ftlnA3j\n" +
    +            "7ml+Usz6udaD\n" +
    +            "-----END CERTIFICATE-----";
    +
    +    // Owner: CN=usertrustrsacertificationauthority-ev.comodoca.com, OU=COMODO EV SGC SSL, O=Sectigo Limited,
    +    // STREET="3rd Floor, 26 Office Village", STREET=Exchange Quay, STREET=Trafford Road, L=Salford,
    +    // ST=Greater Manchester, OID.2.5.4.17=M5 3EQ, C=GB, OID.2.5.4.15=Private Organization,
         // OID.1.3.6.1.4.1.311.60.2.1.3=GB, SERIALNUMBER=04058690
         // Issuer: CN=USERTrust RSA Extended Validation Secure Server CA, O=The USERTRUST Network, L=Jersey City,
         // ST=New Jersey, C=US
         // Serial number: d3c204e8df6a1539568cf15e97e57b1d
         // Valid from: Wed Nov 28 16:00:00 PST 2018 until: Fri Feb 26 15:59:59 PST 2021
    -    private static final String VALID = "-----BEGIN CERTIFICATE-----\n" +
    +    private static final String REVOKED = "-----BEGIN CERTIFICATE-----\n" +
                 "MIIIADCCBuigAwIBAgIRANPCBOjfahU5VozxXpflex0wDQYJKoZIhvcNAQELBQAw\n" +
                 "gZUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5MRQwEgYDVQQHEwtK\n" +
                 "ZXJzZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMTswOQYD\n" +
    @@ -463,81 +557,56 @@
                 "3Ld31zbQaywKdpCsT74/hEBMfcDiP02mmtyrlqHD4R3tdYne\n" +
                 "-----END CERTIFICATE-----";
     
    -    // Owner: CN=usertrustrsacertificationauthority-ev.comodoca.com, OU=COMODO EV SGC SSL, O=COMODO CA Limited,
    -    // STREET="3rd Floor, 26 Office Village", STREET=Exchange Quay, STREET=Trafford Road, L=Salford,
    -    // ST=Greater Manchester, OID.2.5.4.17=M5 3EQ, C=GB, OID.2.5.4.15=Private Organization,
    -    // OID.1.3.6.1.4.1.311.60.2.1.3=GB, SERIALNUMBER=04058690
    -    // Issuer: CN=USERTrust RSA Extended Validation Secure Server CA, O=The USERTRUST Network, L=Jersey City,
    -    // ST=New Jersey, C=US
    -    // Serial number: ffcada019c9fb1155a32300083cb99c9
    -    // Valid from: Mon Jul 03 17:00:00 PDT 2017 until: Thu Oct 03 16:59:59 PDT 2019
    -    private static final String REVOKED = "-----BEGIN CERTIFICATE-----\n" +
    -            "MIIIATCCBumgAwIBAgIRAP/K2gGcn7EVWjIwAIPLmckwDQYJKoZIhvcNAQELBQAw\n" +
    -            "gZUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5MRQwEgYDVQQHEwtK\n" +
    -            "ZXJzZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMTswOQYD\n" +
    -            "VQQDEzJVU0VSVHJ1c3QgUlNBIEV4dGVuZGVkIFZhbGlkYXRpb24gU2VjdXJlIFNl\n" +
    -            "cnZlciBDQTAeFw0xNzA3MDQwMDAwMDBaFw0xOTEwMDMyMzU5NTlaMIIBYDERMA8G\n" +
    -            "A1UEBRMIMDQwNTg2OTAxEzARBgsrBgEEAYI3PAIBAxMCR0IxHTAbBgNVBA8TFFBy\n" +
    -            "aXZhdGUgT3JnYW5pemF0aW9uMQswCQYDVQQGEwJHQjEPMA0GA1UEERMGTTUgM0VR\n" +
    -            "MRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQx\n" +
    -            "FjAUBgNVBAkTDVRyYWZmb3JkIFJvYWQxFjAUBgNVBAkTDUV4Y2hhbmdlIFF1YXkx\n" +
    -            "JTAjBgNVBAkTHDNyZCBGbG9vciwgMjYgT2ZmaWNlIFZpbGxhZ2UxGjAYBgNVBAoT\n" +
    -            "EUNPTU9ETyBDQSBMaW1pdGVkMRowGAYDVQQLExFDT01PRE8gRVYgU0dDIFNTTDE7\n" +
    -            "MDkGA1UEAxMydXNlcnRydXN0cnNhY2VydGlmaWNhdGlvbmF1dGhvcml0eS1ldi5j\n" +
    -            "b21vZG9jYS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCeH+vF\n" +
    -            "6JjCktrrnV4u8adH5ESuENaRNm2plwfD07Lskva4QvIQ9sz6/RrPjRwEdLRtBkll\n" +
    -            "taZc26QxQxLAhvjPu3w5eXHP26/ES5++WoGXip4L/PcukUFFEcR6ujfIYpXCSh7V\n" +
    -            "o/Y+rtR2L7uLt5Vll0DW2JzFlaj9QFT2bBsg5ip//jHNnobz3WEpv40C64R/Ebna\n" +
    -            "9dmXyh0xOF8e4OWR9LudkxAFo7jQol5IQGGv7lMhLt3u1ZbJ78XqgRDT50cGIX0/\n" +
    -            "JnV1eg7xq57/zSY/7QUxhOZEWwoeB7pmOiN8f1wuVHmROq0/lOqHkYFDjOne7IgE\n" +
    -            "FTrKUqn080eR7AZRAgMBAAGjggN8MIIDeDAfBgNVHSMEGDAWgBQvgU/iZvq8aL+Z\n" +
    -            "Q4RSiSA6gvOkpTAdBgNVHQ4EFgQUfPty8OfUth7Yz7PimXBCfuu33fwwDgYDVR0P\n" +
    -            "AQH/BAQDAgWgMAwGA1UdEwEB/wQCMAAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsG\n" +
    -            "AQUFBwMCMEsGA1UdIAREMEIwNwYMKwYBBAGyMQECAQUBMCcwJQYIKwYBBQUHAgEW\n" +
    -            "GWh0dHBzOi8vY3BzLnVzZXJ0cnVzdC5jb20wBwYFZ4EMAQEwWgYDVR0fBFMwUTBP\n" +
    -            "oE2gS4ZJaHR0cDovL2NybC51c2VydHJ1c3QuY29tL1VTRVJUcnVzdFJTQUV4dGVu\n" +
    -            "ZGVkVmFsaWRhdGlvblNlY3VyZVNlcnZlckNBLmNybDCBjQYIKwYBBQUHAQEEgYAw\n" +
    -            "fjBVBggrBgEFBQcwAoZJaHR0cDovL2NydC51c2VydHJ1c3QuY29tL1VTRVJUcnVz\n" +
    -            "dFJTQUV4dGVuZGVkVmFsaWRhdGlvblNlY3VyZVNlcnZlckNBLmNydDAlBggrBgEF\n" +
    -            "BQcwAYYZaHR0cDovL29jc3AudXNlcnRydXN0LmNvbTA9BgNVHREENjA0gjJ1c2Vy\n" +
    -            "dHJ1c3Ryc2FjZXJ0aWZpY2F0aW9uYXV0aG9yaXR5LWV2LmNvbW9kb2NhLmNvbTCC\n" +
    -            "AX8GCisGAQQB1nkCBAIEggFvBIIBawFpAHYApLkJkLQYWBSHuxOizGdwCjw1mAT5\n" +
    -            "G9+443fNDsgN3BAAAAFdDU2iYQAABAMARzBFAiB0o4GnVHD8MeVQ32D0XYu+EQQW\n" +
    -            "jvN78rmCfk0OEBxyFAIhAKgyctIn0IaDJiZzsrtAiqEnkcMtuh8o+R0Rqw1ygAjk\n" +
    -            "AHcAVhQGmi/XwuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFdDU2gFgAABAMA\n" +
    -            "SDBGAiEA7mcmZ8H5uHuNCdI0CVxsqDZQcZX/gVk94KckePkzQoACIQCHwm5hcvNC\n" +
    -            "M8vNmFkboQN79DglRctHrlh143A6mUTk8QB2AO5Lvbd1zmC64UJpH6vhnmajD35f\n" +
    -            "sHLYgwDEe4l6qP3LAAABXQ1NojoAAAQDAEcwRQIhAPqwijgE0Fr6uJ+yF+TvyXco\n" +
    -            "Hduv9h7R5WWwJfghXiMyAiBB4+fJm4rIcOnJBZmOqFnRpIjPN0jwDqJT0nDHxaXA\n" +
    -            "nDANBgkqhkiG9w0BAQsFAAOCAQEACXitF1bTEvV1HX11WrT/XuoMhsoPK4TS16rs\n" +
    -            "FqztV4iXKlA1/h5qbsjYY1gVrM+/6kQkmEs5qrxsek2WNxY80NO3WAzroRJ3H9Sd\n" +
    -            "mPn0No2P8LZ5Fs5hvaD/PfWO5xxey80c3kGyvWOej90P3IrL/1RiULyh95TrXBjI\n" +
    -            "ddCBsZ28904wsQUrPBPMpiu0DKl1HR/em9WkcipMi+onJxxFWjucssz5PW/BzGYF\n" +
    -            "jfWLDEI0tN5L4CWV3iVXFXOURY1Mwhtsey9jvlEyxSsys55QdKF40yGgtV9VC+os\n" +
    -            "7hJP33+qA0cvCTaRytiPP6z/l2G/KSIXTyv6SxzGhsTFfzLAOg==\n" +
    -            "-----END CERTIFICATE-----";
    -
         public void runTest(ValidatePathWithParams pathValidator) throws Exception {
             // Validate valid
    -        pathValidator.validate(new String[]{VALID, INT},
    +        pathValidator.validate(new String[]{VALID, INT_VALID},
                     ValidatePathWithParams.Status.GOOD, null, System.out);
     
             // Validate Revoked
    -        pathValidator.validate(new String[]{REVOKED, INT},
    +        pathValidator.validate(new String[]{REVOKED, INT_REVOKED},
                     ValidatePathWithParams.Status.REVOKED,
    -                "Thu Nov 29 10:58:13 PST 2018", System.out);
    +                "Wed Oct 02 06:07:12 PDT 2019", System.out);
         }
     }
     
     class ComodoUserTrustECC {
     
    +    // Owner: CN=Sectigo ECC Extended Validation Secure Server CA, O=Sectigo Limited, L=Salford,
    +    // ST=Greater Manchester, C=GB
    +    // Issuer: CN=USERTrust ECC Certification Authority, O=The USERTRUST Network, L=Jersey City, ST=New Jersey, C=US
    +    // Serial number: 80f5606d3a162b143adc12fbe8c2066f
    +    // Valid from: Thu Nov 01 17:00:00 PDT 2018 until: Tue Dec 31 15:59:59 PST 2030
    +    private static final String INT_VALID = "-----BEGIN CERTIFICATE-----\n" +
    +            "MIIDyTCCA0+gAwIBAgIRAID1YG06FisUOtwS++jCBm8wCgYIKoZIzj0EAwMwgYgx\n" +
    +            "CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5MRQwEgYDVQQHEwtKZXJz\n" +
    +            "ZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMS4wLAYDVQQD\n" +
    +            "EyVVU0VSVHJ1c3QgRUNDIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTE4MTEw\n" +
    +            "MjAwMDAwMFoXDTMwMTIzMTIzNTk1OVowgZExCzAJBgNVBAYTAkdCMRswGQYDVQQI\n" +
    +            "ExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoT\n" +
    +            "D1NlY3RpZ28gTGltaXRlZDE5MDcGA1UEAxMwU2VjdGlnbyBFQ0MgRXh0ZW5kZWQg\n" +
    +            "VmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENBMFkwEwYHKoZIzj0CAQYIKoZIzj0D\n" +
    +            "AQcDQgAEAyJ5Ca9JyXq8bO+krLVWysbtm7fdMSJ54uFD23t0x6JAC4IjxevfQJzW\n" +
    +            "z4T6yY+FybTBqtOa++ijJFnkB5wKy6OCAY0wggGJMB8GA1UdIwQYMBaAFDrhCYbU\n" +
    +            "zxnClnZ0SXbc4DXGY2OaMB0GA1UdDgQWBBTvwSqVDDLa+3Mw3IoT2BVL9xPo+DAO\n" +
    +            "BgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHSUEFjAUBggr\n" +
    +            "BgEFBQcDAQYIKwYBBQUHAwIwOgYDVR0gBDMwMTAvBgRVHSAAMCcwJQYIKwYBBQUH\n" +
    +            "AgEWGWh0dHBzOi8vY3BzLnVzZXJ0cnVzdC5jb20wUAYDVR0fBEkwRzBFoEOgQYY/\n" +
    +            "aHR0cDovL2NybC51c2VydHJ1c3QuY29tL1VTRVJUcnVzdEVDQ0NlcnRpZmljYXRp\n" +
    +            "b25BdXRob3JpdHkuY3JsMHYGCCsGAQUFBwEBBGowaDA/BggrBgEFBQcwAoYzaHR0\n" +
    +            "cDovL2NydC51c2VydHJ1c3QuY29tL1VTRVJUcnVzdEVDQ0FkZFRydXN0Q0EuY3J0\n" +
    +            "MCUGCCsGAQUFBzABhhlodHRwOi8vb2NzcC51c2VydHJ1c3QuY29tMAoGCCqGSM49\n" +
    +            "BAMDA2gAMGUCMQCjHztBDL90GCRXHlGqm0H7kzP04hd0MxwakKjWzOmstXNFLONj\n" +
    +            "RFa0JqI/iKUJMFcCMCbLgyzcFW7DihtY5XE0XCLCw+git0NjxiFB6FaOFIlyDdqT\n" +
    +            "j+Th+DJ92JLvICVD/g==\n" +
    +            "-----END CERTIFICATE-----";
    +
         // Owner: CN=USERTrust ECC Extended Validation Secure Server CA, O=The USERTRUST Network,
         // L=Jersey City, ST=New Jersey, C=US
         // Issuer: CN=USERTrust ECC Certification Authority, O=The USERTRUST Network,
         // L=Jersey City, ST=New Jersey, C=US
         // Serial number: 3d09b24f5c08a7ce8eb85a51d3c1aa52
         // Valid from: Sun Apr 14 17:00:00 PDT 2013 until: Fri Apr 14 16:59:59 PDT 2028
    -    private static final String INT = "-----BEGIN CERTIFICATE-----\n"
    +    private static final String INT_REVOKED = "-----BEGIN CERTIFICATE-----\n"
                 + "MIIDwTCCA0igAwIBAgIQPQmyT1wIp86OuFpR08GqUjAKBggqhkjOPQQDAzCBiDEL\n"
                 + "MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl\n"
                 + "eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT\n"
    @@ -563,13 +632,58 @@
     
         // Owner: CN=usertrustecccertificationauthority-ev.comodoca.com, OU=COMODO EV SGC SSL, O=Sectigo Limited,
         // STREET="3rd Floor, 26 Office Village", STREET=Exchange Quay, STREET=Trafford Road, L=Salford,
    +    // OID.2.5.4.17=M5 3EQ, C=GB, OID.2.5.4.15=Private Organization, OID.1.3.6.1.4.1.311.60.2.1.3=GB,
    +    // SERIALNUMBER=04058690
    +    // Issuer: CN=Sectigo ECC Extended Validation Secure Server CA, O=Sectigo Limited, L=Salford,
    +    // ST=Greater Manchester, C=GB
    +    // Serial number: 8b72489b7f505a55e2a22659c90ed2ab
    +    // Valid from: Sun Sep 29 17:00:00 PDT 2019 until: Tue Dec 28 15:59:59 PST 2021
    +    private static final String VALID = "-----BEGIN CERTIFICATE-----\n" +
    +            "MIIGRTCCBeugAwIBAgIRAItySJt/UFpV4qImWckO0qswCgYIKoZIzj0EAwIwgZEx\n" +
    +            "CzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNV\n" +
    +            "BAcTB1NhbGZvcmQxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDE5MDcGA1UEAxMw\n" +
    +            "U2VjdGlnbyBFQ0MgRXh0ZW5kZWQgVmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENB\n" +
    +            "MB4XDTE5MDkzMDAwMDAwMFoXDTIxMTIyODIzNTk1OVowggFBMREwDwYDVQQFEwgw\n" +
    +            "NDA1ODY5MDETMBEGCysGAQQBgjc8AgEDEwJHQjEdMBsGA1UEDxMUUHJpdmF0ZSBP\n" +
    +            "cmdhbml6YXRpb24xCzAJBgNVBAYTAkdCMQ8wDQYDVQQREwZNNSAzRVExEDAOBgNV\n" +
    +            "BAcTB1NhbGZvcmQxFjAUBgNVBAkTDVRyYWZmb3JkIFJvYWQxFjAUBgNVBAkTDUV4\n" +
    +            "Y2hhbmdlIFF1YXkxJTAjBgNVBAkTHDNyZCBGbG9vciwgMjYgT2ZmaWNlIFZpbGxh\n" +
    +            "Z2UxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDEaMBgGA1UECxMRQ09NT0RPIEVW\n" +
    +            "IFNHQyBTU0wxOzA5BgNVBAMTMnVzZXJ0cnVzdGVjY2NlcnRpZmljYXRpb25hdXRo\n" +
    +            "b3JpdHktZXYuY29tb2RvY2EuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE\n" +
    +            "LTJfEd92Wlg+h/AVtPsMmwX9Puvi+WGCv3sgFRpur8Iy2kGVpXHRQTCn2j9aky4t\n" +
    +            "FQGm7OG2klJA/MEeevKVaaOCA28wggNrMB8GA1UdIwQYMBaAFO/BKpUMMtr7czDc\n" +
    +            "ihPYFUv3E+j4MB0GA1UdDgQWBBSzrWHzmiHwx2Rrm7SjRC0UegNrKzAOBgNVHQ8B\n" +
    +            "Af8EBAMCB4AwDAYDVR0TAQH/BAIwADAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB\n" +
    +            "BQUHAwIwSQYDVR0gBEIwQDA1BgwrBgEEAbIxAQIBBQEwJTAjBggrBgEFBQcCARYX\n" +
    +            "aHR0cHM6Ly9zZWN0aWdvLmNvbS9DUFMwBwYFZ4EMAQEwVgYDVR0fBE8wTTBLoEmg\n" +
    +            "R4ZFaHR0cDovL2NybC5zZWN0aWdvLmNvbS9TZWN0aWdvRUNDRXh0ZW5kZWRWYWxp\n" +
    +            "ZGF0aW9uU2VjdXJlU2VydmVyQ0EuY3JsMIGGBggrBgEFBQcBAQR6MHgwUQYIKwYB\n" +
    +            "BQUHMAKGRWh0dHA6Ly9jcnQuc2VjdGlnby5jb20vU2VjdGlnb0VDQ0V4dGVuZGVk\n" +
    +            "VmFsaWRhdGlvblNlY3VyZVNlcnZlckNBLmNydDAjBggrBgEFBQcwAYYXaHR0cDov\n" +
    +            "L29jc3Auc2VjdGlnby5jb20wPQYDVR0RBDYwNIIydXNlcnRydXN0ZWNjY2VydGlm\n" +
    +            "aWNhdGlvbmF1dGhvcml0eS1ldi5jb21vZG9jYS5jb20wggF/BgorBgEEAdZ5AgQC\n" +
    +            "BIIBbwSCAWsBaQB2AO5Lvbd1zmC64UJpH6vhnmajD35fsHLYgwDEe4l6qP3LAAAB\n" +
    +            "bYL/SJoAAAQDAEcwRQIhAL7EJt/Rgz6NBnx2v8Hevux3Gpcxy64kaeyLVgFeNqFk\n" +
    +            "AiBRf+OWLOtZzEav/oERljrk8hgZB4CR1nj/Tn98cmRrwwB2AFWB1MIWkDYBSuoL\n" +
    +            "m1c8U/DA5Dh4cCUIFy+jqh0HE9MMAAABbYL/SIgAAAQDAEcwRQIgVtZZaiBMC2lu\n" +
    +            "atBzUHQmOq4qrUQP7nS83cd3VzPhToECIQDnlpOCdaxJwr8C0MtkvYpKSabwBPFL\n" +
    +            "ASEkwmOpjuQErAB3ALvZ37wfinG1k5Qjl6qSe0c4V5UKq1LoGpCWZDaOHtGFAAAB\n" +
    +            "bYL/SJoAAAQDAEgwRgIhAI8OgzP/kzF1bOJRHU2S/ewij/6HpGPy7Mbm7Hyuv3IU\n" +
    +            "AiEAxDmX2FmORlgeerQmQ+ar3D9/TwA9RQckVDu5IrgweREwCgYIKoZIzj0EAwID\n" +
    +            "SAAwRQIhAPwQWGWd3oR7YJ7ngCDQ9TAbdPgND51SiR34WfEgaTQtAiAxD4umKm02\n" +
    +            "59GEMj5NpyF2ZQEq5mEGcjJNojrn+PC4zg==\n" +
    +            "-----END CERTIFICATE-----";
    +
    +    // Owner: CN=usertrustecccertificationauthority-ev.comodoca.com, OU=COMODO EV SGC SSL, O=Sectigo Limited,
    +    // STREET="3rd Floor, 26 Office Village", STREET=Exchange Quay, STREET=Trafford Road, L=Salford,
         // ST=Greater Manchester, OID.2.5.4.17=M5 3EQ, C=GB, OID.2.5.4.15=Private Organization,
         // OID.1.3.6.1.4.1.311.60.2.1.3=GB, SERIALNUMBER=04058690
         // Issuer: CN=USERTrust ECC Extended Validation Secure Server CA, O=The USERTRUST Network, L=Jersey City,
         // ST=New Jersey, C=US
         // Serial number: ab1455f9833ae7783f95de8744181f6a
         // Valid from: Wed Nov 28 16:00:00 PST 2018 until: Fri Feb 26 15:59:59 PST 2021
    -    private static final String VALID = "-----BEGIN CERTIFICATE-----\n" +
    +    private static final String REVOKED = "-----BEGIN CERTIFICATE-----\n" +
                 "MIIGhjCCBiygAwIBAgIRAKsUVfmDOud4P5Xeh0QYH2owCgYIKoZIzj0EAwIwgZUx\n" +
                 "CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5MRQwEgYDVQQHEwtKZXJz\n" +
                 "ZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMTswOQYDVQQD\n" +
    @@ -607,60 +721,14 @@
                 "11EPtBSCEhUCIBcyI0yl5dRff6+4x8IeCrLiAOYsfzM7Y/a5uRKFnbYz\n" +
                 "-----END CERTIFICATE-----";
     
    -    // Owner: CN=usertrustecccertificationauthority-ev.comodoca.com, OU=COMODO EV SGC SSL, O=COMODO CA Limited,
    -    // STREET="3rd Floor, 26 Office Village", STREET=Exchange Quay, STREET=Trafford Road, L=Salford,
    -    // ST=Greater Manchester, OID.2.5.4.17=M5 3EQ, C=GB, OID.2.5.4.15=Private Organization,
    -    // OID.1.3.6.1.4.1.311.60.2.1.3=GB, SERIALNUMBER=04058690
    -    // Issuer: CN=USERTrust ECC Extended Validation Secure Server CA, O=The USERTRUST Network, L=Jersey City,
    -    // ST=New Jersey, C=US
    -    // Serial number: 9bd0c93cac9ca2edc1a7dd923316b3c6
    -    // Valid from: Mon Jul 03 17:00:00 PDT 2017 until: Thu Oct 03 16:59:59 PDT 2019
    -    private static final String REVOKED = "-----BEGIN CERTIFICATE-----\n" +
    -            "MIIGhzCCBi2gAwIBAgIRAJvQyTysnKLtwafdkjMWs8YwCgYIKoZIzj0EAwIwgZUx\n" +
    -            "CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5MRQwEgYDVQQHEwtKZXJz\n" +
    -            "ZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMTswOQYDVQQD\n" +
    -            "EzJVU0VSVHJ1c3QgRUNDIEV4dGVuZGVkIFZhbGlkYXRpb24gU2VjdXJlIFNlcnZl\n" +
    -            "ciBDQTAeFw0xNzA3MDQwMDAwMDBaFw0xOTEwMDMyMzU5NTlaMIIBYDERMA8GA1UE\n" +
    -            "BRMIMDQwNTg2OTAxEzARBgsrBgEEAYI3PAIBAxMCR0IxHTAbBgNVBA8TFFByaXZh\n" +
    -            "dGUgT3JnYW5pemF0aW9uMQswCQYDVQQGEwJHQjEPMA0GA1UEERMGTTUgM0VRMRsw\n" +
    -            "GQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxFjAU\n" +
    -            "BgNVBAkTDVRyYWZmb3JkIFJvYWQxFjAUBgNVBAkTDUV4Y2hhbmdlIFF1YXkxJTAj\n" +
    -            "BgNVBAkTHDNyZCBGbG9vciwgMjYgT2ZmaWNlIFZpbGxhZ2UxGjAYBgNVBAoTEUNP\n" +
    -            "TU9ETyBDQSBMaW1pdGVkMRowGAYDVQQLExFDT01PRE8gRVYgU0dDIFNTTDE7MDkG\n" +
    -            "A1UEAxMydXNlcnRydXN0ZWNjY2VydGlmaWNhdGlvbmF1dGhvcml0eS1ldi5jb21v\n" +
    -            "ZG9jYS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQtMl8R33ZaWD6H8BW0\n" +
    -            "+wybBf0+6+L5YYK/eyAVGm6vwjLaQZWlcdFBMKfaP1qTLi0VAabs4baSUkD8wR56\n" +
    -            "8pVpo4IDjjCCA4owHwYDVR0jBBgwFoAUKpxa+U6hMNpASyvpS/H1nNwC+S4wHQYD\n" +
    -            "VR0OBBYEFLOtYfOaIfDHZGubtKNELRR6A2srMA4GA1UdDwEB/wQEAwIFgDAMBgNV\n" +
    -            "HRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBQBgNVHSAE\n" +
    -            "STBHMDwGDCsGAQQBsjEBAgEFATAsMCoGCCsGAQUFBwIBFh5odHRwczovL2Nwcy50\n" +
    -            "cnVzdC1wcm92aWRlci5jb20wBwYFZ4EMAQEwXwYDVR0fBFgwVjBUoFKgUIZOaHR0\n" +
    -            "cDovL2NybC50cnVzdC1wcm92aWRlci5jb20vVVNFUlRydXN0RUNDRXh0ZW5kZWRW\n" +
    -            "YWxpZGF0aW9uU2VjdXJlU2VydmVyQ0EuY3JsMIGYBggrBgEFBQcBAQSBizCBiDBa\n" +
    -            "BggrBgEFBQcwAoZOaHR0cDovL2NydC50cnVzdC1wcm92aWRlci5jb20vVVNFUlRy\n" +
    -            "dXN0RUNDRXh0ZW5kZWRWYWxpZGF0aW9uU2VjdXJlU2VydmVyQ0EuY3J0MCoGCCsG\n" +
    -            "AQUFBzABhh5odHRwOi8vb2NzcC50cnVzdC1wcm92aWRlci5jb20wPQYDVR0RBDYw\n" +
    -            "NIIydXNlcnRydXN0ZWNjY2VydGlmaWNhdGlvbmF1dGhvcml0eS1ldi5jb21vZG9j\n" +
    -            "YS5jb20wggF8BgorBgEEAdZ5AgQCBIIBbASCAWgBZgB1AKS5CZC0GFgUh7sTosxn\n" +
    -            "cAo8NZgE+RvfuON3zQ7IDdwQAAABXQ0/jQ0AAAQDAEYwRAIgPbaNWgoi6OfyNwL2\n" +
    -            "+jiySsoLrkx+0d4NJE1WnZQcfzwCICW4yvsXaMxoOXpQp3EPgrYk5Ajfvy/dY3Ui\n" +
    -            "0/dbQtHxAHYAVhQGmi/XwuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFdDT+K\n" +
    -            "xwAABAMARzBFAiB3GQasrX+akoHX02ZvXCcvhWCqv6qQOhLCUqflPoRbuAIhALwe\n" +
    -            "hrQo8S1Tm5vbMcxGiViq5ZcawxENWhxZ9hS0BZweAHUA7ku9t3XOYLrhQmkfq+Ge\n" +
    -            "ZqMPfl+wctiDAMR7iXqo/csAAAFdDT+M4AAABAMARjBEAiAjvp8w/fdTVW1VGE0T\n" +
    -            "I0YcCIXTYFDgzUMsEUiKHANAgwIgETQUcac7Hiis2fgQ+GdGF9yuh+xMo2Z8QXNu\n" +
    -            "1Cknf+8wCgYIKoZIzj0EAwIDSAAwRQIgQ5UiUI7xodmmMYNs3CmqlZHw/04BQRAR\n" +
    -            "4gRm7blZSIMCIQDHvIWTaPzSO6vwVzs6wSD6FqebLiFxoddC6aZG8Nm0wQ==\n" +
    -            "-----END CERTIFICATE-----";
    -
         public void runTest(ValidatePathWithParams pathValidator) throws Exception {
             // Validate valid
    -        pathValidator.validate(new String[]{VALID, INT},
    +        pathValidator.validate(new String[]{VALID, INT_VALID},
                     ValidatePathWithParams.Status.GOOD, null, System.out);
     
             // Validate Revoked
    -        pathValidator.validate(new String[]{REVOKED, INT},
    +        pathValidator.validate(new String[]{REVOKED, INT_REVOKED},
                     ValidatePathWithParams.Status.REVOKED,
    -                "Thu Nov 29 10:06:00 PST 2018", System.out);
    +                "Wed Oct 02 06:06:50 PDT 2019", System.out);
         }
     }
    diff -r 2a49d43aaa6a -r ec9da3c0eaec test/jtreg-ext/requires/VMProps.java
    --- a/test/jtreg-ext/requires/VMProps.java	Wed Oct 09 23:21:07 2019 +0200
    +++ b/test/jtreg-ext/requires/VMProps.java	Wed Oct 09 23:22:56 2019 +0200
    @@ -305,6 +305,7 @@
          */
         protected void vmOptFinalFlags(SafeMap map) {
             vmOptFinalFlag(map, "ClassUnloading");
    +        vmOptFinalFlag(map, "ClassUnloadingWithConcurrentMark");
             vmOptFinalFlag(map, "UseCompressedOops");
             vmOptFinalFlag(map, "EnableJVMCI");
             vmOptFinalFlag(map, "EliminateAllocations");
    diff -r 2a49d43aaa6a -r ec9da3c0eaec test/langtools/tools/javac/lib/DPrinter.java
    --- a/test/langtools/tools/javac/lib/DPrinter.java	Wed Oct 09 23:21:07 2019 +0200
    +++ b/test/langtools/tools/javac/lib/DPrinter.java	Wed Oct 09 23:22:56 2019 +0200
    @@ -437,7 +437,8 @@
             Scope scope = (Scope) getField(e, e.getClass(), "scope");
             return "(" + sym.name + ":" + sym
                     + ",shdw:" + entryToString(callMethod(e, e.getClass(), "next"), table, true)
    -                + ",sibl:" + entryToString(getField(e, e.getClass(), "sibling"), table, true)
    +                + ",nextSibling:" + entryToString(getField(e, e.getClass(), "nextSibling"), table, true)
    +                + ",prevSibling:" + entryToString(getField(e, e.getClass(), "prevSibling"), table, true)
                     + ((sym.owner != scope.owner)
                         ? (",BOGUS[" + sym.owner + "," + scope.owner + "]")
                         : "")
    diff -r 2a49d43aaa6a -r ec9da3c0eaec test/langtools/tools/javac/scope/RemoveSymbolUnitTest.java
    --- a/test/langtools/tools/javac/scope/RemoveSymbolUnitTest.java	Wed Oct 09 23:21:07 2019 +0200
    +++ b/test/langtools/tools/javac/scope/RemoveSymbolUnitTest.java	Wed Oct 09 23:22:56 2019 +0200
    @@ -33,8 +33,13 @@
     import com.sun.tools.javac.util.*;
     import com.sun.tools.javac.code.*;
     import com.sun.tools.javac.code.Scope.*;
    +import com.sun.tools.javac.code.Symbol;
     import com.sun.tools.javac.code.Symbol.*;
     import com.sun.tools.javac.file.JavacFileManager;
    +import java.util.ArrayList;
    +import java.util.Arrays;
    +import java.util.Collections;
    +import java.util.List;
     
     public class RemoveSymbolUnitTest {
     
    @@ -63,36 +68,66 @@
     
             // Try enter and remove in different shuffled combinations.
             // working with fresh scope each time.
    -        WriteableScope cs = WriteableScope.create(clazz);
    -        cs.enter(v);
    -        cs.enter(m);
    +        WriteableScope cs = writeableScope(clazz, v, m);
             cs.remove(v);
             Symbol s = cs.findFirst(hasNext);
             if (s != m)
                 throw new AssertionError("Wrong symbol");
     
    -        cs = WriteableScope.create(clazz);
    -        cs.enter(m);
    -        cs.enter(v);
    +        cs = writeableScope(clazz, m, v);
             cs.remove(v);
             s = cs.findFirst(hasNext);
             if (s != m)
                 throw new AssertionError("Wrong symbol");
     
    -        cs = WriteableScope.create(clazz);
    -        cs.enter(v);
    -        cs.enter(m);
    +        cs = writeableScope(clazz, v, m);
             cs.remove(m);
             s = cs.findFirst(hasNext);
             if (s != v)
                 throw new AssertionError("Wrong symbol");
     
    -        cs = WriteableScope.create(clazz);
    +        cs = writeableScope(clazz);
             cs.enter(m);
             cs.enter(v);
             cs.remove(m);
             s = cs.findFirst(hasNext);
             if (s != v)
                 throw new AssertionError("Wrong symbol");
    +
    +        // Test multiple removals in the same scope.
    +        VarSymbol v1 = new VarSymbol(0, names.fromString("name1"), Type.noType, clazz);
    +        VarSymbol v2 = new VarSymbol(0, names.fromString("name2"), Type.noType, clazz);
    +        VarSymbol v3 = new VarSymbol(0, names.fromString("name3"), Type.noType, clazz);
    +        VarSymbol v4 = new VarSymbol(0, names.fromString("name4"), Type.noType, clazz);
    +
    +        cs = writeableScope(clazz, v1, v2, v3, v4);
    +        cs.remove(v2);
    +        assertRemainingSymbols(cs, v1, v3, v4);
    +        cs.remove(v3);
    +        assertRemainingSymbols(cs, v1, v4);
    +        cs.remove(v1);
    +        assertRemainingSymbols(cs, v4);
    +        cs.remove(v4);
    +        assertRemainingSymbols(cs);
    +    }
    +
    +    private WriteableScope writeableScope(ClassSymbol classSymbol, Symbol... symbols) {
    +        WriteableScope cs = WriteableScope.create(classSymbol);
    +        for (Symbol symbol : symbols) {
    +            cs.enter(symbol);
    +        }
    +        return cs;
    +    }
    +
    +    private void assertRemainingSymbols(WriteableScope cs, Symbol... symbols) {
    +      List expectedSymbols = Arrays.asList(symbols);
    +      List actualSymbols = new ArrayList<>();
    +      cs.getSymbols().forEach(symbol -> actualSymbols.add(symbol));
    +      // The symbols are stored in reverse order
    +      Collections.reverse(actualSymbols);
    +      if (!actualSymbols.equals(expectedSymbols)) {
    +          throw new AssertionError(
    +              String.format("Wrong symbols: %s. Expected %s", actualSymbols, expectedSymbols));
    +      }
         }
     }