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