001/* 002 * Copyright (c) 2011, 2015, 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.common; 024 025import static com.oracle.graal.graph.Graph.NodeEvent.*; 026import static jdk.internal.jvmci.meta.LocationIdentity.*; 027 028import java.util.*; 029 030import jdk.internal.jvmci.meta.*; 031 032import com.oracle.graal.compiler.common.*; 033import com.oracle.graal.compiler.common.cfg.*; 034import com.oracle.graal.graph.Graph.NodeEventScope; 035import com.oracle.graal.graph.*; 036import com.oracle.graal.nodes.*; 037import com.oracle.graal.nodes.calc.*; 038import com.oracle.graal.nodes.cfg.*; 039import com.oracle.graal.nodes.extended.*; 040import com.oracle.graal.nodes.memory.*; 041import com.oracle.graal.nodes.util.*; 042import com.oracle.graal.phases.*; 043import com.oracle.graal.phases.common.util.*; 044import com.oracle.graal.phases.graph.*; 045import com.oracle.graal.phases.graph.ReentrantNodeIterator.LoopInfo; 046import com.oracle.graal.phases.graph.ReentrantNodeIterator.NodeIteratorClosure; 047 048public class FloatingReadPhase extends Phase { 049 050 private boolean createFloatingReads; 051 private boolean createMemoryMapNodes; 052 053 public static class MemoryMapImpl implements MemoryMap { 054 055 private final Map<LocationIdentity, MemoryNode> lastMemorySnapshot; 056 057 public MemoryMapImpl(MemoryMapImpl memoryMap) { 058 lastMemorySnapshot = CollectionsFactory.newMap(memoryMap.lastMemorySnapshot); 059 } 060 061 public MemoryMapImpl(StartNode start) { 062 lastMemorySnapshot = CollectionsFactory.newMap(); 063 lastMemorySnapshot.put(any(), start); 064 } 065 066 public MemoryMapImpl() { 067 lastMemorySnapshot = CollectionsFactory.newMap(); 068 } 069 070 @Override 071 public MemoryNode getLastLocationAccess(LocationIdentity locationIdentity) { 072 MemoryNode lastLocationAccess; 073 if (locationIdentity.isImmutable()) { 074 return null; 075 } else { 076 lastLocationAccess = lastMemorySnapshot.get(locationIdentity); 077 if (lastLocationAccess == null) { 078 lastLocationAccess = lastMemorySnapshot.get(any()); 079 assert lastLocationAccess != null; 080 } 081 return lastLocationAccess; 082 } 083 } 084 085 @Override 086 public Collection<LocationIdentity> getLocations() { 087 return lastMemorySnapshot.keySet(); 088 } 089 090 public Map<LocationIdentity, MemoryNode> getMap() { 091 return lastMemorySnapshot; 092 } 093 } 094 095 public FloatingReadPhase() { 096 this(true, false); 097 } 098 099 /** 100 * @param createFloatingReads specifies whether {@link FloatableAccessNode}s like 101 * {@link ReadNode} should be converted into floating nodes (e.g., 102 * {@link FloatingReadNode}s) where possible 103 * @param createMemoryMapNodes a {@link MemoryMapNode} will be created for each return if this 104 * is true 105 */ 106 public FloatingReadPhase(boolean createFloatingReads, boolean createMemoryMapNodes) { 107 this.createFloatingReads = createFloatingReads; 108 this.createMemoryMapNodes = createMemoryMapNodes; 109 } 110 111 /** 112 * Removes nodes from a given set that (transitively) have a usage outside the set. 113 */ 114 private static Set<Node> removeExternallyUsedNodes(Set<Node> set) { 115 boolean change; 116 do { 117 change = false; 118 for (Iterator<Node> iter = set.iterator(); iter.hasNext();) { 119 Node node = iter.next(); 120 for (Node usage : node.usages()) { 121 if (!set.contains(usage)) { 122 change = true; 123 iter.remove(); 124 break; 125 } 126 } 127 } 128 } while (change); 129 return set; 130 } 131 132 protected void processNode(FixedNode node, Set<LocationIdentity> currentState) { 133 if (node instanceof MemoryCheckpoint.Single) { 134 currentState.add(((MemoryCheckpoint.Single) node).getLocationIdentity()); 135 } else if (node instanceof MemoryCheckpoint.Multi) { 136 for (LocationIdentity identity : ((MemoryCheckpoint.Multi) node).getLocationIdentities()) { 137 currentState.add(identity); 138 } 139 } 140 } 141 142 protected void processBlock(Block b, Set<LocationIdentity> currentState) { 143 for (FixedNode n : b.getNodes()) { 144 processNode(n, currentState); 145 } 146 } 147 148 private Set<LocationIdentity> processLoop(HIRLoop loop, Map<LoopBeginNode, Set<LocationIdentity>> modifiedInLoops) { 149 LoopBeginNode loopBegin = (LoopBeginNode) loop.getHeader().getBeginNode(); 150 Set<LocationIdentity> result = modifiedInLoops.get(loopBegin); 151 if (result != null) { 152 return result; 153 } 154 155 result = CollectionsFactory.newSet(); 156 for (Loop<Block> inner : loop.getChildren()) { 157 result.addAll(processLoop((HIRLoop) inner, modifiedInLoops)); 158 } 159 160 for (Block b : loop.getBlocks()) { 161 if (b.getLoop() == loop) { 162 processBlock(b, result); 163 } 164 } 165 166 modifiedInLoops.put(loopBegin, result); 167 return result; 168 } 169 170 @Override 171 protected void run(StructuredGraph graph) { 172 Map<LoopBeginNode, Set<LocationIdentity>> modifiedInLoops = null; 173 if (graph.hasLoops()) { 174 modifiedInLoops = new HashMap<>(); 175 ControlFlowGraph cfg = ControlFlowGraph.compute(graph, true, true, false, false); 176 for (Loop<?> l : cfg.getLoops()) { 177 HIRLoop loop = (HIRLoop) l; 178 processLoop(loop, modifiedInLoops); 179 } 180 } 181 182 HashSetNodeEventListener listener = new HashSetNodeEventListener(EnumSet.of(NODE_ADDED, ZERO_USAGES)); 183 try (NodeEventScope nes = graph.trackNodeEvents(listener)) { 184 ReentrantNodeIterator.apply(new FloatingReadClosure(modifiedInLoops, createFloatingReads, createMemoryMapNodes), graph.start(), new MemoryMapImpl(graph.start())); 185 } 186 187 for (Node n : removeExternallyUsedNodes(listener.getNodes())) { 188 if (n.isAlive() && n instanceof FloatingNode) { 189 n.replaceAtUsages(null); 190 GraphUtil.killWithUnusedFloatingInputs(n); 191 } 192 } 193 if (createFloatingReads) { 194 assert !graph.isAfterFloatingReadPhase(); 195 graph.setAfterFloatingReadPhase(true); 196 } 197 } 198 199 public static MemoryMapImpl mergeMemoryMaps(AbstractMergeNode merge, List<? extends MemoryMap> states) { 200 MemoryMapImpl newState = new MemoryMapImpl(); 201 202 Set<LocationIdentity> keys = CollectionsFactory.newSet(); 203 for (MemoryMap other : states) { 204 keys.addAll(other.getLocations()); 205 } 206 assert checkNoImmutableLocations(keys); 207 208 for (LocationIdentity key : keys) { 209 int mergedStatesCount = 0; 210 boolean isPhi = false; 211 MemoryNode merged = null; 212 for (MemoryMap state : states) { 213 MemoryNode last = state.getLastLocationAccess(key); 214 if (isPhi) { 215 ((MemoryPhiNode) merged).addInput(ValueNodeUtil.asNode(last)); 216 } else { 217 if (merged == last) { 218 // nothing to do 219 } else if (merged == null) { 220 merged = last; 221 } else { 222 MemoryPhiNode phi = merge.graph().addWithoutUnique(new MemoryPhiNode(merge, key)); 223 for (int j = 0; j < mergedStatesCount; j++) { 224 phi.addInput(ValueNodeUtil.asNode(merged)); 225 } 226 phi.addInput(ValueNodeUtil.asNode(last)); 227 merged = phi; 228 isPhi = true; 229 } 230 } 231 mergedStatesCount++; 232 } 233 newState.lastMemorySnapshot.put(key, merged); 234 } 235 return newState; 236 237 } 238 239 private static boolean checkNoImmutableLocations(Set<LocationIdentity> keys) { 240 keys.forEach(t -> { 241 assert !t.isImmutable(); 242 }); 243 return true; 244 } 245 246 public static class FloatingReadClosure extends NodeIteratorClosure<MemoryMapImpl> { 247 248 private final Map<LoopBeginNode, Set<LocationIdentity>> modifiedInLoops; 249 private boolean createFloatingReads; 250 private boolean createMemoryMapNodes; 251 252 public FloatingReadClosure(Map<LoopBeginNode, Set<LocationIdentity>> modifiedInLoops, boolean createFloatingReads, boolean createMemoryMapNodes) { 253 this.modifiedInLoops = modifiedInLoops; 254 this.createFloatingReads = createFloatingReads; 255 this.createMemoryMapNodes = createMemoryMapNodes; 256 } 257 258 @Override 259 protected MemoryMapImpl processNode(FixedNode node, MemoryMapImpl state) { 260 if (node instanceof MemoryAnchorNode) { 261 processAnchor((MemoryAnchorNode) node, state); 262 return state; 263 } 264 265 if (node instanceof MemoryAccess) { 266 processAccess((MemoryAccess) node, state); 267 } 268 269 if (createFloatingReads & node instanceof FloatableAccessNode) { 270 processFloatable((FloatableAccessNode) node, state); 271 } else if (node instanceof MemoryCheckpoint.Single) { 272 processCheckpoint((MemoryCheckpoint.Single) node, state); 273 } else if (node instanceof MemoryCheckpoint.Multi) { 274 processCheckpoint((MemoryCheckpoint.Multi) node, state); 275 } 276 assert MemoryCheckpoint.TypeAssertion.correctType(node) : node; 277 278 if (createMemoryMapNodes && node instanceof ReturnNode) { 279 ((ReturnNode) node).setMemoryMap(node.graph().unique(new MemoryMapNode(state.lastMemorySnapshot))); 280 } 281 return state; 282 } 283 284 /** 285 * Improve the memory graph by re-wiring all usages of a {@link MemoryAnchorNode} to the 286 * real last access location. 287 */ 288 private static void processAnchor(MemoryAnchorNode anchor, MemoryMapImpl state) { 289 for (Node node : anchor.usages().snapshot()) { 290 if (node instanceof MemoryAccess) { 291 MemoryAccess access = (MemoryAccess) node; 292 if (access.getLastLocationAccess() == anchor) { 293 MemoryNode lastLocationAccess = state.getLastLocationAccess(access.getLocationIdentity()); 294 assert lastLocationAccess != null; 295 access.setLastLocationAccess(lastLocationAccess); 296 } 297 } 298 } 299 300 if (anchor.hasNoUsages()) { 301 anchor.graph().removeFixed(anchor); 302 } 303 } 304 305 private static void processAccess(MemoryAccess access, MemoryMapImpl state) { 306 LocationIdentity locationIdentity = access.getLocationIdentity(); 307 if (!locationIdentity.equals(LocationIdentity.any())) { 308 MemoryNode lastLocationAccess = state.getLastLocationAccess(locationIdentity); 309 access.setLastLocationAccess(lastLocationAccess); 310 } 311 } 312 313 private static void processCheckpoint(MemoryCheckpoint.Single checkpoint, MemoryMapImpl state) { 314 processIdentity(checkpoint.getLocationIdentity(), checkpoint, state); 315 } 316 317 private static void processCheckpoint(MemoryCheckpoint.Multi checkpoint, MemoryMapImpl state) { 318 for (LocationIdentity identity : checkpoint.getLocationIdentities()) { 319 processIdentity(identity, checkpoint, state); 320 } 321 } 322 323 private static void processIdentity(LocationIdentity identity, MemoryCheckpoint checkpoint, MemoryMapImpl state) { 324 if (identity.isAny()) { 325 state.lastMemorySnapshot.clear(); 326 } 327 state.lastMemorySnapshot.put(identity, checkpoint); 328 } 329 330 private static void processFloatable(FloatableAccessNode accessNode, MemoryMapImpl state) { 331 StructuredGraph graph = accessNode.graph(); 332 LocationIdentity locationIdentity = accessNode.getLocationIdentity(); 333 if (accessNode.canFloat()) { 334 assert accessNode.getNullCheck() == false; 335 MemoryNode lastLocationAccess = state.getLastLocationAccess(locationIdentity); 336 FloatingAccessNode floatingNode = accessNode.asFloatingNode(lastLocationAccess); 337 ValueAnchorNode anchor = null; 338 GuardingNode guard = accessNode.getGuard(); 339 if (guard != null) { 340 anchor = graph.add(new ValueAnchorNode(guard.asNode())); 341 graph.addAfterFixed(accessNode, anchor); 342 } 343 graph.replaceFixedWithFloating(accessNode, floatingNode); 344 } 345 } 346 347 @Override 348 protected MemoryMapImpl merge(AbstractMergeNode merge, List<MemoryMapImpl> states) { 349 return mergeMemoryMaps(merge, states); 350 } 351 352 @Override 353 protected MemoryMapImpl afterSplit(AbstractBeginNode node, MemoryMapImpl oldState) { 354 MemoryMapImpl result = new MemoryMapImpl(oldState); 355 if (node.predecessor() instanceof InvokeWithExceptionNode) { 356 /* 357 * InvokeWithException cannot be the lastLocationAccess for a FloatingReadNode. 358 * Since it is both the invoke and a control flow split, the scheduler cannot 359 * schedule anything immediately after the invoke. It can only schedule in the 360 * normal or exceptional successor - and we have to tell the scheduler here which 361 * side it needs to choose by putting in the location identity on both successors. 362 */ 363 InvokeWithExceptionNode invoke = (InvokeWithExceptionNode) node.predecessor(); 364 result.lastMemorySnapshot.put(invoke.getLocationIdentity(), (MemoryCheckpoint) node); 365 } 366 return result; 367 } 368 369 @Override 370 protected Map<LoopExitNode, MemoryMapImpl> processLoop(LoopBeginNode loop, MemoryMapImpl initialState) { 371 Set<LocationIdentity> modifiedLocations = modifiedInLoops.get(loop); 372 Map<LocationIdentity, MemoryPhiNode> phis = CollectionsFactory.newMap(); 373 if (modifiedLocations.contains(LocationIdentity.any())) { 374 // create phis for all locations if ANY is modified in the loop 375 modifiedLocations = CollectionsFactory.newSet(modifiedLocations); 376 modifiedLocations.addAll(initialState.lastMemorySnapshot.keySet()); 377 } 378 379 for (LocationIdentity location : modifiedLocations) { 380 createMemoryPhi(loop, initialState, phis, location); 381 } 382 for (Map.Entry<LocationIdentity, MemoryPhiNode> entry : phis.entrySet()) { 383 initialState.lastMemorySnapshot.put(entry.getKey(), entry.getValue()); 384 } 385 386 LoopInfo<MemoryMapImpl> loopInfo = ReentrantNodeIterator.processLoop(this, loop, initialState); 387 388 for (Map.Entry<LoopEndNode, MemoryMapImpl> entry : loopInfo.endStates.entrySet()) { 389 int endIndex = loop.phiPredecessorIndex(entry.getKey()); 390 for (Map.Entry<LocationIdentity, MemoryPhiNode> phiEntry : phis.entrySet()) { 391 LocationIdentity key = phiEntry.getKey(); 392 PhiNode phi = phiEntry.getValue(); 393 phi.initializeValueAt(endIndex, ValueNodeUtil.asNode(entry.getValue().getLastLocationAccess(key))); 394 } 395 } 396 return loopInfo.exitStates; 397 } 398 399 private static void createMemoryPhi(LoopBeginNode loop, MemoryMapImpl initialState, Map<LocationIdentity, MemoryPhiNode> phis, LocationIdentity location) { 400 MemoryPhiNode phi = loop.graph().addWithoutUnique(new MemoryPhiNode(loop, location)); 401 phi.addInput(ValueNodeUtil.asNode(initialState.getLastLocationAccess(location))); 402 phis.put(location, phi); 403 } 404 } 405}