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,
|
Node* GraphKit::maybe_cast_profiled_obj(Node* obj,
|
||||||
ciKlass* type,
|
ciKlass* type,
|
||||||
bool not_null) {
|
bool not_null,
|
||||||
|
SafePointNode* sfpt) {
|
||||||
// type == NULL if profiling tells us this object is always null
|
// type == NULL if profiling tells us this object is always null
|
||||||
if (type != NULL) {
|
if (type != NULL) {
|
||||||
Deoptimization::DeoptReason class_reason = Deoptimization::Reason_speculate_class_check;
|
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;
|
ciKlass* exact_kls = type;
|
||||||
Node* slow_ctl = type_check_receiver(exact_obj, exact_kls, 1.0,
|
Node* slow_ctl = type_check_receiver(exact_obj, exact_kls, 1.0,
|
||||||
&exact_obj);
|
&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);
|
PreserveJVMState pjvms(this);
|
||||||
set_control(slow_ctl);
|
set_control(slow_ctl);
|
||||||
uncommon_trap(class_reason,
|
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
|
// Cast obj to type and emit guard unless we had too many traps here already
|
||||||
Node* maybe_cast_profiled_obj(Node* obj,
|
Node* maybe_cast_profiled_obj(Node* obj,
|
||||||
ciKlass* type,
|
ciKlass* type,
|
||||||
bool not_null = false);
|
bool not_null = false,
|
||||||
|
SafePointNode* sfpt = NULL);
|
||||||
|
|
||||||
// Cast obj to not-null on this path
|
// Cast obj to not-null on this path
|
||||||
Node* cast_not_null(Node* obj, bool do_replace_in_map = true);
|
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* dest_offset = argument(3); // type: int
|
||||||
Node* length = argument(4); // 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
|
// The following tests must be performed
|
||||||
// (1) src and dest are arrays.
|
// (1) src and dest are arrays.
|
||||||
// (2) src and dest arrays must have elements of the same BasicType
|
// (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);
|
src = null_check(src, T_ARRAY);
|
||||||
dest = null_check(dest, 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;
|
bool notest = false;
|
||||||
|
|
||||||
const Type* src_type = _gvn.type(src);
|
const Type* src_type = _gvn.type(src);
|
||||||
|
@ -4762,14 +4788,14 @@ bool LibraryCallKit::inline_arraycopy() {
|
||||||
if (could_have_src && could_have_dest) {
|
if (could_have_src && could_have_dest) {
|
||||||
// This is going to pay off so emit the required guards
|
// This is going to pay off so emit the required guards
|
||||||
if (!has_src) {
|
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);
|
src_type = _gvn.type(src);
|
||||||
top_src = src_type->isa_aryptr();
|
top_src = src_type->isa_aryptr();
|
||||||
has_src = (top_src != NULL && top_src->klass() != NULL);
|
has_src = (top_src != NULL && top_src->klass() != NULL);
|
||||||
src_spec = true;
|
src_spec = true;
|
||||||
}
|
}
|
||||||
if (!has_dest) {
|
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);
|
dest_type = _gvn.type(dest);
|
||||||
top_dest = dest_type->isa_aryptr();
|
top_dest = dest_type->isa_aryptr();
|
||||||
has_dest = (top_dest != NULL && top_dest->klass() != NULL);
|
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 (could_have_src && could_have_dest) {
|
||||||
// If we can have both exact types, emit the missing guards
|
// If we can have both exact types, emit the missing guards
|
||||||
if (could_have_src && !src_spec) {
|
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) {
|
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);
|
Node* not_subtype_ctrl = gen_subtype_check(src_klass, dest_klass);
|
||||||
|
|
||||||
if (not_subtype_ctrl != top()) {
|
if (not_subtype_ctrl != top()) {
|
||||||
PreserveJVMState pjvms(this);
|
if (sfpt != NULL) {
|
||||||
set_control(not_subtype_ctrl);
|
GraphKit kit(sfpt->jvms());
|
||||||
uncommon_trap(Deoptimization::Reason_intrinsic,
|
PreserveJVMState pjvms(&kit);
|
||||||
Deoptimization::Action_make_not_entrant);
|
kit.set_control(not_subtype_ctrl);
|
||||||
assert(stopped(), "Should be stopped");
|
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);
|
PreserveJVMState pjvms(this);
|
||||||
set_control(_gvn.transform(slow_region));
|
set_control(_gvn.transform(slow_region));
|
||||||
uncommon_trap(Deoptimization::Reason_intrinsic,
|
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