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