/** * Similar to {@link #deverbosifyInputsInPlace(com.oracle.graal.nodes.ValueNode)}, except that not * the parent but a fresh clone is updated upon any of its children changing. * * @return the original parent if no updated took place, a copy-on-write version of it otherwise. */ private MethodCallTargetNode deverbosifyInputsCopyOnWrite(MethodCallTargetNode parent) { final CallTargetNode.InvokeKind ik = parent.invokeKind(); final boolean shouldTryDevirt = (ik == CallTargetNode.InvokeKind.Interface || ik == CallTargetNode.InvokeKind.Virtual); boolean shouldDowncastReceiver = shouldTryDevirt; MethodCallTargetNode changed = null; for (ValueNode i : FlowUtil.distinctValueAndConditionInputs(parent)) { ValueNode j = (ValueNode) reasoner.deverbosify(i); if (shouldDowncastReceiver) { shouldDowncastReceiver = false; j = reasoner.downcast(j); } if (i != j) { assert j != parent; if (changed == null) { changed = (MethodCallTargetNode) parent.copyWithInputs(); reasoner.added.add(changed); // copyWithInputs() implies graph.unique(changed) assert changed.isAlive(); assert FlowUtil.lacksUsages(changed); } FlowUtil.replaceInPlace(changed, i, j); } } if (changed == null) { return parent; } FlowUtil.inferStampAndCheck(changed); /* * No need to rememberSubstitution() because not called from deverbosify(). In detail, it's * only deverbosify() that skips visited nodes (thus we'd better have recorded any * substitutions we want for them). Not this case. */ return changed; }