changeset 48432:d6947a2c7ba6 mvt

8193501: [MVT] Compilation fails with "unexpected projection from allocation node" Reviewed-by: dsimms
author roland
date Wed, 20 Dec 2017 11:51:20 +0100
parents 47282d999c2a
children 00be52e4338c
files src/hotspot/share/opto/arraycopynode.cpp src/hotspot/share/opto/callGenerator.cpp src/hotspot/share/opto/callnode.cpp src/hotspot/share/opto/callnode.hpp src/hotspot/share/opto/castnode.cpp src/hotspot/share/opto/graphKit.cpp src/hotspot/share/opto/library_call.cpp src/hotspot/share/opto/macro.cpp src/hotspot/share/opto/stringopts.cpp
diffstat 9 files changed, 162 insertions(+), 136 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/share/opto/arraycopynode.cpp	Wed Dec 20 10:30:18 2017 +0100
+++ b/src/hotspot/share/opto/arraycopynode.cpp	Wed Dec 20 11:51:20 2017 +0100
@@ -477,17 +477,16 @@
     } else {
       // replace fallthrough projections of the ArrayCopyNode by the
       // new memory, control and the input IO.
-      CallProjections callprojs;
-      extract_projections(&callprojs, true, false);
+      CallProjections* callprojs = extract_projections(true, false);
 
-      if (callprojs.fallthrough_ioproj != NULL) {
-        igvn->replace_node(callprojs.fallthrough_ioproj, in(TypeFunc::I_O));
+      if (callprojs->fallthrough_ioproj != NULL) {
+        igvn->replace_node(callprojs->fallthrough_ioproj, in(TypeFunc::I_O));
       }
-      if (callprojs.fallthrough_memproj != NULL) {
-        igvn->replace_node(callprojs.fallthrough_memproj, mem);
+      if (callprojs->fallthrough_memproj != NULL) {
+        igvn->replace_node(callprojs->fallthrough_memproj, mem);
       }
-      if (callprojs.fallthrough_catchproj != NULL) {
-        igvn->replace_node(callprojs.fallthrough_catchproj, ctl);
+      if (callprojs->fallthrough_catchproj != NULL) {
+        igvn->replace_node(callprojs->fallthrough_catchproj, ctl);
       }
 
       // The ArrayCopyNode is not disconnected. It still has the
--- a/src/hotspot/share/opto/callGenerator.cpp	Wed Dec 20 10:30:18 2017 +0100
+++ b/src/hotspot/share/opto/callGenerator.cpp	Wed Dec 20 11:51:20 2017 +0100
@@ -397,18 +397,22 @@
   }
 
   // check for unreachable loop
-  CallProjections callprojs;
-  call->extract_projections(&callprojs, true);
-  if (callprojs.fallthrough_catchproj == call->in(0) ||
-      callprojs.catchall_catchproj == call->in(0) ||
-      callprojs.fallthrough_memproj == call->in(TypeFunc::Memory) ||
-      callprojs.catchall_memproj == call->in(TypeFunc::Memory) ||
-      callprojs.fallthrough_ioproj == call->in(TypeFunc::I_O) ||
-      callprojs.catchall_ioproj == call->in(TypeFunc::I_O) ||
-      (callprojs.resproj != NULL && call->find_edge(callprojs.resproj) != -1) ||
-      (callprojs.exobj != NULL && call->find_edge(callprojs.exobj) != -1)) {
+  CallProjections* callprojs = call->extract_projections(true);
+  if (callprojs->fallthrough_catchproj == call->in(0) ||
+      callprojs->catchall_catchproj == call->in(0) ||
+      callprojs->fallthrough_memproj == call->in(TypeFunc::Memory) ||
+      callprojs->catchall_memproj == call->in(TypeFunc::Memory) ||
+      callprojs->fallthrough_ioproj == call->in(TypeFunc::I_O) ||
+      callprojs->catchall_ioproj == call->in(TypeFunc::I_O) ||
+      (callprojs->exobj != NULL && call->find_edge(callprojs->exobj) != -1)) {
     return;
   }
+  for (uint i = 0; i < callprojs->nb_resproj; i++) {
+    if (callprojs->resproj[i] != NULL && call->find_edge(callprojs->resproj[i]) != -1) {
+      return;
+    }
+  }
+
 
   Compile* C = Compile::current();
   // Remove inlined methods from Compiler's lists.
--- a/src/hotspot/share/opto/callnode.cpp	Wed Dec 20 10:30:18 2017 +0100
+++ b/src/hotspot/share/opto/callnode.cpp	Wed Dec 20 11:51:20 2017 +0100
@@ -881,16 +881,21 @@
 }
 
 
-void CallNode::extract_projections(CallProjections* projs, bool separate_io_proj, bool do_asserts) {
-  projs->fallthrough_proj      = NULL;
-  projs->fallthrough_catchproj = NULL;
-  projs->fallthrough_ioproj    = NULL;
-  projs->catchall_ioproj       = NULL;
-  projs->catchall_catchproj    = NULL;
-  projs->fallthrough_memproj   = NULL;
-  projs->catchall_memproj      = NULL;
-  projs->resproj               = NULL;
-  projs->exobj                 = NULL;
+CallProjections* CallNode::extract_projections(bool separate_io_proj, bool do_asserts) {
+  uint max_res = TypeFunc::Parms-1;
+  for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) {
+    ProjNode *pn = fast_out(i)->as_Proj();
+    max_res = MAX2(max_res, pn->_con);
+  }
+
+  assert(max_res < _tf->range_cc()->cnt(), "result out of bounds");
+
+  uint projs_size = sizeof(CallProjections);
+  if (max_res > TypeFunc::Parms) {
+    projs_size += (max_res-TypeFunc::Parms)*sizeof(Node*);
+  }
+  char* projs_storage = resource_allocate_bytes(projs_size);
+  CallProjections* projs = new(projs_storage)CallProjections(max_res - TypeFunc::Parms + 1);
 
   for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) {
     ProjNode *pn = fast_out(i)->as_Proj();
@@ -937,10 +942,12 @@
         projs->fallthrough_memproj = pn;
       break;
     case TypeFunc::Parms:
-      projs->resproj = pn;
+      projs->resproj[0] = pn;
       break;
     default:
-      assert(false, "unexpected projection from allocation node.");
+      assert(pn->_con <= max_res, "unexpected projection from allocation node.");
+      projs->resproj[pn->_con-TypeFunc::Parms] = pn;
+      break;
     }
   }
 
@@ -957,6 +964,7 @@
     assert(!do_asserts || projs->catchall_memproj    != NULL, "must be found");
     assert(!do_asserts || projs->catchall_ioproj     != NULL, "must be found");
   }
+  return projs;
 }
 
 Node *CallNode::Ideal(PhaseGVN *phase, bool can_reshape) {
@@ -1443,15 +1451,15 @@
       outcnt() != 0 && result_cast() == NULL) {
     // Remove allocation by replacing the projection nodes with its inputs
     PhaseIterGVN* igvn = phase->is_IterGVN();
-    CallProjections projs;
-    extract_projections(&projs, true);
-    igvn->replace_node(projs.fallthrough_catchproj, in(TypeFunc::Control));
-    igvn->replace_node(projs.fallthrough_memproj, in(TypeFunc::Memory));
-    igvn->replace_node(projs.catchall_memproj, phase->C->top());
-    igvn->replace_node(projs.fallthrough_ioproj, in(TypeFunc::I_O));
-    igvn->replace_node(projs.catchall_ioproj, phase->C->top());
-    igvn->replace_node(projs.catchall_catchproj, phase->C->top());
-    igvn->replace_node(projs.resproj, phase->C->top());
+    CallProjections* projs = extract_projections(true);
+    assert(projs->nb_resproj == 1, "unexpected number of results");
+    igvn->replace_node(projs->fallthrough_catchproj, in(TypeFunc::Control));
+    igvn->replace_node(projs->fallthrough_memproj, in(TypeFunc::Memory));
+    igvn->replace_node(projs->catchall_memproj, phase->C->top());
+    igvn->replace_node(projs->fallthrough_ioproj, in(TypeFunc::I_O));
+    igvn->replace_node(projs->catchall_ioproj, phase->C->top());
+    igvn->replace_node(projs->catchall_catchproj, phase->C->top());
+    igvn->replace_node(projs->resproj[0], phase->C->top());
     igvn->remove_dead_node(this);
     return NULL;
   }
