Skip to content
Draft

more #2173

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
292 changes: 177 additions & 115 deletions src/hotspot/share/opto/escape.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1586,6 +1586,37 @@ void ConnectionGraph::add_objload_to_connection_graph(Node *n, Unique_Node_List
}
}

void ConnectionGraph::add_proj(Node* n, Unique_Node_List* delayed_worklist) {
if (n->as_Proj()->_con == TypeFunc::Parms && n->in(0)->is_Call() && n->in(0)->as_Call()->returns_pointer()) {
add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(0), delayed_worklist);
} else if (n->in(0)->is_LoadFlat()) {
// Treat LoadFlat outputs similar to a call return value
add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(0), delayed_worklist);
} else if (n->as_Proj()->_con >= TypeFunc::Parms && n->in(0)->is_Call() && n->bottom_type()->isa_ptr()) {
CallNode* call = n->in(0)->as_Call();
assert(call->tf()->returns_inline_type_as_fields(), "");
ciMethod* meth = n->in(0)->as_CallJava()->method();
BCEscapeAnalyzer* call_analyzer = (meth != nullptr) ? meth->get_bcea() : nullptr;
bool ret_arg = false;
if (call_analyzer != nullptr) {
const TypeTuple* d = call->tf()->domain_sig();
for (uint i = TypeFunc::Parms; i < d->cnt(); i++) {
if (d->field_at(i)->isa_ptr() != nullptr &&
call_analyzer->is_arg_returned(i - TypeFunc::Parms) &&
scalarized_arg_with_compatible_return(call->as_CallJava(), i)) {
ret_arg = true;
break;
}
}
}
if (n->as_Proj()->_con == TypeFunc::Parms || !ret_arg) {
add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(0), delayed_worklist);
} else {
add_local_var(n, PointsToNode::NoEscape);
}
}
}

// 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) {
Expand Down Expand Up @@ -1749,15 +1780,7 @@ void ConnectionGraph::add_node_to_connection_graph(Node *n, Unique_Node_List *de
break;
case Op_Proj: {
// we are only interested in the oop result projection from a call
if (n->as_Proj()->_con >= TypeFunc::Parms && n->in(0)->is_Call() &&
(n->in(0)->as_Call()->returns_pointer() || n->bottom_type()->isa_ptr())) {
assert((n->as_Proj()->_con == TypeFunc::Parms && n->in(0)->as_Call()->returns_pointer()) ||
n->in(0)->as_Call()->tf()->returns_inline_type_as_fields(), "what kind of oop return is it?");
add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(0), delayed_worklist);
} else if (n->as_Proj()->_con >= TypeFunc::Parms && n->in(0)->is_LoadFlat() && igvn->type(n)->isa_ptr()) {
// Treat LoadFlat outputs similar to a call return value
add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(0), delayed_worklist);
}
add_proj(n, delayed_worklist);
break;
}
case Op_Rethrow: // Exception object escapes
Expand Down Expand Up @@ -1829,6 +1852,114 @@ void ConnectionGraph::add_node_to_connection_graph(Node *n, Unique_Node_List *de
return;
}

