Skip to content

Commit

Permalink
8076188: Optimize arraycopy out for non escaping destination
Browse files Browse the repository at this point in the history
If the destination of an arraycopy is non escaping, the arraycopy may be optimized out

Reviewed-by: kvn, vlivanov
  • Loading branch information
rwestrel committed May 12, 2015
1 parent 3dc9461 commit a9cdbd0
Show file tree
Hide file tree
Showing 17 changed files with 1,224 additions and 486 deletions.
18 changes: 17 additions & 1 deletion hotspot/src/share/vm/opto/arraycopynode.cpp
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
7 changes: 6 additions & 1 deletion hotspot/src/share/vm/opto/arraycopynode.hpp
Expand Up @@ -124,6 +124,10 @@ class ArrayCopyNode : public CallNode {
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,
Expand Down Expand Up @@ -154,11 +158,12 @@ class ArrayCopyNode : public CallNode {
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
86 changes: 82 additions & 4 deletions hotspot/src/share/vm/opto/callnode.cpp
Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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("# ");
Expand Down Expand Up @@ -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);
}
7 changes: 6 additions & 1 deletion hotspot/src/share/vm/opto/callnode.hpp
Expand Up @@ -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
Expand Down Expand Up @@ -781,6 +785,8 @@ class CallLeafNode : public CallRuntimeNode {
#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-------------------------------
Expand Down Expand Up @@ -1082,5 +1088,4 @@ class UnlockNode : public AbstractLockNode {
JVMState* dbg_jvms() const { return NULL; }
#endif
};

#endif // SHARE_VM_OPTO_CALLNODE_HPP

0 comments on commit a9cdbd0

Please sign in to comment.