public static void killCFG(FixedNode node) { assert node.isAlive(); if (node instanceof AbstractEndNode) { // We reached a control flow end. AbstractEndNode end = (AbstractEndNode) node; killEnd(end); } else { // Normal control flow node. /* * We do not take a successor snapshot because this iterator supports concurrent * modifications as long as they do not change the size of the successor list. Not * taking a snapshot allows us to see modifications to other branches that may happen * while processing one branch. */ for (Node successor : node.successors()) { killCFG((FixedNode) successor); } } propagateKill(node); }
/** * Precondition: This method assumes that either: * * <ul> * <li>the state has already stabilized (ie no more pending iterations in the "iterative" * dataflow algorithm); or * <li>any rewritings made based on the state in its current form are conservative enough to be * safe. * </ul> * * <p>The overarching goal is to perform just enough rewriting to trigger other phases ( {@link * com.oracle.graal.graph.spi.SimplifierTool SimplifierTool}, {@link * com.oracle.graal.phases.common.DeadCodeEliminationPhase DeadCodeEliminationPhase}, etc) to * perform the bulk of rewriting, thus lowering the maintenance burden. */ @Override protected void node(FixedNode node) { assert node.isAlive(); /*------------------------------------------------------------------------------------- * Step 1: Unreachable paths are still visited (PostOrderNodeIterator requires all ends * of a merge to have been visited), but time is saved by neither updating the state nor * rewriting anything while on an an unreachable path. *------------------------------------------------------------------------------------- */ if (state.isUnreachable) { return; } /*------------------------------------------------------------------------------------- * Step 2: For an AbstractBeginNode, determine whether this path is reachable, register * any associated guards. *------------------------------------------------------------------------------------- */ if (node instanceof BeginNode) { BeginNode begin = (BeginNode) node; Node pred = node.predecessor(); if (pred != null) { registerControlSplit(pred, begin); } return; } /*------------------------------------------------------------------------------------- * Step 3: Check whether EquationalReasoner caches should be cleared upon state updates. *------------------------------------------------------------------------------------- */ reasoner.updateState(state); /*------------------------------------------------------------------------------------- * Step 4: Whatever special-case handling makes sense for the FixedNode at hand before * its inputs are reduced. *------------------------------------------------------------------------------------- */ if (node instanceof AbstractEndNode) { visitAbstractEndNode((AbstractEndNode) node); return; } else if (node instanceof Invoke) { visitInvoke((Invoke) node); return; } else if (node instanceof CheckCastNode) { // it's important not to call deverbosification for visitCheckCastNode() visitCheckCastNode((CheckCastNode) node); return; } else if (node instanceof GuardingPiNode) { visitGuardingPiNode((GuardingPiNode) node); return; } else if (node instanceof NullCheckNode) { visitNullCheckNode((NullCheckNode) node); return; } else if (node instanceof FixedGuardNode) { visitFixedGuardNode((FixedGuardNode) node); return; } else if (node instanceof ConditionAnchorNode) { // ConditionAnchorNode shouldn't occur during HighTier return; } /*------------------------------------------------------------------------------------- * Step 5: After special-case handling, we do our best for those FixedNode-s * where the effort to reduce their inputs might pay off. * * Why is this useful? For example, by the time the BeginNode for an If-branch * is visited (in general a ControlSplitNode), the If-condition will have gone already * through simplification (and thus potentially have been reduced to a * LogicConstantNode). *------------------------------------------------------------------------------------- */ boolean paysOffToReduce = false; if (node instanceof ControlSplitNode) { // desire to simplify control flow paysOffToReduce = true; } else if (node instanceof ReturnNode) { paysOffToReduce = true; } else if (node instanceof AccessFieldNode || node instanceof AccessArrayNode) { // desire to remove null-checks paysOffToReduce = true; } // TODO comb remaining FixedWithNextNode subclasses, pick those with chances of paying-off // TODO UnsafeLoadNode takes a condition if (paysOffToReduce) { deverbosifyInputsInPlace(node); } /*--------------------------------------------------------------------------------------- * Step 6: Any additional special-case handling, this time after having inputs reduced. * For example, leverage anchors provided by the FixedNode, to add facts to the factbase. *--------------------------------------------------------------------------------------- */ // TODO some nodes are GuardingNodes (eg, FixedAccessNode) we could use them to track state // TODO other nodes are guarded (eg JavaReadNode), thus *their* guards could be replaced. }
private void visitDeoptBegin( AbstractBeginNode deoptBegin, DeoptimizationAction deoptAction, DeoptimizationReason deoptReason, JavaConstant speculation, StructuredGraph graph) { if (deoptBegin.predecessor() instanceof AbstractBeginNode) { /* Walk up chains of LoopExitNodes to the "real" BeginNode that leads to deoptimization. */ visitDeoptBegin( (AbstractBeginNode) deoptBegin.predecessor(), deoptAction, deoptReason, speculation, graph); return; } if (deoptBegin instanceof AbstractMergeNode) { AbstractMergeNode mergeNode = (AbstractMergeNode) deoptBegin; Debug.log("Visiting %s", mergeNode); FixedNode next = mergeNode.next(); while (mergeNode.isAlive()) { AbstractEndNode end = mergeNode.forwardEnds().first(); AbstractBeginNode newBeginNode = findBeginNode(end); visitDeoptBegin(newBeginNode, deoptAction, deoptReason, speculation, graph); } assert next.isAlive(); AbstractBeginNode newBeginNode = findBeginNode(next); visitDeoptBegin(newBeginNode, deoptAction, deoptReason, speculation, graph); return; } else if (deoptBegin.predecessor() instanceof IfNode) { IfNode ifNode = (IfNode) deoptBegin.predecessor(); AbstractBeginNode otherBegin = ifNode.trueSuccessor(); LogicNode conditionNode = ifNode.condition(); FixedGuardNode guard = graph.add( new FixedGuardNode( conditionNode, deoptReason, deoptAction, speculation, deoptBegin == ifNode.trueSuccessor())); FixedWithNextNode pred = (FixedWithNextNode) ifNode.predecessor(); AbstractBeginNode survivingSuccessor; if (deoptBegin == ifNode.trueSuccessor()) { survivingSuccessor = ifNode.falseSuccessor(); } else { survivingSuccessor = ifNode.trueSuccessor(); } graph.removeSplitPropagate(ifNode, survivingSuccessor); Node newGuard = guard; if (survivingSuccessor instanceof LoopExitNode) { newGuard = ProxyNode.forGuard(guard, (LoopExitNode) survivingSuccessor, graph); } survivingSuccessor.replaceAtUsages(InputType.Guard, newGuard); Debug.log( "Converting deopt on %-5s branch of %s to guard for remaining branch %s.", deoptBegin == ifNode.trueSuccessor() ? "true" : "false", ifNode, otherBegin); FixedNode next = pred.next(); pred.setNext(guard); guard.setNext(next); survivingSuccessor.simplify(simplifierTool); return; } // We could not convert the control split - at least cut off control flow after the split. FixedWithNextNode deoptPred = deoptBegin; FixedNode next = deoptPred.next(); if (!(next instanceof DeoptimizeNode)) { DeoptimizeNode newDeoptNode = graph.add(new DeoptimizeNode(deoptAction, deoptReason, speculation)); deoptPred.setNext(newDeoptNode); assert deoptPred == newDeoptNode.predecessor(); GraphUtil.killCFG(next); } }
@SuppressWarnings("try") private AnchoringNode process( final Block b, final NodeBitMap activeGuards, final AnchoringNode startAnchor) { final LoweringToolImpl loweringTool = new LoweringToolImpl(context, startAnchor, activeGuards, b.getBeginNode()); // Lower the instructions of this block. List<Node> nodes = schedule.nodesFor(b); for (Node node : nodes) { if (node.isDeleted()) { // This case can happen when previous lowerings deleted nodes. continue; } // Cache the next node to be able to reconstruct the previous of the next node // after lowering. FixedNode nextNode = null; if (node instanceof FixedWithNextNode) { nextNode = ((FixedWithNextNode) node).next(); } else { nextNode = loweringTool.lastFixedNode().next(); } if (node instanceof Lowerable) { Collection<Node> unscheduledUsages = null; assert (unscheduledUsages = getUnscheduledUsages(node)) != null; Mark preLoweringMark = node.graph().getMark(); try (DebugCloseable s = node.graph().withNodeContext(node)) { ((Lowerable) node).lower(loweringTool); } if (loweringTool.guardAnchor.asNode().isDeleted()) { // TODO nextNode could be deleted but this is not currently supported assert nextNode.isAlive(); loweringTool.guardAnchor = AbstractBeginNode.prevBegin(nextNode); } assert checkPostNodeLowering(node, loweringTool, preLoweringMark, unscheduledUsages); } if (!nextNode.isAlive()) { // can happen when the rest of the block is killed by lowering // (e.g. by an unconditional deopt) break; } else { Node nextLastFixed = nextNode.predecessor(); if (!(nextLastFixed instanceof FixedWithNextNode)) { // insert begin node, to have a valid last fixed for next lowerable node. // This is about lowering a FixedWithNextNode to a control split while this // FixedWithNextNode is followed by some kind of BeginNode. // For example the when a FixedGuard followed by a loop exit is lowered to a // control-split + deopt. AbstractBeginNode begin = node.graph().add(new BeginNode()); nextLastFixed.replaceFirstSuccessor(nextNode, begin); begin.setNext(nextNode); nextLastFixed = begin; } loweringTool.setLastFixedNode((FixedWithNextNode) nextLastFixed); } } return loweringTool.getCurrentGuardAnchor(); }