// Iterate over the domains for the scalarized and non scalarized calling conventions: Only move to the next element
// in the non scalarized calling convention once all elements of the scalarized calling convention for that parameter
// have been iterated over. So (ignoring hidden arguments such as the null marker) iterating over:
// value class MyValue {
// int f1;
// float f2;
// }
// void m(Object o, MyValue v, int i)
// produces the pairs:
// (Object, Object), (Myvalue, int), (MyValue, float), (int, int)
class DomainIterator : public StackObj {
private:
const TypeTuple* _domain;
const TypeTuple* _domain_cc;
const GrowableArray<SigEntry>* _sig_cc;

uint _i_domain;
uint _i_domain_cc;
int _i_sig_cc;
uint _depth;
uint _first_field_pos;
const bool _is_static;

void next_helper() {
if (_sig_cc == nullptr) {
return;
}
BasicType prev_bt = _i_sig_cc > 0 ? _sig_cc->at(_i_sig_cc-1)._bt : T_ILLEGAL;
while (_i_sig_cc < _sig_cc->length()) {
BasicType bt = _sig_cc->at(_i_sig_cc)._bt;
assert(bt != T_VOID || _sig_cc->at(_i_sig_cc-1)._bt == prev_bt, "");
if (bt == T_METADATA) {
if (_depth == 0) {
_first_field_pos = _i_domain_cc;
}
_depth++;
} else if (bt == T_VOID && (prev_bt != T_LONG && prev_bt != T_DOUBLE)) {
_depth--;
if (_depth == 0) {
_i_domain++;
}
} else if (bt == T_BOOLEAN && prev_bt == T_METADATA && (_is_static || _i_domain > 0) && _sig_cc->at(_i_sig_cc)._offset == -1) {
assert(_sig_cc->at(_i_sig_cc)._null_marker, "");
assert(_depth == 1, "");
_i_domain_cc++;
_first_field_pos = _i_domain_cc;
// return;
} else {
return;
}
prev_bt = bt;
_i_sig_cc++;
}
}

public:

DomainIterator(CallJavaNode* call) :
_domain(call->tf()->domain_sig()),
_domain_cc(call->tf()->domain_cc()),
_sig_cc(call->method()->get_sig_cc()),
_i_domain(TypeFunc::Parms),
_i_domain_cc(TypeFunc::Parms),
_i_sig_cc(0),
_depth(0),
_first_field_pos(0),
_is_static(call->method()->is_static()) {
next_helper();
}

bool has_next() const {
assert(_sig_cc == nullptr || (_i_sig_cc < _sig_cc->length()) == (_i_domain < _domain->cnt()), "should reach end in sync");
assert((_i_domain < _domain->cnt()) == (_i_domain_cc < _domain_cc->cnt()), "should reach end in sync");
return _i_domain < _domain->cnt();
}

void next() {
assert(_depth != 0 || _domain->field_at(_i_domain) == _domain_cc->field_at(_i_domain_cc), "should produce same non scalarized elements");
_i_sig_cc++;
if (_depth == 0) {
_i_domain++;
}
_i_domain_cc++;
next_helper();
}

uint i_domain() const {
return _i_domain;
}

uint i_domain_cc() const {
return _i_domain_cc;
}

const Type* current_domain() const {
return _domain->field_at(_i_domain);
}

const Type* current_domain_cc() const {
return _domain_cc->field_at(_i_domain_cc);
}

uint first_field_pos() const {
assert(_first_field_pos >= TypeFunc::Parms, "");
return _first_field_pos;
}
};

// Add final simple edges to graph.
void ConnectionGraph::add_final_edges(Node *n) {
PointsToNode* n_ptn = ptnode_adr(n->_idx);
Expand Down Expand Up @@ -1927,15 +2058,7 @@ void ConnectionGraph::add_final_edges(Node *n) {
break;
}
case Op_Proj: {
if (n->in(0)->is_Call()) {
// we are only interested in the oop result projection from a call
assert((n->as_Proj()->_con == TypeFunc::Parms && n->in(0)->as_Call()->returns_pointer()) ||
n->in(0)->as_Call()->tf()->returns_inline_type_as_fields(), "what kind of oop return is it?");
add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(0), nullptr);
} else if (n->in(0)->is_LoadFlat()) {
// Treat LoadFlat outputs similar to a call return value
add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(0), nullptr);
}
add_proj(n, nullptr);
break;
}
case Op_Rethrow: // Exception object escapes
Expand Down Expand Up @@ -2109,95 +2232,6 @@ bool ConnectionGraph::add_final_edges_unsafe_access(Node* n, uint opcode) {
return false;
}