--- a/src/hotspot/share/opto/callnode.hpp	Wed Dec 20 10:30:18 2017 +0100
+++ b/src/hotspot/share/opto/callnode.hpp	Wed Dec 20 11:51:20 2017 +0100
@@ -540,7 +540,7 @@
 
 // Simple container for the outgoing projections of a call.  Useful
 // for serious surgery on calls.
-class CallProjections : public StackObj {
+class CallProjections {
 public:
   Node* fallthrough_proj;
   Node* fallthrough_catchproj;
@@ -549,8 +549,26 @@
   Node* catchall_catchproj;
   Node* catchall_memproj;
   Node* catchall_ioproj;
-  Node* resproj;
   Node* exobj;
+  uint nb_resproj;
+  Node* resproj[1]; // at least one projection
+
+  CallProjections(uint nbres) {
+    fallthrough_proj      = NULL;
+    fallthrough_catchproj = NULL;
+    fallthrough_memproj   = NULL;
+    fallthrough_ioproj    = NULL;
+    catchall_catchproj    = NULL;
+    catchall_memproj      = NULL;
+    catchall_ioproj       = NULL;
+    exobj                 = NULL;
+    nb_resproj            = nbres;
+    resproj[0]            = NULL;
+    for (uint i = 1; i < nb_resproj; i++) {
+      resproj[i]          = NULL;
+    }
+  }
+
 };
 
 class CallGenerator;
@@ -634,7 +652,7 @@
   // Collect all the interesting edges from a call for use in
   // replacing the call by something else.  Used by macro expansion
   // and the late inlining support.
-  void extract_projections(CallProjections* projs, bool separate_io_proj, bool do_asserts = true);
+  CallProjections* extract_projections(bool separate_io_proj, bool do_asserts = true);
 
   virtual uint match_edge(uint idx) const;
 
--- a/src/hotspot/share/opto/castnode.cpp	Wed Dec 20 10:30:18 2017 +0100
+++ b/src/hotspot/share/opto/castnode.cpp	Wed Dec 20 11:51:20 2017 +0100
@@ -445,19 +445,19 @@
 
       // Extract projections from the call and hook users to temporary nodes.
       // We will re-attach them to newly created PhiNodes below.
-      CallProjections projs;
-      call->extract_projections(&projs, true, true);
-      igvn->replace_in_uses(projs.fallthrough_catchproj, ctl_hook);
-      igvn->replace_in_uses(projs.fallthrough_memproj, mem_hook);
-      igvn->replace_in_uses(projs.fallthrough_ioproj, io_hook);
-      igvn->replace_in_uses(projs.resproj, res_hook);
-      igvn->replace_in_uses(projs.catchall_catchproj, ex_ctl_hook);
-      igvn->replace_in_uses(projs.catchall_memproj, ex_mem_hook);
-      igvn->replace_in_uses(projs.catchall_ioproj, ex_io_hook);
+      CallProjections* projs = call->extract_projections(true, true);
+      assert(projs->nb_resproj == 1, "unexpected number of results");
+      igvn->replace_in_uses(projs->fallthrough_catchproj, ctl_hook);
+      igvn->replace_in_uses(projs->fallthrough_memproj, mem_hook);
+      igvn->replace_in_uses(projs->fallthrough_ioproj, io_hook);
+      igvn->replace_in_uses(projs->resproj[0], res_hook);
+      igvn->replace_in_uses(projs->catchall_catchproj, ex_ctl_hook);
+      igvn->replace_in_uses(projs->catchall_memproj, ex_mem_hook);
+      igvn->replace_in_uses(projs->catchall_ioproj, ex_io_hook);
 
       // Restore IO input of the CatchNode
-      CatchNode* catchp = projs.fallthrough_catchproj->in(0)->as_Catch();
-      catchp->set_req(TypeFunc::I_O, projs.catchall_ioproj);
+      CatchNode* catchp = projs->fallthrough_catchproj->in(0)->as_Catch();
+      catchp->set_req(TypeFunc::I_O, projs->catchall_ioproj);
       igvn->rehash_node_delayed(catchp);
 
       // Rebuild the output JVMState from the call and use it to initialize a GraphKit
@@ -466,16 +466,16 @@
       for (uint i = TypeFunc::FramePtr; i < call->req(); i++) {
         new_map->init_req(i, call->in(i));
       }
-      new_map->set_control(projs.fallthrough_catchproj);
-      new_map->set_memory(MergeMemNode::make(projs.fallthrough_memproj));
-      new_map->set_i_o(projs.fallthrough_ioproj);
+      new_map->set_control(projs->fallthrough_catchproj);
+      new_map->set_memory(MergeMemNode::make(projs->fallthrough_memproj));
+      new_map->set_i_o(projs->fallthrough_ioproj);
       new_jvms->set_map(new_map);
 
       GraphKit kit(new_jvms, igvn);
 
       // Either we get a buffered value pointer and we can case use it
       // or we get a tagged klass pointer and we need to allocate a value.
-      Node* cast = igvn->transform(new CastP2XNode(kit.control(), projs.resproj));
+      Node* cast = igvn->transform(new CastP2XNode(kit.control(), projs->resproj[0]));
       Node* masked = igvn->transform(new AndXNode(cast, igvn->MakeConX(0x1)));
       Node* cmp = igvn->transform(new CmpXNode(masked, igvn->MakeConX(0x1)));
       Node* bol = kit.Bool(cmp, BoolTest::eq);
@@ -513,12 +513,12 @@
       // False branch: result is not tagged
       // Load buffered value type from returned oop
       kit.set_control(iffalse);
-      kit.set_all_memory(projs.fallthrough_memproj);
-      kit.set_i_o(projs.fallthrough_ioproj);
+      kit.set_all_memory(projs->fallthrough_memproj);
+      kit.set_i_o(projs->fallthrough_ioproj);
       // Cast oop to NotNull
       ConstraintCastNode* res_cast = clone()->as_ConstraintCast();
       res_cast->set_req(0, kit.control());
-      res_cast->set_req(1, projs.resproj);
+      res_cast->set_req(1, projs->resproj[0]);
       res_cast->set_type(cast_type->cast_to_ptr_type(TypePtr::NotNull));
       Node* ctl = kit.control(); // Control may get updated below
       res = ValueTypePtrNode::make_from_oop(*igvn, ctl, kit.merged_memory(), igvn->transform(res_cast));
@@ -527,9 +527,9 @@
       mem_phi->init_req(2, kit.reset_memory());
       io_phi->init_req(2, kit.i_o());
       res_phi->init_req(2, igvn->transform(res));
-      ex_region->init_req(2, projs.catchall_catchproj);
-      ex_mem_phi->init_req(2, projs.catchall_memproj);
-      ex_io_phi->init_req(2, projs.catchall_ioproj);
+      ex_region->init_req(2, projs->catchall_catchproj);
+      ex_mem_phi->init_req(2, projs->catchall_memproj);
+      ex_io_phi->init_req(2, projs->catchall_ioproj);
 
       igvn->set_delay_transform(false);
 
@@ -555,10 +555,9 @@
         uint last = phase->C->unique();
         CallNode* call = in(1)->in(0)->as_Call();
         // Extract projections from the call and hook control users to temporary node
-        CallProjections projs;
-        call->extract_projections(&projs, true, true);
-        Node* ctl = projs.fallthrough_catchproj;
-        Node* mem = projs.fallthrough_memproj;
+        CallProjections* projs = call->extract_projections(true, true);
+        Node* ctl = projs->fallthrough_catchproj;
+        Node* mem = projs->fallthrough_memproj;
         Node* ctl_hook = new Node(1);
         igvn->replace_in_uses(ctl, ctl_hook);
         Node* vtptr = ValueTypePtrNode::make_from_oop(*phase, ctl, mem, in(1));
--- a/src/hotspot/share/opto/graphKit.cpp	Wed Dec 20 10:30:18 2017 +0100
+++ b/src/hotspot/share/opto/graphKit.cpp	Wed Dec 20 11:51:20 2017 +0100
@@ -1934,8 +1934,7 @@
   SafePointNode* final_state = stop();
 
   // Find all the needed outputs of this call
-  CallProjections callprojs;
-  call->extract_projections(&callprojs, true);
+  CallProjections* callprojs = call->extract_projections(true);
 
   Node* init_mem = call->in(TypeFunc::Memory);
   Node* final_mem = final_state->in(TypeFunc::Memory);
@@ -1943,39 +1942,40 @@
   Node* final_io = final_state->in(TypeFunc::I_O);
 
   // Replace all the old call edges with the edges from the inlining result
-  if (callprojs.fallthrough_catchproj != NULL) {
-    C->gvn_replace_by(callprojs.fallthrough_catchproj, final_ctl);
+  if (callprojs->fallthrough_catchproj != NULL) {
+    C->gvn_replace_by(callprojs->fallthrough_catchproj, final_ctl);
   }
-  if (callprojs.fallthrough_memproj != NULL) {
+  if (callprojs->fallthrough_memproj != NULL) {
     if (final_mem->is_MergeMem()) {
       // Parser's exits MergeMem was not transformed but may be optimized
       final_mem = _gvn.transform(final_mem);
     }
-    C->gvn_replace_by(callprojs.fallthrough_memproj,   final_mem);
+    C->gvn_replace_by(callprojs->fallthrough_memproj,   final_mem);
   }
-  if (callprojs.fallthrough_ioproj != NULL) {
-    C->gvn_replace_by(callprojs.fallthrough_ioproj,    final_io);
+  if (callprojs->fallthrough_ioproj != NULL) {
+    C->gvn_replace_by(callprojs->fallthrough_ioproj,    final_io);
   }
 
   // Replace the result with the new result if it exists and is used
-  if (callprojs.resproj != NULL && result != NULL) {
-    C->gvn_replace_by(callprojs.resproj, result);
+  if (callprojs->resproj[0] != NULL && result != NULL) {
+    assert(callprojs->nb_resproj == 1, "unexpected number of results");
+    C->gvn_replace_by(callprojs->resproj[0], result);
   }
 
   if (ejvms == NULL) {
     // No exception edges to simply kill off those paths
-    if (callprojs.catchall_catchproj != NULL) {
-      C->gvn_replace_by(callprojs.catchall_catchproj, C->top());
+    if (callprojs->catchall_catchproj != NULL) {
+      C->gvn_replace_by(callprojs->catchall_catchproj, C->top());
     }
-    if (callprojs.catchall_memproj != NULL) {
-      C->gvn_replace_by(callprojs.catchall_memproj,   C->top());
+    if (callprojs->catchall_memproj != NULL) {
+      C->gvn_replace_by(callprojs->catchall_memproj,   C->top());
     }
-    if (callprojs.catchall_ioproj != NULL) {
-      C->gvn_replace_by(callprojs.catchall_ioproj,    C->top());
+    if (callprojs->catchall_ioproj != NULL) {
+      C->gvn_replace_by(callprojs->catchall_ioproj,    C->top());
     }
     // Replace the old exception object with top
-    if (callprojs.exobj != NULL) {
-      C->gvn_replace_by(callprojs.exobj, C->top());
+    if (callprojs->exobj != NULL) {
+      C->gvn_replace_by(callprojs->exobj, C->top());
     }
   } else {
     GraphKit ekit(ejvms);
@@ -1986,20 +1986,20 @@
 
     Node* ex_oop = ekit.use_exception_state(ex_map);
 
-    if (callprojs.catchall_catchproj != NULL) {
-      C->gvn_replace_by(callprojs.catchall_catchproj, ekit.control());
+    if (callprojs->catchall_catchproj != NULL) {
+      C->gvn_replace_by(callprojs->catchall_catchproj, ekit.control());
       ex_ctl = ekit.control();
     }
-    if (callprojs.catchall_memproj != NULL) {
-      C->gvn_replace_by(callprojs.catchall_memproj,   ekit.reset_memory());
+    if (callprojs->catchall_memproj != NULL) {
+      C->gvn_replace_by(callprojs->catchall_memproj,   ekit.reset_memory());
     }
-    if (callprojs.catchall_ioproj != NULL) {
-      C->gvn_replace_by(callprojs.catchall_ioproj,    ekit.i_o());
+    if (callprojs->catchall_ioproj != NULL) {
+      C->gvn_replace_by(callprojs->catchall_ioproj,    ekit.i_o());
     }
 
     // Replace the old exception object with the newly created one
-    if (callprojs.exobj != NULL) {
-      C->gvn_replace_by(callprojs.exobj, ex_oop);
+    if (callprojs->exobj != NULL) {
+      C->gvn_replace_by(callprojs->exobj, ex_oop);
     }
   }
 
@@ -2022,7 +2022,7 @@
     }
   }
 
-  if (callprojs.fallthrough_catchproj != NULL && !final_ctl->is_top() && do_replaced_nodes) {
+  if (callprojs->fallthrough_catchproj != NULL && !final_ctl->is_top() && do_replaced_nodes) {
     replaced_nodes.apply(C, final_ctl);
   }
   if (!ex_ctl->is_top() && do_replaced_nodes) {
--- a/src/hotspot/share/opto/library_call.cpp	Wed Dec 20 10:30:18 2017 +0100
+++ b/src/hotspot/share/opto/library_call.cpp	Wed Dec 20 11:51:20 2017 +0100
@@ -5036,11 +5036,10 @@
     _reexecute_sp = saved_reexecute_sp;
 
     // Remove the allocation from above the guards
-    CallProjections callprojs;
-    alloc->extract_projections(&callprojs, true);
+    CallProjections* callprojs = alloc->extract_projections(true);
     InitializeNode* init = alloc->initialization();
     Node* alloc_mem = alloc->in(TypeFunc::Memory);
-    C->gvn_replace_by(callprojs.fallthrough_ioproj, alloc->in(TypeFunc::I_O));
+    C->gvn_replace_by(callprojs->fallthrough_ioproj, alloc->in(TypeFunc::I_O));
     C->gvn_replace_by(init->proj_out(TypeFunc::Memory), alloc_mem);
     C->gvn_replace_by(init->proj_out(TypeFunc::Control), alloc->in(0));
 
@@ -5052,7 +5051,7 @@
     set_all_memory(mem);
     alloc->set_req(TypeFunc::Memory, mem);
     set_control(init->proj_out(TypeFunc::Control));
-    set_i_o(callprojs.fallthrough_ioproj);
+    set_i_o(callprojs->fallthrough_ioproj);
 
     // Update memory as done in GraphKit::set_output_for_allocation()
     const TypeInt* length_type = _gvn.find_int_type(alloc->in(AllocateNode::ALength));
--- a/src/hotspot/share/opto/macro.cpp	Wed Dec 20 10:30:18 2017 +0100
+++ b/src/hotspot/share/opto/macro.cpp	Wed Dec 20 11:51:20 2017 +0100
@@ -1082,12 +1082,11 @@
         assert(ac->is_arraycopy_validated() ||
                ac->is_copyof_validated() ||
                ac->is_copyofrange_validated(), "unsupported");
-        CallProjections callprojs;
-        ac->extract_projections(&callprojs, true);
+        CallProjections* callprojs = ac->extract_projections(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));
+        _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());
@@ -2679,8 +2678,7 @@
   _igvn.set_type(ret, ret->Value(&_igvn));
 
   // Before any new projection is added:
-  CallProjections projs;
-  call->extract_projections(&projs, true, true);
+  CallProjections* projs = call->extract_projections(true, true);
 
   Node* ctl = new Node(1);
   Node* mem = new Node(1);
@@ -2850,21 +2848,22 @@
   transform_later(io_phi);
   transform_later(res_phi);
 
-  _igvn.replace_in_uses(projs.fallthrough_catchproj, r);
-  _igvn.replace_in_uses(projs.fallthrough_memproj, mem_phi);
-  _igvn.replace_in_uses(projs.fallthrough_ioproj, io_phi);
-  _igvn.replace_in_uses(projs.resproj, res_phi);
-  _igvn.replace_in_uses(projs.catchall_catchproj, ex_r);
-  _igvn.replace_in_uses(projs.catchall_memproj, ex_mem_phi);
-  _igvn.replace_in_uses(projs.catchall_ioproj, ex_io_phi);
+  assert(projs->nb_resproj == 1, "unexpected number of results");
+  _igvn.replace_in_uses(projs->fallthrough_catchproj, r);
+  _igvn.replace_in_uses(projs->fallthrough_memproj, mem_phi);
+  _igvn.replace_in_uses(projs->fallthrough_ioproj, io_phi);
+  _igvn.replace_in_uses(projs->resproj[0], res_phi);
+  _igvn.replace_in_uses(projs->catchall_catchproj, ex_r);
+  _igvn.replace_in_uses(projs->catchall_memproj, ex_mem_phi);
+  _igvn.replace_in_uses(projs->catchall_ioproj, ex_io_phi);
 
-  _igvn.replace_node(ctl, projs.fallthrough_catchproj);
-  _igvn.replace_node(mem, projs.fallthrough_memproj);
-  _igvn.replace_node(io, projs.fallthrough_ioproj);
-  _igvn.replace_node(res, projs.resproj);
-  _igvn.replace_node(ex_ctl, projs.catchall_catchproj);
-  _igvn.replace_node(ex_mem, projs.catchall_memproj);
-  _igvn.replace_node(ex_io, projs.catchall_ioproj);
+  _igvn.replace_node(ctl, projs->fallthrough_catchproj);
+  _igvn.replace_node(mem, projs->fallthrough_memproj);
+  _igvn.replace_node(io, projs->fallthrough_ioproj);
+  _igvn.replace_node(res, projs->resproj[0]);
+  _igvn.replace_node(ex_ctl, projs->catchall_catchproj);
+  _igvn.replace_node(ex_mem, projs->catchall_memproj);
+  _igvn.replace_node(ex_io, projs->catchall_ioproj);
  }
 
 //---------------------------eliminate_macro_nodes----------------------
--- a/src/hotspot/share/opto/stringopts.cpp	Wed Dec 20 10:30:18 2017 +0100
+++ b/src/hotspot/share/opto/stringopts.cpp	Wed Dec 20 11:51:20 2017 +0100
@@ -321,37 +321,37 @@
 
 void StringConcat::eliminate_call(CallNode* call) {
   Compile* C = _stringopts->C;
-  CallProjections projs;
-  call->extract_projections(&projs, false);
-  if (projs.fallthrough_catchproj != NULL) {
-    C->gvn_replace_by(projs.fallthrough_catchproj, call->in(TypeFunc::Control));
+  CallProjections* projs = call->extract_projections(false);
+  if (projs->fallthrough_catchproj != NULL) {
+    C->gvn_replace_by(projs->fallthrough_catchproj, call->in(TypeFunc::Control));
   }
-  if (projs.fallthrough_memproj != NULL) {
-    C->gvn_replace_by(projs.fallthrough_memproj, call->in(TypeFunc::Memory));
+  if (projs->fallthrough_memproj != NULL) {
+    C->gvn_replace_by(projs->fallthrough_memproj, call->in(TypeFunc::Memory));
   }
-  if (projs.catchall_memproj != NULL) {
-    C->gvn_replace_by(projs.catchall_memproj, C->top());
+  if (projs->catchall_memproj != NULL) {
+    C->gvn_replace_by(projs->catchall_memproj, C->top());
   }
-  if (projs.fallthrough_ioproj != NULL) {
-    C->gvn_replace_by(projs.fallthrough_ioproj, call->in(TypeFunc::I_O));
+  if (projs->fallthrough_ioproj != NULL) {
+    C->gvn_replace_by(projs->fallthrough_ioproj, call->in(TypeFunc::I_O));
   }
-  if (projs.catchall_ioproj != NULL) {
-    C->gvn_replace_by(projs.catchall_ioproj, C->top());
+  if (projs->catchall_ioproj != NULL) {
+    C->gvn_replace_by(projs->catchall_ioproj, C->top());
   }
-  if (projs.catchall_catchproj != NULL) {
+  if (projs->catchall_catchproj != NULL) {
     // EA can't cope with the partially collapsed graph this
     // creates so put it on the worklist to be collapsed later.
-    for (SimpleDUIterator i(projs.catchall_catchproj); i.has_next(); i.next()) {
+    for (SimpleDUIterator i(projs->catchall_catchproj); i.has_next(); i.next()) {
       Node *use = i.get();
       int opc = use->Opcode();
       if (opc == Op_CreateEx || opc == Op_Region) {
         _stringopts->record_dead_node(use);
       }
     }
-    C->gvn_replace_by(projs.catchall_catchproj, C->top());
+    C->gvn_replace_by(projs->catchall_catchproj, C->top());
   }
-  if (projs.resproj != NULL) {
-    C->gvn_replace_by(projs.resproj, C->top());
+  if (projs->resproj[0] != NULL) {
+    assert(projs->nb_resproj == 1, "unexpected number of results");
+    C->gvn_replace_by(projs->resproj[0], C->top());
   }
   C->gvn_replace_by(call, C->top());
 }