Example #1
0
 /**
  * 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;
 }
Example #2
0
 /**
  * Reduce input nodes based on the state at the program point for the argument (ie, based on
  * "valid facts" only, without relying on any floating-guard-assumption).
  *
  * <p>For each (direct or indirect) child, a copy-on-write version is made in case any of its
  * children changed, with the copy accommodating the updated children. If the parent was shared,
  * copy-on-write prevents the updates from becoming visible to anyone but the invoker of this
  * method.
  *
  * <p><b> Please note the parent node is mutated upon any descendant changing. No copy-on-write is
  * performed for the parent node itself. </b>
  *
  * <p>In more detail, for each direct {@link com.oracle.graal.nodes.ValueNode} input of the node
  * at hand,
  *
  * <ol>
  *   <li>Obtain a lazy-copied version (via spanning tree) of the DAG rooted at the input-usage in
  *       question. Lazy-copying is done by walking a spanning tree of the original DAG, stopping
  *       at non-FloatingNodes but transitively walking FloatingNodes and their inputs. Upon
  *       arriving at a (floating) node N, the state's facts are checked to determine whether a
  *       constant C can be used instead in the resulting lazy-copied DAG. A NodeBitMap is used to
  *       realize the spanning tree.
  *   <li>Provided one or more N-to-C node replacements took place, the resulting lazy-copied DAG
  *       has a parent different from the original (ie different object identity) which indicates
  *       the (copied, updated) DAG should replace the original via replaceFirstInput(), and
  *       inferStamp() should be invoked to reflect the updated inputs.
  * </ol>
  *
  * @return whether any reduction was performed on the inputs of the arguments.
  */
 public boolean deverbosifyInputsInPlace(ValueNode parent) {
   boolean changed = false;
   for (ValueNode i : FlowUtil.distinctValueAndConditionInputs(parent)) {
     assert !(i instanceof GuardNode) : "This phase not intended to run during MidTier";
     ValueNode j = (ValueNode) reasoner.deverbosify(i);
     if (i != j) {
       changed = true;
       FlowUtil.replaceInPlace(parent, i, j);
     }
   }
   if (changed) {
     FlowUtil.inferStampAndCheck(parent);
   }
   return changed;
 }
Example #3
0
  /**
   * In case the scrutinee:
   *
   * <ul>
   *   <li>is known to be null, an unconditional deopt is added.
   *   <li>is known to be non-null, the NullCheckNode is removed.
   *   <li>otherwise, the NullCheckNode is lowered to a FixedGuardNode which then allows using it as
   *       anchor for state-tracking.
   * </ul>
   *
   * <p>Precondition: the input (ie, object) hasn't been deverbosified yet.
   */
  private void visitNullCheckNode(NullCheckNode ncn) {
    ValueNode object = ncn.getObject();
    if (state.isNull(object)) {
      postponedDeopts.addDeoptBefore(ncn, NullCheckException);
      state.impossiblePath();
      return;
    }
    if (state.isNonNull(object)) {
      /*
       * Redundant NullCheckNode. Unlike GuardingPiNode or FixedGuardNode, NullCheckNode-s
       * aren't used as GuardingNode-s, thus in this case can be removed without further ado.
       */
      assert FlowUtil.lacksUsages(ncn);
      graph.removeFixed(ncn);
      return;
    }
    /*
     * Lower the NullCheckNode to a FixedGuardNode which then allows using it as anchor for
     * state-tracking. TODO the assumption here is that the code emitted for the resulting
     * FixedGuardNode is as efficient as for NullCheckNode.
     */
    IsNullNode isNN = graph.unique(IsNullNode.create(object));
    reasoner.added.add(isNN);
    FixedGuardNode nullCheck =
        graph.add(FixedGuardNode.create(isNN, UnreachedCode, InvalidateReprofile, true));
    graph.replaceFixedWithFixed(ncn, nullCheck);

    state.trackNN(object, nullCheck);
  }
Example #4
0
 /**
  * For one or more `invoke` arguments, flow-sensitive information may suggest their narrowing or
  * simplification. In those cases, a new {@link com.oracle.graal.nodes.java.MethodCallTargetNode
  * MethodCallTargetNode} is prepared just for this callsite, consuming reduced arguments.
  *
  * <p>Specializing the {@link com.oracle.graal.nodes.java.MethodCallTargetNode
  * MethodCallTargetNode} as described above may enable two optimizations:
  *
  * <ul>
  *   <li>devirtualization of an {@link com.oracle.graal.nodes.CallTargetNode.InvokeKind#Interface}
  *       or {@link com.oracle.graal.nodes.CallTargetNode.InvokeKind#Virtual} callsite
  *       (devirtualization made possible after narrowing the type of the receiver)
  *   <li>(future work) actual-argument-aware inlining, ie, to specialize callees on the types of
  *       arguments other than the receiver (examples: multi-methods, the inlining problem, lambdas
  *       as arguments).
  * </ul>
  *
  * <p>Precondition: inputs haven't been deverbosified yet.
  */
 private void visitInvoke(Invoke invoke) {
   if (invoke.asNode().stamp() instanceof IllegalStamp) {
     return; // just to be safe
   }
   boolean isMethodCallTarget = invoke.callTarget() instanceof MethodCallTargetNode;
   if (!isMethodCallTarget) {
     return;
   }
   FlowUtil.replaceInPlace(
       invoke.asNode(),
       invoke.callTarget(),
       deverbosifyInputsCopyOnWrite((MethodCallTargetNode) invoke.callTarget()));
   MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget();
   if (callTarget.invokeKind() != CallTargetNode.InvokeKind.Interface
       && callTarget.invokeKind() != CallTargetNode.InvokeKind.Virtual) {
     return;
   }
   ValueNode receiver = callTarget.receiver();
   if (receiver == null) {
     return;
   }
   if (!FlowUtil.hasLegalObjectStamp(receiver)) {
     return;
   }
   Witness w = state.typeInfo(receiver);
   ResolvedJavaType type;
   ResolvedJavaType stampType = StampTool.typeOrNull(receiver);
   if (w == null || w.cluelessAboutType()) {
     // can't improve on stamp but wil try to devirtualize anyway
     type = stampType;
   } else {
     type = FlowUtil.tighten(w.type(), stampType);
   }
   if (type == null) {
     return;
   }
   ResolvedJavaMethod method =
       type.resolveMethod(callTarget.targetMethod(), invoke.getContextType());
   if (method == null) {
     return;
   }
   if (method.canBeStaticallyBound() || Modifier.isFinal(type.getModifiers())) {
     metricMethodResolved.increment();
     callTarget.setInvokeKind(CallTargetNode.InvokeKind.Special);
     callTarget.setTargetMethod(method);
   }
 }