// Iterate over the domains for the scalarized and non scalarized calling conventions: Only move to the next element
// in the non scalarized calling convention once all elements of the scalarized calling convention for that parameter
// have been iterated over. So (ignoring hidden arguments such as the null marker) iterating over:
// value class MyValue {
// int f1;
// float f2;
// }
// void m(Object o, MyValue v, int i)
// produces the pairs:
// (Object, Object), (Myvalue, int), (MyValue, float), (int, int)
class DomainIterator : public StackObj {
private:
const TypeTuple* _domain;
const TypeTuple* _domain_cc;
const GrowableArray<SigEntry>* _sig_cc;

uint _i_domain;
uint _i_domain_cc;
int _i_sig_cc;
uint _depth;

void next_helper() {
if (_sig_cc == nullptr) {
return;
}
BasicType prev_bt = _i_sig_cc > 0 ? _sig_cc->at(_i_sig_cc-1)._bt : T_ILLEGAL;
while (_i_sig_cc < _sig_cc->length()) {
BasicType bt = _sig_cc->at(_i_sig_cc)._bt;
assert(bt != T_VOID || _sig_cc->at(_i_sig_cc-1)._bt == prev_bt, "");
if (bt == T_METADATA) {
_depth++;
} else if (bt == T_VOID && (prev_bt != T_LONG && prev_bt != T_DOUBLE)) {
_depth--;
if (_depth == 0) {
_i_domain++;
}
} else {
return;
}
prev_bt = bt;
_i_sig_cc++;
}
}

public:

DomainIterator(CallJavaNode* call) :
_domain(call->tf()->domain_sig()),
_domain_cc(call->tf()->domain_cc()),
_sig_cc(call->method()->get_sig_cc()),
_i_domain(TypeFunc::Parms),
_i_domain_cc(TypeFunc::Parms),
_i_sig_cc(0),
_depth(0) {
next_helper();
}

bool has_next() const {
assert(_sig_cc == nullptr || (_i_sig_cc < _sig_cc->length()) == (_i_domain < _domain->cnt()), "should reach end in sync");
assert((_i_domain < _domain->cnt()) == (_i_domain_cc < _domain_cc->cnt()), "should reach end in sync");
return _i_domain < _domain->cnt();
}

void next() {
assert(_depth != 0 || _domain->field_at(_i_domain) == _domain_cc->field_at(_i_domain_cc), "should produce same non scalarized elements");
_i_sig_cc++;
if (_depth == 0) {
_i_domain++;
}
_i_domain_cc++;
next_helper();
}

uint i_domain() const {
return _i_domain;
}

uint i_domain_cc() const {
return _i_domain_cc;
}

const Type* current_domain() const {
return _domain->field_at(_i_domain);
}

const Type* current_domain_cc() const {
return _domain_cc->field_at(_i_domain_cc);
}
};

void ConnectionGraph::add_call_node(CallNode* call) {
assert(call->returns_pointer() || call->tf()->returns_inline_type_as_fields(), "only for call which returns pointer");
Expand Down Expand Up @@ -2310,11 +2344,11 @@ void ConnectionGraph::add_call_node(CallNode* call) {
} else {
bool ret_arg = false;
// Determine whether any arguments are returned.
for (DomainIterator di(call->as_CallJava()); di.has_next(); di.next()) {
uint arg = di.i_domain() - TypeFunc::Parms;
if (di.current_domain_cc()->isa_ptr() != nullptr &&
call_analyzer->is_arg_returned(arg) &&
!meth->is_scalarized_arg(arg)) {
const TypeTuple* d = call->tf()->domain_sig();
for (uint i = TypeFunc::Parms; i < d->cnt(); i++) {
if (d->field_at(i)->isa_ptr() != nullptr &&
call_analyzer->is_arg_returned(i - TypeFunc::Parms) &&
!scalarized_arg_with_compatible_return(call->as_CallJava(), i)) {
ret_arg = true;
break;
}
Expand All @@ -2335,6 +2369,15 @@ void ConnectionGraph::add_call_node(CallNode* call) {
}
}

bool ConnectionGraph::scalarized_arg_with_compatible_return(CallJavaNode* call, uint k) {
ciMethod* meth = call->method();
BCEscapeAnalyzer* call_analyzer = meth->get_bcea();
assert(call_analyzer->is_arg_returned(k - TypeFunc::Parms), "");
return meth->is_scalarized_arg(k - TypeFunc::Parms) &&
call->tf()->domain_sig()->field_at(k)->meet(TypePtr::NULL_PTR)->higher_equal(
call->tf()->range_sig()->field_at(TypeFunc::Parms));
}

void ConnectionGraph::process_call_arguments(CallNode *call) {
bool is_arraycopy = false;
switch (call->Opcode()) {
Expand Down Expand Up @@ -2527,11 +2570,30 @@ void ConnectionGraph::process_call_arguments(CallNode *call) {
const Type* at = di.current_domain_cc();
Node* arg = call->in(di.i_domain_cc());
PointsToNode* arg_ptn = ptnode_adr(arg->_idx);
assert(!call_analyzer->is_arg_returned(k) || !scalarized_arg_with_compatible_return(call->as_CallJava(), di.i_domain()) ||
call->proj_out_or_null(di.i_domain_cc() - di.first_field_pos() + TypeFunc::Parms + 1) == nullptr ||
_igvn->type(call->proj_out_or_null(di.i_domain_cc() - di.first_field_pos() + TypeFunc::Parms + 1)) == at,
"");
if (at->isa_ptr() != nullptr &&
call_analyzer->is_arg_returned(k) &&
!meth->is_scalarized_arg(k)) {
call_analyzer->is_arg_returned(k)) {
// The call returns arguments.
if (call_ptn != nullptr) { // Is call's result used?
if (scalarized_arg_with_compatible_return(call->as_CallJava(), di.i_domain())) {
ProjNode* res_proj = call->proj_out_or_null(di.i_domain_cc() - di.first_field_pos() + TypeFunc::Parms + 1);
if (res_proj != nullptr) {
assert(_igvn->type(res_proj)->isa_ptr(), "");
if (res_proj->_con == TypeFunc::Parms) {
// assert(call_ptn->is_LocalVar(), "node should be registered");
// assert(arg_ptn != nullptr, "node should be registered");
// add_edge(call_ptn, arg_ptn);
} else {
PointsToNode* proj_ptn = ptnode_adr(res_proj->_idx);
add_edge(proj_ptn, arg_ptn);
if (!call_analyzer->is_return_local()) {
add_edge(proj_ptn, phantom_obj);
}
}
}
} else if (call_ptn != nullptr) { // Is call's result used?
assert(call_ptn->is_LocalVar(), "node should be registered");
assert(arg_ptn != nullptr, "node should be registered");
add_edge(call_ptn, arg_ptn);
Expand Down
4 changes: 4 additions & 0 deletions src/hotspot/share/opto/escape.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,8 @@ class ConnectionGraph: public ArenaObj {
// Utility function for nodes that load an object
void add_objload_to_connection_graph(Node* n, Unique_Node_List* delayed_worklist);

void add_proj(Node* n, Unique_Node_List* delayed_worklist);

// Add LocalVar node and edge if possible
void add_local_var_and_edge(Node* n, PointsToNode::EscapeState es, Node* to,
Unique_Node_List *delayed_worklist) {
Expand All @@ -690,6 +692,8 @@ class ConnectionGraph: public ArenaObj {
void add_to_congraph_unsafe_access(Node* n, uint opcode, Unique_Node_List* delayed_worklist);
bool add_final_edges_unsafe_access(Node* n, uint opcode);

bool scalarized_arg_with_compatible_return(CallJavaNode* call, uint k);

#ifndef PRODUCT
static int _no_escape_counter;
static int _arg_escape_counter;
Expand Down
5 changes: 5 additions & 0 deletions test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,11 @@ public static void callLeafNoFpOfMethodNodes(String irNodePlaceholder, String ca
beforeMatchingNameRegex(CMP_N, "CmpN");
}

public static final String CMP_P_OR_N = PREFIX + "CMP_P_OR_N" + POSTFIX;
static {
beforeMatchingNameRegex(CMP_P_OR_N, "Cmp(P|N)");
}

public static final String CMP_LT_MASK = PREFIX + "CMP_LT_MASK" + POSTFIX;
static {
beforeMatchingNameRegex(CMP_LT_MASK, "CmpLTMask");
Expand Down
Loading