001/* 002 * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.phases.util; 024 025import java.util.*; 026 027import jdk.internal.jvmci.common.*; 028 029import com.oracle.graal.compiler.common.cfg.*; 030import com.oracle.graal.graph.*; 031import com.oracle.graal.nodes.*; 032import com.oracle.graal.nodes.VirtualState.NodeClosure; 033import com.oracle.graal.nodes.cfg.*; 034import com.oracle.graal.nodes.virtual.*; 035import com.oracle.graal.phases.graph.*; 036import com.oracle.graal.phases.graph.ReentrantBlockIterator.BlockIteratorClosure; 037import com.oracle.graal.phases.schedule.*; 038import com.oracle.graal.phases.schedule.SchedulePhase.SchedulingStrategy; 039 040public final class GraphOrder { 041 042 private GraphOrder() { 043 } 044 045 /** 046 * Quick (and imprecise) assertion that there are no (invalid) cycles in the given graph. First, 047 * an ordered list of all nodes in the graph (a total ordering) is created. A second run over 048 * this list checks whether inputs are scheduled before their usages. 049 * 050 * @param graph the graph to be checked. 051 * @throws AssertionError if a cycle was detected. 052 */ 053 public static boolean assertNonCyclicGraph(StructuredGraph graph) { 054 List<Node> order = createOrder(graph); 055 NodeBitMap visited = graph.createNodeBitMap(); 056 visited.clearAll(); 057 for (Node node : order) { 058 if (node instanceof PhiNode && ((PhiNode) node).merge() instanceof LoopBeginNode) { 059 assert visited.isMarked(((PhiNode) node).valueAt(0)); 060 // nothing to do 061 } else { 062 for (Node input : node.inputs()) { 063 if (!visited.isMarked(input)) { 064 if (input instanceof FrameState) { 065 // nothing to do - frame states are known, allowed cycles 066 } else { 067 assert false : "unexpected cycle detected at input " + node + " -> " + input; 068 } 069 } 070 } 071 } 072 visited.mark(node); 073 } 074 return true; 075 } 076 077 private static List<Node> createOrder(StructuredGraph graph) { 078 final ArrayList<Node> nodes = new ArrayList<>(); 079 final NodeBitMap visited = graph.createNodeBitMap(); 080 081 new StatelessPostOrderNodeIterator(graph.start()) { 082 @Override 083 protected void node(FixedNode node) { 084 visitForward(nodes, visited, node, false); 085 } 086 }.apply(); 087 return nodes; 088 } 089 090 private static void visitForward(ArrayList<Node> nodes, NodeBitMap visited, Node node, boolean floatingOnly) { 091 try { 092 assert node == null || node.isAlive() : node + " not alive"; 093 if (node != null && !visited.isMarked(node)) { 094 if (floatingOnly && node instanceof FixedNode) { 095 throw new JVMCIError("unexpected reference to fixed node: %s (this indicates an unexpected cycle)", node); 096 } 097 visited.mark(node); 098 FrameState stateAfter = null; 099 if (node instanceof StateSplit) { 100 stateAfter = ((StateSplit) node).stateAfter(); 101 } 102 for (Node input : node.inputs()) { 103 if (input != stateAfter) { 104 visitForward(nodes, visited, input, true); 105 } 106 } 107 if (node instanceof EndNode) { 108 EndNode end = (EndNode) node; 109 for (PhiNode phi : end.merge().phis()) { 110 visitForward(nodes, visited, phi.valueAt(end), true); 111 } 112 } 113 nodes.add(node); 114 if (node instanceof AbstractMergeNode) { 115 for (PhiNode phi : ((AbstractMergeNode) node).phis()) { 116 visited.mark(phi); 117 nodes.add(phi); 118 } 119 } 120 if (stateAfter != null) { 121 visitForward(nodes, visited, stateAfter, true); 122 } 123 } 124 } catch (JVMCIError e) { 125 throw GraalGraphJVMCIError.transformAndAddContext(e, node); 126 } 127 } 128 129 /** 130 * This method schedules the graph and makes sure that, for every node, all inputs are available 131 * at the position where it is scheduled. This is a very expensive assertion. 132 * 133 * Also, this phase assumes ProxyNodes to exist at LoopExitNodes, so that it cannot be run after 134 * phases that remove loop proxies or move proxies to BeginNodes. 135 */ 136 public static boolean assertSchedulableGraph(final StructuredGraph graph) { 137 try { 138 final SchedulePhase schedule = new SchedulePhase(SchedulingStrategy.LATEST_OUT_OF_LOOPS); 139 final Map<LoopBeginNode, NodeBitMap> loopEntryStates = Node.newIdentityMap(); 140 schedule.apply(graph, false); 141 142 BlockIteratorClosure<NodeBitMap> closure = new BlockIteratorClosure<NodeBitMap>() { 143 144 @Override 145 protected List<NodeBitMap> processLoop(Loop<Block> loop, NodeBitMap initialState) { 146 return ReentrantBlockIterator.processLoop(this, loop, initialState).exitStates; 147 } 148 149 @Override 150 protected NodeBitMap processBlock(final Block block, final NodeBitMap currentState) { 151 final List<Node> list = schedule.getBlockToNodesMap().get(block); 152 153 /* 154 * A stateAfter is not valid directly after its associated state split, but 155 * right before the next fixed node. Therefore a pending stateAfter is kept that 156 * will be checked at the correct position. 157 */ 158 FrameState pendingStateAfter = null; 159 for (final Node node : list) { 160 if (node instanceof ValueNode) { 161 FrameState stateAfter = node instanceof StateSplit ? ((StateSplit) node).stateAfter() : null; 162 if (node instanceof FullInfopointNode) { 163 stateAfter = ((FullInfopointNode) node).getState(); 164 } 165 166 if (pendingStateAfter != null && node instanceof FixedNode) { 167 pendingStateAfter.applyToNonVirtual(new NodeClosure<Node>() { 168 @Override 169 public void apply(Node usage, Node nonVirtualNode) { 170 assert currentState.isMarked(nonVirtualNode) || nonVirtualNode instanceof VirtualObjectNode || nonVirtualNode instanceof ConstantNode : nonVirtualNode + 171 " not available at virtualstate " + usage + " before " + node + " in block " + block + " \n" + list; 172 } 173 }); 174 pendingStateAfter = null; 175 } 176 177 if (node instanceof AbstractMergeNode) { 178 // phis aren't scheduled, so they need to be added explicitly 179 currentState.markAll(((AbstractMergeNode) node).phis()); 180 if (node instanceof LoopBeginNode) { 181 // remember the state at the loop entry, it's restored at exits 182 loopEntryStates.put((LoopBeginNode) node, currentState.copy()); 183 } 184 } else if (node instanceof ProxyNode) { 185 assert false : "proxy nodes should not be in the schedule"; 186 } else if (node instanceof LoopExitNode) { 187 if (graph.hasValueProxies()) { 188 for (ProxyNode proxy : ((LoopExitNode) node).proxies()) { 189 for (Node input : proxy.inputs()) { 190 if (input != proxy.proxyPoint()) { 191 assert currentState.isMarked(input) : input + " not available at " + proxy + " in block " + block + "\n" + list; 192 } 193 } 194 } 195 196 // loop contents are only accessible via proxies at the exit 197 currentState.clearAll(); 198 currentState.markAll(loopEntryStates.get(((LoopExitNode) node).loopBegin())); 199 } 200 // Loop proxies aren't scheduled, so they need to be added 201 // explicitly 202 currentState.markAll(((LoopExitNode) node).proxies()); 203 } else { 204 for (Node input : node.inputs()) { 205 if (input != stateAfter) { 206 if (input instanceof FrameState) { 207 ((FrameState) input).applyToNonVirtual(new VirtualState.NodeClosure<Node>() { 208 @Override 209 public void apply(Node usage, Node nonVirtual) { 210 assert currentState.isMarked(nonVirtual) : nonVirtual + " not available at " + node + " in block " + block + "\n" + list; 211 } 212 }); 213 } else { 214 assert currentState.isMarked(input) || input instanceof VirtualObjectNode || input instanceof ConstantNode : input + " not available at " + node + 215 " in block " + block + "\n" + list; 216 } 217 } 218 } 219 } 220 if (node instanceof AbstractEndNode) { 221 AbstractMergeNode merge = ((AbstractEndNode) node).merge(); 222 for (PhiNode phi : merge.phis()) { 223 ValueNode phiValue = phi.valueAt((AbstractEndNode) node); 224 assert phiValue == null || currentState.isMarked(phiValue) || phiValue instanceof ConstantNode : phiValue + " not available at phi " + phi + " / end " + node + 225 " in block " + block; 226 } 227 } 228 if (stateAfter != null) { 229 assert pendingStateAfter == null; 230 pendingStateAfter = stateAfter; 231 } 232 currentState.mark(node); 233 } 234 } 235 if (pendingStateAfter != null) { 236 pendingStateAfter.applyToNonVirtual(new NodeClosure<Node>() { 237 @Override 238 public void apply(Node usage, Node nonVirtualNode) { 239 assert currentState.isMarked(nonVirtualNode) || nonVirtualNode instanceof VirtualObjectNode || nonVirtualNode instanceof ConstantNode : nonVirtualNode + 240 " not available at virtualstate " + usage + " at end of block " + block + " \n" + list; 241 } 242 }); 243 } 244 return currentState; 245 } 246 247 @Override 248 protected NodeBitMap merge(Block merge, List<NodeBitMap> states) { 249 NodeBitMap result = states.get(0); 250 for (int i = 1; i < states.size(); i++) { 251 result.intersect(states.get(i)); 252 } 253 return result; 254 } 255 256 @Override 257 protected NodeBitMap getInitialState() { 258 NodeBitMap ret = graph.createNodeBitMap(); 259 ret.markAll(graph.getNodes().filter(ConstantNode.class)); 260 return ret; 261 } 262 263 @Override 264 protected NodeBitMap cloneState(NodeBitMap oldState) { 265 return oldState.copy(); 266 } 267 }; 268 269 ReentrantBlockIterator.apply(closure, schedule.getCFG().getStartBlock()); 270 271 } catch (Throwable t) { 272 throw new AssertionError("unschedulable graph", t); 273 } 274 return true; 275 } 276}