1 /* 2 * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.graalvm.compiler.phases.util; 24 25 import java.util.ArrayList; 26 import java.util.List; 27 import java.util.Map; 28 29 import org.graalvm.compiler.core.common.cfg.Loop; 30 import org.graalvm.compiler.debug.Debug; 31 import org.graalvm.compiler.debug.GraalError; 32 import org.graalvm.compiler.graph.GraalGraphError; 33 import org.graalvm.compiler.graph.Node; 34 import org.graalvm.compiler.graph.NodeBitMap; 35 import org.graalvm.compiler.nodes.AbstractEndNode; 36 import org.graalvm.compiler.nodes.AbstractMergeNode; 37 import org.graalvm.compiler.nodes.ConstantNode; 38 import org.graalvm.compiler.nodes.EndNode; 39 import org.graalvm.compiler.nodes.FixedNode; 40 import org.graalvm.compiler.nodes.FrameState; 41 import org.graalvm.compiler.nodes.FullInfopointNode; 42 import org.graalvm.compiler.nodes.LoopBeginNode; 43 import org.graalvm.compiler.nodes.LoopExitNode; 44 import org.graalvm.compiler.nodes.PhiNode; 45 import org.graalvm.compiler.nodes.ProxyNode; 46 import org.graalvm.compiler.nodes.StateSplit; 47 import org.graalvm.compiler.nodes.StructuredGraph; 48 import org.graalvm.compiler.nodes.StructuredGraph.GuardsStage; 49 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult; 50 import org.graalvm.compiler.nodes.ValueNode; 51 import org.graalvm.compiler.nodes.VirtualState; 52 import org.graalvm.compiler.nodes.VirtualState.NodeClosure; 53 import org.graalvm.compiler.nodes.cfg.Block; 54 import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; 55 import org.graalvm.compiler.phases.graph.ReentrantBlockIterator; 56 import org.graalvm.compiler.phases.graph.ReentrantBlockIterator.BlockIteratorClosure; 57 import org.graalvm.compiler.phases.graph.StatelessPostOrderNodeIterator; 58 import org.graalvm.compiler.phases.schedule.SchedulePhase; 59 import org.graalvm.compiler.phases.schedule.SchedulePhase.SchedulingStrategy; 60 61 public final class GraphOrder { 62 63 private GraphOrder() { 64 } 65 66 /** 67 * Quick (and imprecise) assertion that there are no (invalid) cycles in the given graph. First, 68 * an ordered list of all nodes in the graph (a total ordering) is created. A second run over 69 * this list checks whether inputs are scheduled before their usages. 70 * 71 * @param graph the graph to be checked. 72 * @throws AssertionError if a cycle was detected. 73 */ 74 public static boolean assertNonCyclicGraph(StructuredGraph graph) { 75 List<Node> order = createOrder(graph); 76 NodeBitMap visited = graph.createNodeBitMap(); 77 visited.clearAll(); 78 for (Node node : order) { 79 if (node instanceof PhiNode && ((PhiNode) node).merge() instanceof LoopBeginNode) { 80 assert visited.isMarked(((PhiNode) node).valueAt(0)); 81 // nothing to do 82 } else { 83 for (Node input : node.inputs()) { 84 if (!visited.isMarked(input)) { 85 if (input instanceof FrameState) { 86 // nothing to do - frame states are known, allowed cycles 87 } else { 88 assert false : "unexpected cycle detected at input " + node + " -> " + input; 89 } 90 } 91 } 92 } 93 visited.mark(node); 94 } 95 return true; 96 } 97 98 private static List<Node> createOrder(StructuredGraph graph) { 99 final ArrayList<Node> nodes = new ArrayList<>(); 100 final NodeBitMap visited = graph.createNodeBitMap(); 101 102 new StatelessPostOrderNodeIterator(graph.start()) { 103 @Override 104 protected void node(FixedNode node) { 105 visitForward(nodes, visited, node, false); 106 } 107 }.apply(); 108 return nodes; 109 } 110 111 private static void visitForward(ArrayList<Node> nodes, NodeBitMap visited, Node node, boolean floatingOnly) { 112 try { 113 assert node == null || node.isAlive() : node + " not alive"; 114 if (node != null && !visited.isMarked(node)) { 115 if (floatingOnly && node instanceof FixedNode) { 116 throw new GraalError("unexpected reference to fixed node: %s (this indicates an unexpected cycle)", node); 117 } 118 visited.mark(node); 119 FrameState stateAfter = null; 120 if (node instanceof StateSplit) { 121 stateAfter = ((StateSplit) node).stateAfter(); 122 } 123 for (Node input : node.inputs()) { 124 if (input != stateAfter) { 125 visitForward(nodes, visited, input, true); 126 } 127 } 128 if (node instanceof EndNode) { 129 EndNode end = (EndNode) node; 130 for (PhiNode phi : end.merge().phis()) { 131 visitForward(nodes, visited, phi.valueAt(end), true); 132 } 133 } 134 nodes.add(node); 135 if (node instanceof AbstractMergeNode) { 136 for (PhiNode phi : ((AbstractMergeNode) node).phis()) { 137 visited.mark(phi); 138 nodes.add(phi); 139 } 140 } 141 if (stateAfter != null) { 142 visitForward(nodes, visited, stateAfter, true); 143 } 144 } 145 } catch (GraalError e) { 146 throw GraalGraphError.transformAndAddContext(e, node); 147 } 148 } 149 150 /** 151 * This method schedules the graph and makes sure that, for every node, all inputs are available 152 * at the position where it is scheduled. This is a very expensive assertion. 153 */ 154 public static boolean assertSchedulableGraph(final StructuredGraph graph) { 155 assert graph.getGuardsStage() != GuardsStage.AFTER_FSA : "Cannot use the BlockIteratorClosure after FrameState Assignment, HIR Loop Data Structures are no longer valid."; 156 try { 157 final SchedulePhase schedulePhase = new SchedulePhase(SchedulingStrategy.LATEST_OUT_OF_LOOPS); 158 final Map<LoopBeginNode, NodeBitMap> loopEntryStates = Node.newIdentityMap(); 159 schedulePhase.apply(graph, false); 160 final ScheduleResult schedule = graph.getLastSchedule(); 161 162 BlockIteratorClosure<NodeBitMap> closure = new BlockIteratorClosure<NodeBitMap>() { 163 164 @Override 165 protected List<NodeBitMap> processLoop(Loop<Block> loop, NodeBitMap initialState) { 166 return ReentrantBlockIterator.processLoop(this, loop, initialState).exitStates; 167 } 168 169 @Override 170 protected NodeBitMap processBlock(final Block block, final NodeBitMap currentState) { 171 final List<Node> list = graph.getLastSchedule().getBlockToNodesMap().get(block); 172 173 /* 174 * A stateAfter is not valid directly after its associated state split, but 175 * right before the next fixed node. Therefore a pending stateAfter is kept that 176 * will be checked at the correct position. 177 */ 178 FrameState pendingStateAfter = null; 179 for (final Node node : list) { 180 if (node instanceof ValueNode) { 181 FrameState stateAfter = node instanceof StateSplit ? ((StateSplit) node).stateAfter() : null; 182 if (node instanceof FullInfopointNode) { 183 stateAfter = ((FullInfopointNode) node).getState(); 184 } 185 186 if (pendingStateAfter != null && node instanceof FixedNode) { 187 pendingStateAfter.applyToNonVirtual(new NodeClosure<Node>() { 188 @Override 189 public void apply(Node usage, Node nonVirtualNode) { 190 assert currentState.isMarked(nonVirtualNode) || nonVirtualNode instanceof VirtualObjectNode || nonVirtualNode instanceof ConstantNode : nonVirtualNode + 191 " not available at virtualstate " + usage + " before " + node + " in block " + block + " \n" + list; 192 } 193 }); 194 pendingStateAfter = null; 195 } 196 197 if (node instanceof AbstractMergeNode) { 198 // phis aren't scheduled, so they need to be added explicitly 199 currentState.markAll(((AbstractMergeNode) node).phis()); 200 if (node instanceof LoopBeginNode) { 201 // remember the state at the loop entry, it's restored at exits 202 loopEntryStates.put((LoopBeginNode) node, currentState.copy()); 203 } 204 } else if (node instanceof ProxyNode) { 205 assert false : "proxy nodes should not be in the schedule"; 206 } else if (node instanceof LoopExitNode) { 207 if (graph.hasValueProxies()) { 208 for (ProxyNode proxy : ((LoopExitNode) node).proxies()) { 209 for (Node input : proxy.inputs()) { 210 if (input != proxy.proxyPoint()) { 211 assert currentState.isMarked(input) : input + " not available at " + proxy + " in block " + block + "\n" + list; 212 } 213 } 214 } 215 216 // loop contents are only accessible via proxies at the exit 217 currentState.clearAll(); 218 currentState.markAll(loopEntryStates.get(((LoopExitNode) node).loopBegin())); 219 } 220 // Loop proxies aren't scheduled, so they need to be added 221 // explicitly 222 currentState.markAll(((LoopExitNode) node).proxies()); 223 } else { 224 for (Node input : node.inputs()) { 225 if (input != stateAfter) { 226 if (input instanceof FrameState) { 227 ((FrameState) input).applyToNonVirtual(new VirtualState.NodeClosure<Node>() { 228 @Override 229 public void apply(Node usage, Node nonVirtual) { 230 assert currentState.isMarked(nonVirtual) : nonVirtual + " not available at " + node + " in block " + block + "\n" + list; 231 } 232 }); 233 } else { 234 assert currentState.isMarked(input) || input instanceof VirtualObjectNode || input instanceof ConstantNode : input + " not available at " + node + 235 " in block " + block + "\n" + list; 236 } 237 } 238 } 239 } 240 if (node instanceof AbstractEndNode) { 241 AbstractMergeNode merge = ((AbstractEndNode) node).merge(); 242 for (PhiNode phi : merge.phis()) { 243 ValueNode phiValue = phi.valueAt((AbstractEndNode) node); 244 assert phiValue == null || currentState.isMarked(phiValue) || phiValue instanceof ConstantNode : phiValue + " not available at phi " + phi + " / end " + node + 245 " in block " + block; 246 } 247 } 248 if (stateAfter != null) { 249 assert pendingStateAfter == null; 250 pendingStateAfter = stateAfter; 251 } 252 currentState.mark(node); 253 } 254 } 255 if (pendingStateAfter != null) { 256 pendingStateAfter.applyToNonVirtual(new NodeClosure<Node>() { 257 @Override 258 public void apply(Node usage, Node nonVirtualNode) { 259 assert currentState.isMarked(nonVirtualNode) || nonVirtualNode instanceof VirtualObjectNode || nonVirtualNode instanceof ConstantNode : nonVirtualNode + 260 " not available at virtualstate " + usage + " at end of block " + block + " \n" + list; 261 } 262 }); 263 } 264 return currentState; 265 } 266 267 @Override 268 protected NodeBitMap merge(Block merge, List<NodeBitMap> states) { 269 NodeBitMap result = states.get(0); 270 for (int i = 1; i < states.size(); i++) { 271 result.intersect(states.get(i)); 272 } 273 return result; 274 } 275 276 @Override 277 protected NodeBitMap getInitialState() { 278 NodeBitMap ret = graph.createNodeBitMap(); 279 ret.markAll(graph.getNodes().filter(ConstantNode.class)); 280 return ret; 281 } 282 283 @Override 284 protected NodeBitMap cloneState(NodeBitMap oldState) { 285 return oldState.copy(); 286 } 287 }; 288 289 ReentrantBlockIterator.apply(closure, schedule.getCFG().getStartBlock()); 290 291 } catch (Throwable t) { 292 Debug.handle(t); 293 } 294 return true; 295 } 296 }