Example #5
0
 /**
  * TODO When tracking integer-stamps, the state at each successor of a TypeSwitchNode should track
  * an integer-stamp for the LoadHubNode (meet over the constants leading to that successor).
  * However, are LoadHubNode-s shared frequently enough?
  */
 private void registerTypeSwitchNode(TypeSwitchNode typeSwitch, BeginNode begin) {
   if (typeSwitch.value() instanceof LoadHubNode) {
     LoadHubNode loadHub = (LoadHubNode) typeSwitch.value();
     ResolvedJavaType type = null;
     for (int i = 0; i < typeSwitch.keyCount(); i++) {
       if (typeSwitch.keySuccessor(i) == begin) {
         if (type == null) {
           type = typeSwitch.typeAt(i);
         } else {
           type = FlowUtil.widen(type, typeSwitch.typeAt(i));
         }
       }
     }
     if (type == null) {
       // `begin` denotes the default case of the TypeSwitchNode
       return;
     }
     // it's unwarranted to assume loadHub.object() to be non-null
     state.trackCC(loadHub.getValue(), type, begin);
   }
 }
Example #6
0
 /**
  * This method performs two kinds of cleanup:
  *
  * <ol>
  *   <li>marking as unreachable certain code-paths, as described in {@link
  *       com.oracle.graal.phases.common.cfs.BaseReduction.PostponedDeopt}
  *   <li>Removing nodes not in use that were added during this phase, as described next.
  * </ol>
  *
  * <p>Methods like {@link
  * com.oracle.graal.phases.common.cfs.FlowUtil#replaceInPlace(com.oracle.graal.graph.Node,
  * com.oracle.graal.graph.Node, com.oracle.graal.graph.Node)} may result in old inputs becoming
  * disconnected from the graph. It's not advisable to {@link
  * com.oracle.graal.nodes.util.GraphUtil#tryKillUnused(com.oracle.graal.graph.Node)} at that
  * moment, because one of the inputs that might get killed is one of {@link #nullConstant}, {@link
  * #falseConstant}, or {@link #trueConstant}; which thus could get killed too early, before
  * another invocation of {@link
  * com.oracle.graal.phases.common.cfs.EquationalReasoner#deverbosify(com.oracle.graal.graph.Node)}
  * needs them. To recap, {@link
  * com.oracle.graal.nodes.util.GraphUtil#tryKillUnused(com.oracle.graal.graph.Node)} also
  * recursively visits the inputs of the its argument.
  *
  * <p>This method goes over all of the nodes that deverbosification might have added, which are
  * either:
  *
  * <ul>
  *   <li>{@link com.oracle.graal.nodes.calc.FloatingNode}, added by {@link
  *       com.oracle.graal.phases.common.cfs.EquationalReasoner#deverbosifyFloatingNode(com.oracle.graal.nodes.calc.FloatingNode)}
  *       ; or
  *   <li>{@link com.oracle.graal.nodes.java.MethodCallTargetNode}, added by {@link
  *       #deverbosifyInputsCopyOnWrite(com.oracle.graal.nodes.java.MethodCallTargetNode)}
  * </ul>
  *
  * Checking if they aren't in use, proceeding to remove them in that case.
  */
 @Override
 public void finished() {
   if (!postponedDeopts.isEmpty()) {
     for (PostponedDeopt postponed : postponedDeopts) {
       postponed.doRewrite(falseConstant);
     }
     new DeadCodeEliminationPhase(Optional).apply(graph);
   }
   for (MethodCallTargetNode mcn : graph.getNodes().filter(MethodCallTargetNode.class)) {
     if (mcn.isAlive() && FlowUtil.lacksUsages(mcn)) {
       mcn.safeDelete();
     }
   }
   for (Node n : graph.getNodes().filter(FloatingNode.class)) {
     GraphUtil.tryKillUnused(n);
   }
   assert !isAliveWithoutUsages(trueConstant);
   assert !isAliveWithoutUsages(falseConstant);
   assert !isAliveWithoutUsages(nullConstant);
   super.finished();
 }
Example #7
0
 private static boolean isAliveWithoutUsages(FloatingNode node) {
   return node.isAlive() && FlowUtil.lacksUsages(node);
 }