mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 06:14:49 +02:00
8076188: Optimize arraycopy out for non escaping destination
If the destination of an arraycopy is non escaping, the arraycopy may be optimized out Reviewed-by: kvn, vlivanov
This commit is contained in:
parent
3dc9461bc8
commit
a9cdbd0407
17 changed files with 1224 additions and 486 deletions
|
@ -30,7 +30,9 @@ ArrayCopyNode::ArrayCopyNode(Compile* C, bool alloc_tightly_coupled)
|
|||
: CallNode(arraycopy_type(), NULL, TypeRawPtr::BOTTOM),
|
||||
_alloc_tightly_coupled(alloc_tightly_coupled),
|
||||
_kind(None),
|
||||
_arguments_validated(false) {
|
||||
_arguments_validated(false),
|
||||
_src_type(TypeOopPtr::BOTTOM),
|
||||
_dest_type(TypeOopPtr::BOTTOM) {
|
||||
init_class_id(Class_ArrayCopy);
|
||||
init_flags(Flag_is_macro);
|
||||
C->add_macro_node(this);
|
||||
|
@ -595,3 +597,17 @@ Node *ArrayCopyNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
|||
|
||||
return mem;
|
||||
}
|
||||
|
||||
bool ArrayCopyNode::may_modify(const TypeOopPtr *t_oop, PhaseTransform *phase) {
|
||||
const TypeOopPtr* dest_t = phase->type(in(ArrayCopyNode::Dest))->is_oopptr();
|
||||
assert(!dest_t->is_known_instance() || _dest_type->is_known_instance(), "result of EA not recorded");
|
||||
const TypeOopPtr* src_t = phase->type(in(ArrayCopyNode::Src))->is_oopptr();
|
||||
assert(!src_t->is_known_instance() || _src_type->is_known_instance(), "result of EA not recorded");
|
||||
|
||||
if (_dest_type != TypeOopPtr::BOTTOM || t_oop->is_known_instance()) {
|
||||
assert(_dest_type == TypeOopPtr::BOTTOM || _dest_type->is_known_instance(), "result of EA is known instance");
|
||||
return t_oop->instance_id() == _dest_type->instance_id();
|
||||
}
|
||||
|
||||
return CallNode::may_modify_arraycopy_helper(dest_t, t_oop, phase);
|
||||
}
|
||||
|
|
|
@ -124,6 +124,10 @@ public:
|
|||
ParmLimit
|
||||
};
|
||||
|
||||
// Results from escape analysis for non escaping inputs
|
||||
const TypeOopPtr* _src_type;
|
||||
const TypeOopPtr* _dest_type;
|
||||
|
||||
static ArrayCopyNode* make(GraphKit* kit, bool may_throw,
|
||||
Node* src, Node* src_offset,
|
||||
Node* dest, Node* dest_offset,
|
||||
|
@ -154,11 +158,12 @@ public:
|
|||
virtual bool guaranteed_safepoint() { return false; }
|
||||
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
|
||||
|
||||
virtual bool may_modify(const TypeOopPtr *t_oop, PhaseTransform *phase);
|
||||
|
||||
bool is_alloc_tightly_coupled() const { return _alloc_tightly_coupled; }
|
||||
|
||||
#ifndef PRODUCT
|
||||
virtual void dump_spec(outputStream *st) const;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // SHARE_VM_OPTO_ARRAYCOPYNODE_HPP
|
||||
|
|
|
@ -797,11 +797,12 @@ Node *CallNode::result_cast() {
|
|||
}
|
||||
cast = use;
|
||||
} else if (!use->is_Initialize() &&
|
||||
!use->is_AddP()) {
|
||||
!use->is_AddP() &&
|
||||
use->Opcode() != Op_MemBarStoreStore) {
|
||||
// Expected uses are restricted to a CheckCastPP, an Initialize
|
||||
// node, and AddP nodes. If we encounter any other use (a Phi
|
||||
// node can be seen in rare cases) return this to prevent
|
||||
// incorrect optimizations.
|
||||
// node, a MemBarStoreStore (clone) and AddP nodes. If we
|
||||
// encounter any other use (a Phi node can be seen in rare
|
||||
// cases) return this to prevent incorrect optimizations.
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -1006,6 +1007,14 @@ void CallRuntimeNode::calling_convention( BasicType* sig_bt, VMRegPair *parm_reg
|
|||
|
||||
|
||||
//=============================================================================
|
||||
bool CallLeafNode::is_call_to_arraycopystub() const {
|
||||
if (_name != NULL && strstr(_name, "arraycopy") != 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#ifndef PRODUCT
|
||||
void CallLeafNode::dump_spec(outputStream *st) const {
|
||||
st->print("# ");
|
||||
|
@ -1875,3 +1884,72 @@ void AbstractLockNode::log_lock_optimization(Compile *C, const char * tag) cons
|
|||
log->tail(tag);
|
||||
}
|
||||
}
|
||||
|
||||
bool CallNode::may_modify_arraycopy_helper(const TypeOopPtr* dest_t, const TypeOopPtr *t_oop, PhaseTransform *phase) {
|
||||
if (dest_t->is_known_instance() && t_oop->is_known_instance()) {
|
||||
return dest_t->instance_id() == t_oop->instance_id();
|
||||
}
|
||||
|
||||
if (dest_t->isa_instptr() && !dest_t->klass()->equals(phase->C->env()->Object_klass())) {
|
||||
// clone
|
||||
if (t_oop->isa_aryptr()) {
|
||||
return false;
|
||||
}
|
||||
if (!t_oop->isa_instptr()) {
|
||||
return true;
|
||||
}
|
||||
if (dest_t->klass()->is_subtype_of(t_oop->klass()) || t_oop->klass()->is_subtype_of(dest_t->klass())) {
|
||||
return true;
|
||||
}
|
||||
// unrelated
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dest_t->isa_aryptr()) {
|
||||
// arraycopy or array clone
|
||||
if (t_oop->isa_instptr()) {
|
||||
return false;
|
||||
}
|
||||
if (!t_oop->isa_aryptr()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const Type* elem = dest_t->is_aryptr()->elem();
|
||||
if (elem == Type::BOTTOM) {
|
||||
// An array but we don't know what elements are
|
||||
return true;
|
||||
}
|
||||
|
||||
dest_t = dest_t->add_offset(Type::OffsetBot)->is_oopptr();
|
||||
uint dest_alias = phase->C->get_alias_index(dest_t);
|
||||
uint t_oop_alias = phase->C->get_alias_index(t_oop);
|
||||
|
||||
return dest_alias == t_oop_alias;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CallLeafNode::may_modify(const TypeOopPtr *t_oop, PhaseTransform *phase) {
|
||||
if (is_call_to_arraycopystub()) {
|
||||
const TypeTuple* args = _tf->domain();
|
||||
Node* dest = NULL;
|
||||
// Stubs that can be called once an ArrayCopyNode is expanded have
|
||||
// different signatures. Look for the second pointer argument,
|
||||
// that is the destination of the copy.
|
||||
for (uint i = TypeFunc::Parms, j = 0; i < args->cnt(); i++) {
|
||||
if (args->field_at(i)->isa_ptr()) {
|
||||
j++;
|
||||
if (j == 2) {
|
||||
dest = in(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (may_modify_arraycopy_helper(phase->type(dest)->is_oopptr(), t_oop, phase)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return CallNode::may_modify(t_oop, phase);
|
||||
}
|
||||
|
|
|
@ -556,6 +556,10 @@ class CallGenerator;
|
|||
// contain the functionality of a full scope chain of debug nodes.
|
||||
class CallNode : public SafePointNode {
|
||||
friend class VMStructs;
|
||||
|
||||
protected:
|
||||
bool may_modify_arraycopy_helper(const TypeOopPtr* dest_t, const TypeOopPtr *t_oop, PhaseTransform *phase);
|
||||
|
||||
public:
|
||||
const TypeFunc *_tf; // Function type
|
||||
address _entry_point; // Address of method being called
|
||||
|
@ -781,6 +785,8 @@ public:
|
|||
#ifndef PRODUCT
|
||||
virtual void dump_spec(outputStream *st) const;
|
||||
#endif
|
||||
bool is_call_to_arraycopystub() const;
|
||||
virtual bool may_modify(const TypeOopPtr *t_oop, PhaseTransform *phase);
|
||||
};
|
||||
|
||||
//------------------------------CallLeafNoFPNode-------------------------------
|
||||
|
@ -1082,5 +1088,4 @@ public:
|
|||
JVMState* dbg_jvms() const { return NULL; }
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // SHARE_VM_OPTO_CALLNODE_HPP
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "libadt/vectset.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
#include "opto/c2compiler.hpp"
|
||||
#include "opto/arraycopynode.hpp"
|
||||
#include "opto/callnode.hpp"
|
||||
#include "opto/cfgnode.hpp"
|
||||
#include "opto/compile.hpp"
|
||||
|
@ -113,6 +114,7 @@ bool ConnectionGraph::compute_escape() {
|
|||
GrowableArray<Node*> alloc_worklist;
|
||||
GrowableArray<Node*> ptr_cmp_worklist;
|
||||
GrowableArray<Node*> storestore_worklist;
|
||||
GrowableArray<ArrayCopyNode*> arraycopy_worklist;
|
||||
GrowableArray<PointsToNode*> ptnodes_worklist;
|
||||
GrowableArray<JavaObjectNode*> java_objects_worklist;
|
||||
GrowableArray<JavaObjectNode*> non_escaped_worklist;
|
||||
|
@ -173,6 +175,10 @@ bool ConnectionGraph::compute_escape() {
|
|||
// Collect address nodes for graph verification.
|
||||
addp_worklist.append(n);
|
||||
#endif
|
||||
} else if (n->is_ArrayCopy()) {
|
||||
// Keep a list of ArrayCopy nodes so if one of its input is non
|
||||
// escaping, we can record a unique type
|
||||
arraycopy_worklist.append(n->as_ArrayCopy());
|
||||
}
|
||||
for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) {
|
||||
Node* m = n->fast_out(i); // Get user
|
||||
|
@ -289,7 +295,7 @@ bool ConnectionGraph::compute_escape() {
|
|||
C->AliasLevel() >= 3 && EliminateAllocations) {
|
||||
// Now use the escape information to create unique types for
|
||||
// scalar replaceable objects.
|
||||
split_unique_types(alloc_worklist);
|
||||
split_unique_types(alloc_worklist, arraycopy_worklist);
|
||||
if (C->failing()) return false;
|
||||
C->print_method(PHASE_AFTER_EA, 2);
|
||||
|
||||
|
@ -333,7 +339,7 @@ void ConnectionGraph::add_objload_to_connection_graph(Node *n, Unique_Node_List
|
|||
// Populate Connection Graph with PointsTo nodes and create simple
|
||||
// connection graph edges.
|
||||
void ConnectionGraph::add_node_to_connection_graph(Node *n, Unique_Node_List *delayed_worklist) {
|
||||
assert(!_verify, "this method sould not be called for verification");
|
||||
assert(!_verify, "this method should not be called for verification");
|
||||
PhaseGVN* igvn = _igvn;
|
||||
uint n_idx = n->_idx;
|
||||
PointsToNode* n_ptn = ptnode_adr(n_idx);
|
||||
|
@ -901,8 +907,7 @@ void ConnectionGraph::process_call_arguments(CallNode *call) {
|
|||
// are still a few direct calls to the copy subroutines (See
|
||||
// PhaseStringOpts::copy_string())
|
||||
is_arraycopy = (call->Opcode() == Op_ArrayCopy) ||
|
||||
(call->as_CallLeaf()->_name != NULL &&
|
||||
strstr(call->as_CallLeaf()->_name, "arraycopy") != 0);
|
||||
call->as_CallLeaf()->is_call_to_arraycopystub();
|
||||
// fall through
|
||||
case Op_CallLeaf: {
|
||||
// Stub calls, objects do not escape but they are not scale replaceable.
|
||||
|
@ -980,7 +985,17 @@ void ConnectionGraph::process_call_arguments(CallNode *call) {
|
|||
!arg_is_arraycopy_dest) {
|
||||
continue;
|
||||
}
|
||||
set_escape_state(arg_ptn, PointsToNode::ArgEscape);
|
||||
PointsToNode::EscapeState es = PointsToNode::ArgEscape;
|
||||
if (call->is_ArrayCopy()) {
|
||||
ArrayCopyNode* ac = call->as_ArrayCopy();
|
||||
if (ac->is_clonebasic() ||
|
||||
ac->is_arraycopy_validated() ||
|
||||
ac->is_copyof_validated() ||
|
||||
ac->is_copyofrange_validated()) {
|
||||
es = PointsToNode::NoEscape;
|
||||
}
|
||||
}
|
||||
set_escape_state(arg_ptn, es);
|
||||
if (arg_is_arraycopy_dest) {
|
||||
Node* src = call->in(TypeFunc::Parms);
|
||||
if (src->is_AddP()) {
|
||||
|
@ -994,7 +1009,7 @@ void ConnectionGraph::process_call_arguments(CallNode *call) {
|
|||
// as base since objects escape states are not related.
|
||||
// Only escape state of destination object's fields affects
|
||||
// escape state of fields in source object.
|
||||
add_arraycopy(call, PointsToNode::ArgEscape, src_ptn, arg_ptn);
|
||||
add_arraycopy(call, es, src_ptn, arg_ptn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1272,12 +1287,12 @@ bool ConnectionGraph::find_non_escaped_objects(GrowableArray<PointsToNode*>& ptn
|
|||
if ((e->escape_state() < field_es) &&
|
||||
e->is_Field() && ptn->is_JavaObject() &&
|
||||
e->as_Field()->is_oop()) {
|
||||
// Change escape state of referenced fileds.
|
||||
// Change escape state of referenced fields.
|
||||
set_escape_state(e, field_es);
|
||||
es_changed = true;;
|
||||
es_changed = true;
|
||||
} else if (e->escape_state() < es) {
|
||||
set_escape_state(e, es);
|
||||
es_changed = true;;
|
||||
es_changed = true;
|
||||
}
|
||||
if (es_changed) {
|
||||
escape_worklist.push(e);
|
||||
|
@ -1389,7 +1404,7 @@ void ConnectionGraph::add_field_uses_to_worklist(FieldNode* field) {
|
|||
for (UseIterator k(arycp); k.has_next(); k.next()) {
|
||||
PointsToNode* abase = k.get();
|
||||
if (abase->arraycopy_dst() && abase != base) {
|
||||
// Look for the same arracopy reference.
|
||||
// Look for the same arraycopy reference.
|
||||
add_fields_to_worklist(field, abase);
|
||||
}
|
||||
}
|
||||
|
@ -1469,12 +1484,13 @@ int ConnectionGraph::find_init_values(JavaObjectNode* pta, PointsToNode* init_va
|
|||
int new_edges = 0;
|
||||
Node* alloc = pta->ideal_node();
|
||||
if (init_val == phantom_obj) {
|
||||
// Do nothing for Allocate nodes since its fields values are "known".
|
||||
if (alloc->is_Allocate())
|
||||
// Do nothing for Allocate nodes since its fields values are
|
||||
// "known" unless they are initialized by arraycopy/clone.
|
||||
if (alloc->is_Allocate() && !pta->arraycopy_dst())
|
||||
return 0;
|
||||
assert(alloc->as_CallStaticJava(), "sanity");
|
||||
assert(pta->arraycopy_dst() || alloc->as_CallStaticJava(), "sanity");
|
||||
#ifdef ASSERT
|
||||
if (alloc->as_CallStaticJava()->method() == NULL) {
|
||||
if (!pta->arraycopy_dst() && alloc->as_CallStaticJava()->method() == NULL) {
|
||||
const char* name = alloc->as_CallStaticJava()->_name;
|
||||
assert(strncmp(name, "_multianewarray", 15) == 0, "sanity");
|
||||
}
|
||||
|
@ -1623,11 +1639,12 @@ void ConnectionGraph::adjust_scalar_replaceable_state(JavaObjectNode* jobj) {
|
|||
//
|
||||
for (UseIterator i(jobj); i.has_next(); i.next()) {
|
||||
PointsToNode* use = i.get();
|
||||
assert(!use->is_Arraycopy(), "sanity");
|
||||
if (use->is_Arraycopy()) {
|
||||
continue;
|
||||
}
|
||||
if (use->is_Field()) {
|
||||
FieldNode* field = use->as_Field();
|
||||
assert(field->is_oop() && field->scalar_replaceable() &&
|
||||
field->fields_escape_state() == PointsToNode::NoEscape, "sanity");
|
||||
assert(field->is_oop() && field->scalar_replaceable(), "sanity");
|
||||
if (field->offset() == Type::OffsetBot) {
|
||||
jobj->set_scalar_replaceable(false);
|
||||
return;
|
||||
|
@ -1660,6 +1677,10 @@ void ConnectionGraph::adjust_scalar_replaceable_state(JavaObjectNode* jobj) {
|
|||
}
|
||||
|
||||
for (EdgeIterator j(jobj); j.has_next(); j.next()) {
|
||||
if (j.get()->is_Arraycopy()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Non-escaping object node should point only to field nodes.
|
||||
FieldNode* field = j.get()->as_Field();
|
||||
int offset = field->as_Field()->offset();
|
||||
|
@ -2636,6 +2657,7 @@ Node* ConnectionGraph::find_inst_mem(Node *orig_mem, int alias_idx, GrowableArra
|
|||
if (proj_in->is_Allocate() && proj_in->_idx == (uint)toop->instance_id()) {
|
||||
break; // hit one of our sentinels
|
||||
} else if (proj_in->is_Call()) {
|
||||
// ArrayCopy node processed here as well
|
||||
CallNode *call = proj_in->as_Call();
|
||||
if (!call->may_modify(toop, igvn)) {
|
||||
result = call->in(TypeFunc::Memory);
|
||||
|
@ -2648,6 +2670,15 @@ Node* ConnectionGraph::find_inst_mem(Node *orig_mem, int alias_idx, GrowableArra
|
|||
result = proj_in->in(TypeFunc::Memory);
|
||||
}
|
||||
} else if (proj_in->is_MemBar()) {
|
||||
if (proj_in->in(TypeFunc::Memory)->is_MergeMem() &&
|
||||
proj_in->in(TypeFunc::Memory)->as_MergeMem()->in(Compile::AliasIdxRaw)->is_Proj() &&
|
||||
proj_in->in(TypeFunc::Memory)->as_MergeMem()->in(Compile::AliasIdxRaw)->in(0)->is_ArrayCopy()) {
|
||||
// clone
|
||||
ArrayCopyNode* ac = proj_in->in(TypeFunc::Memory)->as_MergeMem()->in(Compile::AliasIdxRaw)->in(0)->as_ArrayCopy();
|
||||
if (ac->may_modify(toop, igvn)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
result = proj_in->in(TypeFunc::Memory);
|
||||
}
|
||||
} else if (result->is_MergeMem()) {
|
||||
|
@ -2724,7 +2755,7 @@ Node* ConnectionGraph::find_inst_mem(Node *orig_mem, int alias_idx, GrowableArra
|
|||
//
|
||||
// Phase 1: Process possible allocations from alloc_worklist. Create instance
|
||||
// types for the CheckCastPP for allocations where possible.
|
||||
// Propagate the the new types through users as follows:
|
||||
// Propagate the new types through users as follows:
|
||||
// casts and Phi: push users on alloc_worklist
|
||||
// AddP: cast Base and Address inputs to the instance type
|
||||
// push any AddP users on alloc_worklist and push any memnode
|
||||
|
@ -2803,7 +2834,7 @@ Node* ConnectionGraph::find_inst_mem(Node *orig_mem, int alias_idx, GrowableArra
|
|||
// 90 LoadP _ 120 30 ... alias_index=6
|
||||
// 100 LoadP _ 80 20 ... alias_index=4
|
||||
//
|
||||
void ConnectionGraph::split_unique_types(GrowableArray<Node *> &alloc_worklist) {
|
||||
void ConnectionGraph::split_unique_types(GrowableArray<Node *> &alloc_worklist, GrowableArray<ArrayCopyNode*> &arraycopy_worklist) {
|
||||
GrowableArray<Node *> memnode_worklist;
|
||||
GrowableArray<PhiNode *> orig_phis;
|
||||
PhaseIterGVN *igvn = _igvn;
|
||||
|
@ -2912,9 +2943,12 @@ void ConnectionGraph::split_unique_types(GrowableArray<Node *> &alloc_worklist)
|
|||
if (alloc->is_Allocate() && (t->isa_instptr() || t->isa_aryptr())) {
|
||||
|
||||
// First, put on the worklist all Field edges from Connection Graph
|
||||
// which is more accurate then putting immediate users from Ideal Graph.
|
||||
// which is more accurate than putting immediate users from Ideal Graph.
|
||||
for (EdgeIterator e(ptn); e.has_next(); e.next()) {
|
||||
PointsToNode* tgt = e.get();
|
||||
if (tgt->is_Arraycopy()) {
|
||||
continue;
|
||||
}
|
||||
Node* use = tgt->ideal_node();
|
||||
assert(tgt->is_Field() && use->is_AddP(),
|
||||
"only AddP nodes are Field edges in CG");
|
||||
|
@ -3068,6 +3102,38 @@ void ConnectionGraph::split_unique_types(GrowableArray<Node *> &alloc_worklist)
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
// Go over all ArrayCopy nodes and if one of the inputs has a unique
|
||||
// type, record it in the ArrayCopy node so we know what memory this
|
||||
// node uses/modified.
|
||||
for (int next = 0; next < arraycopy_worklist.length(); next++) {
|
||||
ArrayCopyNode* ac = arraycopy_worklist.at(next);
|
||||
Node* dest = ac->in(ArrayCopyNode::Dest);
|
||||
if (dest->is_AddP()) {
|
||||
dest = get_addp_base(dest);
|
||||
}
|
||||
JavaObjectNode* jobj = unique_java_object(dest);
|
||||
if (jobj != NULL) {
|
||||
Node *base = get_map(jobj->idx());
|
||||
if (base != NULL) {
|
||||
const TypeOopPtr *base_t = _igvn->type(base)->isa_oopptr();
|
||||
ac->_dest_type = base_t;
|
||||
}
|
||||
}
|
||||
Node* src = ac->in(ArrayCopyNode::Src);
|
||||
if (src->is_AddP()) {
|
||||
src = get_addp_base(src);
|
||||
}
|
||||
jobj = unique_java_object(src);
|
||||
if (jobj != NULL) {
|
||||
Node* base = get_map(jobj->idx());
|
||||
if (base != NULL) {
|
||||
const TypeOopPtr *base_t = _igvn->type(base)->isa_oopptr();
|
||||
ac->_src_type = base_t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New alias types were created in split_AddP().
|
||||
uint new_index_end = (uint) _compile->num_alias_types();
|
||||
assert(unique_old == _compile->unique(), "there should be no new ideal nodes after Phase 1");
|
||||
|
|
|
@ -536,7 +536,7 @@ private:
|
|||
|
||||
// Propagate unique types created for unescaped allocated objects
|
||||
// through the graph
|
||||
void split_unique_types(GrowableArray<Node *> &alloc_worklist);
|
||||
void split_unique_types(GrowableArray<Node *> &alloc_worklist, GrowableArray<ArrayCopyNode*> &arraycopy_worklist);
|
||||
|
||||
// Helper methods for unique types split.
|
||||
bool split_AddP(Node *addp, Node *base);
|
||||
|
|
|
@ -483,7 +483,7 @@ Block* PhaseCFG::insert_anti_dependences(Block* LCA, Node* load, bool verify) {
|
|||
|
||||
// Compute the alias index. Loads and stores with different alias indices
|
||||
// do not need anti-dependence edges.
|
||||
uint load_alias_idx = C->get_alias_index(load->adr_type());
|
||||
int load_alias_idx = C->get_alias_index(load->adr_type());
|
||||
#ifdef ASSERT
|
||||
if (load_alias_idx == Compile::AliasIdxBot && C->AliasLevel() > 0 &&
|
||||
(PrintOpto || VerifyAliases ||
|
||||
|
|
|
@ -973,6 +973,9 @@ void IfNode::improve_address_types(Node* l, Node* r, ProjNode* fail, PhaseIterGV
|
|||
assert(init_n->Opcode() == Op_ConvI2L, "unexpected first node");
|
||||
Node* new_n = igvn->C->conv_I2X_index(igvn, l, array_size);
|
||||
|
||||
// The type of the ConvI2L may be widen and so the new
|
||||
// ConvI2L may not be better than an existing ConvI2L
|
||||
if (new_n != init_n) {
|
||||
for (uint j = 2; j < stack.size(); j++) {
|
||||
Node* n = stack.node_at(j);
|
||||
Node* clone = n->clone();
|
||||
|
@ -990,6 +993,7 @@ void IfNode::improve_address_types(Node* l, Node* r, ProjNode* fail, PhaseIterGV
|
|||
igvn->_worklist.push(init_n);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (use->in(0) == NULL && (igvn->type(use)->isa_long() ||
|
||||
igvn->type(use)->isa_ptr())) {
|
||||
stack.set_index(i+1);
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "compiler/compileLog.hpp"
|
||||
#include "libadt/vectset.hpp"
|
||||
#include "opto/addnode.hpp"
|
||||
#include "opto/arraycopynode.hpp"
|
||||
#include "opto/callnode.hpp"
|
||||
#include "opto/castnode.hpp"
|
||||
#include "opto/cfgnode.hpp"
|
||||
|
@ -613,7 +614,10 @@ bool PhaseMacroExpand::can_eliminate_allocation(AllocateNode *alloc, GrowableArr
|
|||
for (DUIterator_Fast kmax, k = use->fast_outs(kmax);
|
||||
k < kmax && can_eliminate; k++) {
|
||||
Node* n = use->fast_out(k);
|
||||
if (!n->is_Store() && n->Opcode() != Op_CastP2X) {
|
||||
if (!n->is_Store() && n->Opcode() != Op_CastP2X &&
|
||||
!(n->is_ArrayCopy() &&
|
||||
n->as_ArrayCopy()->is_clonebasic() &&
|
||||
n->in(ArrayCopyNode::Dest) == use)) {
|
||||
DEBUG_ONLY(disq_node = n;)
|
||||
if (n->is_Load() || n->is_LoadStore()) {
|
||||
NOT_PRODUCT(fail_eliminate = "Field load";)
|
||||
|
@ -623,6 +627,12 @@ bool PhaseMacroExpand::can_eliminate_allocation(AllocateNode *alloc, GrowableArr
|
|||
can_eliminate = false;
|
||||
}
|
||||
}
|
||||
} else if (use->is_ArrayCopy() &&
|
||||
(use->as_ArrayCopy()->is_arraycopy_validated() ||
|
||||
use->as_ArrayCopy()->is_copyof_validated() ||
|
||||
use->as_ArrayCopy()->is_copyofrange_validated()) &&
|
||||
use->in(ArrayCopyNode::Dest) == res) {
|
||||
// ok to eliminate
|
||||
} else if (use->is_SafePoint()) {
|
||||
SafePointNode* sfpt = use->as_SafePoint();
|
||||
if (sfpt->is_Call() && sfpt->as_Call()->has_non_debug_use(res)) {
|
||||
|
@ -887,11 +897,49 @@ void PhaseMacroExpand::process_users_of_allocation(CallNode *alloc) {
|
|||
}
|
||||
#endif
|
||||
_igvn.replace_node(n, n->in(MemNode::Memory));
|
||||
} else if (n->is_ArrayCopy()) {
|
||||
// Disconnect ArrayCopy node
|
||||
ArrayCopyNode* ac = n->as_ArrayCopy();
|
||||
assert(ac->is_clonebasic(), "unexpected array copy kind");
|
||||
Node* ctl_proj = ac->proj_out(TypeFunc::Control);
|
||||
Node* mem_proj = ac->proj_out(TypeFunc::Memory);
|
||||
if (ctl_proj != NULL) {
|
||||
_igvn.replace_node(ctl_proj, n->in(0));
|
||||
}
|
||||
if (mem_proj != NULL) {
|
||||
_igvn.replace_node(mem_proj, n->in(TypeFunc::Memory));
|
||||
}
|
||||
} else {
|
||||
eliminate_card_mark(n);
|
||||
}
|
||||
k -= (oc2 - use->outcnt());
|
||||
}
|
||||
} else if (use->is_ArrayCopy()) {
|
||||
// Disconnect ArrayCopy node
|
||||
ArrayCopyNode* ac = use->as_ArrayCopy();
|
||||
assert(ac->is_arraycopy_validated() ||
|
||||
ac->is_copyof_validated() ||
|
||||
ac->is_copyofrange_validated(), "unsupported");
|
||||
CallProjections callprojs;
|
||||
ac->extract_projections(&callprojs, true);
|
||||
|
||||
_igvn.replace_node(callprojs.fallthrough_ioproj, ac->in(TypeFunc::I_O));
|
||||
_igvn.replace_node(callprojs.fallthrough_memproj, ac->in(TypeFunc::Memory));
|
||||
_igvn.replace_node(callprojs.fallthrough_catchproj, ac->in(TypeFunc::Control));
|
||||
|
||||
// Set control to top. IGVN will remove the remaining projections
|
||||
ac->set_req(0, top());
|
||||
ac->replace_edge(res, top());
|
||||
|
||||
// Disconnect src right away: it can help find new
|
||||
// opportunities for allocation elimination
|
||||
Node* src = ac->in(ArrayCopyNode::Src);
|
||||
ac->replace_edge(src, top());
|
||||
if (src->outcnt() == 0) {
|
||||
_igvn.remove_dead_node(src);
|
||||
}
|
||||
|
||||
_igvn._worklist.push(ac);
|
||||
} else {
|
||||
eliminate_card_mark(use);
|
||||
}
|
||||
|
|
|
@ -1097,8 +1097,15 @@ void PhaseMacroExpand::expand_arraycopy_node(ArrayCopyNode *ac) {
|
|||
assert(alloc != NULL, "expect alloc");
|
||||
}
|
||||
|
||||
const TypePtr* adr_type = _igvn.type(dest)->is_oopptr()->add_offset(Type::OffsetBot);
|
||||
if (ac->_dest_type != TypeOopPtr::BOTTOM) {
|
||||
adr_type = ac->_dest_type->add_offset(Type::OffsetBot)->is_ptr();
|
||||
}
|
||||
if (ac->_src_type != ac->_dest_type) {
|
||||
adr_type = TypeRawPtr::BOTTOM;
|
||||
}
|
||||
generate_arraycopy(ac, alloc, &ctrl, merge_mem, &io,
|
||||
TypeAryPtr::OOPS, T_OBJECT,
|
||||
adr_type, T_OBJECT,
|
||||
src, src_offset, dest, dest_offset, length,
|
||||
true, !ac->is_copyofrange());
|
||||
|
||||
|
@ -1232,6 +1239,13 @@ void PhaseMacroExpand::expand_arraycopy_node(ArrayCopyNode *ac) {
|
|||
}
|
||||
// This is where the memory effects are placed:
|
||||
const TypePtr* adr_type = TypeAryPtr::get_array_body_type(dest_elem);
|
||||
if (ac->_dest_type != TypeOopPtr::BOTTOM) {
|
||||
adr_type = ac->_dest_type->add_offset(Type::OffsetBot)->is_ptr();
|
||||
}
|
||||
if (ac->_src_type != ac->_dest_type) {
|
||||
adr_type = TypeRawPtr::BOTTOM;
|
||||
}
|
||||
|
||||
generate_arraycopy(ac, alloc, &ctrl, merge_mem, &io,
|
||||
adr_type, dest_elem,
|
||||
src, src_offset, dest, dest_offset, length,
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "memory/allocation.inline.hpp"
|
||||
#include "oops/objArrayKlass.hpp"
|
||||
#include "opto/addnode.hpp"
|
||||
#include "opto/arraycopynode.hpp"
|
||||
#include "opto/cfgnode.hpp"
|
||||
#include "opto/compile.hpp"
|
||||
#include "opto/connode.hpp"
|
||||
|
@ -107,6 +108,32 @@ extern void print_alias_types();
|
|||
|
||||
#endif
|
||||
|
||||
static bool membar_for_arraycopy_helper(const TypeOopPtr *t_oop, MergeMemNode* mm, PhaseTransform *phase) {
|
||||
if (mm->memory_at(Compile::AliasIdxRaw)->is_Proj()) {
|
||||
Node* n = mm->memory_at(Compile::AliasIdxRaw)->in(0);
|
||||
if ((n->is_ArrayCopy() && n->as_ArrayCopy()->may_modify(t_oop, phase)) ||
|
||||
(n->is_CallLeaf() && n->as_CallLeaf()->may_modify(t_oop, phase))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool membar_for_arraycopy(const TypeOopPtr *t_oop, MemBarNode* mb, PhaseTransform *phase) {
|
||||
Node* mem = mb->in(TypeFunc::Memory);
|
||||
if (mem->is_MergeMem()) {
|
||||
return membar_for_arraycopy_helper(t_oop, mem->as_MergeMem(), phase);
|
||||
} else if (mem->is_Phi()) {
|
||||
// after macro expansion of an ArrayCopyNode we may have a Phi
|
||||
for (uint i = 1; i < mem->req(); i++) {
|
||||
if (mem->in(i) != NULL && mem->in(i)->is_MergeMem() && membar_for_arraycopy_helper(t_oop, mem->in(i)->as_MergeMem(), phase)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Node *MemNode::optimize_simple_memory_chain(Node *mchain, const TypeOopPtr *t_oop, Node *load, PhaseGVN *phase) {
|
||||
assert((t_oop != NULL), "sanity");
|
||||
bool is_instance = t_oop->is_known_instance_field();
|
||||
|
@ -129,6 +156,7 @@ Node *MemNode::optimize_simple_memory_chain(Node *mchain, const TypeOopPtr *t_oo
|
|||
if (proj_in->is_Allocate() && proj_in->_idx == instance_id) {
|
||||
break; // hit one of our sentinels
|
||||
} else if (proj_in->is_Call()) {
|
||||
// ArrayCopyNodes processed here as well
|
||||
CallNode *call = proj_in->as_Call();
|
||||
if (!call->may_modify(t_oop, phase)) { // returns false for instances
|
||||
result = call->in(TypeFunc::Memory);
|
||||
|
@ -136,7 +164,7 @@ Node *MemNode::optimize_simple_memory_chain(Node *mchain, const TypeOopPtr *t_oo
|
|||
} else if (proj_in->is_Initialize()) {
|
||||
AllocateNode* alloc = proj_in->as_Initialize()->allocation();
|
||||
// Stop if this is the initialization for the object instance which
|
||||
// which contains this memory slice, otherwise skip over it.
|
||||
// contains this memory slice, otherwise skip over it.
|
||||
if ((alloc == NULL) || (alloc->_idx == instance_id)) {
|
||||
break;
|
||||
}
|
||||
|
@ -150,6 +178,9 @@ Node *MemNode::optimize_simple_memory_chain(Node *mchain, const TypeOopPtr *t_oo
|
|||
}
|
||||
}
|
||||
} else if (proj_in->is_MemBar()) {
|
||||
if (membar_for_arraycopy(t_oop, proj_in->as_MemBar(), phase)) {
|
||||
break;
|
||||
}
|
||||
result = proj_in->in(TypeFunc::Memory);
|
||||
} else {
|
||||
assert(false, "unexpected projection");
|
||||
|
@ -477,6 +508,75 @@ bool MemNode::detect_ptr_independence(Node* p1, AllocateNode* a1,
|
|||
}
|
||||
|
||||
|
||||
// Find an arraycopy that must have set (can_see_stored_value=true) or
|
||||
// could have set (can_see_stored_value=false) the value for this load
|
||||
Node* LoadNode::find_previous_arraycopy(PhaseTransform* phase, Node* ld_alloc, Node*& mem, bool can_see_stored_value) const {
|
||||
if (mem->is_Proj() && mem->in(0) != NULL && (mem->in(0)->Opcode() == Op_MemBarStoreStore ||
|
||||
mem->in(0)->Opcode() == Op_MemBarCPUOrder)) {
|
||||
Node* mb = mem->in(0);
|
||||
if (mb->in(0) != NULL && mb->in(0)->is_Proj() &&
|
||||
mb->in(0)->in(0) != NULL && mb->in(0)->in(0)->is_ArrayCopy()) {
|
||||
ArrayCopyNode* ac = mb->in(0)->in(0)->as_ArrayCopy();
|
||||
if (ac->is_clonebasic()) {
|
||||
intptr_t offset;
|
||||
AllocateNode* alloc = AllocateNode::Ideal_allocation(ac->in(ArrayCopyNode::Dest), phase, offset);
|
||||
assert(alloc != NULL && alloc->initialization()->is_complete_with_arraycopy(), "broken allocation");
|
||||
if (alloc == ld_alloc) {
|
||||
return ac;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (mem->is_Proj() && mem->in(0) != NULL && mem->in(0)->is_ArrayCopy()) {
|
||||
ArrayCopyNode* ac = mem->in(0)->as_ArrayCopy();
|
||||
|
||||
if (ac->is_arraycopy_validated() ||
|
||||
ac->is_copyof_validated() ||
|
||||
ac->is_copyofrange_validated()) {
|
||||
Node* ld_addp = in(MemNode::Address);
|
||||
if (ld_addp->is_AddP()) {
|
||||
Node* ld_base = ld_addp->in(AddPNode::Address);
|
||||
Node* ld_offs = ld_addp->in(AddPNode::Offset);
|
||||
|
||||
Node* dest = ac->in(ArrayCopyNode::Dest);
|
||||
|
||||
if (dest == ld_base) {
|
||||
Node* src_pos = ac->in(ArrayCopyNode::SrcPos);
|
||||
Node* dest_pos = ac->in(ArrayCopyNode::DestPos);
|
||||
Node* len = ac->in(ArrayCopyNode::Length);
|
||||
|
||||
const TypeInt *dest_pos_t = phase->type(dest_pos)->isa_int();
|
||||
const TypeX *ld_offs_t = phase->type(ld_offs)->isa_intptr_t();
|
||||
const TypeInt *len_t = phase->type(len)->isa_int();
|
||||
const TypeAryPtr* ary_t = phase->type(dest)->isa_aryptr();
|
||||
|
||||
if (dest_pos_t != NULL && ld_offs_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);
|
||||
|
||||
intptr_t dest_pos_plus_len_lo = (((intptr_t)dest_pos_t->_lo) + len_t->_lo) * elemsize + header;
|
||||
intptr_t dest_pos_plus_len_hi = (((intptr_t)dest_pos_t->_hi) + len_t->_hi) * elemsize + header;
|
||||
intptr_t dest_pos_lo = ((intptr_t)dest_pos_t->_lo) * elemsize + header;
|
||||
intptr_t dest_pos_hi = ((intptr_t)dest_pos_t->_hi) * elemsize + header;
|
||||
|
||||
if (can_see_stored_value) {
|
||||
if (ld_offs_t->_lo >= dest_pos_hi && ld_offs_t->_hi < dest_pos_plus_len_lo) {
|
||||
return ac;
|
||||
}
|
||||
} else {
|
||||
if (ld_offs_t->_hi < dest_pos_lo || ld_offs_t->_lo >= dest_pos_plus_len_hi) {
|
||||
mem = ac->in(TypeFunc::Memory);
|
||||
}
|
||||
return ac;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// The logic for reordering loads and stores uses four steps:
|
||||
// (a) Walk carefully past stores and initializations which we
|
||||
// can prove are independent of this load.
|
||||
|
@ -510,6 +610,7 @@ Node* MemNode::find_previous_store(PhaseTransform* phase) {
|
|||
for (;;) { // While we can dance past unrelated stores...
|
||||
if (--cnt < 0) break; // Caught in cycle or a complicated dance?
|
||||
|
||||
Node* prev = mem;
|
||||
if (mem->is_Store()) {
|
||||
Node* st_adr = mem->in(MemNode::Address);
|
||||
intptr_t st_offset = 0;
|
||||
|
@ -580,15 +681,26 @@ Node* MemNode::find_previous_store(PhaseTransform* phase) {
|
|||
return mem; // let caller handle steps (c), (d)
|
||||
}
|
||||
|
||||
} else if (find_previous_arraycopy(phase, alloc, mem, false) != NULL) {
|
||||
if (prev != mem) {
|
||||
// Found an arraycopy but it doesn't affect that load
|
||||
continue;
|
||||
}
|
||||
// Found an arraycopy that may affect that load
|
||||
return mem;
|
||||
} else if (addr_t != NULL && addr_t->is_known_instance_field()) {
|
||||
// Can't use optimize_simple_memory_chain() since it needs PhaseGVN.
|
||||
if (mem->is_Proj() && mem->in(0)->is_Call()) {
|
||||
// ArrayCopyNodes processed here as well.
|
||||
CallNode *call = mem->in(0)->as_Call();
|
||||
if (!call->may_modify(addr_t, phase)) {
|
||||
mem = call->in(TypeFunc::Memory);
|
||||
continue; // (a) advance through independent call memory
|
||||
}
|
||||
} else if (mem->is_Proj() && mem->in(0)->is_MemBar()) {
|
||||
if (membar_for_arraycopy(addr_t, mem->in(0)->as_MemBar(), phase)) {
|
||||
break;
|
||||
}
|
||||
mem = mem->in(0)->in(TypeFunc::Memory);
|
||||
continue; // (a) advance through independent MemBar memory
|
||||
} else if (mem->is_ClearArray()) {
|
||||
|
@ -760,6 +872,66 @@ static bool skip_through_membars(Compile::AliasType* atp, const TypeInstPtr* tp,
|
|||
return false;
|
||||
}
|
||||
|
||||
// Is the value loaded previously stored by an arraycopy? If so return
|
||||
// a load node that reads from the source array so we may be able to
|
||||
// optimize out the ArrayCopy node later.
|
||||
Node* MemNode::can_see_arraycopy_value(Node* st, PhaseTransform* phase) const {
|
||||
Node* ld_adr = in(MemNode::Address);
|
||||
intptr_t ld_off = 0;
|
||||
AllocateNode* ld_alloc = AllocateNode::Ideal_allocation(ld_adr, phase, ld_off);
|
||||
Node* ac = find_previous_arraycopy(phase, ld_alloc, st, true);
|
||||
if (ac != NULL) {
|
||||
assert(ac->is_ArrayCopy(), "what kind of node can this be?");
|
||||
assert(is_Load(), "only for loads");
|
||||
|
||||
if (ac->as_ArrayCopy()->is_clonebasic()) {
|
||||
assert(ld_alloc != NULL, "need an alloc");
|
||||
Node* ld = clone();
|
||||
Node* addp = in(MemNode::Address)->clone();
|
||||
assert(addp->is_AddP(), "address must be addp");
|
||||
assert(addp->in(AddPNode::Base) == ac->in(ArrayCopyNode::Dest)->in(AddPNode::Base), "strange pattern");
|
||||
assert(addp->in(AddPNode::Address) == ac->in(ArrayCopyNode::Dest)->in(AddPNode::Address), "strange pattern");
|
||||
addp->set_req(AddPNode::Base, ac->in(ArrayCopyNode::Src)->in(AddPNode::Base));
|
||||
addp->set_req(AddPNode::Address, ac->in(ArrayCopyNode::Src)->in(AddPNode::Address));
|
||||
ld->set_req(MemNode::Address, phase->transform(addp));
|
||||
if (in(0) != NULL) {
|
||||
assert(ld_alloc->in(0) != NULL, "alloc must have control");
|
||||
ld->set_req(0, ld_alloc->in(0));
|
||||
}
|
||||
return ld;
|
||||
} else {
|
||||
Node* ld = clone();
|
||||
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));
|
||||
|
||||
const TypeAryPtr* ary_t = phase->type(in(MemNode::Address))->isa_aryptr();
|
||||
BasicType ary_elem = ary_t->klass()->as_array_klass()->element_type()->basic_type();
|
||||
uint header = arrayOopDesc::base_offset_in_bytes(ary_elem);
|
||||
uint shift = exact_log2(type2aelembytes(ary_elem));
|
||||
|
||||
Node* diff = phase->transform(new SubINode(ac->in(ArrayCopyNode::SrcPos), ac->in(ArrayCopyNode::DestPos)));
|
||||
#ifdef _LP64
|
||||
diff = phase->transform(new ConvI2LNode(diff));
|
||||
#endif
|
||||
diff = phase->transform(new LShiftXNode(diff, phase->intcon(shift)));
|
||||
|
||||
Node* offset = phase->transform(new AddXNode(addp->in(AddPNode::Offset), diff));
|
||||
addp->set_req(AddPNode::Offset, offset);
|
||||
ld->set_req(MemNode::Address, phase->transform(addp));
|
||||
|
||||
if (in(0) != NULL) {
|
||||
assert(ac->in(0) != NULL, "alloc must have control");
|
||||
ld->set_req(0, ac->in(0));
|
||||
}
|
||||
return ld;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------can_see_stored_value------------------------------
|
||||
// This routine exists to make sure this set of tests is done the same
|
||||
// everywhere. We need to make a coordinated change: first LoadNode::Ideal
|
||||
|
@ -793,6 +965,7 @@ Node* MemNode::can_see_stored_value(Node* st, PhaseTransform* phase) const {
|
|||
opc == Op_MemBarRelease ||
|
||||
opc == Op_StoreFence ||
|
||||
opc == Op_MemBarReleaseLock ||
|
||||
opc == Op_MemBarStoreStore ||
|
||||
opc == Op_MemBarCPUOrder) {
|
||||
Node* mem = current->in(0)->in(TypeFunc::Memory);
|
||||
if (mem->is_MergeMem()) {
|
||||
|
@ -863,10 +1036,11 @@ Node* MemNode::can_see_stored_value(Node* st, PhaseTransform* phase) const {
|
|||
if ((alloc != NULL) && (alloc == ld_alloc)) {
|
||||
// examine a captured store value
|
||||
st = init->find_captured_store(ld_off, memory_size(), phase);
|
||||
if (st != NULL)
|
||||
if (st != NULL) {
|
||||
continue; // take one more trip around
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load boxed value from result of valueOf() call is input parameter.
|
||||
if (this->is_Load() && ld_adr->is_AddP() &&
|
||||
|
@ -1335,6 +1509,29 @@ Node *LoadNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
|||
}
|
||||
}
|
||||
|
||||
// Is there a dominating load that loads the same value? Leave
|
||||
// anything that is not a load of a field/array element (like
|
||||
// barriers etc.) alone
|
||||
if (in(0) != NULL && adr_type() != TypeRawPtr::BOTTOM && can_reshape) {
|
||||
for (DUIterator_Fast imax, i = mem->fast_outs(imax); i < imax; i++) {
|
||||
Node *use = mem->fast_out(i);
|
||||
if (use != this &&
|
||||
use->Opcode() == Opcode() &&
|
||||
use->in(0) != NULL &&
|
||||
use->in(0) != in(0) &&
|
||||
use->in(Address) == in(Address)) {
|
||||
Node* ctl = in(0);
|
||||
for (int i = 0; i < 10 && ctl != NULL; i++) {
|
||||
ctl = IfNode::up_one_dom(ctl);
|
||||
if (ctl == use->in(0)) {
|
||||
set_req(0, use->in(0));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for prior store with a different base or offset; make Load
|
||||
// independent. Skip through any number of them. Bail out if the stores
|
||||
// are in an endless dead cycle and report no progress. This is a key
|
||||
|
@ -1348,6 +1545,12 @@ Node *LoadNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
|||
// the alias index stuff. So instead, peek through Stores and IFF we can
|
||||
// fold up, do so.
|
||||
Node* prev_mem = find_previous_store(phase);
|
||||
if (prev_mem != NULL) {
|
||||
Node* value = can_see_arraycopy_value(prev_mem, phase);
|
||||
if (value != NULL) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
// Steps (a), (b): Walk past independent stores to find an exact match.
|
||||
if (prev_mem != NULL && prev_mem != in(MemNode::Memory)) {
|
||||
// (c) See if we can fold up on the spot, but don't fold up here.
|
||||
|
@ -2529,7 +2732,6 @@ LoadStoreConditionalNode::LoadStoreConditionalNode( Node *c, Node *mem, Node *ad
|
|||
|
||||
//=============================================================================
|
||||
//-------------------------------adr_type--------------------------------------
|
||||
// Do we Match on this edge index or not? Do not match memory
|
||||
const TypePtr* ClearArrayNode::adr_type() const {
|
||||
Node *adr = in(3);
|
||||
if (adr == NULL) return NULL; // node is dead
|
||||
|
|
|
@ -72,6 +72,8 @@ protected:
|
|||
debug_only(_adr_type=at; adr_type();)
|
||||
}
|
||||
|
||||
virtual Node* find_previous_arraycopy(PhaseTransform* phase, Node* ld_alloc, Node*& mem, bool can_see_stored_value) const { return NULL; }
|
||||
|
||||
public:
|
||||
// Helpers for the optimizer. Documented in memnode.cpp.
|
||||
static bool detect_ptr_independence(Node* p1, AllocateNode* a1,
|
||||
|
@ -124,6 +126,7 @@ public:
|
|||
// Can this node (load or store) accurately see a stored value in
|
||||
// the given memory state? (The state may or may not be in(Memory).)
|
||||
Node* can_see_stored_value(Node* st, PhaseTransform* phase) const;
|
||||
Node* can_see_arraycopy_value(Node* st, PhaseTransform* phase) const;
|
||||
|
||||
#ifndef PRODUCT
|
||||
static void dump_adr_type(const Node* mem, const TypePtr* adr_type, outputStream *st);
|
||||
|
@ -147,6 +150,8 @@ protected:
|
|||
// Should LoadNode::Ideal() attempt to remove control edges?
|
||||
virtual bool can_remove_control() const;
|
||||
const Type* const _type; // What kind of value is loaded?
|
||||
|
||||
virtual Node* find_previous_arraycopy(PhaseTransform* phase, Node* ld_alloc, Node*& mem, bool can_see_stored_value) const;
|
||||
public:
|
||||
|
||||
LoadNode(Node *c, Node *mem, Node *adr, const TypePtr* at, const Type *rt, MemOrd mo)
|
||||
|
|
|
@ -25,50 +25,15 @@
|
|||
* @test
|
||||
* @bug 6912521
|
||||
* @summary small array copy as loads/stores
|
||||
* @compile TestArrayCopyAsLoadsStores.java TestArrayCopyUtils.java
|
||||
* @run main/othervm -ea -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:CompileCommand=dontinline,TestArrayCopyAsLoadsStores::m* -XX:TypeProfileLevel=200 TestArrayCopyAsLoadsStores
|
||||
* @run main/othervm -ea -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:CompileCommand=dontinline,TestArrayCopyAsLoadsStores::m* -XX:+IgnoreUnrecognizedVMOptions -XX:+StressArrayCopyMacroNode -XX:TypeProfileLevel=200 TestArrayCopyAsLoadsStores
|
||||
*
|
||||
*/
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
|
||||
public class TestArrayCopyAsLoadsStores {
|
||||
|
||||
public enum ArraySrc {
|
||||
SMALL,
|
||||
LARGE,
|
||||
ZERO
|
||||
}
|
||||
|
||||
public enum ArrayDst {
|
||||
NONE,
|
||||
NEW,
|
||||
SRC
|
||||
}
|
||||
|
||||
static class A {
|
||||
}
|
||||
|
||||
static class B extends A {
|
||||
}
|
||||
|
||||
static final A[] small_a_src = new A[5];
|
||||
static final A[] large_a_src = new A[10];
|
||||
static final A[] zero_a_src = new A[0];
|
||||
static final int[] small_int_src = new int[5];
|
||||
static final int[] large_int_src = new int[10];
|
||||
static final int[] zero_int_src = new int[0];
|
||||
static final Object[] small_object_src = new Object[5];
|
||||
static Object src;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Args {
|
||||
ArraySrc src();
|
||||
ArrayDst dst() default ArrayDst.NONE;
|
||||
int[] extra_args() default {};
|
||||
}
|
||||
public class TestArrayCopyAsLoadsStores extends TestArrayCopyUtils {
|
||||
|
||||
// array clone should be compiled as loads/stores
|
||||
@Args(src=ArraySrc.SMALL)
|
||||
|
@ -349,166 +314,7 @@ public class TestArrayCopyAsLoadsStores {
|
|||
return false;
|
||||
}
|
||||
|
||||
final HashMap<String,Method> tests = new HashMap<>();
|
||||
{
|
||||
for (Method m : this.getClass().getDeclaredMethods()) {
|
||||
if (m.getName().matches("m[0-9]+(_check)?")) {
|
||||
assert(Modifier.isStatic(m.getModifiers())) : m;
|
||||
tests.put(m.getName(), m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean success = true;
|
||||
|
||||
void doTest(String name) throws Exception {
|
||||
Method m = tests.get(name);
|
||||
Method m_check = tests.get(name + "_check");
|
||||
Class[] paramTypes = m.getParameterTypes();
|
||||
Object[] params = new Object[paramTypes.length];
|
||||
Class retType = m.getReturnType();
|
||||
boolean isIntArray = (retType.isPrimitive() && !retType.equals(Void.TYPE)) ||
|
||||
(retType.equals(Void.TYPE) && paramTypes[0].getComponentType().isPrimitive()) ||
|
||||
(retType.isArray() && retType.getComponentType().isPrimitive());
|
||||
|
||||
Args args = m.getAnnotation(Args.class);
|
||||
|
||||
Object src = null;
|
||||
switch(args.src()) {
|
||||
case SMALL: {
|
||||
if (isIntArray) {
|
||||
src = small_int_src;
|
||||
} else {
|
||||
src = small_a_src;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LARGE: {
|
||||
if (isIntArray) {
|
||||
src = large_int_src;
|
||||
} else {
|
||||
src = large_a_src;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ZERO: {
|
||||
if (isIntArray) {
|
||||
src = zero_int_src;
|
||||
} else {
|
||||
src = zero_a_src;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 20000; i++) {
|
||||
boolean failure = false;
|
||||
|
||||
int p = 0;
|
||||
|
||||
if (params.length > 0) {
|
||||
if (isIntArray) {
|
||||
params[0] = ((int[])src).clone();
|
||||
} else {
|
||||
params[0] = ((A[])src).clone();
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
if (params.length > 1) {
|
||||
switch(args.dst()) {
|
||||
case NEW: {
|
||||
if (isIntArray) {
|
||||
params[1] = new int[((int[])params[0]).length];
|
||||
} else {
|
||||
params[1] = new A[((A[])params[0]).length];
|
||||
}
|
||||
p++;
|
||||
break;
|
||||
}
|
||||
case SRC: {
|
||||
params[1] = params[0];
|
||||
p++;
|
||||
break;
|
||||
}
|
||||
case NONE: break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < args.extra_args().length; j++) {
|
||||
params[p+j] = args.extra_args()[j];
|
||||
}
|
||||
|
||||
Object res = m.invoke(null, params);
|
||||
|
||||
if (retType.isPrimitive() && !retType.equals(Void.TYPE)) {
|
||||
int s = (int)res;
|
||||
int sum = 0;
|
||||
int[] int_res = (int[])src;
|
||||
for (int j = 0; j < int_res.length; j++) {
|
||||
sum += int_res[j];
|
||||
}
|
||||
failure = (s != sum);
|
||||
if (failure) {
|
||||
System.out.println("Test " + name + " failed: result = " + s + " != " + sum);
|
||||
}
|
||||
} else {
|
||||
Object dest = null;
|
||||
if (!retType.equals(Void.TYPE)) {
|
||||
dest = res;
|
||||
} else {
|
||||
dest = params[1];
|
||||
}
|
||||
|
||||
if (m_check != null) {
|
||||
failure = (boolean)m_check.invoke(null, new Object[] { src, dest });
|
||||
} else {
|
||||
if (isIntArray) {
|
||||
int[] int_res = (int[])src;
|
||||
int[] int_dest = (int[])dest;
|
||||
for (int j = 0; j < int_res.length; j++) {
|
||||
if (int_res[j] != int_dest[j]) {
|
||||
System.out.println("Test " + name + " failed for " + j + " src[" + j +"]=" + int_res[j] + ", dest[" + j + "]=" + int_dest[j]);
|
||||
failure = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Object[] object_res = (Object[])src;
|
||||
Object[] object_dest = (Object[])dest;
|
||||
for (int j = 0; j < object_res.length; j++) {
|
||||
if (object_res[j] != object_dest[j]) {
|
||||
System.out.println("Test " + name + " failed for " + j + " src[" + j +"]=" + object_res[j] + ", dest[" + j + "]=" + object_dest[j]);
|
||||
failure = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (failure) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
for (int i = 0; i < small_a_src.length; i++) {
|
||||
small_a_src[i] = new A();
|
||||
}
|
||||
|
||||
for (int i = 0; i < small_int_src.length; i++) {
|
||||
small_int_src[i] = i;
|
||||
}
|
||||
|
||||
for (int i = 0; i < large_int_src.length; i++) {
|
||||
large_int_src[i] = i;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
small_object_src[i] = new Object();
|
||||
}
|
||||
|
||||
TestArrayCopyAsLoadsStores test = new TestArrayCopyAsLoadsStores();
|
||||
|
||||
test.doTest("m1");
|
||||
|
|
223
hotspot/test/compiler/arraycopy/TestArrayCopyUtils.java
Normal file
223
hotspot/test/compiler/arraycopy/TestArrayCopyUtils.java
Normal file
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 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.
|
||||
*/
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
|
||||
abstract class TestArrayCopyUtils {
|
||||
public enum ArraySrc {
|
||||
SMALL,
|
||||
LARGE,
|
||||
ZERO
|
||||
}
|
||||
|
||||
public enum ArrayDst {
|
||||
NONE,
|
||||
NEW,
|
||||
SRC
|
||||
}
|
||||
|
||||
static class A {
|
||||
}
|
||||
|
||||
static class B extends A {
|
||||
}
|
||||
|
||||
static final A[] small_a_src = new A[5];
|
||||
static final A[] large_a_src = new A[10];
|
||||
static final A[] zero_a_src = new A[0];
|
||||
static final int[] small_int_src = new int[5];
|
||||
static final int[] large_int_src = new int[10];
|
||||
static final int[] zero_int_src = new int[0];
|
||||
static final Object[] small_object_src = new Object[5];
|
||||
static Object src;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Args {
|
||||
ArraySrc src();
|
||||
ArrayDst dst() default ArrayDst.NONE;
|
||||
int[] extra_args() default {};
|
||||
}
|
||||
|
||||
final HashMap<String,Method> tests = new HashMap<>();
|
||||
{
|
||||
for (Method m : this.getClass().getDeclaredMethods()) {
|
||||
if (m.getName().matches("m[0-9]+(_check)?")) {
|
||||
assert(Modifier.isStatic(m.getModifiers())) : m;
|
||||
tests.put(m.getName(), m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean success = true;
|
||||
|
||||
void doTest(String name) throws Exception {
|
||||
Method m = tests.get(name);
|
||||
Method m_check = tests.get(name + "_check");
|
||||
Class[] paramTypes = m.getParameterTypes();
|
||||
Object[] params = new Object[paramTypes.length];
|
||||
Class retType = m.getReturnType();
|
||||
boolean isIntArray = (retType.isPrimitive() && !retType.equals(Void.TYPE)) ||
|
||||
(retType.equals(Void.TYPE) && paramTypes[0].getComponentType().isPrimitive()) ||
|
||||
(retType.isArray() && retType.getComponentType().isPrimitive());
|
||||
|
||||
Args args = m.getAnnotation(Args.class);
|
||||
|
||||
Object src = null;
|
||||
switch(args.src()) {
|
||||
case SMALL: {
|
||||
if (isIntArray) {
|
||||
src = small_int_src;
|
||||
} else {
|
||||
src = small_a_src;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LARGE: {
|
||||
if (isIntArray) {
|
||||
src = large_int_src;
|
||||
} else {
|
||||
src = large_a_src;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ZERO: {
|
||||
if (isIntArray) {
|
||||
src = zero_int_src;
|
||||
} else {
|
||||
src = zero_a_src;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 20000; i++) {
|
||||
boolean failure = false;
|
||||
|
||||
int p = 0;
|
||||
|
||||
if (params.length > 0) {
|
||||
if (isIntArray) {
|
||||
params[0] = ((int[])src).clone();
|
||||
} else {
|
||||
params[0] = ((A[])src).clone();
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
if (params.length > 1) {
|
||||
switch(args.dst()) {
|
||||
case NEW: {
|
||||
if (isIntArray) {
|
||||
params[1] = new int[((int[])params[0]).length];
|
||||
} else {
|
||||
params[1] = new A[((A[])params[0]).length];
|
||||
}
|
||||
p++;
|
||||
break;
|
||||
}
|
||||
case SRC: {
|
||||
params[1] = params[0];
|
||||
p++;
|
||||
break;
|
||||
}
|
||||
case NONE: break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < args.extra_args().length; j++) {
|
||||
params[p+j] = args.extra_args()[j];
|
||||
}
|
||||
|
||||
Object res = m.invoke(null, params);
|
||||
|
||||
if (retType.isPrimitive() && !retType.equals(Void.TYPE)) {
|
||||
int s = (int)res;
|
||||
int sum = 0;
|
||||
int[] int_res = (int[])src;
|
||||
for (int j = 0; j < int_res.length; j++) {
|
||||
sum += int_res[j];
|
||||
}
|
||||
failure = (s != sum);
|
||||
if (failure) {
|
||||
System.out.println("Test " + name + " failed: result = " + s + " != " + sum);
|
||||
}
|
||||
} else {
|
||||
Object dest = null;
|
||||
if (!retType.equals(Void.TYPE)) {
|
||||
dest = res;
|
||||
} else {
|
||||
dest = params[1];
|
||||
}
|
||||
|
||||
if (m_check != null) {
|
||||
failure = (boolean)m_check.invoke(null, new Object[] { src, dest });
|
||||
} else {
|
||||
if (isIntArray) {
|
||||
int[] int_res = (int[])src;
|
||||
int[] int_dest = (int[])dest;
|
||||
for (int j = 0; j < int_res.length; j++) {
|
||||
if (int_res[j] != int_dest[j]) {
|
||||
System.out.println("Test " + name + " failed for " + j + " src[" + j +"]=" + int_res[j] + ", dest[" + j + "]=" + int_dest[j]);
|
||||
failure = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Object[] object_res = (Object[])src;
|
||||
Object[] object_dest = (Object[])dest;
|
||||
for (int j = 0; j < object_res.length; j++) {
|
||||
if (object_res[j] != object_dest[j]) {
|
||||
System.out.println("Test " + name + " failed for " + j + " src[" + j +"]=" + object_res[j] + ", dest[" + j + "]=" + object_dest[j]);
|
||||
failure = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (failure) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TestArrayCopyUtils() {
|
||||
for (int i = 0; i < small_a_src.length; i++) {
|
||||
small_a_src[i] = new A();
|
||||
}
|
||||
|
||||
for (int i = 0; i < small_int_src.length; i++) {
|
||||
small_int_src[i] = i;
|
||||
}
|
||||
|
||||
for (int i = 0; i < large_int_src.length; i++) {
|
||||
large_int_src[i] = i;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
small_object_src[i] = new Object();
|
||||
}
|
||||
}
|
||||
}
|
195
hotspot/test/compiler/arraycopy/TestEliminateArrayCopy.java
Normal file
195
hotspot/test/compiler/arraycopy/TestEliminateArrayCopy.java
Normal file
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 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 8076188
|
||||
* @summary arraycopy to non escaping destination may be eliminated
|
||||
* @compile TestEliminateArrayCopy.java TestArrayCopyUtils.java
|
||||
* @run main/othervm -ea -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:CompileCommand=dontinline,TestEliminateArrayCopy*::m* TestEliminateArrayCopy
|
||||
*
|
||||
*/
|
||||
|
||||
public class TestEliminateArrayCopy {
|
||||
|
||||
static class CloneTests extends TestInstanceCloneUtils {
|
||||
// object allocation and ArrayCopyNode should be eliminated
|
||||
static void m1(E src) throws CloneNotSupportedException {
|
||||
src.clone();
|
||||
}
|
||||
|
||||
// both object allocations and ArrayCopyNode should be eliminated
|
||||
static void m2(Object dummy) throws CloneNotSupportedException {
|
||||
E src = new E(false);
|
||||
src.clone();
|
||||
}
|
||||
|
||||
// object allocation and ArrayCopyNode should be eliminated. Fields should be loaded from src.
|
||||
static int m3(E src) throws CloneNotSupportedException {
|
||||
E dest = (E)src.clone();
|
||||
return dest.i1 + dest.i2 + dest.i3 + dest.i4 + dest.i5 +
|
||||
dest.i6 + dest.i7 + dest.i8 + dest.i9;
|
||||
}
|
||||
}
|
||||
|
||||
static class ArrayCopyTests extends TestArrayCopyUtils {
|
||||
|
||||
// object allocation and ArrayCopyNode should be eliminated.
|
||||
@Args(src=ArraySrc.LARGE)
|
||||
static int m1() throws CloneNotSupportedException {
|
||||
int[] array_clone = (int[])large_int_src.clone();
|
||||
return array_clone[0] + array_clone[1] + array_clone[2] +
|
||||
array_clone[3] + array_clone[4] + array_clone[5] +
|
||||
array_clone[6] + array_clone[7] + array_clone[8] +
|
||||
array_clone[9];
|
||||
}
|
||||
|
||||
// object allocation and ArrayCopyNode should be eliminated.
|
||||
@Args(src=ArraySrc.LARGE)
|
||||
static int m2() {
|
||||
int[] dest = new int[10];
|
||||
System.arraycopy(large_int_src, 0, dest, 0, 10);
|
||||
return dest[0] + dest[1] + dest[2] + dest[3] + dest[4] +
|
||||
dest[5] + dest[6] + dest[7] + dest[8] + dest[9];
|
||||
}
|
||||
|
||||
// object allocations and ArrayCopyNodes should be eliminated.
|
||||
@Args(src=ArraySrc.LARGE)
|
||||
static int m3() {
|
||||
int[] dest1 = new int[10];
|
||||
System.arraycopy(large_int_src, 0, dest1, 0, 10);
|
||||
|
||||
int[] dest2 = new int[10];
|
||||
System.arraycopy(dest1, 0, dest2, 0, 10);
|
||||
|
||||
return dest2[0] + dest2[1] + dest2[2] + dest2[3] + dest2[4] +
|
||||
dest2[5] + dest2[6] + dest2[7] + dest2[8] + dest2[9];
|
||||
}
|
||||
|
||||
static class m4_class {
|
||||
Object f;
|
||||
}
|
||||
|
||||
static void m4_helper() {}
|
||||
|
||||
// allocations eliminated and arraycopy optimized out
|
||||
@Args(src=ArraySrc.LARGE)
|
||||
static int m4() {
|
||||
int[] dest = new int[10];
|
||||
m4_class o = new m4_class();
|
||||
o.f = dest;
|
||||
m4_helper();
|
||||
System.arraycopy(large_int_src, 0, o.f, 0, 10);
|
||||
return dest[0] + dest[1] + dest[2] + dest[3] + dest[4] +
|
||||
dest[5] + dest[6] + dest[7] + dest[8] + dest[9];
|
||||
}
|
||||
|
||||
static void m5_helper() {}
|
||||
|
||||
// Small copy cannot be converted to loads/stores because
|
||||
// allocation is not close enough to arraycopy but arraycopy
|
||||
// itself can be eliminated
|
||||
@Args(src=ArraySrc.SMALL, dst=ArrayDst.NEW)
|
||||
static void m5(A[] src, A[] dest) {
|
||||
A[] temp = new A[5];
|
||||
m5_helper();
|
||||
System.arraycopy(src, 0, temp, 0, 5);
|
||||
dest[0] = temp[0];
|
||||
dest[1] = temp[1];
|
||||
dest[2] = temp[2];
|
||||
dest[3] = temp[3];
|
||||
dest[4] = temp[4];
|
||||
}
|
||||
|
||||
// object allocation and ArrayCopyNode should be eliminated.
|
||||
@Args(src=ArraySrc.LARGE)
|
||||
static int m6(int [] src) {
|
||||
int res = src[0] + src[1] + src[2] + src[3] + src[4] +
|
||||
src[5] + src[6] + src[7] + src[8] + src[9];
|
||||
|
||||
int[] dest = new int[10];
|
||||
|
||||
System.arraycopy(src, 0, dest, 0, 10);
|
||||
|
||||
res += dest[0] + dest[1] + dest[2] + dest[3] + dest[4] +
|
||||
dest[5] + dest[6] + dest[7] + dest[8] + dest[9];
|
||||
return res/2;
|
||||
}
|
||||
|
||||
@Args(src=ArraySrc.LARGE)
|
||||
static int m7() {
|
||||
int[] dest = new int[10];
|
||||
dest[0] = large_int_src[8];
|
||||
dest[1] = large_int_src[9];
|
||||
System.arraycopy(large_int_src, 0, dest, 2, 8);
|
||||
return dest[0] + dest[1] + dest[2] + dest[3] + dest[4] +
|
||||
dest[5] + dest[6] + dest[7] + dest[8] + dest[9];
|
||||
}
|
||||
}
|
||||
|
||||
// test that OptimizePtrCompare still works
|
||||
static final Object[] m1_array = new Object[10];
|
||||
static boolean m1_array_null_element = false;
|
||||
static void m1(int i) {
|
||||
Object[] array_clone = (Object[])m1_array.clone();
|
||||
if (array_clone[i] == null) {
|
||||
m1_array_null_element = true;
|
||||
}
|
||||
}
|
||||
|
||||
static public void main(String[] args) throws Exception {
|
||||
CloneTests clone_tests = new CloneTests();
|
||||
|
||||
clone_tests.doTest(clone_tests.e, "m1");
|
||||
clone_tests.doTest(null, "m2");
|
||||
clone_tests.doTest(clone_tests.e, "m3");
|
||||
|
||||
ArrayCopyTests ac_tests = new ArrayCopyTests();
|
||||
|
||||
ac_tests.doTest("m1");
|
||||
ac_tests.doTest("m2");
|
||||
ac_tests.doTest("m3");
|
||||
ac_tests.doTest("m4");
|
||||
ac_tests.doTest("m5");
|
||||
ac_tests.doTest("m6");
|
||||
ac_tests.doTest("m7");
|
||||
|
||||
if (!clone_tests.success || !ac_tests.success) {
|
||||
throw new RuntimeException("some tests failed");
|
||||
}
|
||||
|
||||
// Make sure both branches of the if in m1() appear taken
|
||||
for (int i = 0; i < 7000; i++) {
|
||||
m1(0);
|
||||
}
|
||||
m1_array[0] = new Object();
|
||||
for (int i = 0; i < 20000; i++) {
|
||||
m1(0);
|
||||
}
|
||||
m1_array_null_element = false;
|
||||
m1(0);
|
||||
if (m1_array_null_element) {
|
||||
throw new RuntimeException("OptimizePtrCompare test failed");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,200 +25,13 @@
|
|||
* @test
|
||||
* @bug 6700100
|
||||
* @summary small instance clone as loads/stores
|
||||
* @compile TestInstanceCloneAsLoadsStores.java TestInstanceCloneUtils.java
|
||||
* @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:CompileCommand=dontinline,TestInstanceCloneAsLoadsStores::m* TestInstanceCloneAsLoadsStores
|
||||
* @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:CompileCommand=dontinline,TestInstanceCloneAsLoadsStores::m* -XX:+IgnoreUnrecognizedVMOptions -XX:+StressArrayCopyMacroNode TestInstanceCloneAsLoadsStores
|
||||
*
|
||||
*/
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
|
||||
public class TestInstanceCloneAsLoadsStores {
|
||||
static class Base implements Cloneable {
|
||||
void initialize(Class c, int i) {
|
||||
for (Field f : c.getDeclaredFields()) {
|
||||
setVal(f, i);
|
||||
i++;
|
||||
}
|
||||
if (c != Base.class) {
|
||||
initialize(c.getSuperclass(), i);
|
||||
}
|
||||
}
|
||||
|
||||
Base() {
|
||||
initialize(getClass(), 0);
|
||||
}
|
||||
|
||||
void setVal(Field f, int i) {
|
||||
try {
|
||||
if (f.getType() == int.class) {
|
||||
f.setInt(this, i);
|
||||
return;
|
||||
} else if (f.getType() == short.class) {
|
||||
f.setShort(this, (short)i);
|
||||
return;
|
||||
} else if (f.getType() == byte.class) {
|
||||
f.setByte(this, (byte)i);
|
||||
return;
|
||||
} else if (f.getType() == long.class) {
|
||||
f.setLong(this, i);
|
||||
return;
|
||||
}
|
||||
} catch(IllegalAccessException iae) {
|
||||
throw new RuntimeException("Getting fields failed");
|
||||
}
|
||||
throw new RuntimeException("unexpected field type");
|
||||
}
|
||||
|
||||
int getVal(Field f) {
|
||||
try {
|
||||
if (f.getType() == int.class) {
|
||||
return f.getInt(this);
|
||||
} else if (f.getType() == short.class) {
|
||||
return (int)f.getShort(this);
|
||||
} else if (f.getType() == byte.class) {
|
||||
return (int)f.getByte(this);
|
||||
} else if (f.getType() == long.class) {
|
||||
return (int)f.getLong(this);
|
||||
}
|
||||
} catch(IllegalAccessException iae) {
|
||||
throw new RuntimeException("Setting fields failed");
|
||||
}
|
||||
throw new RuntimeException("unexpected field type");
|
||||
}
|
||||
|
||||
boolean fields_equal(Class c, Base o) {
|
||||
for (Field f : c.getDeclaredFields()) {
|
||||
if (getVal(f) != o.getVal(f)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (c != Base.class) {
|
||||
return fields_equal(c.getSuperclass(), o);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
return fields_equal(getClass(), (Base)obj);
|
||||
}
|
||||
|
||||
String print_fields(Class c, String s) {
|
||||
for (Field f : c.getDeclaredFields()) {
|
||||
if (s != "") {
|
||||
s += "\n";
|
||||
}
|
||||
s = s + f + " = " + getVal(f);
|
||||
}
|
||||
if (c != Base.class) {
|
||||
return print_fields(c.getSuperclass(), s);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return print_fields(getClass(), "");
|
||||
}
|
||||
|
||||
int fields_sum(Class c, int s) {
|
||||
for (Field f : c.getDeclaredFields()) {
|
||||
s += getVal(f);
|
||||
}
|
||||
if (c != Base.class) {
|
||||
return fields_sum(c.getSuperclass(), s);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public int sum() {
|
||||
return fields_sum(getClass(), 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class A extends Base {
|
||||
int i1;
|
||||
int i2;
|
||||
int i3;
|
||||
int i4;
|
||||
int i5;
|
||||
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
}
|
||||
|
||||
static class B extends A {
|
||||
int i6;
|
||||
}
|
||||
|
||||
static final class D extends Base {
|
||||
byte i1;
|
||||
short i2;
|
||||
long i3;
|
||||
int i4;
|
||||
int i5;
|
||||
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
}
|
||||
|
||||
static final class E extends Base {
|
||||
int i1;
|
||||
int i2;
|
||||
int i3;
|
||||
int i4;
|
||||
int i5;
|
||||
int i6;
|
||||
int i7;
|
||||
int i8;
|
||||
int i9;
|
||||
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
}
|
||||
|
||||
static final class F extends Base {
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
}
|
||||
|
||||
static class G extends Base {
|
||||
int i1;
|
||||
int i2;
|
||||
int i3;
|
||||
|
||||
public Object myclone() throws CloneNotSupportedException {
|
||||
return clone();
|
||||
}
|
||||
}
|
||||
|
||||
static class H extends G {
|
||||
int i4;
|
||||
int i5;
|
||||
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
}
|
||||
|
||||
static class J extends Base {
|
||||
int i1;
|
||||
int i2;
|
||||
int i3;
|
||||
|
||||
public Object myclone() throws CloneNotSupportedException {
|
||||
return clone();
|
||||
}
|
||||
}
|
||||
|
||||
static class K extends J {
|
||||
int i4;
|
||||
int i5;
|
||||
}
|
||||
public class TestInstanceCloneAsLoadsStores extends TestInstanceCloneUtils {
|
||||
|
||||
// Should be compiled as loads/stores
|
||||
static Object m1(D src) throws CloneNotSupportedException {
|
||||
|
@ -269,62 +82,10 @@ public class TestInstanceCloneAsLoadsStores {
|
|||
return (J)src.myclone();
|
||||
}
|
||||
|
||||
final HashMap<String,Method> tests = new HashMap<>();
|
||||
{
|
||||
for (Method m : this.getClass().getDeclaredMethods()) {
|
||||
if (m.getName().matches("m[0-9]+")) {
|
||||
assert(Modifier.isStatic(m.getModifiers())) : m;
|
||||
tests.put(m.getName(), m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean success = true;
|
||||
|
||||
void doTest(Base src, String name) throws Exception {
|
||||
Method m = tests.get(name);
|
||||
|
||||
for (int i = 0; i < 20000; i++) {
|
||||
boolean failure = false;
|
||||
Base res = null;
|
||||
int s = 0;
|
||||
if (m.getReturnType().isPrimitive()) {
|
||||
s = (int)m.invoke(null, src);
|
||||
failure = (s != src.sum());
|
||||
} else {
|
||||
res = (Base)m.invoke(null, src);
|
||||
failure = !res.equals(src);
|
||||
}
|
||||
if (failure) {
|
||||
System.out.println("Test " + name + " failed");
|
||||
System.out.println("source: ");
|
||||
System.out.println(src);
|
||||
System.out.println("result: ");
|
||||
if (m.getReturnType().isPrimitive()) {
|
||||
System.out.println(s);
|
||||
} else {
|
||||
System.out.println(res);
|
||||
}
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
TestInstanceCloneAsLoadsStores test = new TestInstanceCloneAsLoadsStores();
|
||||
|
||||
A a = new A();
|
||||
B b = new B();
|
||||
D d = new D();
|
||||
E e = new E();
|
||||
F f = new F();
|
||||
G g = new G();
|
||||
H h = new H();
|
||||
J j = new J();
|
||||
K k = new K();
|
||||
|
||||
test.doTest(d, "m1");
|
||||
test.doTest(d, "m2");
|
||||
test.doTest(e, "m3");
|
||||
|
|
310
hotspot/test/compiler/arraycopy/TestInstanceCloneUtils.java
Normal file
310
hotspot/test/compiler/arraycopy/TestInstanceCloneUtils.java
Normal file
|
@ -0,0 +1,310 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 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.
|
||||
*/
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
|
||||
abstract class TestInstanceCloneUtils {
|
||||
static class Base implements Cloneable {
|
||||
void initialize(Class c, int i) {
|
||||
for (Field f : c.getDeclaredFields()) {
|
||||
setVal(f, i);
|
||||
i++;
|
||||
}
|
||||
if (c != Base.class) {
|
||||
initialize(c.getSuperclass(), i);
|
||||
}
|
||||
}
|
||||
|
||||
Base(boolean initialize) {
|
||||
if (initialize) {
|
||||
initialize(getClass(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void setVal(Field f, int i) {
|
||||
try {
|
||||
if (f.getType() == int.class) {
|
||||
f.setInt(this, i);
|
||||
return;
|
||||
} else if (f.getType() == short.class) {
|
||||
f.setShort(this, (short)i);
|
||||
return;
|
||||
} else if (f.getType() == byte.class) {
|
||||
f.setByte(this, (byte)i);
|
||||
return;
|
||||
} else if (f.getType() == long.class) {
|
||||
f.setLong(this, i);
|
||||
return;
|
||||
}
|
||||
} catch(IllegalAccessException iae) {
|
||||
throw new RuntimeException("Getting fields failed");
|
||||
}
|
||||
throw new RuntimeException("unexpected field type");
|
||||
}
|
||||
|
||||
int getVal(Field f) {
|
||||
try {
|
||||
if (f.getType() == int.class) {
|
||||
return f.getInt(this);
|
||||
} else if (f.getType() == short.class) {
|
||||
return (int)f.getShort(this);
|
||||
} else if (f.getType() == byte.class) {
|
||||
return (int)f.getByte(this);
|
||||
} else if (f.getType() == long.class) {
|
||||
return (int)f.getLong(this);
|
||||
}
|
||||
} catch(IllegalAccessException iae) {
|
||||
throw new RuntimeException("Setting fields failed");
|
||||
}
|
||||
throw new RuntimeException("unexpected field type");
|
||||
}
|
||||
|
||||
boolean fields_equal(Class c, Base o) {
|
||||
for (Field f : c.getDeclaredFields()) {
|
||||
if (getVal(f) != o.getVal(f)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (c != Base.class) {
|
||||
return fields_equal(c.getSuperclass(), o);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
return fields_equal(getClass(), (Base)obj);
|
||||
}
|
||||
|
||||
String print_fields(Class c, String s) {
|
||||
for (Field f : c.getDeclaredFields()) {
|
||||
if (s != "") {
|
||||
s += "\n";
|
||||
}
|
||||
s = s + f + " = " + getVal(f);
|
||||
}
|
||||
if (c != Base.class) {
|
||||
return print_fields(c.getSuperclass(), s);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return print_fields(getClass(), "");
|
||||
}
|
||||
|
||||
int fields_sum(Class c, int s) {
|
||||
for (Field f : c.getDeclaredFields()) {
|
||||
s += getVal(f);
|
||||
}
|
||||
if (c != Base.class) {
|
||||
return fields_sum(c.getSuperclass(), s);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public int sum() {
|
||||
return fields_sum(getClass(), 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class A extends Base {
|
||||
int i1;
|
||||
int i2;
|
||||
int i3;
|
||||
int i4;
|
||||
int i5;
|
||||
|
||||
A(boolean initialize) {
|
||||
super(initialize);
|
||||
}
|
||||
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
}
|
||||
|
||||
static class B extends A {
|
||||
int i6;
|
||||
|
||||
B(boolean initialize) {
|
||||
super(initialize);
|
||||
}
|
||||
}
|
||||
|
||||
static final class D extends Base {
|
||||
byte i1;
|
||||
short i2;
|
||||
long i3;
|
||||
int i4;
|
||||
int i5;
|
||||
|
||||
D(boolean initialize) {
|
||||
super(initialize);
|
||||
}
|
||||
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
}
|
||||
|
||||
static final class E extends Base {
|
||||
int i1;
|
||||
int i2;
|
||||
int i3;
|
||||
int i4;
|
||||
int i5;
|
||||
int i6;
|
||||
int i7;
|
||||
int i8;
|
||||
int i9;
|
||||
|
||||
E(boolean initialize) {
|
||||
super(initialize);
|
||||
}
|
||||
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
}
|
||||
|
||||
static final class F extends Base {
|
||||
F(boolean initialize) {
|
||||
super(initialize);
|
||||
}
|
||||
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
}
|
||||
|
||||
static class G extends Base {
|
||||
int i1;
|
||||
int i2;
|
||||
int i3;
|
||||
|
||||
G(boolean initialize) {
|
||||
super(initialize);
|
||||
}
|
||||
|
||||
public Object myclone() throws CloneNotSupportedException {
|
||||
return clone();
|
||||
}
|
||||
}
|
||||
|
||||
static class H extends G {
|
||||
int i4;
|
||||
int i5;
|
||||
|
||||
H(boolean initialize) {
|
||||
super(initialize);
|
||||
}
|
||||
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
}
|
||||
|
||||
static class J extends Base {
|
||||
int i1;
|
||||
int i2;
|
||||
int i3;
|
||||
|
||||
J(boolean initialize) {
|
||||
super(initialize);
|
||||
}
|
||||
|
||||
public Object myclone() throws CloneNotSupportedException {
|
||||
return clone();
|
||||
}
|
||||
}
|
||||
|
||||
static class K extends J {
|
||||
int i4;
|
||||
int i5;
|
||||
|
||||
K(boolean initialize) {
|
||||
super(initialize);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static final A a = new A(true);
|
||||
static final B b = new B(true);
|
||||
static final D d = new D(true);
|
||||
static final E e = new E(true);
|
||||
static final F f = new F(true);
|
||||
static final G g = new G(true);
|
||||
static final H h = new H(true);
|
||||
static final J j = new J(true);
|
||||
static final K k = new K(true);
|
||||
|
||||
final HashMap<String,Method> tests = new HashMap<>();
|
||||
{
|
||||
for (Method m : this.getClass().getDeclaredMethods()) {
|
||||
if (m.getName().matches("m[0-9]+")) {
|
||||
assert(Modifier.isStatic(m.getModifiers())) : m;
|
||||
tests.put(m.getName(), m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean success = true;
|
||||
|
||||
void doTest(Base src, String name) throws Exception {
|
||||
Method m = tests.get(name);
|
||||
|
||||
for (int i = 0; i < 20000; i++) {
|
||||
boolean failure = false;
|
||||
Base res = null;
|
||||
int s = 0;
|
||||
Class retType = m.getReturnType();
|
||||
if (retType.isPrimitive()) {
|
||||
if (!retType.equals(Void.TYPE)) {
|
||||
s = (int)m.invoke(null, src);
|
||||
failure = (s != src.sum());
|
||||
} else {
|
||||
m.invoke(null, src);
|
||||
}
|
||||
} else {
|
||||
res = (Base)m.invoke(null, src);
|
||||
failure = !res.equals(src);
|
||||
}
|
||||
if (failure) {
|
||||
System.out.println("Test " + name + " failed");
|
||||
System.out.println("source: ");
|
||||
System.out.println(src);
|
||||
System.out.println("result: ");
|
||||
if (m.getReturnType().isPrimitive()) {
|
||||
System.out.println(s);
|
||||
} else {
|
||||
System.out.println(res);
|
||||
}
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue