diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 043f590bc83..29d412b4ebf 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -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) { @@ -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 @@ -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* _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); @@ -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 @@ -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* _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"); @@ -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; } @@ -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()) { @@ -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); diff --git a/src/hotspot/share/opto/escape.hpp b/src/hotspot/share/opto/escape.hpp index 36da0560fc6..4921601ff7e 100644 --- a/src/hotspot/share/opto/escape.hpp +++ b/src/hotspot/share/opto/escape.hpp @@ -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) { @@ -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; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index eae34c42968..c09c3764672 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -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"); diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestOptimizePtrCompare.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestOptimizePtrCompare.java index b2a3ce30357..9962631c4af 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestOptimizePtrCompare.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestOptimizePtrCompare.java @@ -36,11 +36,14 @@ public class TestOptimizePtrCompare { public static void main(String[] args) { - TestFramework.runWithFlags("--enable-preview"); + TestFramework.runWithFlags("--enable-preview", "-XX:+InlineTypePassFieldsAsArgs", "-XX:+InlineTypeReturnedAsFields"); + TestFramework.runWithFlags("--enable-preview", "-XX:-InlineTypePassFieldsAsArgs", "-XX:+InlineTypeReturnedAsFields"); + TestFramework.runWithFlags("--enable-preview", "-XX:+InlineTypePassFieldsAsArgs", "-XX:-InlineTypeReturnedAsFields"); + TestFramework.runWithFlags("--enable-preview", "-XX:-InlineTypePassFieldsAsArgs", "-XX:-InlineTypeReturnedAsFields"); } @Test - @IR(failOn = {IRNode.CMP_P}) + @IR(failOn = {IRNode.CMP_P_OR_N}) public static void test1() { Object notUsed = new Object(); // make sure EA runs Object arg = null; @@ -64,7 +67,10 @@ static value class MyValue { } @Test - @IR(counts = {IRNode.CMP_P, "1"}) + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "true", "InlineTypeReturnedAsFields", "true"}, failOn = {IRNode.CMP_P_OR_N}) + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "false", "InlineTypeReturnedAsFields", "true"}, counts = {IRNode.CMP_P_OR_N, "2", IRNode.CMP_I, "1"}) + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "true", "InlineTypeReturnedAsFields", "false"}, counts = {IRNode.CMP_P_OR_N, "2"}) + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "false", "InlineTypeReturnedAsFields", "false"}, counts = {IRNode.CMP_P_OR_N, "3"}) public static void test2() { Object notUsed = new Object(); // make sure EA runs MyValue arg = new MyValue(null); @@ -78,4 +84,150 @@ public static void test2() { static MyValue notInlined2(MyValue v) { return v; } + + static value class MyValue2 { + Object o1; + Object o2; + + MyValue2(Object o1, Object o2) { + this.o1 = o1; + this.o2 = o2; + } + } + + static value class MyValue3 { + MyValue v1; + MyValue2 v2; + + MyValue3(MyValue v1, MyValue2 v2) { + this.v1 = v1; + this.v2 = v2; + } + } + + static Object fieldO = new Object(); + + @Test + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "true", "InlineTypeReturnedAsFields", "true"}, failOn = {IRNode.CMP_P_OR_N}) + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "false", "InlineTypeReturnedAsFields", "true"}, counts = {IRNode.CMP_P_OR_N, "2", IRNode.CMP_I, "1"}) + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "true", "InlineTypeReturnedAsFields", "false"}, counts = {IRNode.CMP_P_OR_N, "2"}) + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "false", "InlineTypeReturnedAsFields", "false"}, counts = {IRNode.CMP_P_OR_N, "3"}) + public static void test3() { + Object notUsed = new Object(); // make sure EA runs + MyValue2 arg = new MyValue2(null, fieldO); + MyValue2 res = notInlined3(arg); + if (res.o1 != null) { + throw new RuntimeException("never taken"); + } + } + + @DontInline + static MyValue2 notInlined3(MyValue2 v) { + return v; + } + + @Test + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "true", "InlineTypeReturnedAsFields", "true"}, counts = {IRNode.CMP_P_OR_N, "1"}) + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "false", "InlineTypeReturnedAsFields", "true"}, counts = {IRNode.CMP_P_OR_N, "2"}) + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "true", "InlineTypeReturnedAsFields", "false"}, counts = {IRNode.CMP_P_OR_N, "1"}) + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "false", "InlineTypeReturnedAsFields", "false"}, counts = {IRNode.CMP_P_OR_N, "2"}) + public static void test4() { + Object notUsed = new Object(); // make sure EA runs + MyValue arg = new MyValue(null); + Object res = notInlined4(arg); + if (res == null) { + throw new RuntimeException("never taken"); + } + } + + @DontInline + static Object notInlined4(MyValue v) { + return v; + } + + @Test + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "true", "InlineTypeReturnedAsFields", "true"}, failOn = {IRNode.CMP_P_OR_N}) + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "false", "InlineTypeReturnedAsFields", "true"}, counts = {IRNode.CMP_P_OR_N, "2", IRNode.CMP_I, "1"}) + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "true", "InlineTypeReturnedAsFields", "false"}, counts = {IRNode.CMP_P_OR_N, "2"}) + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "false", "InlineTypeReturnedAsFields", "false"}, counts = {IRNode.CMP_P_OR_N, "3"}) + public static void test5() { + Object notUsed = new Object(); // make sure EA runs + MyValue2 arg = new MyValue2(fieldO, null); + MyValue2 res = notInlined5(arg); + if (res.o2 != null) { + throw new RuntimeException("never taken"); + } + } + + @DontInline + static MyValue2 notInlined5(MyValue2 v) { + return v; + } + + @Test(allowNotCompilable = true) + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "true", "InlineTypeReturnedAsFields", "true"}, failOn = {IRNode.CMP_P_OR_N}) + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "false", "InlineTypeReturnedAsFields", "true"}, counts = {IRNode.CMP_P_OR_N, "4", IRNode.CMP_I, "2"}) + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "true", "InlineTypeReturnedAsFields", "false"}, counts = {IRNode.CMP_P_OR_N, "2"}) + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "false", "InlineTypeReturnedAsFields", "false"}, counts = {IRNode.CMP_P_OR_N, "5"}) + public static void test6() { + Object notUsed = new Object(); // make sure EA runs + MyValue v1 = new MyValue(fieldO); + MyValue2 v2 = new MyValue2(fieldO, null); + MyValue3 v3 = new MyValue3(v1, v2); + MyValue3 res = notInlined6(v1, v2, v3); + if (res.v2.o2 != null) { + throw new RuntimeException("never taken"); + } + } + + @DontInline + static MyValue3 notInlined6(MyValue v1, MyValue2 v2, MyValue3 v3) { + return v3; + } + + @Test + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "true", "InlineTypeReturnedAsFields", "true"}, counts = {IRNode.CMP_P_OR_N, "1"}) + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "false", "InlineTypeReturnedAsFields", "true"}, counts = {IRNode.CMP_P_OR_N, "2", IRNode.CMP_I, "1"}) + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "true", "InlineTypeReturnedAsFields", "false"}, counts = {IRNode.CMP_P_OR_N, "2"}) + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "false", "InlineTypeReturnedAsFields", "false"}, counts = {IRNode.CMP_P_OR_N, "3"}) + public static void test7() { + Object notUsed = new Object(); // make sure EA runs + MyValue2 arg = new MyValue2(fieldO, null); + MyValue2 res = notInlined7(arg, true); + if (res.o2 != null) { + throw new RuntimeException("never taken"); + } + } + + @DontInline + static MyValue2 notInlined7(MyValue2 v, boolean flag) { + if (flag) { + return v; + } + return new MyValue2(fieldO, fieldO); + } + + @Test + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "true", "InlineTypeReturnedAsFields", "true"}, counts = {IRNode.CMP_P_OR_N, "1"}) + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "false", "InlineTypeReturnedAsFields", "true"}, counts = {IRNode.CMP_P_OR_N, "2", IRNode.CMP_I, "1"}) + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "true", "InlineTypeReturnedAsFields", "false"}, counts = {IRNode.CMP_P_OR_N, "2"}) + @IR(applyIfAnd = {"InlineTypePassFieldsAsArgs", "false", "InlineTypeReturnedAsFields", "false"}, counts = {IRNode.CMP_P_OR_N, "3"}) + public static void test8() { + Object notUsed = new Object(); // make sure EA runs + MyValue2 arg = new MyValue2(fieldO, null); + MyValue2 res = notInlined8(arg, true); + if (res.o2 != null) { + throw new RuntimeException("never taken"); + } + } + + static MyValue2 fieldValue2 = new MyValue2(fieldO, fieldO); + + @DontInline + static MyValue2 notInlined8(MyValue2 v, boolean flag) { + if (flag) { + return v; + } + return fieldValue2; + } }