1 /*
   2  * Copyright (c) 2011, 2013, 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 java.util.ArrayList;
  26 import java.util.Arrays;
  27 import java.util.Iterator;
  28 import java.util.List;
  29 import java.util.Map;
  30 
  31 import org.graalvm.compiler.nodes.FixedNode;
  32 import org.graalvm.compiler.nodes.FixedWithNextNode;
  33 import org.graalvm.compiler.nodes.ValueNode;
  34 import org.graalvm.compiler.nodes.calc.FloatingNode;
  35 import org.graalvm.compiler.nodes.java.MonitorIdNode;
  36 import org.graalvm.compiler.nodes.virtual.AllocatedObjectNode;
  37 import org.graalvm.compiler.nodes.virtual.CommitAllocationNode;
  38 import org.graalvm.compiler.nodes.virtual.LockState;
  39 import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
  40 
  41 public abstract class PartialEscapeBlockState<T extends PartialEscapeBlockState<T>> extends EffectsBlockState<T> {
  42 
  43     private static final ObjectState[] EMPTY_ARRAY = new ObjectState[0];
  44 
  45     private ObjectState[] objectStates;
  46 
  47     private static class RefCount {
  48         private int refCount = 1;
  49     }
  50 
  51     private RefCount arrayRefCount;
  52 
  53     /**
  54      * Final subclass of PartialEscapeBlockState, for performance and to make everything behave
  55      * nicely with generics.
  56      */
  57     public static final class Final extends PartialEscapeBlockState<Final> {
  58 
  59         public Final() {
  60         }
  61 
  62         public Final(Final other) {
  63             super(other);
  64         }
  65     }
  66 
  67     protected PartialEscapeBlockState() {
  68         objectStates = EMPTY_ARRAY;
  69         arrayRefCount = new RefCount();
  70     }
  71 
  72     protected PartialEscapeBlockState(PartialEscapeBlockState<T> other) {
  73         super(other);
  74         adoptAddObjectStates(other);
  75     }
  76 
  77     public ObjectState getObjectState(int object) {
  78         ObjectState state = objectStates[object];
  79         assert state != null;
  80         return state;
  81     }
  82 
  83     public ObjectState getObjectStateOptional(int object) {
  84         return object >= objectStates.length ? null : objectStates[object];
  85     }
  86 
  87     public ObjectState getObjectState(VirtualObjectNode object) {
  88         ObjectState state = objectStates[object.getObjectId()];
  89         assert state != null;
  90         return state;
  91     }
  92 
  93     public ObjectState getObjectStateOptional(VirtualObjectNode object) {
  94         int id = object.getObjectId();
  95         return id >= objectStates.length ? null : objectStates[id];
  96     }
  97 
  98     private ObjectState[] getObjectStateArrayForModification() {
  99         if (arrayRefCount.refCount > 1) {
 100             objectStates = objectStates.clone();
 101             arrayRefCount = new RefCount();
 102         }
 103         return objectStates;
 104     }
 105 
 106     private ObjectState getObjectStateForModification(int object) {
 107         ObjectState[] array = getObjectStateArrayForModification();
 108         ObjectState objectState = array[object];
 109         if (objectState.copyOnWrite) {
 110             array[object] = objectState = objectState.cloneState();
 111         }
 112         return objectState;
 113     }
 114 
 115     public void setEntry(int object, int entryIndex, ValueNode value) {
 116         if (objectStates[object].getEntry(entryIndex) != value) {
 117             getObjectStateForModification(object).setEntry(entryIndex, value);
 118         }
 119     }
 120 
 121     public void escape(int object, ValueNode materialized) {
 122         getObjectStateForModification(object).escape(materialized);
 123     }
 124 
 125     public void addLock(int object, MonitorIdNode monitorId) {
 126         getObjectStateForModification(object).addLock(monitorId);
 127     }
 128 
 129     public MonitorIdNode removeLock(int object) {
 130         return getObjectStateForModification(object).removeLock();
 131     }
 132 
 133     public void setEnsureVirtualized(int object, boolean ensureVirtualized) {
 134         if (objectStates[object].getEnsureVirtualized() != ensureVirtualized) {
 135             getObjectStateForModification(object).setEnsureVirtualized(ensureVirtualized);
 136         }
 137     }
 138 
 139     public void updateMaterializedValue(int object, ValueNode value) {
 140         if (objectStates[object].getMaterializedValue() != value) {
 141             getObjectStateForModification(object).updateMaterializedValue(value);
 142         }
 143     }
 144 
 145     public void materializeBefore(FixedNode fixed, VirtualObjectNode virtual, GraphEffectList materializeEffects) {
 146         PartialEscapeClosure.COUNTER_MATERIALIZATIONS.increment();
 147         List<AllocatedObjectNode> objects = new ArrayList<>(2);
 148         List<ValueNode> values = new ArrayList<>(8);
 149         List<List<MonitorIdNode>> locks = new ArrayList<>(2);
 150         List<ValueNode> otherAllocations = new ArrayList<>(2);
 151         List<Boolean> ensureVirtual = new ArrayList<>(2);
 152         materializeWithCommit(fixed, virtual, objects, locks, values, ensureVirtual, otherAllocations);
 153         assert fixed != null;
 154 
 155         materializeEffects.add("materializeBefore", (graph, obsoleteNodes) -> {
 156             for (ValueNode otherAllocation : otherAllocations) {
 157                 graph.addWithoutUnique(otherAllocation);
 158                 if (otherAllocation instanceof FixedWithNextNode) {
 159                     graph.addBeforeFixed(fixed, (FixedWithNextNode) otherAllocation);
 160                 } else {
 161                     assert otherAllocation instanceof FloatingNode;
 162                 }
 163             }
 164             if (!objects.isEmpty()) {
 165                 CommitAllocationNode commit;
 166                 if (fixed.predecessor() instanceof CommitAllocationNode) {
 167                     commit = (CommitAllocationNode) fixed.predecessor();
 168                 } else {
 169                     commit = graph.add(new CommitAllocationNode());
 170                     graph.addBeforeFixed(fixed, commit);
 171                 }
 172                 for (AllocatedObjectNode obj : objects) {
 173                     graph.addWithoutUnique(obj);
 174                     commit.getVirtualObjects().add(obj.getVirtualObject());
 175                     obj.setCommit(commit);
 176                 }
 177                 commit.getValues().addAll(values);
 178                 for (List<MonitorIdNode> monitorIds : locks) {
 179                     commit.addLocks(monitorIds);
 180                 }
 181                 commit.getEnsureVirtual().addAll(ensureVirtual);
 182 
 183                 assert commit.usages().filter(AllocatedObjectNode.class).count() == commit.getUsageCount();
 184                 List<AllocatedObjectNode> materializedValues = commit.usages().filter(AllocatedObjectNode.class).snapshot();
 185                 for (int i = 0; i < commit.getValues().size(); i++) {
 186                     if (materializedValues.contains(commit.getValues().get(i))) {
 187                         commit.getValues().set(i, ((AllocatedObjectNode) commit.getValues().get(i)).getVirtualObject());
 188                     }
 189                 }
 190             }
 191         });
 192     }
 193 
 194     private void materializeWithCommit(FixedNode fixed, VirtualObjectNode virtual, List<AllocatedObjectNode> objects, List<List<MonitorIdNode>> locks, List<ValueNode> values,
 195                     List<Boolean> ensureVirtual, List<ValueNode> otherAllocations) {
 196         ObjectState obj = getObjectState(virtual);
 197 
 198         ValueNode[] entries = obj.getEntries();
 199         ValueNode representation = virtual.getMaterializedRepresentation(fixed, entries, obj.getLocks());
 200         escape(virtual.getObjectId(), representation);
 201         obj = getObjectState(virtual);
 202         PartialEscapeClosure.updateStatesForMaterialized(this, virtual, obj.getMaterializedValue());
 203         if (representation instanceof AllocatedObjectNode) {
 204             objects.add((AllocatedObjectNode) representation);
 205             locks.add(LockState.asList(obj.getLocks()));
 206             ensureVirtual.add(obj.getEnsureVirtualized());
 207             int pos = values.size();
 208             while (values.size() < pos + entries.length) {
 209                 values.add(null);
 210             }
 211             for (int i = 0; i < entries.length; i++) {
 212                 if (entries[i] instanceof VirtualObjectNode) {
 213                     VirtualObjectNode entryVirtual = (VirtualObjectNode) entries[i];
 214                     ObjectState entryObj = getObjectState(entryVirtual);
 215                     if (entryObj.isVirtual()) {
 216                         materializeWithCommit(fixed, entryVirtual, objects, locks, values, ensureVirtual, otherAllocations);
 217                         entryObj = getObjectState(entryVirtual);
 218                     }
 219                     values.set(pos + i, entryObj.getMaterializedValue());
 220                 } else {
 221                     values.set(pos + i, entries[i]);
 222                 }
 223             }
 224             objectMaterialized(virtual, (AllocatedObjectNode) representation, values.subList(pos, pos + entries.length));
 225         } else {
 226             VirtualUtil.trace("materialized %s as %s", virtual, representation);
 227             otherAllocations.add(representation);
 228             assert obj.getLocks() == null;
 229         }
 230     }
 231 
 232     protected void objectMaterialized(VirtualObjectNode virtual, AllocatedObjectNode representation, List<ValueNode> values) {
 233         VirtualUtil.trace("materialized %s as %s with values %s", virtual, representation, values);
 234     }
 235 
 236     public void addObject(int virtual, ObjectState state) {
 237         ensureSize(virtual)[virtual] = state;
 238     }
 239 
 240     private ObjectState[] ensureSize(int objectId) {
 241         if (objectStates.length <= objectId) {
 242             objectStates = Arrays.copyOf(objectStates, Math.max(objectId * 2, 4));
 243             arrayRefCount.refCount--;
 244             arrayRefCount = new RefCount();
 245             return objectStates;
 246         } else {
 247             return getObjectStateArrayForModification();
 248         }
 249     }
 250 
 251     public int getStateCount() {
 252         return objectStates.length;
 253     }
 254 
 255     @Override
 256     public String toString() {
 257         return super.toString() + ", Object States: " + Arrays.toString(objectStates);
 258     }
 259 
 260     @Override
 261     public boolean equivalentTo(T other) {
 262         int length = Math.max(objectStates.length, other.getStateCount());
 263         for (int i = 0; i < length; i++) {
 264             ObjectState left = getObjectStateOptional(i);
 265             ObjectState right = other.getObjectStateOptional(i);
 266             if (left != right) {
 267                 if (left == null || right == null) {
 268                     return false;
 269                 }
 270                 if (!left.equals(right)) {
 271                     return false;
 272                 }
 273             }
 274         }
 275         return true;
 276     }
 277 
 278     protected static <K, V> boolean compareMaps(Map<K, V> left, Map<K, V> right) {
 279         if (left.size() != right.size()) {
 280             return false;
 281         }
 282         return compareMapsNoSize(left, right);
 283     }
 284 
 285     protected static <K, V> boolean compareMapsNoSize(Map<K, V> left, Map<K, V> right) {
 286         if (left == right) {
 287             return true;
 288         }
 289         for (Map.Entry<K, V> entry : right.entrySet()) {
 290             K key = entry.getKey();
 291             V value = entry.getValue();
 292             assert value != null;
 293             V otherValue = left.get(key);
 294             if (otherValue != value && !value.equals(otherValue)) {
 295                 return false;
 296             }
 297         }
 298         return true;
 299     }
 300 
 301     protected static <U, V> void meetMaps(Map<U, V> target, Map<U, V> source) {
 302         Iterator<Map.Entry<U, V>> iter = target.entrySet().iterator();
 303         while (iter.hasNext()) {
 304             Map.Entry<U, V> entry = iter.next();
 305             if (source.containsKey(entry.getKey())) {
 306                 assert source.get(entry.getKey()) == entry.getValue();
 307             } else {
 308                 iter.remove();
 309             }
 310         }
 311     }
 312 
 313     public void resetObjectStates(int size) {
 314         objectStates = new ObjectState[size];
 315     }
 316 
 317     public static boolean identicalObjectStates(PartialEscapeBlockState<?>[] states) {
 318         for (int i = 1; i < states.length; i++) {
 319             if (states[0].objectStates != states[i].objectStates) {
 320                 return false;
 321             }
 322         }
 323         return true;
 324     }
 325 
 326     public static boolean identicalObjectStates(PartialEscapeBlockState<?>[] states, int object) {
 327         for (int i = 1; i < states.length; i++) {
 328             if (states[0].objectStates[object] != states[i].objectStates[object]) {
 329                 return false;
 330             }
 331         }
 332         return true;
 333     }
 334 
 335     public void adoptAddObjectStates(PartialEscapeBlockState<?> other) {
 336         if (objectStates != null) {
 337             arrayRefCount.refCount--;
 338         }
 339         objectStates = other.objectStates;
 340         arrayRefCount = other.arrayRefCount;
 341 
 342         if (arrayRefCount.refCount == 1) {
 343             for (ObjectState state : objectStates) {
 344                 if (state != null) {
 345                     state.share();
 346                 }
 347             }
 348         }
 349         arrayRefCount.refCount++;
 350     }
 351 }