Merge
This commit is contained in:
commit
578fd1677d
@ -574,3 +574,4 @@ fbb9c802649585d19f6d7e81b4a519d44806225a jdk-9+168
|
||||
16d692be099c5c38eb48cc9aca78b0c900910d5b jdk-9+169
|
||||
38a240fd58a287acb1963920b92ed4d9c2fd39e3 jdk-9+170
|
||||
d53171650a2cc6c6f699c966c533b914ca9c0602 jdk-9+171
|
||||
1ae9e84f68b359420d2d153ecfe5ee2903e33a2e jdk-9+172
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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.)
|
||||
|
@ -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
106
hotspot/test/compiler/arraycopy/TestACSameSrcDst.java
Normal file
106
hotspot/test/compiler/arraycopy/TestACSameSrcDst.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user