This commit is contained in:
Bob Vandette 2017-06-02 10:37:05 -04:00
commit 578fd1677d
8 changed files with 1676 additions and 1460 deletions

View File

@ -574,3 +574,4 @@ fbb9c802649585d19f6d7e81b4a519d44806225a jdk-9+168
16d692be099c5c38eb48cc9aca78b0c900910d5b jdk-9+169
38a240fd58a287acb1963920b92ed4d9c2fd39e3 jdk-9+170
d53171650a2cc6c6f699c966c533b914ca9c0602 jdk-9+171
1ae9e84f68b359420d2d153ecfe5ee2903e33a2e jdk-9+172

View File

@ -25,6 +25,7 @@
#include "precompiled.hpp"
#include "opto/arraycopynode.hpp"
#include "opto/graphKit.hpp"
#include "runtime/sharedRuntime.hpp"
ArrayCopyNode::ArrayCopyNode(Compile* C, bool alloc_tightly_coupled, bool has_negative_length_guard)
: CallNode(arraycopy_type(), NULL, TypeRawPtr::BOTTOM),
@ -631,42 +632,76 @@ bool ArrayCopyNode::may_modify(const TypeOopPtr *t_oop, PhaseTransform *phase) {
return CallNode::may_modify_arraycopy_helper(dest_t, t_oop, phase);
}
bool ArrayCopyNode::may_modify_helper(const TypeOopPtr *t_oop, Node* n, PhaseTransform *phase, ArrayCopyNode*& ac) {
if (n->Opcode() == Op_StoreCM ||
n->Opcode() == Op_StoreB) {
// Ignore card mark stores
n = n->in(MemNode::Memory);
}
if (n->is_Proj()) {
n = n->in(0);
if (n->is_Call() && n->as_Call()->may_modify(t_oop, phase)) {
if (n->isa_ArrayCopy() != NULL) {
ac = n->as_ArrayCopy();
}
return true;
}
bool ArrayCopyNode::may_modify_helper(const TypeOopPtr *t_oop, Node* n, PhaseTransform *phase, CallNode*& call) {
if (n != NULL &&
n->is_Call() &&
n->as_Call()->may_modify(t_oop, phase) &&
(n->as_Call()->is_ArrayCopy() || n->as_Call()->is_call_to_arraycopystub())) {
call = n->as_Call();
return true;
}
return false;
}
bool ArrayCopyNode::may_modify(const TypeOopPtr *t_oop, MemBarNode* mb, PhaseTransform *phase, ArrayCopyNode*& ac) {
Node* mem = mb->in(TypeFunc::Memory);
if (mem->is_MergeMem()) {
Node* n = mem->as_MergeMem()->memory_at(Compile::AliasIdxRaw);
if (may_modify_helper(t_oop, n, phase, ac)) {
return true;
} else if (n->is_Phi()) {
for (uint i = 1; i < n->req(); i++) {
if (n->in(i) != NULL) {
if (may_modify_helper(t_oop, n->in(i), phase, ac)) {
return true;
static Node* step_over_gc_barrier(Node* c) {
if (UseG1GC && !GraphKit::use_ReduceInitialCardMarks() &&
c != NULL && c->is_Region() && c->req() == 3) {
for (uint i = 1; i < c->req(); i++) {
if (c->in(i) != NULL && c->in(i)->is_Region() &&
c->in(i)->req() == 3) {
Node* r = c->in(i);
for (uint j = 1; j < r->req(); j++) {
if (r->in(j) != NULL && r->in(j)->is_Proj() &&
r->in(j)->in(0) != NULL &&
r->in(j)->in(0)->Opcode() == Op_CallLeaf &&
r->in(j)->in(0)->as_Call()->entry_point() == CAST_FROM_FN_PTR(address, SharedRuntime::g1_wb_post)) {
Node* call = r->in(j)->in(0);
c = c->in(i == 1 ? 2 : 1);
if (c != NULL) {
c = c->in(0);
if (c != NULL) {
c = c->in(0);
assert(call->in(0) == NULL ||
call->in(0)->in(0) == NULL ||
call->in(0)->in(0)->in(0) == NULL ||
call->in(0)->in(0)->in(0)->in(0) == NULL ||
call->in(0)->in(0)->in(0)->in(0)->in(0) == NULL ||
c == call->in(0)->in(0)->in(0)->in(0)->in(0), "bad barrier shape");
return c;
}
}
}
}
}
}
}
return c;
}
bool ArrayCopyNode::may_modify(const TypeOopPtr *t_oop, MemBarNode* mb, PhaseTransform *phase, ArrayCopyNode*& ac) {
Node* c = mb->in(0);
// step over g1 gc barrier if we're at a clone with ReduceInitialCardMarks off
c = step_over_gc_barrier(c);
CallNode* call = NULL;
if (c != NULL && c->is_Region()) {
for (uint i = 1; i < c->req(); i++) {
if (c->in(i) != NULL) {
Node* n = c->in(i)->in(0);
if (may_modify_helper(t_oop, n, phase, call)) {
ac = call->isa_ArrayCopy();
assert(c == mb->in(0), "only for clone");
return true;
}
}
}
} else if (may_modify_helper(t_oop, c->in(0), phase, call)) {
ac = call->isa_ArrayCopy();
assert(c == mb->in(0) || (ac != NULL && ac->is_clonebasic() && !GraphKit::use_ReduceInitialCardMarks()), "only for clone");
return true;
}
return false;
}
@ -677,37 +712,77 @@ bool ArrayCopyNode::may_modify(const TypeOopPtr *t_oop, MemBarNode* mb, PhaseTra
// between offset_lo and offset_hi
// if must_modify is true, return true if the copy is guaranteed to
// write between offset_lo and offset_hi
bool ArrayCopyNode::modifies(intptr_t offset_lo, intptr_t offset_hi, PhaseTransform* phase, bool must_modify) {
bool ArrayCopyNode::modifies(intptr_t offset_lo, intptr_t offset_hi, PhaseTransform* phase, bool must_modify) const {
assert(_kind == ArrayCopy || _kind == CopyOf || _kind == CopyOfRange, "only for real array copies");
Node* dest = in(ArrayCopyNode::Dest);
Node* src_pos = in(ArrayCopyNode::SrcPos);
Node* dest_pos = in(ArrayCopyNode::DestPos);
Node* len = in(ArrayCopyNode::Length);
Node* dest = in(Dest);
Node* dest_pos = in(DestPos);
Node* len = in(Length);
const TypeInt *dest_pos_t = phase->type(dest_pos)->isa_int();
const TypeInt *len_t = phase->type(len)->isa_int();
const TypeAryPtr* ary_t = phase->type(dest)->isa_aryptr();
if (dest_pos_t != NULL && len_t != NULL && ary_t != NULL) {
BasicType ary_elem = ary_t->klass()->as_array_klass()->element_type()->basic_type();
uint header = arrayOopDesc::base_offset_in_bytes(ary_elem);
uint elemsize = type2aelembytes(ary_elem);
if (dest_pos_t == NULL || len_t == NULL || ary_t == NULL) {
return !must_modify;
}
jlong dest_pos_plus_len_lo = (((jlong)dest_pos_t->_lo) + len_t->_lo) * elemsize + header;
jlong dest_pos_plus_len_hi = (((jlong)dest_pos_t->_hi) + len_t->_hi) * elemsize + header;
jlong dest_pos_lo = ((jlong)dest_pos_t->_lo) * elemsize + header;
jlong dest_pos_hi = ((jlong)dest_pos_t->_hi) * elemsize + header;
BasicType ary_elem = ary_t->klass()->as_array_klass()->element_type()->basic_type();
uint header = arrayOopDesc::base_offset_in_bytes(ary_elem);
uint elemsize = type2aelembytes(ary_elem);
if (must_modify) {
if (offset_lo >= dest_pos_hi && offset_hi < dest_pos_plus_len_lo) {
return true;
}
} else {
if (offset_hi >= dest_pos_lo && offset_lo < dest_pos_plus_len_hi) {
return true;
}
jlong dest_pos_plus_len_lo = (((jlong)dest_pos_t->_lo) + len_t->_lo) * elemsize + header;
jlong dest_pos_plus_len_hi = (((jlong)dest_pos_t->_hi) + len_t->_hi) * elemsize + header;
jlong dest_pos_lo = ((jlong)dest_pos_t->_lo) * elemsize + header;
jlong dest_pos_hi = ((jlong)dest_pos_t->_hi) * elemsize + header;
if (must_modify) {
if (offset_lo >= dest_pos_hi && offset_hi < dest_pos_plus_len_lo) {
return true;
}
} else {
if (offset_hi >= dest_pos_lo && offset_lo < dest_pos_plus_len_hi) {
return true;
}
}
return false;
}
// We try to replace a load from the destination of an arraycopy with
// a load from the source so the arraycopy has a chance to be
// eliminated. It's only valid if the arraycopy doesn't change the
// element that would be loaded from the source array.
bool ArrayCopyNode::can_replace_dest_load_with_src_load(intptr_t offset_lo, intptr_t offset_hi, PhaseTransform* phase) const {
assert(_kind == ArrayCopy || _kind == CopyOf || _kind == CopyOfRange, "only for real array copies");
Node* src = in(Src);
Node* dest = in(Dest);
// Check whether, assuming source and destination are the same
// array, the arraycopy modifies the element from the source we
// would load.
if ((src != dest && in(SrcPos) == in(DestPos)) || !modifies(offset_lo, offset_hi, phase, false)) {
// if not the transformation is legal
return true;
}
AllocateNode* src_alloc = AllocateNode::Ideal_allocation(src, phase);
AllocateNode* dest_alloc = AllocateNode::Ideal_allocation(dest, phase);
// Check whether source and destination can be proved to be
// different arrays
const TypeOopPtr* t_src = phase->type(src)->isa_oopptr();
const TypeOopPtr* t_dest = phase->type(dest)->isa_oopptr();
if (t_src != NULL && t_dest != NULL &&
(t_src->is_known_instance() || t_dest->is_known_instance()) &&
t_src->instance_id() != t_dest->instance_id()) {
return true;
}
if (MemNode::detect_ptr_independence(src->uncast(), src_alloc, dest->uncast(), dest_alloc, phase)) {
return true;
}
return false;
}

View File

@ -108,7 +108,7 @@ private:
BasicType copy_type, const Type* value_type, int count);
bool finish_transform(PhaseGVN *phase, bool can_reshape,
Node* ctl, Node *mem);
static bool may_modify_helper(const TypeOopPtr *t_oop, Node* n, PhaseTransform *phase, ArrayCopyNode*& ac);
static bool may_modify_helper(const TypeOopPtr *t_oop, Node* n, PhaseTransform *phase, CallNode*& call);
public:
@ -167,7 +167,8 @@ public:
bool has_negative_length_guard() const { return _has_negative_length_guard; }
static bool may_modify(const TypeOopPtr *t_oop, MemBarNode* mb, PhaseTransform *phase, ArrayCopyNode*& ac);
bool modifies(intptr_t offset_lo, intptr_t offset_hi, PhaseTransform* phase, bool must_modify);
bool modifies(intptr_t offset_lo, intptr_t offset_hi, PhaseTransform* phase, bool must_modify) const;
bool can_replace_dest_load_with_src_load(intptr_t offset_lo, intptr_t offset_hi, PhaseTransform* phase) const;
#ifndef PRODUCT
virtual void dump_spec(outputStream *st) const;

View File

@ -1047,7 +1047,9 @@ void PhaseMacroExpand::process_users_of_allocation(CallNode *alloc) {
// opportunities for allocation elimination
Node* src = ac->in(ArrayCopyNode::Src);
ac->replace_edge(src, top());
if (src->outcnt() == 0) {
// src can be top at this point if src and dest of the
// arraycopy were the same
if (src->outcnt() == 0 && !src->is_top()) {
_igvn.remove_dead_node(src);
}

View File

@ -718,6 +718,15 @@ Node* PhaseMacroExpand::generate_arraycopy(ArrayCopyNode *ac, AllocateArrayNode*
_igvn.replace_node(_ioproj_fallthrough, *io);
_igvn.replace_node(_fallthroughcatchproj, *ctrl);
#ifdef ASSERT
const TypeOopPtr* dest_t = _igvn.type(dest)->is_oopptr();
if (dest_t->is_known_instance()) {
ArrayCopyNode* ac = NULL;
assert(ArrayCopyNode::may_modify(dest_t, (*ctrl)->in(0)->as_MemBar(), &_igvn, ac), "dependency on arraycopy lost");
assert(ac == NULL, "no arraycopy anymore");
}
#endif
return out_mem;
}
@ -1139,8 +1148,25 @@ void PhaseMacroExpand::expand_arraycopy_node(ArrayCopyNode *ac) {
const TypeAryPtr* top_src = src_type->isa_aryptr();
const TypeAryPtr* top_dest = dest_type->isa_aryptr();
if (top_src == NULL || top_src->klass() == NULL ||
top_dest == NULL || top_dest->klass() == NULL) {
BasicType src_elem = T_CONFLICT;
BasicType dest_elem = T_CONFLICT;
if (top_dest != NULL && top_dest->klass() != NULL) {
dest_elem = top_dest->klass()->as_array_klass()->element_type()->basic_type();
}
if (top_src != NULL && top_src->klass() != NULL) {
src_elem = top_src->klass()->as_array_klass()->element_type()->basic_type();
}
if (src_elem == T_ARRAY) src_elem = T_OBJECT;
if (dest_elem == T_ARRAY) dest_elem = T_OBJECT;
if (ac->is_arraycopy_validated() &&
dest_elem != T_CONFLICT &&
src_elem == T_CONFLICT) {
src_elem = dest_elem;
}
if (src_elem == T_CONFLICT || dest_elem == T_CONFLICT) {
// Conservatively insert a memory barrier on all memory slices.
// Do not let writes into the source float below the arraycopy.
{
@ -1169,13 +1195,11 @@ void PhaseMacroExpand::expand_arraycopy_node(ArrayCopyNode *ac) {
}
return;
}
assert(!ac->is_arraycopy_validated() || (src_elem == dest_elem && dest_elem != T_VOID), "validated but different basic types");
// (2) src and dest arrays must have elements of the same BasicType
// Figure out the size and type of the elements we will be copying.
BasicType src_elem = top_src->klass()->as_array_klass()->element_type()->basic_type();
BasicType dest_elem = top_dest->klass()->as_array_klass()->element_type()->basic_type();
if (src_elem == T_ARRAY) src_elem = T_OBJECT;
if (dest_elem == T_ARRAY) dest_elem = T_OBJECT;
if (src_elem != dest_elem || dest_elem == T_VOID) {
// The component types are not the same or are not recognized. Punt.
// (But, avoid the native method wrapper to JVM_ArrayCopy.)

View File

@ -908,10 +908,11 @@ Node* LoadNode::can_see_arraycopy_value(Node* st, PhaseTransform* phase) const {
ld->set_req(0, ld_alloc->in(0));
}
} else {
Node* src = ac->in(ArrayCopyNode::Src);
Node* addp = in(MemNode::Address)->clone();
assert(addp->in(AddPNode::Base) == addp->in(AddPNode::Address), "should be");
addp->set_req(AddPNode::Base, ac->in(ArrayCopyNode::Src));
addp->set_req(AddPNode::Address, ac->in(ArrayCopyNode::Src));
addp->set_req(AddPNode::Base, src);
addp->set_req(AddPNode::Address, src);
const TypeAryPtr* ary_t = phase->type(in(MemNode::Address))->isa_aryptr();
BasicType ary_elem = ary_t->klass()->as_array_klass()->element_type()->basic_type();
@ -928,6 +929,12 @@ Node* LoadNode::can_see_arraycopy_value(Node* st, PhaseTransform* phase) const {
addp->set_req(AddPNode::Offset, offset);
ld->set_req(MemNode::Address, phase->transform(addp));
const TypeX *ld_offs_t = phase->type(offset)->isa_intptr_t();
if (!ac->as_ArrayCopy()->can_replace_dest_load_with_src_load(ld_offs_t->_lo, ld_offs_t->_hi, phase)) {
return NULL;
}
if (in(0) != NULL) {
assert(ac->in(0) != NULL, "alloc must have control");
ld->set_req(0, ac->in(0));

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,106 @@
/*
* Copyright (c) 2017, Red Hat, Inc. 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 8179678
* @summary ArrayCopy with same src and dst can cause incorrect execution or compiler crash
*
* @run main/othervm -XX:CompileCommand=compileonly,TestACSameSrcDst::test* TestACSameSrcDst
*
*/
public class TestACSameSrcDst {
static int test1(int[] src, int[] dst) {
System.arraycopy(src, 5, dst, 0, 10);
// this shouldn't be transformed to src[5] because the copy
// can modify src[5] if src and dst are the same.
return dst[0];
}
static int test2(int[] src) {
System.arraycopy(src, 0, src, 0, 10);
// same source and destination. If load from destination is
// transformed to load of source, the compiler performs that
// optimization in an infinite loop.
return src[0];
}
static int test3() {
int[] src = new int[15];
src[5] = 0x42;
System.arraycopy(src, 5, src, 0, 10);
// That load can't bypass the arraycopy
return src[0];
}
static int test4() {
int[] src = new int[15];
System.arraycopy(src, 0, src, 5, 10);
return src[0];
}
// The dst[0] load can't bypass the arraycopy. After ArrayCopyNode
// is expanded, C2 looks for a stub call on the control paths of
// the array copy subgraph to decide whether the load's memory
// input can bypass the arraycopy. This test verifies the case of
// a source array that's not declared as an array.
static int test5(Object src, int l, boolean flag) {
int[] dst = new int[10];
if (flag) {
dst[0] = 0x42;
System.arraycopy(src, 0, dst, 0, l);
return dst[0];
}
return 0;
}
public static void main(String[] args) {
int[] array = new int[15];
for (int i = 0; i < 20000; i++) {
int res;
for (int j = 0; j < array.length; j++) {
array[j] = j;
}
int expected = array[5];
res = test1(array, array);
if (res != expected) {
throw new RuntimeException("bad result: " + res + " != " + expected);
}
test2(array);
res = test3();
if (res != 0x42) {
throw new RuntimeException("bad result: " + res + " != " + 0x42);
}
test4();
for (int j = 0; j < array.length; j++) {
array[j] = j;
}
res = test5(array, 10, (i%2) == 0);
if (res != 0) {
throw new RuntimeException("bad result: " + res + " != " + 0);
}
}
}
}