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