mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-24 04:54:40 +02:00
8064703
: crash running specjvm98's javac following 8060252
Uncommon trap between arraycopy and initialization may leave array initialized Reviewed-by: kvn, vlivanov, goetz
This commit is contained in:
parent
65fe921d3d
commit
686e5a0a6f
4 changed files with 310 additions and 17 deletions
|
@ -2809,7 +2809,8 @@ Node* GraphKit::maybe_cast_profiled_receiver(Node* not_null_obj,
|
|||
*/
|
||||
Node* GraphKit::maybe_cast_profiled_obj(Node* obj,
|
||||
ciKlass* type,
|
||||
bool not_null) {
|
||||
bool not_null,
|
||||
SafePointNode* sfpt) {
|
||||
// type == NULL if profiling tells us this object is always null
|
||||
if (type != NULL) {
|
||||
Deoptimization::DeoptReason class_reason = Deoptimization::Reason_speculate_class_check;
|
||||
|
@ -2831,7 +2832,13 @@ Node* GraphKit::maybe_cast_profiled_obj(Node* obj,
|
|||
ciKlass* exact_kls = type;
|
||||
Node* slow_ctl = type_check_receiver(exact_obj, exact_kls, 1.0,
|
||||
&exact_obj);
|
||||
{
|
||||
if (sfpt != NULL) {
|
||||
GraphKit kit(sfpt->jvms());
|
||||
PreserveJVMState pjvms(&kit);
|
||||
kit.set_control(slow_ctl);
|
||||
kit.uncommon_trap(class_reason,
|
||||
Deoptimization::Action_maybe_recompile);
|
||||
} else {
|
||||
PreserveJVMState pjvms(this);
|
||||
set_control(slow_ctl);
|
||||
uncommon_trap(class_reason,
|
||||
|
|
|
@ -418,7 +418,8 @@ class GraphKit : public Phase {
|
|||
// Cast obj to type and emit guard unless we had too many traps here already
|
||||
Node* maybe_cast_profiled_obj(Node* obj,
|
||||
ciKlass* type,
|
||||
bool not_null = false);
|
||||
bool not_null = false,
|
||||
SafePointNode* sfpt = NULL);
|
||||
|
||||
// Cast obj to not-null on this path
|
||||
Node* cast_not_null(Node* obj, bool do_replace_in_map = true);
|
||||
|
|
|
@ -4697,10 +4697,6 @@ bool LibraryCallKit::inline_arraycopy() {
|
|||
Node* dest_offset = argument(3); // type: int
|
||||
Node* length = argument(4); // type: int
|
||||
|
||||
// Check for allocation before we add nodes that would confuse
|
||||
// tightly_coupled_allocation()
|
||||
AllocateArrayNode* alloc = tightly_coupled_allocation(dest, NULL);
|
||||
|
||||
// The following tests must be performed
|
||||
// (1) src and dest are arrays.
|
||||
// (2) src and dest arrays must have elements of the same BasicType
|
||||
|
@ -4717,6 +4713,36 @@ bool LibraryCallKit::inline_arraycopy() {
|
|||
src = null_check(src, T_ARRAY);
|
||||
dest = null_check(dest, T_ARRAY);
|
||||
|
||||
// Check for allocation before we add nodes that would confuse
|
||||
// tightly_coupled_allocation()
|
||||
AllocateArrayNode* alloc = tightly_coupled_allocation(dest, NULL);
|
||||
|
||||
SafePointNode* sfpt = NULL;
|
||||
if (alloc != NULL) {
|
||||
// The JVM state for uncommon traps between the allocation and
|
||||
// arraycopy is set to the state before the allocation: if the
|
||||
// initialization is performed by the array copy, we don't want to
|
||||
// go back to the interpreter with an unitialized array.
|
||||
JVMState* old_jvms = alloc->jvms();
|
||||
JVMState* jvms = old_jvms->clone_shallow(C);
|
||||
uint size = alloc->req();
|
||||
sfpt = new SafePointNode(size, jvms);
|
||||
jvms->set_map(sfpt);
|
||||
for (uint i = 0; i < size; i++) {
|
||||
sfpt->init_req(i, alloc->in(i));
|
||||
}
|
||||
// re-push array length for deoptimization
|
||||
sfpt->ins_req(jvms->stkoff() + jvms->sp(), alloc->in(AllocateNode::ALength));
|
||||
jvms->set_sp(jvms->sp()+1);
|
||||
jvms->set_monoff(jvms->monoff()+1);
|
||||
jvms->set_scloff(jvms->scloff()+1);
|
||||
jvms->set_endoff(jvms->endoff()+1);
|
||||
jvms->set_should_reexecute(true);
|
||||
|
||||
sfpt->set_i_o(map()->i_o());
|
||||
sfpt->set_memory(map()->memory());
|
||||
}
|
||||
|
||||
bool notest = false;
|
||||
|
||||
const Type* src_type = _gvn.type(src);
|
||||
|
@ -4762,14 +4788,14 @@ bool LibraryCallKit::inline_arraycopy() {
|
|||
if (could_have_src && could_have_dest) {
|
||||
// This is going to pay off so emit the required guards
|
||||
if (!has_src) {
|
||||
src = maybe_cast_profiled_obj(src, src_k);
|
||||
src = maybe_cast_profiled_obj(src, src_k, true, sfpt);
|
||||
src_type = _gvn.type(src);
|
||||
top_src = src_type->isa_aryptr();
|
||||
has_src = (top_src != NULL && top_src->klass() != NULL);
|
||||
src_spec = true;
|
||||
}
|
||||
if (!has_dest) {
|
||||
dest = maybe_cast_profiled_obj(dest, dest_k);
|
||||
dest = maybe_cast_profiled_obj(dest, dest_k, true);
|
||||
dest_type = _gvn.type(dest);
|
||||
top_dest = dest_type->isa_aryptr();
|
||||
has_dest = (top_dest != NULL && top_dest->klass() != NULL);
|
||||
|
@ -4810,10 +4836,10 @@ bool LibraryCallKit::inline_arraycopy() {
|
|||
if (could_have_src && could_have_dest) {
|
||||
// If we can have both exact types, emit the missing guards
|
||||
if (could_have_src && !src_spec) {
|
||||
src = maybe_cast_profiled_obj(src, src_k);
|
||||
src = maybe_cast_profiled_obj(src, src_k, true, sfpt);
|
||||
}
|
||||
if (could_have_dest && !dest_spec) {
|
||||
dest = maybe_cast_profiled_obj(dest, dest_k);
|
||||
dest = maybe_cast_profiled_obj(dest, dest_k, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4855,13 +4881,28 @@ bool LibraryCallKit::inline_arraycopy() {
|
|||
Node* not_subtype_ctrl = gen_subtype_check(src_klass, dest_klass);
|
||||
|
||||
if (not_subtype_ctrl != top()) {
|
||||
PreserveJVMState pjvms(this);
|
||||
set_control(not_subtype_ctrl);
|
||||
uncommon_trap(Deoptimization::Reason_intrinsic,
|
||||
Deoptimization::Action_make_not_entrant);
|
||||
assert(stopped(), "Should be stopped");
|
||||
if (sfpt != NULL) {
|
||||
GraphKit kit(sfpt->jvms());
|
||||
PreserveJVMState pjvms(&kit);
|
||||
kit.set_control(not_subtype_ctrl);
|
||||
kit.uncommon_trap(Deoptimization::Reason_intrinsic,
|
||||
Deoptimization::Action_make_not_entrant);
|
||||
assert(kit.stopped(), "Should be stopped");
|
||||
} else {
|
||||
PreserveJVMState pjvms(this);
|
||||
set_control(not_subtype_ctrl);
|
||||
uncommon_trap(Deoptimization::Reason_intrinsic,
|
||||
Deoptimization::Action_make_not_entrant);
|
||||
assert(stopped(), "Should be stopped");
|
||||
}
|
||||
}
|
||||
{
|
||||
if (sfpt != NULL) {
|
||||
GraphKit kit(sfpt->jvms());
|
||||
kit.set_control(_gvn.transform(slow_region));
|
||||
kit.uncommon_trap(Deoptimization::Reason_intrinsic,
|
||||
Deoptimization::Action_make_not_entrant);
|
||||
assert(kit.stopped(), "Should be stopped");
|
||||
} else {
|
||||
PreserveJVMState pjvms(this);
|
||||
set_control(_gvn.transform(slow_region));
|
||||
uncommon_trap(Deoptimization::Reason_intrinsic,
|
||||
|
|
244
hotspot/test/compiler/arraycopy/TestArrayCopyNoInit.java
Normal file
244
hotspot/test/compiler/arraycopy/TestArrayCopyNoInit.java
Normal file
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 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 8064703
|
||||
* @summary Deoptimization between array allocation and arraycopy may result in non initialized array
|
||||
* @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:TypeProfileLevel=020 TestArrayCopyNoInit
|
||||
*
|
||||
*/
|
||||
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class TestArrayCopyNoInit {
|
||||
|
||||
static int[] m1(int[] src) {
|
||||
int[] dest = new int[10];
|
||||
try {
|
||||
System.arraycopy(src, 0, dest, 0, 10);
|
||||
} catch (NullPointerException npe) {
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
static int[] m2(Object src, boolean flag) {
|
||||
Class tmp = src.getClass();
|
||||
if (flag) {
|
||||
return null;
|
||||
}
|
||||
int[] dest = new int[10];
|
||||
try {
|
||||
System.arraycopy(src, 0, dest, 0, 10);
|
||||
} catch (ArrayStoreException npe) {
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
static int[] m3(int[] src, int src_offset) {
|
||||
int tmp = src[0];
|
||||
int[] dest = new int[10];
|
||||
try {
|
||||
System.arraycopy(src, src_offset, dest, 0, 10);
|
||||
} catch (IndexOutOfBoundsException npe) {
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
static int[] m4(int[] src, int length) {
|
||||
int tmp = src[0];
|
||||
int[] dest = new int[10];
|
||||
try {
|
||||
System.arraycopy(src, 0, dest, 0, length);
|
||||
} catch (IndexOutOfBoundsException npe) {
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
static TestArrayCopyNoInit[] m5(Object[] src) {
|
||||
Object tmp = src[0];
|
||||
TestArrayCopyNoInit[] dest = new TestArrayCopyNoInit[10];
|
||||
System.arraycopy(src, 0, dest, 0, 0);
|
||||
return dest;
|
||||
}
|
||||
|
||||
static class A {
|
||||
}
|
||||
|
||||
static class B extends A {
|
||||
}
|
||||
|
||||
static class C extends B {
|
||||
}
|
||||
|
||||
static class D extends C {
|
||||
}
|
||||
|
||||
static class E extends D {
|
||||
}
|
||||
|
||||
static class F extends E {
|
||||
}
|
||||
|
||||
static class G extends F {
|
||||
}
|
||||
|
||||
static class H extends G {
|
||||
}
|
||||
|
||||
static class I extends H {
|
||||
}
|
||||
|
||||
static H[] m6(Object[] src) {
|
||||
Object tmp = src[0];
|
||||
H[] dest = new H[10];
|
||||
System.arraycopy(src, 0, dest, 0, 0);
|
||||
return dest;
|
||||
}
|
||||
|
||||
static Object m7_src(Object src) {
|
||||
return src;
|
||||
}
|
||||
|
||||
static int[] m7(Object src, boolean flag) {
|
||||
Class tmp = src.getClass();
|
||||
if (flag) {
|
||||
return null;
|
||||
}
|
||||
src = m7_src(src);
|
||||
int[] dest = new int[10];
|
||||
try {
|
||||
System.arraycopy(src, 0, dest, 0, 10);
|
||||
} catch (ArrayStoreException npe) {
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
static public void main(String[] args) throws Throwable {
|
||||
boolean success = true;
|
||||
int[] src = new int[10];
|
||||
TestArrayCopyNoInit[] src2 = new TestArrayCopyNoInit[10];
|
||||
int[] res = null;
|
||||
TestArrayCopyNoInit[] res2 = null;
|
||||
Object src_obj = new Object();
|
||||
|
||||
for (int i = 0; i < 20000; i++) {
|
||||
m1(src);
|
||||
}
|
||||
|
||||
res = m1(null);
|
||||
for (int i = 0; i < res.length; i++) {
|
||||
if (res[i] != 0) {
|
||||
success = false;
|
||||
System.out.println("Uninitialized array following NPE");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 20000; i++) {
|
||||
if ((i%2) == 0) {
|
||||
m2(src, false);
|
||||
} else {
|
||||
m2(src_obj, true);
|
||||
}
|
||||
}
|
||||
res = m2(src_obj, false);
|
||||
for (int i = 0; i < res.length; i++) {
|
||||
if (res[i] != 0) {
|
||||
success = false;
|
||||
System.out.println("Uninitialized array following failed array check");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 20000; i++) {
|
||||
m3(src, 0);
|
||||
}
|
||||
res = m3(src, -1);
|
||||
for (int i = 0; i < res.length; i++) {
|
||||
if (res[i] != 0) {
|
||||
success = false;
|
||||
System.out.println("Uninitialized array following failed src offset check");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 20000; i++) {
|
||||
m4(src, 0);
|
||||
}
|
||||
res = m4(src, -1);
|
||||
for (int i = 0; i < res.length; i++) {
|
||||
if (res[i] != 0) {
|
||||
success = false;
|
||||
System.out.println("Uninitialized array following failed length check");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 20000; i++) {
|
||||
m5(src2);
|
||||
}
|
||||
res2 = m5(new Object[10]);
|
||||
for (int i = 0; i < res2.length; i++) {
|
||||
if (res2[i] != null) {
|
||||
success = false;
|
||||
System.out.println("Uninitialized array following failed type check");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
H[] src3 = new H[10];
|
||||
I b = new I();
|
||||
for (int i = 0; i < 20000; i++) {
|
||||
m6(src3);
|
||||
}
|
||||
H[] res3 = m6(new Object[10]);
|
||||
for (int i = 0; i < res3.length; i++) {
|
||||
if (res3[i] != null) {
|
||||
success = false;
|
||||
System.out.println("Uninitialized array following failed full type check");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 20000; i++) {
|
||||
if ((i%2) == 0) {
|
||||
m7(src, false);
|
||||
} else {
|
||||
m7(src_obj, true);
|
||||
}
|
||||
}
|
||||
res = m7(src_obj, false);
|
||||
for (int i = 0; i < res.length; i++) {
|
||||
if (res[i] != 0) {
|
||||
success = false;
|
||||
System.out.println("Uninitialized array following failed type check with return value profiling");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
throw new RuntimeException("Some tests failed");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue