1 /* 2 * Copyright (c) 2011, 2016, 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.virtual.phases.ea; 24 25 import static org.graalvm.compiler.core.common.GraalOptions.ReadEliminationMaxLoopVisits; 26 import static org.graalvm.compiler.nodes.NamedLocationIdentity.ARRAY_LENGTH_LOCATION; 27 28 import java.util.ArrayList; 29 import java.util.EnumMap; 30 import java.util.HashSet; 31 import java.util.Iterator; 32 import java.util.List; 33 import java.util.Map; 34 import java.util.Set; 35 36 import org.graalvm.compiler.core.common.LocationIdentity; 37 import org.graalvm.compiler.core.common.cfg.Loop; 38 import org.graalvm.compiler.core.common.spi.ConstantFieldProvider; 39 import org.graalvm.compiler.debug.Debug; 40 import org.graalvm.compiler.graph.Node; 41 import org.graalvm.compiler.nodes.FieldLocationIdentity; 42 import org.graalvm.compiler.nodes.FixedNode; 43 import org.graalvm.compiler.nodes.FixedWithNextNode; 44 import org.graalvm.compiler.nodes.LoopBeginNode; 45 import org.graalvm.compiler.nodes.LoopExitNode; 46 import org.graalvm.compiler.nodes.NamedLocationIdentity; 47 import org.graalvm.compiler.nodes.PhiNode; 48 import org.graalvm.compiler.nodes.ProxyNode; 49 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult; 50 import org.graalvm.compiler.nodes.ValueNode; 51 import org.graalvm.compiler.nodes.ValueProxyNode; 52 import org.graalvm.compiler.nodes.cfg.Block; 53 import org.graalvm.compiler.nodes.extended.UnboxNode; 54 import org.graalvm.compiler.nodes.extended.UnsafeLoadNode; 55 import org.graalvm.compiler.nodes.extended.UnsafeStoreNode; 56 import org.graalvm.compiler.nodes.java.ArrayLengthNode; 57 import org.graalvm.compiler.nodes.java.LoadFieldNode; 58 import org.graalvm.compiler.nodes.java.LoadIndexedNode; 59 import org.graalvm.compiler.nodes.java.StoreFieldNode; 60 import org.graalvm.compiler.nodes.java.StoreIndexedNode; 61 import org.graalvm.compiler.nodes.memory.MemoryCheckpoint; 62 import org.graalvm.compiler.nodes.spi.LoweringProvider; 63 import org.graalvm.compiler.nodes.type.StampTool; 64 import org.graalvm.compiler.nodes.util.GraphUtil; 65 import org.graalvm.compiler.nodes.virtual.VirtualArrayNode; 66 import org.graalvm.compiler.virtual.phases.ea.PEReadEliminationBlockState.ReadCacheEntry; 67 68 import jdk.vm.ci.meta.ConstantReflectionProvider; 69 import jdk.vm.ci.meta.JavaConstant; 70 import jdk.vm.ci.meta.JavaKind; 71 import jdk.vm.ci.meta.MetaAccessProvider; 72 import jdk.vm.ci.meta.ResolvedJavaType; 73 74 public class PEReadEliminationClosure extends PartialEscapeClosure<PEReadEliminationBlockState> { 75 76 private static final EnumMap<JavaKind, LocationIdentity> UNBOX_LOCATIONS; 77 78 static { 79 UNBOX_LOCATIONS = new EnumMap<>(JavaKind.class); 80 for (JavaKind kind : JavaKind.values()) { 81 UNBOX_LOCATIONS.put(kind, NamedLocationIdentity.immutable("PEA unbox " + kind.getJavaName())); 82 } 83 } 84 85 public PEReadEliminationClosure(ScheduleResult schedule, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, ConstantFieldProvider constantFieldProvider, 86 LoweringProvider loweringProvider) { 87 super(schedule, metaAccess, constantReflection, constantFieldProvider, loweringProvider); 88 } 89 90 @Override 91 protected PEReadEliminationBlockState getInitialState() { 92 return new PEReadEliminationBlockState(); 93 } 94 95 @Override 96 protected boolean processNode(Node node, PEReadEliminationBlockState state, GraphEffectList effects, FixedWithNextNode lastFixedNode) { 97 if (super.processNode(node, state, effects, lastFixedNode)) { 98 return true; 99 } 100 101 if (node instanceof LoadFieldNode) { 102 return processLoadField((LoadFieldNode) node, state, effects); 103 } else if (node instanceof StoreFieldNode) { 104 return processStoreField((StoreFieldNode) node, state, effects); 105 } else if (node instanceof LoadIndexedNode) { 106 return processLoadIndexed((LoadIndexedNode) node, state, effects); 107 } else if (node instanceof StoreIndexedNode) { 108 return processStoreIndexed((StoreIndexedNode) node, state, effects); 109 } else if (node instanceof ArrayLengthNode) { 110 return processArrayLength((ArrayLengthNode) node, state, effects); 111 } else if (node instanceof UnboxNode) { 112 return processUnbox((UnboxNode) node, state, effects); 113 } else if (node instanceof UnsafeLoadNode) { 114 return processUnsafeLoad((UnsafeLoadNode) node, state, effects); 115 } else if (node instanceof UnsafeStoreNode) { 116 return processUnsafeStore((UnsafeStoreNode) node, state, effects); 117 } else if (node instanceof MemoryCheckpoint.Single) { 118 COUNTER_MEMORYCHECKPOINT.increment(); 119 LocationIdentity identity = ((MemoryCheckpoint.Single) node).getLocationIdentity(); 120 processIdentity(state, identity); 121 } else if (node instanceof MemoryCheckpoint.Multi) { 122 COUNTER_MEMORYCHECKPOINT.increment(); 123 for (LocationIdentity identity : ((MemoryCheckpoint.Multi) node).getLocationIdentities()) { 124 processIdentity(state, identity); 125 } 126 } 127 128 return false; 129 } 130 131 private boolean processStore(FixedNode store, ValueNode object, LocationIdentity identity, int index, ValueNode value, PEReadEliminationBlockState state, GraphEffectList effects) { 132 ValueNode unproxiedObject = GraphUtil.unproxify(object); 133 ValueNode cachedValue = state.getReadCache(object, identity, index, this); 134 135 ValueNode finalValue = getScalarAlias(value); 136 boolean result = false; 137 if (GraphUtil.unproxify(finalValue) == GraphUtil.unproxify(cachedValue)) { 138 effects.deleteNode(store); 139 result = true; 140 } 141 state.killReadCache(identity, index); 142 state.addReadCache(unproxiedObject, identity, index, finalValue, this); 143 return result; 144 } 145 146 private boolean processLoad(FixedNode load, ValueNode object, LocationIdentity identity, int index, PEReadEliminationBlockState state, GraphEffectList effects) { 147 ValueNode unproxiedObject = GraphUtil.unproxify(object); 148 ValueNode cachedValue = state.getReadCache(unproxiedObject, identity, index, this); 149 if (cachedValue != null) { 150 effects.replaceAtUsages(load, cachedValue); 151 addScalarAlias(load, cachedValue); 152 return true; 153 } else { 154 state.addReadCache(unproxiedObject, identity, index, load, this); 155 return false; 156 } 157 } 158 159 private boolean processUnsafeLoad(UnsafeLoadNode load, PEReadEliminationBlockState state, GraphEffectList effects) { 160 if (load.offset().isConstant()) { 161 ResolvedJavaType type = StampTool.typeOrNull(load.object()); 162 if (type != null && type.isArray()) { 163 long offset = load.offset().asJavaConstant().asLong(); 164 int index = VirtualArrayNode.entryIndexForOffset(offset, load.accessKind(), type.getComponentType(), Integer.MAX_VALUE); 165 ValueNode object = GraphUtil.unproxify(load.object()); 166 LocationIdentity location = NamedLocationIdentity.getArrayLocation(type.getComponentType().getJavaKind()); 167 ValueNode cachedValue = state.getReadCache(object, location, index, this); 168 if (cachedValue != null && load.stamp().isCompatible(cachedValue.stamp())) { 169 effects.replaceAtUsages(load, cachedValue); 170 addScalarAlias(load, cachedValue); 171 return true; 172 } else { 173 state.addReadCache(object, location, index, load, this); 174 } 175 } 176 } 177 return false; 178 } 179 180 private boolean processUnsafeStore(UnsafeStoreNode store, PEReadEliminationBlockState state, GraphEffectList effects) { 181 ResolvedJavaType type = StampTool.typeOrNull(store.object()); 182 if (type != null && type.isArray()) { 183 LocationIdentity location = NamedLocationIdentity.getArrayLocation(type.getComponentType().getJavaKind()); 184 if (store.offset().isConstant()) { 185 long offset = store.offset().asJavaConstant().asLong(); 186 int index = VirtualArrayNode.entryIndexForOffset(offset, store.accessKind(), type.getComponentType(), Integer.MAX_VALUE); 187 return processStore(store, store.object(), location, index, store.value(), state, effects); 188 } else { 189 processIdentity(state, location); 190 } 191 } else { 192 state.killReadCache(); 193 } 194 return false; 195 } 196 197 private boolean processArrayLength(ArrayLengthNode length, PEReadEliminationBlockState state, GraphEffectList effects) { 198 return processLoad(length, length.array(), ARRAY_LENGTH_LOCATION, -1, state, effects); 199 } 200 201 private boolean processStoreField(StoreFieldNode store, PEReadEliminationBlockState state, GraphEffectList effects) { 202 if (store.isVolatile()) { 203 state.killReadCache(); 204 return false; 205 } 206 return processStore(store, store.object(), new FieldLocationIdentity(store.field()), -1, store.value(), state, effects); 207 } 208 209 private boolean processLoadField(LoadFieldNode load, PEReadEliminationBlockState state, GraphEffectList effects) { 210 if (load.isVolatile()) { 211 state.killReadCache(); 212 return false; 213 } 214 return processLoad(load, load.object(), new FieldLocationIdentity(load.field()), -1, state, effects); 215 } 216 217 private boolean processStoreIndexed(StoreIndexedNode store, PEReadEliminationBlockState state, GraphEffectList effects) { 218 LocationIdentity arrayLocation = NamedLocationIdentity.getArrayLocation(store.elementKind()); 219 if (store.index().isConstant()) { 220 int index = ((JavaConstant) store.index().asConstant()).asInt(); 221 return processStore(store, store.array(), arrayLocation, index, store.value(), state, effects); 222 } else { 223 state.killReadCache(arrayLocation, -1); 224 } 225 return false; 226 } 227 228 private boolean processLoadIndexed(LoadIndexedNode load, PEReadEliminationBlockState state, GraphEffectList effects) { 229 if (load.index().isConstant()) { 230 int index = ((JavaConstant) load.index().asConstant()).asInt(); 231 LocationIdentity arrayLocation = NamedLocationIdentity.getArrayLocation(load.elementKind()); 232 return processLoad(load, load.array(), arrayLocation, index, state, effects); 233 } 234 return false; 235 } 236 237 private boolean processUnbox(UnboxNode unbox, PEReadEliminationBlockState state, GraphEffectList effects) { 238 return processLoad(unbox, unbox.getValue(), UNBOX_LOCATIONS.get(unbox.getBoxingKind()), -1, state, effects); 239 } 240 241 private static void processIdentity(PEReadEliminationBlockState state, LocationIdentity identity) { 242 if (identity.isAny()) { 243 state.killReadCache(); 244 } else { 245 state.killReadCache(identity, -1); 246 } 247 } 248 249 @Override 250 protected void processInitialLoopState(Loop<Block> loop, PEReadEliminationBlockState initialState) { 251 super.processInitialLoopState(loop, initialState); 252 253 for (PhiNode phi : ((LoopBeginNode) loop.getHeader().getBeginNode()).phis()) { 254 ValueNode firstValue = phi.valueAt(0); 255 if (firstValue != null) { 256 firstValue = GraphUtil.unproxify(firstValue); 257 for (Map.Entry<ReadCacheEntry, ValueNode> entry : new ArrayList<>(initialState.getReadCache().entrySet())) { 258 if (entry.getKey().object == firstValue) { 259 initialState.addReadCache(phi, entry.getKey().identity, entry.getKey().index, entry.getValue(), this); 260 } 261 } 262 } 263 } 264 } 265 266 @Override 267 protected void processLoopExit(LoopExitNode exitNode, PEReadEliminationBlockState initialState, PEReadEliminationBlockState exitState, GraphEffectList effects) { 268 super.processLoopExit(exitNode, initialState, exitState, effects); 269 270 if (exitNode.graph().hasValueProxies()) { 271 for (Map.Entry<ReadCacheEntry, ValueNode> entry : exitState.getReadCache().entrySet()) { 272 if (initialState.getReadCache().get(entry.getKey()) != entry.getValue()) { 273 ValueNode value = exitState.getReadCache(entry.getKey().object, entry.getKey().identity, entry.getKey().index, this); 274 assert value != null : "Got null from read cache, entry's value:" + entry.getValue(); 275 if (!(value instanceof ProxyNode) || ((ProxyNode) value).proxyPoint() != exitNode) { 276 ProxyNode proxy = new ValueProxyNode(value, exitNode); 277 effects.addFloatingNode(proxy, "readCacheProxy"); 278 entry.setValue(proxy); 279 } 280 } 281 } 282 } 283 } 284 285 @Override 286 protected PEReadEliminationBlockState cloneState(PEReadEliminationBlockState other) { 287 return new PEReadEliminationBlockState(other); 288 } 289 290 @Override 291 protected MergeProcessor createMergeProcessor(Block merge) { 292 return new ReadEliminationMergeProcessor(merge); 293 } 294 295 private class ReadEliminationMergeProcessor extends MergeProcessor { 296 297 ReadEliminationMergeProcessor(Block mergeBlock) { 298 super(mergeBlock); 299 } 300 301 @Override 302 protected void merge(List<PEReadEliminationBlockState> states) { 303 super.merge(states); 304 305 mergeReadCache(states); 306 } 307 308 private void mergeReadCache(List<PEReadEliminationBlockState> states) { 309 for (Map.Entry<ReadCacheEntry, ValueNode> entry : states.get(0).readCache.entrySet()) { 310 ReadCacheEntry key = entry.getKey(); 311 ValueNode value = entry.getValue(); 312 boolean phi = false; 313 for (int i = 1; i < states.size(); i++) { 314 ValueNode otherValue = states.get(i).readCache.get(key); 315 // e.g. unsafe loads / stores with different access kinds have different stamps 316 // although location, object and offset are the same, in this case we cannot 317 // create a phi nor can we set a common value 318 if (otherValue == null || !value.stamp().isCompatible(otherValue.stamp())) { 319 value = null; 320 phi = false; 321 break; 322 } 323 if (!phi && otherValue != value) { 324 phi = true; 325 } 326 } 327 if (phi) { 328 PhiNode phiNode = getPhi(entry, value.stamp().unrestricted()); 329 mergeEffects.addFloatingNode(phiNode, "mergeReadCache"); 330 for (int i = 0; i < states.size(); i++) { 331 ValueNode v = states.get(i).getReadCache(key.object, key.identity, key.index, PEReadEliminationClosure.this); 332 assert phiNode.stamp().isCompatible(v.stamp()) : "Cannot create read elimination phi for inputs with incompatible stamps."; 333 setPhiInput(phiNode, i, v); 334 } 335 newState.readCache.put(key, phiNode); 336 } else if (value != null) { 337 newState.readCache.put(key, value); 338 } 339 } 340 for (PhiNode phi : getPhis()) { 341 if (phi.getStackKind() == JavaKind.Object) { 342 for (Map.Entry<ReadCacheEntry, ValueNode> entry : states.get(0).readCache.entrySet()) { 343 if (entry.getKey().object == getPhiValueAt(phi, 0)) { 344 mergeReadCachePhi(phi, entry.getKey().identity, entry.getKey().index, states); 345 } 346 } 347 } 348 } 349 } 350 351 private void mergeReadCachePhi(PhiNode phi, LocationIdentity identity, int index, List<PEReadEliminationBlockState> states) { 352 ValueNode[] values = new ValueNode[states.size()]; 353 values[0] = states.get(0).getReadCache(getPhiValueAt(phi, 0), identity, index, PEReadEliminationClosure.this); 354 if (values[0] != null) { 355 for (int i = 1; i < states.size(); i++) { 356 ValueNode value = states.get(i).getReadCache(getPhiValueAt(phi, i), identity, index, PEReadEliminationClosure.this); 357 // e.g. unsafe loads / stores with same identity and different access kinds see 358 // mergeReadCache(states) 359 if (value == null || !values[i - 1].stamp().isCompatible(value.stamp())) { 360 return; 361 } 362 values[i] = value; 363 } 364 365 PhiNode phiNode = getPhi(new ReadCacheEntry(identity, phi, index), values[0].stamp().unrestricted()); 366 mergeEffects.addFloatingNode(phiNode, "mergeReadCachePhi"); 367 for (int i = 0; i < values.length; i++) { 368 setPhiInput(phiNode, i, values[i]); 369 } 370 newState.readCache.put(new ReadCacheEntry(identity, phi, index), phiNode); 371 } 372 } 373 } 374 375 @Override 376 protected void processKilledLoopLocations(Loop<Block> loop, PEReadEliminationBlockState initialState, PEReadEliminationBlockState mergedStates) { 377 assert initialState != null; 378 assert mergedStates != null; 379 if (initialState.readCache.size() > 0) { 380 LoopKillCache loopKilledLocations = loopLocationKillCache.get(loop); 381 // we have fully processed this loop the first time, remember to cache it the next time 382 // it is visited 383 if (loopKilledLocations == null) { 384 loopKilledLocations = new LoopKillCache(1/* 1.visit */); 385 loopLocationKillCache.put(loop, loopKilledLocations); 386 } else { 387 if (loopKilledLocations.visits() > ReadEliminationMaxLoopVisits.getValue()) { 388 // we have processed the loop too many times, kill all locations so the inner 389 // loop will never be processed more than once again on visit 390 loopKilledLocations.setKillsAll(); 391 } else { 392 // we have fully processed this loop >1 times, update the killed locations 393 Set<LocationIdentity> forwardEndLiveLocations = new HashSet<>(); 394 for (ReadCacheEntry entry : initialState.readCache.keySet()) { 395 forwardEndLiveLocations.add(entry.identity); 396 } 397 for (ReadCacheEntry entry : mergedStates.readCache.keySet()) { 398 forwardEndLiveLocations.remove(entry.identity); 399 } 400 // every location that is alive before the loop but not after is killed by the 401 // loop 402 for (LocationIdentity location : forwardEndLiveLocations) { 403 loopKilledLocations.rememberLoopKilledLocation(location); 404 } 405 if (Debug.isLogEnabled() && loopKilledLocations != null) { 406 Debug.log("[Early Read Elimination] Setting loop killed locations of loop at node %s with %s", 407 loop.getHeader().getBeginNode(), forwardEndLiveLocations); 408 } 409 } 410 // remember the loop visit 411 loopKilledLocations.visited(); 412 } 413 } 414 } 415 416 @Override 417 protected PEReadEliminationBlockState stripKilledLoopLocations(Loop<Block> loop, PEReadEliminationBlockState originalInitialState) { 418 PEReadEliminationBlockState initialState = super.stripKilledLoopLocations(loop, originalInitialState); 419 LoopKillCache loopKilledLocations = loopLocationKillCache.get(loop); 420 if (loopKilledLocations != null && loopKilledLocations.loopKillsLocations()) { 421 Set<ReadCacheEntry> forwardEndLiveLocations = initialState.readCache.keySet(); 422 Iterator<ReadCacheEntry> it = forwardEndLiveLocations.iterator(); 423 while (it.hasNext()) { 424 ReadCacheEntry entry = it.next(); 425 if (loopKilledLocations.containsLocation(entry.identity)) { 426 it.remove(); 427 } 428 } 429 } 430 return initialState; 431 } 432 433 }