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 24 25 package org.graalvm.compiler.virtual.phases.ea; 26 27 import java.util.ArrayList; 28 import java.util.BitSet; 29 import java.util.Iterator; 30 import java.util.List; 31 import java.util.function.IntUnaryOperator; 32 33 import jdk.internal.vm.compiler.collections.EconomicMap; 34 import jdk.internal.vm.compiler.collections.EconomicSet; 35 import jdk.internal.vm.compiler.collections.Equivalence; 36 import org.graalvm.compiler.core.common.GraalOptions; 37 import org.graalvm.compiler.core.common.cfg.Loop; 38 import org.graalvm.compiler.core.common.spi.ConstantFieldProvider; 39 import org.graalvm.compiler.core.common.type.Stamp; 40 import org.graalvm.compiler.core.common.type.StampFactory; 41 import org.graalvm.compiler.debug.CounterKey; 42 import org.graalvm.compiler.debug.DebugContext; 43 import org.graalvm.compiler.graph.Node; 44 import org.graalvm.compiler.graph.NodeBitMap; 45 import org.graalvm.compiler.graph.Position; 46 import org.graalvm.compiler.graph.spi.Canonicalizable; 47 import org.graalvm.compiler.nodes.AbstractEndNode; 48 import org.graalvm.compiler.nodes.CallTargetNode; 49 import org.graalvm.compiler.nodes.ConstantNode; 50 import org.graalvm.compiler.nodes.ControlSinkNode; 51 import org.graalvm.compiler.nodes.FixedNode; 52 import org.graalvm.compiler.nodes.FixedWithNextNode; 53 import org.graalvm.compiler.nodes.FrameState; 54 import org.graalvm.compiler.nodes.Invoke; 55 import org.graalvm.compiler.nodes.LoopBeginNode; 56 import org.graalvm.compiler.nodes.LoopExitNode; 57 import org.graalvm.compiler.nodes.NodeView; 58 import org.graalvm.compiler.nodes.PhiNode; 59 import org.graalvm.compiler.nodes.ProxyNode; 60 import org.graalvm.compiler.nodes.StructuredGraph; 61 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult; 62 import org.graalvm.compiler.nodes.ValueNode; 63 import org.graalvm.compiler.nodes.ValuePhiNode; 64 import org.graalvm.compiler.nodes.ValueProxyNode; 65 import org.graalvm.compiler.nodes.VirtualState; 66 import org.graalvm.compiler.nodes.VirtualState.NodeClosure; 67 import org.graalvm.compiler.nodes.cfg.Block; 68 import org.graalvm.compiler.nodes.spi.LoweringProvider; 69 import org.graalvm.compiler.nodes.spi.NodeWithState; 70 import org.graalvm.compiler.nodes.spi.Virtualizable; 71 import org.graalvm.compiler.nodes.spi.VirtualizableAllocation; 72 import org.graalvm.compiler.nodes.spi.VirtualizerTool; 73 import org.graalvm.compiler.nodes.virtual.AllocatedObjectNode; 74 import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; 75 import org.graalvm.compiler.virtual.nodes.VirtualObjectState; 76 77 import jdk.vm.ci.meta.ConstantReflectionProvider; 78 import jdk.vm.ci.meta.JavaConstant; 79 import jdk.vm.ci.meta.JavaKind; 80 import jdk.vm.ci.meta.MetaAccessProvider; 81 82 public abstract class PartialEscapeClosure<BlockT extends PartialEscapeBlockState<BlockT>> extends EffectsClosure<BlockT> { 83 84 public static final CounterKey COUNTER_MATERIALIZATIONS = DebugContext.counter("Materializations"); 85 public static final CounterKey COUNTER_MATERIALIZATIONS_PHI = DebugContext.counter("MaterializationsPhi"); 86 public static final CounterKey COUNTER_MATERIALIZATIONS_MERGE = DebugContext.counter("MaterializationsMerge"); 87 public static final CounterKey COUNTER_MATERIALIZATIONS_UNHANDLED = DebugContext.counter("MaterializationsUnhandled"); 88 public static final CounterKey COUNTER_MATERIALIZATIONS_LOOP_REITERATION = DebugContext.counter("MaterializationsLoopReiteration"); 89 public static final CounterKey COUNTER_MATERIALIZATIONS_LOOP_END = DebugContext.counter("MaterializationsLoopEnd"); 90 public static final CounterKey COUNTER_ALLOCATION_REMOVED = DebugContext.counter("AllocationsRemoved"); 91 public static final CounterKey COUNTER_MEMORYCHECKPOINT = DebugContext.counter("MemoryCheckpoint"); 92 93 /** 94 * Nodes with inputs that were modified during analysis are marked in this bitset - this way 95 * nodes that are not influenced at all by analysis can be rejected quickly. 96 */ 97 private final NodeBitMap hasVirtualInputs; 98 99 /** 100 * This is handed out to implementers of {@link Virtualizable}. 101 */ 102 protected final VirtualizerToolImpl tool; 103 104 /** 105 * The indexes into this array correspond to {@link VirtualObjectNode#getObjectId()}. 106 */ 107 public final ArrayList<VirtualObjectNode> virtualObjects = new ArrayList<>(); 108 public final DebugContext debug; 109 110 @Override 111 public boolean needsApplyEffects() { 112 if (hasChanged()) { 113 return true; 114 } 115 /* 116 * If there is a mismatch between the number of materializations and the number of 117 * virtualizations, we need to apply effects, even if there were no other significant 118 * changes to the graph. This applies to each block, since moving from one block to the 119 * other can also be important (if the probabilities of the block differ). 120 */ 121 for (Block block : cfg.getBlocks()) { 122 GraphEffectList effects = blockEffects.get(block); 123 if (effects != null) { 124 if (effects.getVirtualizationDelta() != 0) { 125 return true; 126 } 127 } 128 } 129 return false; 130 } 131 132 private final class CollectVirtualObjectsClosure extends NodeClosure<ValueNode> { 133 private final EconomicSet<VirtualObjectNode> virtual; 134 private final GraphEffectList effects; 135 private final BlockT state; 136 137 private CollectVirtualObjectsClosure(EconomicSet<VirtualObjectNode> virtual, GraphEffectList effects, BlockT state) { 138 this.virtual = virtual; 139 this.effects = effects; 140 this.state = state; 141 } 142 143 @Override 144 public void apply(Node usage, ValueNode value) { 145 if (value instanceof VirtualObjectNode) { 146 VirtualObjectNode object = (VirtualObjectNode) value; 147 if (object.getObjectId() != -1 && state.getObjectStateOptional(object) != null) { 148 virtual.add(object); 149 } 150 } else { 151 ValueNode alias = getAlias(value); 152 if (alias instanceof VirtualObjectNode) { 153 VirtualObjectNode object = (VirtualObjectNode) alias; 154 virtual.add(object); 155 effects.replaceFirstInput(usage, value, object); 156 } 157 } 158 } 159 } 160 161 /** 162 * Final subclass of PartialEscapeClosure, for performance and to make everything behave nicely 163 * with generics. 164 */ 165 public static final class Final extends PartialEscapeClosure<PartialEscapeBlockState.Final> { 166 167 public Final(ScheduleResult schedule, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, ConstantFieldProvider constantFieldProvider, 168 LoweringProvider loweringProvider) { 169 super(schedule, metaAccess, constantReflection, constantFieldProvider, loweringProvider); 170 } 171 172 @Override 173 protected PartialEscapeBlockState.Final getInitialState() { 174 return new PartialEscapeBlockState.Final(tool.getOptions(), tool.getDebug()); 175 } 176 177 @Override 178 protected PartialEscapeBlockState.Final cloneState(PartialEscapeBlockState.Final oldState) { 179 return new PartialEscapeBlockState.Final(oldState); 180 } 181 } 182 183 public PartialEscapeClosure(ScheduleResult schedule, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, ConstantFieldProvider constantFieldProvider) { 184 this(schedule, metaAccess, constantReflection, constantFieldProvider, null); 185 } 186 187 public PartialEscapeClosure(ScheduleResult schedule, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, ConstantFieldProvider constantFieldProvider, 188 LoweringProvider loweringProvider) { 189 super(schedule, schedule.getCFG()); 190 StructuredGraph graph = schedule.getCFG().graph; 191 this.hasVirtualInputs = graph.createNodeBitMap(); 192 this.debug = graph.getDebug(); 193 this.tool = new VirtualizerToolImpl(metaAccess, constantReflection, constantFieldProvider, this, graph.getAssumptions(), graph.getOptions(), debug, loweringProvider); 194 } 195 196 /** 197 * @return true if the node was deleted, false otherwise 198 */ 199 @Override 200 protected boolean processNode(Node node, BlockT state, GraphEffectList effects, FixedWithNextNode lastFixedNode) { 201 /* 202 * These checks make up for the fact that an earliest schedule moves CallTargetNodes upwards 203 * and thus materializes virtual objects needlessly. Also, FrameStates and ConstantNodes are 204 * scheduled, but can safely be ignored. 205 */ 206 if (node instanceof CallTargetNode || node instanceof FrameState || node instanceof ConstantNode) { 207 return false; 208 } else if (node instanceof Invoke) { 209 processNodeInternal(((Invoke) node).callTarget(), state, effects, lastFixedNode); 210 } 211 return processNodeInternal(node, state, effects, lastFixedNode); 212 } 213 214 private boolean processNodeInternal(Node node, BlockT state, GraphEffectList effects, FixedWithNextNode lastFixedNode) { 215 FixedNode nextFixedNode = lastFixedNode == null ? null : lastFixedNode.next(); 216 VirtualUtil.trace(node.getOptions(), debug, "%s", node); 217 218 if (requiresProcessing(node)) { 219 if (processVirtualizable((ValueNode) node, nextFixedNode, state, effects) == false) { 220 return false; 221 } 222 if (tool.isDeleted()) { 223 VirtualUtil.trace(node.getOptions(), debug, "deleted virtualizable allocation %s", node); 224 return true; 225 } 226 } 227 if (hasVirtualInputs.isMarked(node) && node instanceof ValueNode) { 228 if (node instanceof Virtualizable) { 229 if (processVirtualizable((ValueNode) node, nextFixedNode, state, effects) == false) { 230 return false; 231 } 232 if (tool.isDeleted()) { 233 VirtualUtil.trace(node.getOptions(), debug, "deleted virtualizable node %s", node); 234 return true; 235 } 236 } 237 processNodeInputs((ValueNode) node, nextFixedNode, state, effects); 238 } 239 240 if (hasScalarReplacedInputs(node) && node instanceof ValueNode) { 241 if (processNodeWithScalarReplacedInputs((ValueNode) node, nextFixedNode, state, effects)) { 242 return true; 243 } 244 } 245 return false; 246 } 247 248 protected boolean requiresProcessing(Node node) { 249 return node instanceof VirtualizableAllocation; 250 } 251 252 private boolean processVirtualizable(ValueNode node, FixedNode insertBefore, BlockT state, GraphEffectList effects) { 253 tool.reset(state, node, insertBefore, effects); 254 return virtualize(node, tool); 255 } 256 257 protected boolean virtualize(ValueNode node, VirtualizerTool vt) { 258 ((Virtualizable) node).virtualize(vt); 259 return true; // request further processing 260 } 261 262 /** 263 * This tries to canonicalize the node based on improved (replaced) inputs. 264 */ 265 @SuppressWarnings("unchecked") 266 private boolean processNodeWithScalarReplacedInputs(ValueNode node, FixedNode insertBefore, BlockT state, GraphEffectList effects) { 267 ValueNode canonicalizedValue = node; 268 if (node instanceof Canonicalizable.Unary<?>) { 269 Canonicalizable.Unary<ValueNode> canonicalizable = (Canonicalizable.Unary<ValueNode>) node; 270 ObjectState valueObj = getObjectState(state, canonicalizable.getValue()); 271 ValueNode valueAlias = valueObj != null ? valueObj.getMaterializedValue() : getScalarAlias(canonicalizable.getValue()); 272 if (valueAlias != canonicalizable.getValue()) { 273 canonicalizedValue = (ValueNode) canonicalizable.canonical(tool, valueAlias); 274 } 275 } else if (node instanceof Canonicalizable.Binary<?>) { 276 Canonicalizable.Binary<ValueNode> canonicalizable = (Canonicalizable.Binary<ValueNode>) node; 277 ObjectState xObj = getObjectState(state, canonicalizable.getX()); 278 ValueNode xAlias = xObj != null ? xObj.getMaterializedValue() : getScalarAlias(canonicalizable.getX()); 279 ObjectState yObj = getObjectState(state, canonicalizable.getY()); 280 ValueNode yAlias = yObj != null ? yObj.getMaterializedValue() : getScalarAlias(canonicalizable.getY()); 281 if (xAlias != canonicalizable.getX() || yAlias != canonicalizable.getY()) { 282 canonicalizedValue = (ValueNode) canonicalizable.canonical(tool, xAlias, yAlias); 283 } 284 } else { 285 return false; 286 } 287 if (canonicalizedValue != node && canonicalizedValue != null) { 288 if (canonicalizedValue.isAlive()) { 289 ValueNode alias = getAliasAndResolve(state, canonicalizedValue); 290 if (alias instanceof VirtualObjectNode) { 291 addVirtualAlias((VirtualObjectNode) alias, node); 292 effects.deleteNode(node); 293 } else { 294 effects.replaceAtUsages(node, alias, insertBefore); 295 addScalarAlias(node, alias); 296 } 297 } else { 298 if (!prepareCanonicalNode(canonicalizedValue, state, effects)) { 299 VirtualUtil.trace(node.getOptions(), debug, "replacement via canonicalization too complex: %s -> %s", node, canonicalizedValue); 300 return false; 301 } 302 if (canonicalizedValue instanceof ControlSinkNode) { 303 effects.replaceWithSink((FixedWithNextNode) node, (ControlSinkNode) canonicalizedValue); 304 state.markAsDead(); 305 } else { 306 effects.replaceAtUsages(node, canonicalizedValue, insertBefore); 307 addScalarAlias(node, canonicalizedValue); 308 } 309 } 310 VirtualUtil.trace(node.getOptions(), debug, "replaced via canonicalization: %s -> %s", node, canonicalizedValue); 311 return true; 312 } 313 return false; 314 } 315 316 /** 317 * Nodes created during canonicalizations need to be scanned for values that were replaced. 318 */ 319 private boolean prepareCanonicalNode(ValueNode node, BlockT state, GraphEffectList effects) { 320 assert !node.isAlive(); 321 for (Position pos : node.inputPositions()) { 322 Node input = pos.get(node); 323 if (input instanceof ValueNode) { 324 if (input.isAlive()) { 325 if (!(input instanceof VirtualObjectNode)) { 326 ObjectState obj = getObjectState(state, (ValueNode) input); 327 if (obj != null) { 328 if (obj.isVirtual()) { 329 return false; 330 } else { 331 pos.initialize(node, obj.getMaterializedValue()); 332 } 333 } else { 334 pos.initialize(node, getScalarAlias((ValueNode) input)); 335 } 336 } 337 } else { 338 if (!prepareCanonicalNode((ValueNode) input, state, effects)) { 339 return false; 340 } 341 } 342 } 343 } 344 return true; 345 } 346 347 /** 348 * This replaces all inputs that point to virtual or materialized values with the actual value, 349 * materializing if necessary. Also takes care of frame states, adding the necessary 350 * {@link VirtualObjectState}. 351 */ 352 private void processNodeInputs(ValueNode node, FixedNode insertBefore, BlockT state, GraphEffectList effects) { 353 VirtualUtil.trace(node.getOptions(), debug, "processing nodewithstate: %s", node); 354 for (Node input : node.inputs()) { 355 if (input instanceof ValueNode) { 356 ValueNode alias = getAlias((ValueNode) input); 357 if (alias instanceof VirtualObjectNode) { 358 int id = ((VirtualObjectNode) alias).getObjectId(); 359 ensureMaterialized(state, id, insertBefore, effects, COUNTER_MATERIALIZATIONS_UNHANDLED); 360 effects.replaceFirstInput(node, input, state.getObjectState(id).getMaterializedValue()); 361 VirtualUtil.trace(node.getOptions(), debug, "replacing input %s at %s", input, node); 362 } 363 } 364 } 365 if (node instanceof NodeWithState) { 366 processNodeWithState((NodeWithState) node, state, effects); 367 } 368 } 369 370 private void processNodeWithState(NodeWithState nodeWithState, BlockT state, GraphEffectList effects) { 371 for (FrameState fs : nodeWithState.states()) { 372 FrameState frameState = getUniqueFramestate(nodeWithState, fs); 373 EconomicSet<VirtualObjectNode> virtual = EconomicSet.create(Equivalence.IDENTITY_WITH_SYSTEM_HASHCODE); 374 frameState.applyToNonVirtual(new CollectVirtualObjectsClosure(virtual, effects, state)); 375 collectLockedVirtualObjects(state, virtual); 376 collectReferencedVirtualObjects(state, virtual); 377 addVirtualMappings(frameState, virtual, state, effects); 378 } 379 } 380 381 private static FrameState getUniqueFramestate(NodeWithState nodeWithState, FrameState frameState) { 382 if (frameState.hasMoreThanOneUsage()) { 383 // Can happen for example from inlined snippets with multiple state split nodes. 384 FrameState copy = (FrameState) frameState.copyWithInputs(); 385 nodeWithState.asNode().replaceFirstInput(frameState, copy); 386 return copy; 387 } 388 return frameState; 389 } 390 391 private void addVirtualMappings(FrameState frameState, EconomicSet<VirtualObjectNode> virtual, BlockT state, GraphEffectList effects) { 392 for (VirtualObjectNode obj : virtual) { 393 effects.addVirtualMapping(frameState, state.getObjectState(obj).createEscapeObjectState(debug, obj)); 394 } 395 } 396 397 private void collectReferencedVirtualObjects(BlockT state, EconomicSet<VirtualObjectNode> virtual) { 398 Iterator<VirtualObjectNode> iterator = virtual.iterator(); 399 while (iterator.hasNext()) { 400 VirtualObjectNode object = iterator.next(); 401 int id = object.getObjectId(); 402 if (id != -1) { 403 ObjectState objState = state.getObjectStateOptional(id); 404 if (objState != null && objState.isVirtual()) { 405 for (ValueNode entry : objState.getEntries()) { 406 if (entry instanceof VirtualObjectNode) { 407 VirtualObjectNode entryVirtual = (VirtualObjectNode) entry; 408 if (!virtual.contains(entryVirtual)) { 409 virtual.add(entryVirtual); 410 } 411 } 412 } 413 } 414 } 415 } 416 } 417 418 private void collectLockedVirtualObjects(BlockT state, EconomicSet<VirtualObjectNode> virtual) { 419 for (int i = 0; i < state.getStateCount(); i++) { 420 ObjectState objState = state.getObjectStateOptional(i); 421 if (objState != null && objState.isVirtual() && objState.hasLocks()) { 422 virtual.add(virtualObjects.get(i)); 423 } 424 } 425 } 426 427 /** 428 * @return true if materialization happened, false if not. 429 */ 430 protected boolean ensureMaterialized(PartialEscapeBlockState<?> state, int object, FixedNode materializeBefore, GraphEffectList effects, CounterKey counter) { 431 if (state.getObjectState(object).isVirtual()) { 432 counter.increment(debug); 433 VirtualObjectNode virtual = virtualObjects.get(object); 434 state.materializeBefore(materializeBefore, virtual, effects); 435 assert !updateStatesForMaterialized(state, virtual, state.getObjectState(object).getMaterializedValue()) : "method must already have been called before"; 436 return true; 437 } else { 438 return false; 439 } 440 } 441 442 public static boolean updateStatesForMaterialized(PartialEscapeBlockState<?> state, VirtualObjectNode virtual, ValueNode materializedValue) { 443 // update all existing states with the newly materialized object 444 boolean change = false; 445 for (int i = 0; i < state.getStateCount(); i++) { 446 ObjectState objState = state.getObjectStateOptional(i); 447 if (objState != null && objState.isVirtual()) { 448 ValueNode[] entries = objState.getEntries(); 449 for (int i2 = 0; i2 < entries.length; i2++) { 450 if (entries[i2] == virtual) { 451 state.setEntry(i, i2, materializedValue); 452 change = true; 453 } 454 } 455 } 456 } 457 return change; 458 } 459 460 @Override 461 protected BlockT stripKilledLoopLocations(Loop<Block> loop, BlockT originalInitialState) { 462 BlockT initialState = super.stripKilledLoopLocations(loop, originalInitialState); 463 if (loop.getDepth() > GraalOptions.EscapeAnalysisLoopCutoff.getValue(cfg.graph.getOptions())) { 464 /* 465 * After we've reached the maximum loop nesting, we'll simply materialize everything we 466 * can to make sure that the loops only need to be iterated one time. Care is taken here 467 * to not materialize virtual objects that have the "ensureVirtualized" flag set. 468 */ 469 LoopBeginNode loopBegin = (LoopBeginNode) loop.getHeader().getBeginNode(); 470 AbstractEndNode end = loopBegin.forwardEnd(); 471 Block loopPredecessor = loop.getHeader().getFirstPredecessor(); 472 assert loopPredecessor.getEndNode() == end; 473 int length = initialState.getStateCount(); 474 475 boolean change; 476 BitSet ensureVirtualized = new BitSet(length); 477 for (int i = 0; i < length; i++) { 478 ObjectState state = initialState.getObjectStateOptional(i); 479 if (state != null && state.isVirtual() && state.getEnsureVirtualized()) { 480 ensureVirtualized.set(i); 481 } 482 } 483 do { 484 // propagate "ensureVirtualized" flag 485 change = false; 486 for (int i = 0; i < length; i++) { 487 if (!ensureVirtualized.get(i)) { 488 ObjectState state = initialState.getObjectStateOptional(i); 489 if (state != null && state.isVirtual()) { 490 for (ValueNode entry : state.getEntries()) { 491 if (entry instanceof VirtualObjectNode) { 492 if (ensureVirtualized.get(((VirtualObjectNode) entry).getObjectId())) { 493 change = true; 494 ensureVirtualized.set(i); 495 break; 496 } 497 } 498 } 499 } 500 } 501 } 502 } while (change); 503 504 for (int i = 0; i < length; i++) { 505 ObjectState state = initialState.getObjectStateOptional(i); 506 if (state != null && state.isVirtual() && !ensureVirtualized.get(i)) { 507 initialState.materializeBefore(end, virtualObjects.get(i), blockEffects.get(loopPredecessor)); 508 } 509 } 510 } 511 return initialState; 512 } 513 514 @Override 515 protected void processInitialLoopState(Loop<Block> loop, BlockT initialState) { 516 for (PhiNode phi : ((LoopBeginNode) loop.getHeader().getBeginNode()).phis()) { 517 if (phi.valueAt(0) != null) { 518 ValueNode alias = getAliasAndResolve(initialState, phi.valueAt(0)); 519 if (alias instanceof VirtualObjectNode) { 520 VirtualObjectNode virtual = (VirtualObjectNode) alias; 521 addVirtualAlias(virtual, phi); 522 } else { 523 aliases.set(phi, null); 524 } 525 } 526 } 527 } 528 529 @Override 530 protected void processLoopExit(LoopExitNode exitNode, BlockT initialState, BlockT exitState, GraphEffectList effects) { 531 if (exitNode.graph().hasValueProxies()) { 532 EconomicMap<Integer, ProxyNode> proxies = EconomicMap.create(Equivalence.DEFAULT); 533 for (ProxyNode proxy : exitNode.proxies()) { 534 ValueNode alias = getAlias(proxy.value()); 535 if (alias instanceof VirtualObjectNode) { 536 VirtualObjectNode virtual = (VirtualObjectNode) alias; 537 proxies.put(virtual.getObjectId(), proxy); 538 } 539 } 540 for (int i = 0; i < exitState.getStateCount(); i++) { 541 ObjectState exitObjState = exitState.getObjectStateOptional(i); 542 if (exitObjState != null) { 543 ObjectState initialObjState = initialState.getObjectStateOptional(i); 544 545 if (exitObjState.isVirtual()) { 546 processVirtualAtLoopExit(exitNode, effects, i, exitObjState, initialObjState, exitState); 547 } else { 548 processMaterializedAtLoopExit(exitNode, effects, proxies, i, exitObjState, initialObjState, exitState); 549 } 550 } 551 } 552 } 553 } 554 555 private static void processMaterializedAtLoopExit(LoopExitNode exitNode, GraphEffectList effects, EconomicMap<Integer, ProxyNode> proxies, int object, ObjectState exitObjState, 556 ObjectState initialObjState, PartialEscapeBlockState<?> exitState) { 557 if (initialObjState == null || initialObjState.isVirtual()) { 558 ProxyNode proxy = proxies.get(object); 559 if (proxy == null) { 560 proxy = new ValueProxyNode(exitObjState.getMaterializedValue(), exitNode); 561 effects.addFloatingNode(proxy, "proxy"); 562 } else { 563 effects.replaceFirstInput(proxy, proxy.value(), exitObjState.getMaterializedValue()); 564 // nothing to do - will be handled in processNode 565 } 566 exitState.updateMaterializedValue(object, proxy); 567 } else { 568 if (initialObjState.getMaterializedValue() != exitObjState.getMaterializedValue()) { 569 exitNode.getDebug().log("materialized value changes within loop: %s vs. %s at %s", initialObjState.getMaterializedValue(), exitObjState.getMaterializedValue(), exitNode); 570 } 571 } 572 } 573 574 private static void processVirtualAtLoopExit(LoopExitNode exitNode, GraphEffectList effects, int object, ObjectState exitObjState, ObjectState initialObjState, 575 PartialEscapeBlockState<?> exitState) { 576 for (int i = 0; i < exitObjState.getEntries().length; i++) { 577 ValueNode value = exitState.getObjectState(object).getEntry(i); 578 if (!(value instanceof VirtualObjectNode || value.isConstant())) { 579 if (exitNode.loopBegin().isPhiAtMerge(value) || initialObjState == null || !initialObjState.isVirtual() || initialObjState.getEntry(i) != value) { 580 ProxyNode proxy = new ValueProxyNode(value, exitNode); 581 exitState.setEntry(object, i, proxy); 582 effects.addFloatingNode(proxy, "virtualProxy"); 583 } 584 } 585 } 586 } 587 588 @Override 589 protected MergeProcessor createMergeProcessor(Block merge) { 590 return new MergeProcessor(merge); 591 } 592 593 protected class MergeProcessor extends EffectsClosure<BlockT>.MergeProcessor { 594 595 private EconomicMap<Object, ValuePhiNode> materializedPhis; 596 private EconomicMap<ValueNode, ValuePhiNode[]> valuePhis; 597 private EconomicMap<ValuePhiNode, VirtualObjectNode> valueObjectVirtuals; 598 private final boolean needsCaching; 599 600 public MergeProcessor(Block mergeBlock) { 601 super(mergeBlock); 602 // merge will only be called multiple times for loop headers 603 needsCaching = mergeBlock.isLoopHeader(); 604 } 605 606 protected <T> PhiNode getPhi(T virtual, Stamp stamp) { 607 if (needsCaching) { 608 return getPhiCached(virtual, stamp); 609 } else { 610 return createValuePhi(stamp); 611 } 612 } 613 614 private <T> PhiNode getPhiCached(T virtual, Stamp stamp) { 615 if (materializedPhis == null) { 616 materializedPhis = EconomicMap.create(Equivalence.DEFAULT); 617 } 618 ValuePhiNode result = materializedPhis.get(virtual); 619 if (result == null) { 620 result = createValuePhi(stamp); 621 materializedPhis.put(virtual, result); 622 } 623 return result; 624 } 625 626 private PhiNode[] getValuePhis(ValueNode key, int entryCount) { 627 if (needsCaching) { 628 return getValuePhisCached(key, entryCount); 629 } else { 630 return new ValuePhiNode[entryCount]; 631 } 632 } 633 634 private PhiNode[] getValuePhisCached(ValueNode key, int entryCount) { 635 if (valuePhis == null) { 636 valuePhis = EconomicMap.create(Equivalence.IDENTITY_WITH_SYSTEM_HASHCODE); 637 } 638 ValuePhiNode[] result = valuePhis.get(key); 639 if (result == null) { 640 result = new ValuePhiNode[entryCount]; 641 valuePhis.put(key, result); 642 } 643 assert result.length == entryCount; 644 return result; 645 } 646 647 private VirtualObjectNode getValueObjectVirtual(ValuePhiNode phi, VirtualObjectNode virtual) { 648 if (needsCaching) { 649 return getValueObjectVirtualCached(phi, virtual); 650 } else { 651 VirtualObjectNode duplicate = virtual.duplicate(); 652 duplicate.setNodeSourcePosition(virtual.getNodeSourcePosition()); 653 return duplicate; 654 } 655 } 656 657 private VirtualObjectNode getValueObjectVirtualCached(ValuePhiNode phi, VirtualObjectNode virtual) { 658 if (valueObjectVirtuals == null) { 659 valueObjectVirtuals = EconomicMap.create(Equivalence.IDENTITY); 660 } 661 VirtualObjectNode result = valueObjectVirtuals.get(phi); 662 if (result == null) { 663 result = virtual.duplicate(); 664 result.setNodeSourcePosition(virtual.getNodeSourcePosition()); 665 valueObjectVirtuals.put(phi, result); 666 } 667 return result; 668 } 669 670 /** 671 * Merge all predecessor block states into one block state. This is an iterative process, 672 * because merging states can lead to materializations which make previous parts of the 673 * merging operation invalid. The merging process is executed until a stable state has been 674 * reached. This method needs to be careful to place the effects of the merging operation 675 * into the correct blocks. 676 * 677 * @param statesList the predecessor block states of the merge 678 */ 679 @Override 680 protected void merge(List<BlockT> statesList) { 681 682 PartialEscapeBlockState<?>[] states = new PartialEscapeBlockState<?>[statesList.size()]; 683 for (int i = 0; i < statesList.size(); i++) { 684 states[i] = statesList.get(i); 685 } 686 687 // calculate the set of virtual objects that exist in all predecessors 688 int[] virtualObjTemp = intersectVirtualObjects(states); 689 690 boolean materialized; 691 do { 692 materialized = false; 693 694 if (PartialEscapeBlockState.identicalObjectStates(states)) { 695 newState.adoptAddObjectStates(states[0]); 696 } else { 697 698 for (int object : virtualObjTemp) { 699 if (PartialEscapeBlockState.identicalObjectStates(states, object)) { 700 newState.addObject(object, states[0].getObjectState(object).share()); 701 continue; 702 } 703 704 // determine if all inputs are virtual or the same materialized value 705 int virtualCount = 0; 706 ObjectState startObj = states[0].getObjectState(object); 707 boolean locksMatch = true; 708 boolean ensureVirtual = true; 709 ValueNode uniqueMaterializedValue = startObj.isVirtual() ? null : startObj.getMaterializedValue(); 710 for (int i = 0; i < states.length; i++) { 711 ObjectState obj = states[i].getObjectState(object); 712 ensureVirtual &= obj.getEnsureVirtualized(); 713 if (obj.isVirtual()) { 714 virtualCount++; 715 uniqueMaterializedValue = null; 716 locksMatch &= obj.locksEqual(startObj); 717 } else if (obj.getMaterializedValue() != uniqueMaterializedValue) { 718 uniqueMaterializedValue = null; 719 } 720 } 721 722 if (virtualCount == states.length && locksMatch) { 723 materialized |= mergeObjectStates(object, null, states); 724 } else { 725 if (uniqueMaterializedValue != null) { 726 newState.addObject(object, new ObjectState(uniqueMaterializedValue, null, ensureVirtual)); 727 } else { 728 PhiNode materializedValuePhi = getPhi(object, StampFactory.forKind(JavaKind.Object)); 729 mergeEffects.addFloatingNode(materializedValuePhi, "materializedPhi"); 730 for (int i = 0; i < states.length; i++) { 731 ObjectState obj = states[i].getObjectState(object); 732 if (obj.isVirtual()) { 733 Block predecessor = getPredecessor(i); 734 if (!ensureVirtual && obj.isVirtual()) { 735 // we can materialize if not all inputs are 736 // "ensureVirtualized" 737 obj.setEnsureVirtualized(false); 738 } 739 materialized |= ensureMaterialized(states[i], object, predecessor.getEndNode(), blockEffects.get(predecessor), COUNTER_MATERIALIZATIONS_MERGE); 740 obj = states[i].getObjectState(object); 741 } 742 setPhiInput(materializedValuePhi, i, obj.getMaterializedValue()); 743 } 744 newState.addObject(object, new ObjectState(materializedValuePhi, null, false)); 745 } 746 } 747 } 748 } 749 750 for (PhiNode phi : getPhis()) { 751 aliases.set(phi, null); 752 if (hasVirtualInputs.isMarked(phi) && phi instanceof ValuePhiNode) { 753 materialized |= processPhi((ValuePhiNode) phi, states); 754 } 755 } 756 if (materialized) { 757 newState.resetObjectStates(virtualObjects.size()); 758 mergeEffects.clear(); 759 afterMergeEffects.clear(); 760 } 761 } while (materialized); 762 } 763 764 private int[] intersectVirtualObjects(PartialEscapeBlockState<?>[] states) { 765 int length = states[0].getStateCount(); 766 for (int i = 1; i < states.length; i++) { 767 length = Math.min(length, states[i].getStateCount()); 768 } 769 770 int count = 0; 771 for (int objectIndex = 0; objectIndex < length; objectIndex++) { 772 if (intersectObjectState(states, objectIndex)) { 773 count++; 774 } 775 } 776 777 int index = 0; 778 int[] resultInts = new int[count]; 779 for (int objectIndex = 0; objectIndex < length; objectIndex++) { 780 if (intersectObjectState(states, objectIndex)) { 781 resultInts[index++] = objectIndex; 782 } 783 } 784 assert index == count; 785 return resultInts; 786 } 787 788 private boolean intersectObjectState(PartialEscapeBlockState<?>[] states, int objectIndex) { 789 for (int i = 0; i < states.length; i++) { 790 PartialEscapeBlockState<?> state = states[i]; 791 if (state.getObjectStateOptional(objectIndex) == null) { 792 return false; 793 } 794 } 795 return true; 796 } 797 798 /** 799 * Try to merge multiple virtual object states into a single object state. If the incoming 800 * object states are compatible, then this method will create PhiNodes for the object's 801 * entries where needed. If they are incompatible, then all incoming virtual objects will be 802 * materialized, and a PhiNode for the materialized values will be created. Object states 803 * can be incompatible if they contain {@code long} or {@code double} values occupying two 804 * {@code int} slots in such a way that that their values cannot be merged using PhiNodes. 805 * 806 * @param states the predecessor block states of the merge 807 * @return true if materialization happened during the merge, false otherwise 808 */ 809 private boolean mergeObjectStates(int resultObject, int[] sourceObjects, PartialEscapeBlockState<?>[] states) { 810 boolean compatible = true; 811 boolean ensureVirtual = true; 812 IntUnaryOperator getObject = index -> sourceObjects == null ? resultObject : sourceObjects[index]; 813 814 VirtualObjectNode virtual = virtualObjects.get(resultObject); 815 int entryCount = virtual.entryCount(); 816 817 // determine all entries that have a two-slot value 818 JavaKind[] twoSlotKinds = null; 819 outer: for (int i = 0; i < states.length; i++) { 820 ObjectState objectState = states[i].getObjectState(getObject.applyAsInt(i)); 821 ValueNode[] entries = objectState.getEntries(); 822 int valueIndex = 0; 823 ensureVirtual &= objectState.getEnsureVirtualized(); 824 while (valueIndex < entryCount) { 825 JavaKind otherKind = entries[valueIndex].getStackKind(); 826 JavaKind entryKind = virtual.entryKind(valueIndex); 827 if (entryKind == JavaKind.Int && otherKind.needsTwoSlots()) { 828 if (twoSlotKinds == null) { 829 twoSlotKinds = new JavaKind[entryCount]; 830 } 831 if (twoSlotKinds[valueIndex] != null && twoSlotKinds[valueIndex] != otherKind) { 832 compatible = false; 833 break outer; 834 } 835 twoSlotKinds[valueIndex] = otherKind; 836 // skip the next entry 837 valueIndex++; 838 } else { 839 assert entryKind.getStackKind() == otherKind.getStackKind() || (entryKind == JavaKind.Int && otherKind == JavaKind.Illegal) || 840 entryKind.getBitCount() >= otherKind.getBitCount() : entryKind + " vs " + otherKind; 841 } 842 valueIndex++; 843 } 844 } 845 if (compatible && twoSlotKinds != null) { 846 // if there are two-slot values then make sure the incoming states can be merged 847 outer: for (int valueIndex = 0; valueIndex < entryCount; valueIndex++) { 848 if (twoSlotKinds[valueIndex] != null) { 849 assert valueIndex < virtual.entryCount() - 1 && virtual.entryKind(valueIndex) == JavaKind.Int && virtual.entryKind(valueIndex + 1) == JavaKind.Int; 850 for (int i = 0; i < states.length; i++) { 851 int object = getObject.applyAsInt(i); 852 ObjectState objectState = states[i].getObjectState(object); 853 ValueNode value = objectState.getEntry(valueIndex); 854 JavaKind valueKind = value.getStackKind(); 855 if (valueKind != twoSlotKinds[valueIndex]) { 856 ValueNode nextValue = objectState.getEntry(valueIndex + 1); 857 if (value.isConstant() && value.asConstant().equals(JavaConstant.INT_0) && nextValue.isConstant() && nextValue.asConstant().equals(JavaConstant.INT_0)) { 858 // rewrite to a zero constant of the larger kind 859 debug.log("Rewriting entry %s to constant of larger size", valueIndex); 860 states[i].setEntry(object, valueIndex, ConstantNode.defaultForKind(twoSlotKinds[valueIndex], graph())); 861 states[i].setEntry(object, valueIndex + 1, ConstantNode.forConstant(JavaConstant.forIllegal(), tool.getMetaAccessProvider(), graph())); 862 } else { 863 compatible = false; 864 break outer; 865 } 866 } 867 } 868 } 869 } 870 } 871 872 if (compatible) { 873 // virtual objects are compatible: create phis for all entries that need them 874 ValueNode[] values = states[0].getObjectState(getObject.applyAsInt(0)).getEntries().clone(); 875 PhiNode[] phis = getValuePhis(virtual, virtual.entryCount()); 876 int valueIndex = 0; 877 while (valueIndex < values.length) { 878 for (int i = 1; i < states.length; i++) { 879 if (phis[valueIndex] == null) { 880 ValueNode field = states[i].getObjectState(getObject.applyAsInt(i)).getEntry(valueIndex); 881 if (values[valueIndex] != field) { 882 phis[valueIndex] = createValuePhi(values[valueIndex].stamp(NodeView.DEFAULT).unrestricted()); 883 } 884 } 885 } 886 if (phis[valueIndex] != null && !phis[valueIndex].stamp(NodeView.DEFAULT).isCompatible(values[valueIndex].stamp(NodeView.DEFAULT))) { 887 phis[valueIndex] = createValuePhi(values[valueIndex].stamp(NodeView.DEFAULT).unrestricted()); 888 } 889 if (twoSlotKinds != null && twoSlotKinds[valueIndex] != null) { 890 // skip an entry after a long/double value that occupies two int slots 891 valueIndex++; 892 phis[valueIndex] = null; 893 values[valueIndex] = ConstantNode.forConstant(JavaConstant.forIllegal(), tool.getMetaAccessProvider(), graph()); 894 } 895 valueIndex++; 896 } 897 898 boolean materialized = false; 899 for (int i = 0; i < values.length; i++) { 900 PhiNode phi = phis[i]; 901 if (phi != null) { 902 mergeEffects.addFloatingNode(phi, "virtualMergePhi"); 903 if (virtual.entryKind(i) == JavaKind.Object) { 904 materialized |= mergeObjectEntry(getObject, states, phi, i); 905 } else { 906 for (int i2 = 0; i2 < states.length; i2++) { 907 ObjectState state = states[i2].getObjectState(getObject.applyAsInt(i2)); 908 if (!state.isVirtual()) { 909 break; 910 } 911 setPhiInput(phi, i2, state.getEntry(i)); 912 } 913 } 914 values[i] = phi; 915 } 916 } 917 newState.addObject(resultObject, new ObjectState(values, states[0].getObjectState(getObject.applyAsInt(0)).getLocks(), ensureVirtual)); 918 return materialized; 919 } else { 920 // not compatible: materialize in all predecessors 921 PhiNode materializedValuePhi = getPhi(resultObject, StampFactory.forKind(JavaKind.Object)); 922 for (int i = 0; i < states.length; i++) { 923 Block predecessor = getPredecessor(i); 924 if (!ensureVirtual && states[i].getObjectState(getObject.applyAsInt(i)).isVirtual()) { 925 // we can materialize if not all inputs are "ensureVirtualized" 926 states[i].getObjectState(getObject.applyAsInt(i)).setEnsureVirtualized(false); 927 } 928 ensureMaterialized(states[i], getObject.applyAsInt(i), predecessor.getEndNode(), blockEffects.get(predecessor), COUNTER_MATERIALIZATIONS_MERGE); 929 setPhiInput(materializedValuePhi, i, states[i].getObjectState(getObject.applyAsInt(i)).getMaterializedValue()); 930 } 931 newState.addObject(resultObject, new ObjectState(materializedValuePhi, null, ensureVirtual)); 932 return true; 933 } 934 } 935 936 /** 937 * Fill the inputs of the PhiNode corresponding to one {@link JavaKind#Object} entry in the 938 * virtual object. 939 * 940 * @return true if materialization happened during the merge, false otherwise 941 */ 942 private boolean mergeObjectEntry(IntUnaryOperator objectIdFunc, PartialEscapeBlockState<?>[] states, PhiNode phi, int entryIndex) { 943 boolean materialized = false; 944 for (int i = 0; i < states.length; i++) { 945 int object = objectIdFunc.applyAsInt(i); 946 ObjectState objectState = states[i].getObjectState(object); 947 if (!objectState.isVirtual()) { 948 break; 949 } 950 ValueNode entry = objectState.getEntry(entryIndex); 951 if (entry instanceof VirtualObjectNode) { 952 VirtualObjectNode entryVirtual = (VirtualObjectNode) entry; 953 Block predecessor = getPredecessor(i); 954 materialized |= ensureMaterialized(states[i], entryVirtual.getObjectId(), predecessor.getEndNode(), blockEffects.get(predecessor), COUNTER_MATERIALIZATIONS_MERGE); 955 objectState = states[i].getObjectState(object); 956 if (objectState.isVirtual()) { 957 states[i].setEntry(object, entryIndex, entry = states[i].getObjectState(entryVirtual.getObjectId()).getMaterializedValue()); 958 } 959 } 960 setPhiInput(phi, i, entry); 961 } 962 return materialized; 963 } 964 965 /** 966 * Examine a PhiNode and try to replace it with merging of virtual objects if all its inputs 967 * refer to virtual object states. In order for the merging to happen, all incoming object 968 * states need to be compatible and without object identity (meaning that their object 969 * identity if not used later on). 970 * 971 * @param phi the PhiNode that should be processed 972 * @param states the predecessor block states of the merge 973 * @return true if materialization happened during the merge, false otherwise 974 */ 975 private boolean processPhi(ValuePhiNode phi, PartialEscapeBlockState<?>[] states) { 976 977 // determine how many inputs are virtual and if they're all the same virtual object 978 int virtualInputs = 0; 979 boolean uniqueVirtualObject = true; 980 boolean ensureVirtual = true; 981 VirtualObjectNode[] virtualObjs = new VirtualObjectNode[states.length]; 982 for (int i = 0; i < states.length; i++) { 983 ValueNode alias = getAlias(getPhiValueAt(phi, i)); 984 if (alias instanceof VirtualObjectNode) { 985 VirtualObjectNode virtual = (VirtualObjectNode) alias; 986 virtualObjs[i] = virtual; 987 ObjectState objectState = states[i].getObjectStateOptional(virtual); 988 if (objectState == null) { 989 assert getPhiValueAt(phi, i) instanceof PhiNode : "this should only happen for phi nodes"; 990 return false; 991 } 992 if (objectState.isVirtual()) { 993 if (virtualObjs[0] != alias) { 994 uniqueVirtualObject = false; 995 } 996 ensureVirtual &= objectState.getEnsureVirtualized(); 997 virtualInputs++; 998 } 999 } 1000 } 1001 if (virtualInputs == states.length) { 1002 if (uniqueVirtualObject) { 1003 // all inputs refer to the same object: just make the phi node an alias 1004 addVirtualAlias(virtualObjs[0], phi); 1005 mergeEffects.deleteNode(phi); 1006 return false; 1007 } else { 1008 // all inputs are virtual: check if they're compatible and without identity 1009 boolean compatible = true; 1010 VirtualObjectNode firstVirtual = virtualObjs[0]; 1011 for (int i = 0; i < states.length; i++) { 1012 VirtualObjectNode virtual = virtualObjs[i]; 1013 1014 if (!firstVirtual.type().equals(virtual.type()) || firstVirtual.entryCount() != virtual.entryCount()) { 1015 compatible = false; 1016 break; 1017 } 1018 if (!states[0].getObjectState(firstVirtual).locksEqual(states[i].getObjectState(virtual))) { 1019 compatible = false; 1020 break; 1021 } 1022 } 1023 if (compatible) { 1024 for (int i = 0; i < states.length; i++) { 1025 VirtualObjectNode virtual = virtualObjs[i]; 1026 /* 1027 * check whether we trivially see that this is the only reference to 1028 * this allocation 1029 */ 1030 if (virtual.hasIdentity() && !isSingleUsageAllocation(getPhiValueAt(phi, i), virtualObjs, states[i])) { 1031 compatible = false; 1032 } 1033 } 1034 } 1035 if (compatible) { 1036 VirtualObjectNode virtual = getValueObjectVirtual(phi, virtualObjs[0]); 1037 mergeEffects.addFloatingNode(virtual, "valueObjectNode"); 1038 mergeEffects.deleteNode(phi); 1039 if (virtual.getObjectId() == -1) { 1040 int id = virtualObjects.size(); 1041 virtualObjects.add(virtual); 1042 virtual.setObjectId(id); 1043 } 1044 1045 int[] virtualObjectIds = new int[states.length]; 1046 for (int i = 0; i < states.length; i++) { 1047 virtualObjectIds[i] = virtualObjs[i].getObjectId(); 1048 } 1049 boolean materialized = mergeObjectStates(virtual.getObjectId(), virtualObjectIds, states); 1050 addVirtualAlias(virtual, virtual); 1051 addVirtualAlias(virtual, phi); 1052 return materialized; 1053 } 1054 } 1055 } 1056 1057 // otherwise: materialize all phi inputs 1058 boolean materialized = false; 1059 if (virtualInputs > 0) { 1060 for (int i = 0; i < states.length; i++) { 1061 VirtualObjectNode virtual = virtualObjs[i]; 1062 if (virtual != null) { 1063 Block predecessor = getPredecessor(i); 1064 if (!ensureVirtual && states[i].getObjectState(virtual).isVirtual()) { 1065 // we can materialize if not all inputs are "ensureVirtualized" 1066 states[i].getObjectState(virtual).setEnsureVirtualized(false); 1067 } 1068 materialized |= ensureMaterialized(states[i], virtual.getObjectId(), predecessor.getEndNode(), blockEffects.get(predecessor), COUNTER_MATERIALIZATIONS_PHI); 1069 } 1070 } 1071 } 1072 for (int i = 0; i < states.length; i++) { 1073 VirtualObjectNode virtual = virtualObjs[i]; 1074 if (virtual != null) { 1075 setPhiInput(phi, i, getAliasAndResolve(states[i], virtual)); 1076 } 1077 } 1078 return materialized; 1079 } 1080 1081 private boolean isSingleUsageAllocation(ValueNode value, VirtualObjectNode[] virtualObjs, PartialEscapeBlockState<?> state) { 1082 /* 1083 * If the phi input is an allocation, we know that it is a "fresh" value, i.e., that 1084 * this is a value that will only appear through this source, and cannot appear anywhere 1085 * else. If the phi is also the only usage of this input, we know that no other place 1086 * can check object identity against it, so it is safe to lose the object identity here. 1087 */ 1088 if (!(value instanceof AllocatedObjectNode && value.hasExactlyOneUsage())) { 1089 return false; 1090 } 1091 1092 /* 1093 * Check that the state only references the one virtual object from the Phi. 1094 */ 1095 VirtualObjectNode singleVirtual = null; 1096 for (int v = 0; v < virtualObjs.length; v++) { 1097 if (state.contains(virtualObjs[v])) { 1098 if (singleVirtual == null) { 1099 singleVirtual = virtualObjs[v]; 1100 } else if (singleVirtual != virtualObjs[v]) { 1101 /* 1102 * More than one virtual object is visible in the object state. 1103 */ 1104 return false; 1105 } 1106 } 1107 } 1108 return true; 1109 } 1110 } 1111 1112 public ObjectState getObjectState(PartialEscapeBlockState<?> state, ValueNode value) { 1113 if (value == null) { 1114 return null; 1115 } 1116 if (value.isAlive() && !aliases.isNew(value)) { 1117 ValueNode object = aliases.get(value); 1118 return object instanceof VirtualObjectNode ? state.getObjectStateOptional((VirtualObjectNode) object) : null; 1119 } else { 1120 if (value instanceof VirtualObjectNode) { 1121 return state.getObjectStateOptional((VirtualObjectNode) value); 1122 } 1123 return null; 1124 } 1125 } 1126 1127 public ValueNode getAlias(ValueNode value) { 1128 if (value != null && !(value instanceof VirtualObjectNode)) { 1129 if (value.isAlive() && !aliases.isNew(value)) { 1130 ValueNode result = aliases.get(value); 1131 if (result != null) { 1132 return result; 1133 } 1134 } 1135 } 1136 return value; 1137 } 1138 1139 public ValueNode getAliasAndResolve(PartialEscapeBlockState<?> state, ValueNode value) { 1140 ValueNode result = getAlias(value); 1141 if (result instanceof VirtualObjectNode) { 1142 int id = ((VirtualObjectNode) result).getObjectId(); 1143 if (id != -1 && !state.getObjectState(id).isVirtual()) { 1144 result = state.getObjectState(id).getMaterializedValue(); 1145 } 1146 } 1147 return result; 1148 } 1149 1150 void addVirtualAlias(VirtualObjectNode virtual, ValueNode node) { 1151 if (node.isAlive()) { 1152 aliases.set(node, virtual); 1153 for (Node usage : node.usages()) { 1154 markVirtualUsages(usage); 1155 } 1156 } 1157 } 1158 1159 private void markVirtualUsages(Node node) { 1160 if (!hasVirtualInputs.isNew(node) && !hasVirtualInputs.isMarked(node)) { 1161 hasVirtualInputs.mark(node); 1162 if (node instanceof VirtualState) { 1163 for (Node usage : node.usages()) { 1164 markVirtualUsages(usage); 1165 } 1166 } 1167 } 1168 } 1169 }