# HG changeset patch # User rkennke # Date 1531254049 -7200 # Tue Jul 10 22:20:49 2018 +0200 # Node ID 281a56a03a675087d5061b4984b7c37f6ce75371 # Parent 3bd1f65e748b4143dda7ce7a6a9a2f0b89d0c67d [mq]: cleanup-c1.patch diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -2166,6 +2166,9 @@ Register length = op->length()->as_register(); Register tmp = op->tmp()->as_register(); + __ resolve_for_read(IN_HEAP, src); + __ resolve_for_write(IN_HEAP, dst); + CodeStub* stub = op->stub(); int flags = op->flags(); BasicType basic_type = default_type != NULL ? default_type->element_type()->basic_type() : T_ILLEGAL; @@ -2509,6 +2512,7 @@ scratch = op->scratch_opr()->as_register(); } assert(BasicLock::displaced_header_offset_in_bytes() == 0, "lock_reg must point to the displaced header"); + __ resolve_for_write(IN_HEAP, obj); // add debug info for NullPointerException only if one is possible int null_check_offset = __ lock_object(hdr, obj, lock, scratch, *op->stub()->entry()); if (op->info() != NULL) { diff --git a/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp --- a/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp @@ -343,13 +343,7 @@ // this CodeEmitInfo must not have the xhandlers because here the // object is already locked (xhandlers expect object to be unlocked) CodeEmitInfo* info = state_for(x, x->state(), true); - LIR_Opr obj_opr = obj.result(); - DecoratorSet decorators = IN_HEAP; - if (!x->needs_null_check()) { - decorators |= IS_NOT_NULL; - } - obj_opr = access_resolve_for_write(decorators, obj_opr, state_for(x)); - monitor_enter(obj_opr, lock, syncTempOpr(), scratch, + monitor_enter(obj.result(), lock, syncTempOpr(), scratch, x->monitor_no(), info_for_exception, info); } @@ -878,19 +872,6 @@ LIRItem dst_pos(x->argument_at(3), this); LIRItem length(x->argument_at(4), this); - LIR_Opr dst_op = dst.result(); - LIR_Opr src_op = src.result(); - DecoratorSet decorators = IN_HEAP; - if (!x->arg_needs_null_check(2)) { - decorators |= IS_NOT_NULL; - } - dst_op = access_resolve_for_write(decorators, dst_op, info); - decorators = IN_HEAP; - if (!x->arg_needs_null_check(0)) { - decorators |= IS_NOT_NULL; - } - src_op = access_resolve_for_read(decorators, src_op, info); - // operands for arraycopy must use fixed registers, otherwise // LinearScan will fail allocation (because arraycopy always needs a // call) @@ -903,9 +884,9 @@ // of the C convention we can process the java args trivially into C // args without worry of overwriting during the xfer - src_op = force_opr_to(src_op, FrameMap::as_oop_opr(j_rarg0)); + src.load_item_force (FrameMap::as_oop_opr(j_rarg0)); src_pos.load_item_force (FrameMap::as_opr(j_rarg1)); - dst_op = force_opr_to(dst_op, FrameMap::as_oop_opr(j_rarg2)); + dst.load_item_force (FrameMap::as_oop_opr(j_rarg2)); dst_pos.load_item_force (FrameMap::as_opr(j_rarg3)); length.load_item_force (FrameMap::as_opr(j_rarg4)); @@ -917,7 +898,7 @@ ciArrayKlass* expected_type; arraycopy_helper(x, &flags, &expected_type); - __ arraycopy(src_op, src_pos.result(), dst_op, dst_pos.result(), length.result(), tmp, expected_type, flags, info); // does add_safepoint + __ arraycopy(src.result(), src_pos.result(), dst.result(), dst_pos.result(), length.result(), tmp, expected_type, flags, info); // does add_safepoint } void LIRGenerator::do_update_CRC32(Intrinsic* x) { diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp @@ -465,7 +465,15 @@ } void ShenandoahBarrierSetAssembler::resolve_for_write(MacroAssembler* masm, DecoratorSet decorators, Register obj) { - write_barrier(masm, obj); + bool oop_not_null = (decorators & IS_NOT_NULL) != 0; + if (oop_not_null) { + write_barrier(masm, obj); + } else { + Label is_null; + __ cbz(obj, is_null); + write_barrier(masm, obj); + __ bind(is_null); + } } void ShenandoahBarrierSetAssembler::cmpxchg_oop(MacroAssembler* masm, Register addr, Register expected, Register new_val, diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp --- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp @@ -3023,6 +3023,9 @@ Register length = op->length()->as_register(); Register tmp = op->tmp()->as_register(); + __ resolve_for_read(IN_HEAP, src); + __ resolve_for_write(IN_HEAP, dst); + CodeStub* stub = op->stub(); int flags = op->flags(); BasicType basic_type = default_type != NULL ? default_type->element_type()->basic_type() : T_ILLEGAL; @@ -3461,6 +3464,7 @@ scratch = op->scratch_opr()->as_register(); } assert(BasicLock::displaced_header_offset_in_bytes() == 0, "lock_reg must point to the displaced header"); + __ resolve_for_write(IN_HEAP, obj); // add debug info for NullPointerException only if one is possible int null_check_offset = __ lock_object(hdr, obj, lock, scratch, *op->stub()->entry()); if (op->info() != NULL) { diff --git a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp --- a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp @@ -296,13 +296,7 @@ // this CodeEmitInfo must not have the xhandlers because here the // object is already locked (xhandlers expect object to be unlocked) CodeEmitInfo* info = state_for(x, x->state(), true); - LIR_Opr obj_opr = obj.result(); - DecoratorSet decorators = IN_HEAP; - if (!x->needs_null_check()) { - decorators |= IS_NOT_NULL; - } - obj_opr = access_resolve_for_write(decorators, obj_opr, state_for(x)); - monitor_enter(obj_opr, lock, syncTempOpr(), scratch, + monitor_enter(obj.result(), lock, syncTempOpr(), scratch, x->monitor_no(), info_for_exception, info); } @@ -917,29 +911,14 @@ LIRItem dst_pos(x->argument_at(3), this); LIRItem length(x->argument_at(4), this); - dst.load_item(); - LIR_Opr dst_op = dst.result(); - DecoratorSet decorators = IN_HEAP; - if (!x->arg_needs_null_check(2)) { - decorators |= IS_NOT_NULL; - } - dst_op = access_resolve_for_write(decorators, dst_op, info); - src.load_item(); - LIR_Opr src_op = src.result(); - decorators = IN_HEAP; - if (!x->arg_needs_null_check(0)) { - decorators |= IS_NOT_NULL; - } - src_op = access_resolve_for_read(decorators, src_op, info); - // operands for arraycopy must use fixed registers, otherwise // LinearScan will fail allocation (because arraycopy always needs a // call) #ifndef _LP64 - src_op = force_opr_to(src_op, FrameMap::rcx_oop_opr); + src.load_item_force (FrameMap::rcx_oop_opr); src_pos.load_item_force (FrameMap::rdx_opr); - dst_op = force_opr_to(dst_op, FrameMap::rax_oop_opr); + dst.load_item_force (FrameMap::rax_oop_opr); dst_pos.load_item_force (FrameMap::rbx_opr); length.load_item_force (FrameMap::rdi_opr); LIR_Opr tmp = (FrameMap::rsi_opr); @@ -953,9 +932,9 @@ // of the C convention we can process the java args trivially into C // args without worry of overwriting during the xfer - src_op = force_opr_to(src_op, FrameMap::as_oop_opr(j_rarg0)); + src.load_item_force (FrameMap::as_oop_opr(j_rarg0)); src_pos.load_item_force (FrameMap::as_opr(j_rarg1)); - dst_op = force_opr_to(dst_op, FrameMap::as_oop_opr(j_rarg2)); + dst.load_item_force (FrameMap::as_oop_opr(j_rarg2)); dst_pos.load_item_force (FrameMap::as_opr(j_rarg3)); length.load_item_force (FrameMap::as_opr(j_rarg4)); @@ -968,7 +947,7 @@ ciArrayKlass* expected_type; arraycopy_helper(x, &flags, &expected_type); - __ arraycopy(src_op, src_pos.result(), dst_op, dst_pos.result(), length.result(), tmp, expected_type, flags, info); // does add_safepoint + __ arraycopy(src.result(), src_pos.result(), dst.result(), dst_pos.result(), length.result(), tmp, expected_type, flags, info); // does add_safepoint } void LIRGenerator::do_update_CRC32(Intrinsic* x) { @@ -1080,8 +1059,7 @@ constant_aOffset = result_aOffset->as_jlong(); result_aOffset = LIR_OprFact::illegalOpr; } - LIR_Opr result_a = a.result(); - result_a = access_resolve_for_read(IN_HEAP, result_a, NULL); + LIR_Opr result_a = access_resolve_for_read(IN_HEAP, a.result(), NULL); long constant_bOffset = 0; LIR_Opr result_bOffset = bOffset.result(); @@ -1089,8 +1067,7 @@ constant_bOffset = result_bOffset->as_jlong(); result_bOffset = LIR_OprFact::illegalOpr; } - LIR_Opr result_b = b.result(); - result_b = access_resolve_for_read(IN_HEAP, result_b, NULL); + LIR_Opr result_b = access_resolve_for_read(IN_HEAP, b.result(), NULL); #ifndef _LP64 result_a = new_register(T_INT); diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp @@ -638,7 +638,16 @@ } void ShenandoahBarrierSetAssembler::resolve_for_write(MacroAssembler* masm, DecoratorSet decorators, Register obj) { - write_barrier(masm, obj); + bool oop_not_null = (decorators & IS_NOT_NULL) != 0; + if (oop_not_null) { + write_barrier(masm, obj); + } else { + Label done; + __ testptr(obj, obj); + __ jcc(Assembler::zero, done); + write_barrier(masm, obj); + __ bind(done); + } } // Special Shenandoah CAS implementation that handles false negatives diff --git a/src/hotspot/share/c1/c1_LIR.hpp b/src/hotspot/share/c1/c1_LIR.hpp --- a/src/hotspot/share/c1/c1_LIR.hpp +++ b/src/hotspot/share/c1/c1_LIR.hpp @@ -923,7 +923,6 @@ , lir_pack64 , lir_unpack64 , lir_unwind - , lir_shenandoah_wb , end_op1 , begin_op2 , lir_cmp @@ -1450,6 +1449,7 @@ virtual void print_instr(outputStream* out) const PRODUCT_RETURN; }; + class ConversionStub; class LIR_OpConvert: public LIR_Op1 { diff --git a/src/hotspot/share/c1/c1_LIRGenerator.cpp b/src/hotspot/share/c1/c1_LIRGenerator.cpp --- a/src/hotspot/share/c1/c1_LIRGenerator.cpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.cpp @@ -246,22 +246,14 @@ void LIRItem::load_item_force(LIR_Opr reg) { LIR_Opr r = result(); if (r != reg) { - _result = _gen->force_opr_to(r, reg); - } -} - -LIR_Opr LIRGenerator::force_opr_to(LIR_Opr op, LIR_Opr reg) { - if (op != reg) { #if !defined(ARM) && !defined(E500V2) - if (op->type() != reg->type()) { + if (r->type() != reg->type()) { // moves between different types need an intervening spill slot - op = force_to_spill(op, reg->type()); + r = _gen->force_to_spill(r, reg->type()); } #endif - __ move(op, reg); - return reg; - } else { - return op; + __ move(r, reg); + _result = reg; } } @@ -1534,15 +1526,13 @@ } #endif - LIR_Opr obj = object.result(); - if (x->needs_null_check() && (needs_patching || MacroAssembler::needs_explicit_null_check(x->offset()))) { // Emit an explicit null check because the offset is too large. // If the class is not loaded and the object is NULL, we need to deoptimize to throw a // NoClassDefFoundError in the interpreter instead of an implicit NPE from compiled code. - __ null_check(obj, new CodeEmitInfo(info), /* deoptimize */ needs_patching); + __ null_check(object.result(), new CodeEmitInfo(info), /* deoptimize */ needs_patching); } DecoratorSet decorators = IN_HEAP; @@ -1652,7 +1642,6 @@ decorators |= C1_WRITE_ACCESS; decorators |= ((decorators & MO_DECORATOR_MASK) != 0) ? MO_SEQ_CST : 0; LIRAccess access(this, decorators, base, offset, type); - new_value.load_item(); if (access.is_raw()) { return _barrier_set->BarrierSetC1::atomic_cmpxchg_at(access, cmp_value, new_value); } else { @@ -1729,12 +1718,12 @@ } #endif - LIR_Opr obj = object.result(); bool stress_deopt = StressLoopInvariantCodeMotion && info && info->deoptimize_on_exception(); if (x->needs_null_check() && (needs_patching || MacroAssembler::needs_explicit_null_check(x->offset()) || stress_deopt)) { + LIR_Opr obj = object.result(); if (stress_deopt) { obj = new_register(T_OBJECT); __ move(LIR_OprFact::oopConst(NULL), obj); @@ -2689,7 +2678,6 @@ __ load_stack_address_monitor(0, lock); CodeEmitInfo* info = new CodeEmitInfo(scope()->start()->state()->copy(ValueStack::StateBefore, SynchronizationEntryBCI), NULL, x->check_flag(Instruction::DeoptimizeOnException)); - obj = access_resolve_for_write(IN_HEAP | IS_NOT_NULL, obj, info); CodeStub* slow_path = new MonitorEnterStub(obj, lock, info); // receiver is guaranteed non-NULL so don't need CodeEmitInfo diff --git a/src/hotspot/share/c1/c1_LIRGenerator.hpp b/src/hotspot/share/c1/c1_LIRGenerator.hpp --- a/src/hotspot/share/c1/c1_LIRGenerator.hpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.hpp @@ -235,8 +235,6 @@ LIR_Opr round_item(LIR_Opr opr); LIR_Opr force_to_spill(LIR_Opr value, BasicType t); - LIR_Opr force_opr_to(LIR_Opr op, LIR_Opr reg); - PhiResolverState& resolver_state() { return _resolver_state; } void move_to_phi(PhiResolver* resolver, Value cur_val, Value sux_val); diff --git a/test/hotspot/jtreg/gc/shenandoah/compiler/C1ArrayCopyNPE.java b/test/hotspot/jtreg/gc/shenandoah/compiler/C1ArrayCopyNPE.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/gc/shenandoah/compiler/C1ArrayCopyNPE.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 Red Hat, Inc. and/or its affiliates. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +/* @test + * @summary test C1 arraycopy intrinsic + * @run main/othervm -XX:TieredStopAtLevel=1 -XX:+UseShenandoahGC -XX:+UnlockDiagnosticVMOptions -XX:ShenandoahGCHeuristics=aggressive C1ArrayCopyNPE + */ + +public class C1ArrayCopyNPE { + + private static final int NUM_RUNS = 10000; + private static final int ARRAY_SIZE=10000; + private static int[] a; + private static int[] b; + + public static void main(String[] args) { + a = null; + b = new int[ARRAY_SIZE]; + for (int i = 0; i < NUM_RUNS; i++) { + test(); + } + a = new int[ARRAY_SIZE]; + b = null; + for (int i = 0; i < NUM_RUNS; i++) { + test(); + } + } + + private static void test() { + try { + System.arraycopy(a, 0, b, 0, ARRAY_SIZE); + throw new RuntimeException("test failed"); + } catch (NullPointerException ex) { + // Ok + } + } +}