001/* 002 * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.virtual.phases.ea; 024 025import java.util.*; 026import java.util.function.*; 027 028import com.oracle.graal.debug.*; 029import jdk.internal.jvmci.meta.*; 030 031import com.oracle.graal.compiler.common.*; 032import com.oracle.graal.compiler.common.cfg.*; 033import com.oracle.graal.compiler.common.type.*; 034import com.oracle.graal.compiler.common.util.*; 035import com.oracle.graal.graph.*; 036import com.oracle.graal.graph.spi.*; 037import com.oracle.graal.nodes.*; 038import com.oracle.graal.nodes.VirtualState.NodeClosure; 039import com.oracle.graal.nodes.cfg.*; 040import com.oracle.graal.nodes.spi.*; 041import com.oracle.graal.nodes.virtual.*; 042import com.oracle.graal.phases.schedule.*; 043 044public abstract class PartialEscapeClosure<BlockT extends PartialEscapeBlockState<BlockT>> extends EffectsClosure<BlockT> { 045 046 public static final DebugMetric METRIC_MATERIALIZATIONS = Debug.metric("Materializations"); 047 public static final DebugMetric METRIC_MATERIALIZATIONS_PHI = Debug.metric("MaterializationsPhi"); 048 public static final DebugMetric METRIC_MATERIALIZATIONS_MERGE = Debug.metric("MaterializationsMerge"); 049 public static final DebugMetric METRIC_MATERIALIZATIONS_UNHANDLED = Debug.metric("MaterializationsUnhandled"); 050 public static final DebugMetric METRIC_MATERIALIZATIONS_LOOP_REITERATION = Debug.metric("MaterializationsLoopReiteration"); 051 public static final DebugMetric METRIC_MATERIALIZATIONS_LOOP_END = Debug.metric("MaterializationsLoopEnd"); 052 public static final DebugMetric METRIC_ALLOCATION_REMOVED = Debug.metric("AllocationsRemoved"); 053 054 public static final DebugMetric METRIC_MEMORYCHECKPOINT = Debug.metric("MemoryCheckpoint"); 055 056 private final NodeBitMap hasVirtualInputs; 057 private final VirtualizerToolImpl tool; 058 059 public final ArrayList<VirtualObjectNode> virtualObjects = new ArrayList<>(); 060 061 private final class CollectVirtualObjectsClosure extends NodeClosure<ValueNode> { 062 private final Set<VirtualObjectNode> virtual; 063 private final GraphEffectList effects; 064 private final BlockT state; 065 066 private CollectVirtualObjectsClosure(Set<VirtualObjectNode> virtual, GraphEffectList effects, BlockT state) { 067 this.virtual = virtual; 068 this.effects = effects; 069 this.state = state; 070 } 071 072 @Override 073 public void apply(Node usage, ValueNode value) { 074 if (value instanceof VirtualObjectNode) { 075 VirtualObjectNode object = (VirtualObjectNode) value; 076 if (object.getObjectId() != -1 && state.getObjectStateOptional(object) != null) { 077 virtual.add(object); 078 } 079 } else { 080 ValueNode alias = getAlias(value); 081 if (alias instanceof VirtualObjectNode) { 082 VirtualObjectNode object = (VirtualObjectNode) alias; 083 virtual.add(object); 084 effects.replaceFirstInput(usage, value, object); 085 } 086 } 087 } 088 } 089 090 /** 091 * Final subclass of PartialEscapeClosure, for performance and to make everything behave nicely 092 * with generics. 093 */ 094 public static final class Final extends PartialEscapeClosure<PartialEscapeBlockState.Final> { 095 096 public Final(SchedulePhase schedule, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection) { 097 super(schedule, metaAccess, constantReflection); 098 } 099 100 @Override 101 protected PartialEscapeBlockState.Final getInitialState() { 102 return new PartialEscapeBlockState.Final(); 103 } 104 105 @Override 106 protected PartialEscapeBlockState.Final cloneState(PartialEscapeBlockState.Final oldState) { 107 return new PartialEscapeBlockState.Final(oldState); 108 } 109 } 110 111 public PartialEscapeClosure(SchedulePhase schedule, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection) { 112 super(schedule, schedule.getCFG()); 113 this.hasVirtualInputs = schedule.getCFG().graph.createNodeBitMap(); 114 this.tool = new VirtualizerToolImpl(metaAccess, constantReflection, this); 115 } 116 117 /** 118 * @return true if the node was deleted, false otherwise 119 */ 120 @Override 121 protected boolean processNode(Node node, BlockT state, GraphEffectList effects, FixedWithNextNode lastFixedNode) { 122 /* 123 * These checks make up for the fact that an earliest schedule moves CallTargetNodes upwards 124 * and thus materializes virtual objects needlessly. Also, FrameStates and ConstantNodes are 125 * scheduled, but can safely be ignored. 126 */ 127 if (node instanceof CallTargetNode || node instanceof FrameState || node instanceof ConstantNode) { 128 return false; 129 } else if (node instanceof Invoke) { 130 processNodeInternal(((Invoke) node).callTarget(), state, effects, lastFixedNode); 131 } 132 return processNodeInternal(node, state, effects, lastFixedNode); 133 } 134 135 private boolean processNodeInternal(Node node, BlockT state, GraphEffectList effects, FixedWithNextNode lastFixedNode) { 136 FixedNode nextFixedNode = lastFixedNode == null ? null : lastFixedNode.next(); 137 VirtualUtil.trace("%s", node); 138 139 if (requiresProcessing(node)) { 140 if (processVirtualizable((ValueNode) node, nextFixedNode, state, effects) == false) { 141 return false; 142 } 143 if (tool.isDeleted()) { 144 VirtualUtil.trace("deleted virtualizable allocation %s", node); 145 return true; 146 } 147 } 148 if (hasVirtualInputs.isMarked(node) && node instanceof ValueNode) { 149 if (node instanceof Virtualizable) { 150 if (processVirtualizable((ValueNode) node, nextFixedNode, state, effects) == false) { 151 return false; 152 } 153 if (tool.isDeleted()) { 154 VirtualUtil.trace("deleted virtualizable node %s", node); 155 return true; 156 } 157 } 158 processNodeInputs((ValueNode) node, nextFixedNode, state, effects); 159 } 160 161 if (hasScalarReplacedInputs(node) && node instanceof ValueNode) { 162 if (processNodeWithScalarReplacedInputs((ValueNode) node, nextFixedNode, state, effects)) { 163 return true; 164 } 165 } 166 return false; 167 } 168 169 protected boolean requiresProcessing(Node node) { 170 return node instanceof VirtualizableAllocation; 171 } 172 173 private boolean processNodeWithScalarReplacedInputs(ValueNode node, FixedNode insertBefore, BlockT state, GraphEffectList effects) { 174 ValueNode canonicalizedValue = node; 175 if (node instanceof Canonicalizable.Unary<?>) { 176 @SuppressWarnings("unchecked") 177 Canonicalizable.Unary<ValueNode> canonicalizable = (Canonicalizable.Unary<ValueNode>) node; 178 ObjectState valueObj = getObjectState(state, canonicalizable.getValue()); 179 ValueNode valueAlias = valueObj != null ? valueObj.getMaterializedValue() : getScalarAlias(canonicalizable.getValue()); 180 if (valueAlias != canonicalizable.getValue()) { 181 canonicalizedValue = (ValueNode) canonicalizable.canonical(tool, valueAlias); 182 } 183 } else if (node instanceof Canonicalizable.Binary<?>) { 184 @SuppressWarnings("unchecked") 185 Canonicalizable.Binary<ValueNode> canonicalizable = (Canonicalizable.Binary<ValueNode>) node; 186 ObjectState xObj = getObjectState(state, canonicalizable.getX()); 187 ValueNode xAlias = xObj != null ? xObj.getMaterializedValue() : getScalarAlias(canonicalizable.getX()); 188 ObjectState yObj = getObjectState(state, canonicalizable.getY()); 189 ValueNode yAlias = yObj != null ? yObj.getMaterializedValue() : getScalarAlias(canonicalizable.getY()); 190 if (xAlias != canonicalizable.getX() || yAlias != canonicalizable.getY()) { 191 canonicalizedValue = (ValueNode) canonicalizable.canonical(tool, xAlias, yAlias); 192 } 193 } else { 194 return false; 195 } 196 if (canonicalizedValue != node && canonicalizedValue != null) { 197 if (canonicalizedValue.isAlive()) { 198 ValueNode alias = getAliasAndResolve(state, canonicalizedValue); 199 if (alias instanceof VirtualObjectNode) { 200 addAndMarkAlias((VirtualObjectNode) alias, node); 201 effects.deleteNode(node); 202 } else { 203 effects.replaceAtUsages(node, alias); 204 addScalarAlias(node, alias); 205 } 206 } else { 207 if (!prepareCanonicalNode(canonicalizedValue, state, effects)) { 208 VirtualUtil.trace("replacement via canonicalization too complex: %s -> %s", node, canonicalizedValue); 209 return false; 210 } 211 effects.ensureAdded(canonicalizedValue, insertBefore); 212 if (canonicalizedValue instanceof ControlSinkNode) { 213 effects.replaceWithSink((FixedWithNextNode) node, (ControlSinkNode) canonicalizedValue); 214 state.markAsDead(); 215 } else { 216 effects.replaceAtUsages(node, canonicalizedValue); 217 addScalarAlias(node, canonicalizedValue); 218 } 219 } 220 VirtualUtil.trace("replaced via canonicalization: %s -> %s", node, canonicalizedValue); 221 return true; 222 } 223 return false; 224 } 225 226 private boolean prepareCanonicalNode(ValueNode node, BlockT state, GraphEffectList effects) { 227 assert !node.isAlive(); 228 NodePosIterator iter = node.inputs().iterator(); 229 while (iter.hasNext()) { 230 Position pos = iter.nextPosition(); 231 Node input = pos.get(node); 232 if (input instanceof ValueNode) { 233 if (input.isAlive()) { 234 ObjectState obj = getObjectState(state, (ValueNode) input); 235 if (obj != null) { 236 if (obj.isVirtual()) { 237 return false; 238 } else { 239 pos.initialize(node, obj.getMaterializedValue()); 240 } 241 } else { 242 pos.initialize(node, getScalarAlias((ValueNode) input)); 243 } 244 } else { 245 if (!prepareCanonicalNode((ValueNode) input, state, effects)) { 246 return false; 247 } 248 } 249 } 250 } 251 return true; 252 } 253 254 private void processNodeInputs(ValueNode node, FixedNode insertBefore, BlockT state, GraphEffectList effects) { 255 VirtualUtil.trace("processing nodewithstate: %s", node); 256 for (Node input : node.inputs()) { 257 if (input instanceof ValueNode) { 258 ValueNode alias = getAlias((ValueNode) input); 259 if (alias instanceof VirtualObjectNode) { 260 int id = ((VirtualObjectNode) alias).getObjectId(); 261 ensureMaterialized(state, id, insertBefore, effects, METRIC_MATERIALIZATIONS_UNHANDLED); 262 effects.replaceFirstInput(node, input, state.getObjectState(id).getMaterializedValue()); 263 VirtualUtil.trace("replacing input %s at %s", input, node); 264 } 265 } 266 } 267 if (node instanceof NodeWithState) { 268 processNodeWithState((NodeWithState) node, state, effects); 269 } 270 } 271 272 private boolean processVirtualizable(ValueNode node, FixedNode insertBefore, BlockT state, GraphEffectList effects) { 273 tool.reset(state, node, insertBefore, effects); 274 return virtualize(node, tool); 275 } 276 277 protected boolean virtualize(ValueNode node, VirtualizerTool vt) { 278 ((Virtualizable) node).virtualize(vt); 279 return true; // request further processing 280 } 281 282 private void processNodeWithState(NodeWithState nodeWithState, BlockT state, GraphEffectList effects) { 283 for (FrameState fs : nodeWithState.states()) { 284 FrameState frameState = getUniqueFramestate(nodeWithState, fs); 285 Set<VirtualObjectNode> virtual = new ArraySet<>(); 286 frameState.applyToNonVirtual(new CollectVirtualObjectsClosure(virtual, effects, state)); 287 collectLockedVirtualObjects(state, virtual); 288 collectReferencedVirtualObjects(state, virtual); 289 addVirtualMappings(frameState, virtual, state, effects); 290 } 291 } 292 293 private static FrameState getUniqueFramestate(NodeWithState nodeWithState, FrameState frameState) { 294 if (frameState.getUsageCount() > 1) { 295 // Can happen for example from inlined snippets with multiple state split nodes. 296 FrameState copy = (FrameState) frameState.copyWithInputs(); 297 nodeWithState.asNode().replaceFirstInput(frameState, copy); 298 return copy; 299 } 300 return frameState; 301 } 302 303 private void addVirtualMappings(FrameState frameState, Set<VirtualObjectNode> virtual, BlockT state, GraphEffectList effects) { 304 for (VirtualObjectNode obj : virtual) { 305 effects.addVirtualMapping(frameState, state.getObjectState(obj).createEscapeObjectState(obj)); 306 } 307 } 308 309 private void collectReferencedVirtualObjects(BlockT state, Set<VirtualObjectNode> virtual) { 310 ArrayDeque<VirtualObjectNode> queue = new ArrayDeque<>(virtual); 311 while (!queue.isEmpty()) { 312 VirtualObjectNode object = queue.removeLast(); 313 int id = object.getObjectId(); 314 if (id != -1) { 315 ObjectState objState = state.getObjectStateOptional(id); 316 if (objState != null && objState.isVirtual()) { 317 for (ValueNode entry : objState.getEntries()) { 318 if (entry instanceof VirtualObjectNode) { 319 VirtualObjectNode entryVirtual = (VirtualObjectNode) entry; 320 if (!virtual.contains(entryVirtual)) { 321 virtual.add(entryVirtual); 322 queue.addLast(entryVirtual); 323 } 324 } 325 } 326 } 327 } 328 } 329 } 330 331 private void collectLockedVirtualObjects(BlockT state, Set<VirtualObjectNode> virtual) { 332 for (int i = 0; i < state.getStateCount(); i++) { 333 ObjectState objState = state.getObjectStateOptional(i); 334 if (objState != null && objState.isVirtual() && objState.hasLocks()) { 335 virtual.add(virtualObjects.get(i)); 336 } 337 } 338 } 339 340 /** 341 * @return true if materialization happened, false if not. 342 */ 343 private boolean ensureMaterialized(PartialEscapeBlockState<?> state, int object, FixedNode materializeBefore, GraphEffectList effects, DebugMetric metric) { 344 if (state.getObjectState(object).isVirtual()) { 345 metric.increment(); 346 VirtualObjectNode virtual = virtualObjects.get(object); 347 state.materializeBefore(materializeBefore, virtual, effects); 348 updateStatesForMaterialized(state, virtual, state.getObjectState(object).getMaterializedValue()); 349 return true; 350 } else { 351 return false; 352 } 353 } 354 355 public static void updateStatesForMaterialized(PartialEscapeBlockState<?> state, VirtualObjectNode virtual, ValueNode materializedValue) { 356 // update all existing states with the newly materialized object 357 for (int i = 0; i < state.getStateCount(); i++) { 358 ObjectState objState = state.getObjectStateOptional(i); 359 if (objState != null && objState.isVirtual()) { 360 ValueNode[] entries = objState.getEntries(); 361 for (int i2 = 0; i2 < entries.length; i2++) { 362 if (entries[i2] == virtual) { 363 state.setEntry(i, i2, materializedValue); 364 } 365 } 366 } 367 } 368 } 369 370 @Override 371 protected void processInitialLoopState(Loop<Block> loop, BlockT initialState) { 372 for (PhiNode phi : ((LoopBeginNode) loop.getHeader().getBeginNode()).phis()) { 373 if (phi.valueAt(0) != null) { 374 ValueNode alias = getAliasAndResolve(initialState, phi.valueAt(0)); 375 if (alias instanceof VirtualObjectNode) { 376 VirtualObjectNode virtual = (VirtualObjectNode) alias; 377 addAndMarkAlias(virtual, phi); 378 } 379 } 380 } 381 } 382 383 @Override 384 protected void processLoopExit(LoopExitNode exitNode, BlockT initialState, BlockT exitState, GraphEffectList effects) { 385 if (exitNode.graph().hasValueProxies()) { 386 Map<Integer, ProxyNode> proxies = new IdentityHashMap<>(); 387 for (ProxyNode proxy : exitNode.proxies()) { 388 ValueNode alias = getAlias(proxy.value()); 389 if (alias instanceof VirtualObjectNode) { 390 VirtualObjectNode virtual = (VirtualObjectNode) alias; 391 proxies.put(virtual.getObjectId(), proxy); 392 } 393 } 394 for (int i = 0; i < exitState.getStateCount(); i++) { 395 ObjectState exitObjState = exitState.getObjectStateOptional(i); 396 if (exitObjState != null) { 397 ObjectState initialObjState = initialState.getObjectStateOptional(i); 398 399 if (exitObjState.isVirtual()) { 400 processVirtualAtLoopExit(exitNode, effects, i, exitObjState, initialObjState, exitState); 401 } else { 402 processMaterializedAtLoopExit(exitNode, effects, proxies, i, exitObjState, initialObjState, exitState); 403 } 404 } 405 } 406 } 407 } 408 409 private static void processMaterializedAtLoopExit(LoopExitNode exitNode, GraphEffectList effects, Map<Integer, ProxyNode> proxies, int object, ObjectState exitObjState, 410 ObjectState initialObjState, PartialEscapeBlockState<?> exitState) { 411 if (initialObjState == null || initialObjState.isVirtual()) { 412 ProxyNode proxy = proxies.get(object); 413 if (proxy == null) { 414 proxy = new ValueProxyNode(exitObjState.getMaterializedValue(), exitNode); 415 effects.addFloatingNode(proxy, "proxy"); 416 } else { 417 effects.replaceFirstInput(proxy, proxy.value(), exitObjState.getMaterializedValue()); 418 // nothing to do - will be handled in processNode 419 } 420 exitState.updateMaterializedValue(object, proxy); 421 } else { 422 if (initialObjState.getMaterializedValue() != exitObjState.getMaterializedValue()) { 423 Debug.log("materialized value changes within loop: %s vs. %s at %s", initialObjState.getMaterializedValue(), exitObjState.getMaterializedValue(), exitNode); 424 } 425 } 426 } 427 428 private static void processVirtualAtLoopExit(LoopExitNode exitNode, GraphEffectList effects, int object, ObjectState exitObjState, ObjectState initialObjState, PartialEscapeBlockState<?> exitState) { 429 for (int i = 0; i < exitObjState.getEntries().length; i++) { 430 ValueNode value = exitState.getObjectState(object).getEntry(i); 431 if (!(value instanceof VirtualObjectNode || value.isConstant())) { 432 if (exitNode.loopBegin().isPhiAtMerge(value) || initialObjState == null || !initialObjState.isVirtual() || initialObjState.getEntry(i) != value) { 433 ProxyNode proxy = new ValueProxyNode(value, exitNode); 434 exitState.setEntry(object, i, proxy); 435 effects.addFloatingNode(proxy, "virtualProxy"); 436 } 437 } 438 } 439 } 440 441 @Override 442 protected MergeProcessor createMergeProcessor(Block merge) { 443 return new MergeProcessor(merge); 444 } 445 446 protected class MergeProcessor extends EffectsClosure<BlockT>.MergeProcessor { 447 448 private HashMap<Object, ValuePhiNode> materializedPhis; 449 private Map<ValueNode, ValuePhiNode[]> valuePhis; 450 private Map<ValuePhiNode, VirtualObjectNode> valueObjectVirtuals; 451 private final boolean needsCaching; 452 453 public MergeProcessor(Block mergeBlock) { 454 super(mergeBlock); 455 needsCaching = mergeBlock.isLoopHeader(); 456 } 457 458 protected <T> PhiNode getPhi(T virtual, Stamp stamp) { 459 if (needsCaching) { 460 return getPhiCached(virtual, stamp); 461 } else { 462 return createValuePhi(stamp); 463 } 464 } 465 466 private <T> PhiNode getPhiCached(T virtual, Stamp stamp) { 467 if (materializedPhis == null) { 468 materializedPhis = CollectionsFactory.newMap(); 469 } 470 ValuePhiNode result = materializedPhis.get(virtual); 471 if (result == null) { 472 result = createValuePhi(stamp); 473 materializedPhis.put(virtual, result); 474 } 475 return result; 476 } 477 478 private PhiNode[] getValuePhis(ValueNode key, int entryCount) { 479 if (needsCaching) { 480 return getValuePhisCached(key, entryCount); 481 } else { 482 return new ValuePhiNode[entryCount]; 483 } 484 } 485 486 private PhiNode[] getValuePhisCached(ValueNode key, int entryCount) { 487 if (valuePhis == null) { 488 valuePhis = Node.newIdentityMap(); 489 } 490 ValuePhiNode[] result = valuePhis.get(key); 491 if (result == null) { 492 result = new ValuePhiNode[entryCount]; 493 valuePhis.put(key, result); 494 } 495 assert result.length == entryCount; 496 return result; 497 } 498 499 private VirtualObjectNode getValueObjectVirtual(ValuePhiNode phi, VirtualObjectNode virtual) { 500 if (needsCaching) { 501 return getValueObjectVirtualCached(phi, virtual); 502 } else { 503 return virtual.duplicate(); 504 } 505 } 506 507 private VirtualObjectNode getValueObjectVirtualCached(ValuePhiNode phi, VirtualObjectNode virtual) { 508 if (valueObjectVirtuals == null) { 509 valueObjectVirtuals = Node.newIdentityMap(); 510 } 511 VirtualObjectNode result = valueObjectVirtuals.get(phi); 512 if (result == null) { 513 result = virtual.duplicate(); 514 valueObjectVirtuals.put(phi, result); 515 } 516 return result; 517 } 518 519 /** 520 * Merge all predecessor block states into one block state. This is an iterative process, 521 * because merging states can lead to materializations which make previous parts of the 522 * merging operation invalid. The merging process is executed until a stable state has been 523 * reached. This method needs to be careful to place the effects of the merging operation 524 * into the correct blocks. 525 * 526 * @param statesList the predecessor block states of the merge 527 */ 528 @Override 529 protected void merge(List<BlockT> statesList) { 530 super.merge(statesList); 531 532 PartialEscapeBlockState<?>[] states = new PartialEscapeBlockState<?>[statesList.size()]; 533 for (int i = 0; i < statesList.size(); i++) { 534 states[i] = statesList.get(i); 535 } 536 537 // calculate the set of virtual objects that exist in all predecessors 538 int[] virtualObjTemp = intersectVirtualObjects(states); 539 540 boolean materialized; 541 do { 542 materialized = false; 543 544 if (PartialEscapeBlockState.identicalObjectStates(states)) { 545 newState.adoptAddObjectStates(states[0]); 546 } else { 547 548 for (int object : virtualObjTemp) { 549 550 if (PartialEscapeBlockState.identicalObjectStates(states, object)) { 551 newState.addObject(object, states[0].getObjectState(object).share()); 552 continue; 553 } 554 555 // determine if all inputs are virtual or the same materialized value 556 int virtualCount = 0; 557 ObjectState startObj = states[0].getObjectState(object); 558 boolean locksMatch = true; 559 boolean ensureVirtual = true; 560 ValueNode uniqueMaterializedValue = startObj.isVirtual() ? null : startObj.getMaterializedValue(); 561 for (int i = 0; i < states.length; i++) { 562 ObjectState obj = states[i].getObjectState(object); 563 ensureVirtual &= obj.getEnsureVirtualized(); 564 if (obj.isVirtual()) { 565 virtualCount++; 566 uniqueMaterializedValue = null; 567 locksMatch &= obj.locksEqual(startObj); 568 } else if (obj.getMaterializedValue() != uniqueMaterializedValue) { 569 uniqueMaterializedValue = null; 570 } 571 } 572 573 if (virtualCount == states.length && locksMatch) { 574 materialized |= mergeObjectStates(object, null, states); 575 } else { 576 if (uniqueMaterializedValue != null) { 577 newState.addObject(object, new ObjectState(uniqueMaterializedValue, null, ensureVirtual)); 578 } else { 579 PhiNode materializedValuePhi = getPhi(object, StampFactory.forKind(Kind.Object)); 580 mergeEffects.addFloatingNode(materializedValuePhi, "materializedPhi"); 581 for (int i = 0; i < states.length; i++) { 582 ObjectState obj = states[i].getObjectState(object); 583 if (obj.isVirtual()) { 584 Block predecessor = getPredecessor(i); 585 materialized |= ensureMaterialized(states[i], object, predecessor.getEndNode(), blockEffects.get(predecessor), METRIC_MATERIALIZATIONS_MERGE); 586 obj = states[i].getObjectState(object); 587 } 588 setPhiInput(materializedValuePhi, i, obj.getMaterializedValue()); 589 } 590 newState.addObject(object, new ObjectState(materializedValuePhi, null, false)); 591 } 592 } 593 } 594 } 595 596 for (PhiNode phi : getPhis()) { 597 if (hasVirtualInputs.isMarked(phi) && phi instanceof ValuePhiNode) { 598 materialized |= processPhi((ValuePhiNode) phi, states, virtualObjTemp); 599 } 600 } 601 if (materialized) { 602 newState.resetObjectStates(virtualObjects.size()); 603 mergeEffects.clear(); 604 afterMergeEffects.clear(); 605 } 606 } while (materialized); 607 } 608 609 private int[] intersectVirtualObjects(PartialEscapeBlockState<?>[] states) { 610 int length = states[0].getStateCount(); 611 for (int i = 1; i < states.length; i++) { 612 length = Math.min(length, states[i].getStateCount()); 613 } 614 boolean[] result = new boolean[length]; 615 Arrays.fill(result, true); 616 int count = length; 617 for (int i = 0; i < states.length; i++) { 618 PartialEscapeBlockState<?> state = states[i]; 619 for (int i2 = 0; i2 < length; i2++) { 620 if (result[i2]) { 621 if (state.getObjectStateOptional(i2) == null) { 622 result[i2] = false; 623 count--; 624 } 625 } 626 } 627 } 628 int[] resultInts = new int[count]; 629 int index = 0; 630 for (int i = 0; i < length; i++) { 631 if (result[i]) { 632 resultInts[index++] = i; 633 } 634 } 635 assert index == count; 636 return resultInts; 637 } 638 639 /** 640 * Try to merge multiple virtual object states into a single object state. If the incoming 641 * object states are compatible, then this method will create PhiNodes for the object's 642 * entries where needed. If they are incompatible, then all incoming virtual objects will be 643 * materialized, and a PhiNode for the materialized values will be created. Object states 644 * can be incompatible if they contain {@code long} or {@code double} values occupying two 645 * {@code int} slots in such a way that that their values cannot be merged using PhiNodes. 646 * 647 * @param states the predecessor block states of the merge 648 * @return true if materialization happened during the merge, false otherwise 649 */ 650 private boolean mergeObjectStates(int resultObject, int[] sourceObjects, PartialEscapeBlockState<?>[] states) { 651 boolean compatible = true; 652 boolean ensureVirtual = true; 653 IntFunction<Integer> getObject = index -> sourceObjects == null ? resultObject : sourceObjects[index]; 654 655 VirtualObjectNode virtual = virtualObjects.get(resultObject); 656 int entryCount = virtual.entryCount(); 657 658 // determine all entries that have a two-slot value 659 Kind[] twoSlotKinds = null; 660 outer: for (int i = 0; i < states.length; i++) { 661 ObjectState objectState = states[i].getObjectState(getObject.apply(i)); 662 ValueNode[] entries = objectState.getEntries(); 663 int valueIndex = 0; 664 ensureVirtual &= objectState.getEnsureVirtualized(); 665 while (valueIndex < entryCount) { 666 Kind otherKind = entries[valueIndex].getKind(); 667 Kind entryKind = virtual.entryKind(valueIndex); 668 if (entryKind == Kind.Int && otherKind.needsTwoSlots()) { 669 if (twoSlotKinds == null) { 670 twoSlotKinds = new Kind[entryCount]; 671 } 672 if (twoSlotKinds[valueIndex] != null && twoSlotKinds[valueIndex] != otherKind) { 673 compatible = false; 674 break outer; 675 } 676 twoSlotKinds[valueIndex] = otherKind; 677 // skip the next entry 678 valueIndex++; 679 } else { 680 assert entryKind.getStackKind() == otherKind.getStackKind() || (entryKind == Kind.Int && otherKind == Kind.Illegal) || entryKind.getBitCount() >= otherKind.getBitCount() : entryKind + 681 " vs " + otherKind; 682 } 683 valueIndex++; 684 } 685 } 686 if (compatible && twoSlotKinds != null) { 687 // if there are two-slot values then make sure the incoming states can be merged 688 outer: for (int valueIndex = 0; valueIndex < entryCount; valueIndex++) { 689 if (twoSlotKinds[valueIndex] != null) { 690 assert valueIndex < virtual.entryCount() - 1 && virtual.entryKind(valueIndex) == Kind.Int && virtual.entryKind(valueIndex + 1) == Kind.Int; 691 for (int i = 0; i < states.length; i++) { 692 int object = getObject.apply(i); 693 ObjectState objectState = states[i].getObjectState(object); 694 ValueNode value = objectState.getEntry(valueIndex); 695 Kind valueKind = value.getKind(); 696 if (valueKind != twoSlotKinds[valueIndex]) { 697 ValueNode nextValue = objectState.getEntry(valueIndex + 1); 698 if (value.isConstant() && value.asConstant().equals(JavaConstant.INT_0) && nextValue.isConstant() && nextValue.asConstant().equals(JavaConstant.INT_0)) { 699 // rewrite to a zero constant of the larger kind 700 states[i].setEntry(object, valueIndex, ConstantNode.defaultForKind(twoSlotKinds[valueIndex], graph())); 701 states[i].setEntry(object, valueIndex + 1, ConstantNode.forConstant(JavaConstant.forIllegal(), tool.getMetaAccessProvider(), graph())); 702 } else { 703 compatible = false; 704 break outer; 705 } 706 } 707 } 708 } 709 } 710 } 711 712 if (compatible) { 713 // virtual objects are compatible: create phis for all entries that need them 714 ValueNode[] values = states[0].getObjectState(getObject.apply(0)).getEntries().clone(); 715 PhiNode[] phis = getValuePhis(virtual, virtual.entryCount()); 716 int valueIndex = 0; 717 while (valueIndex < values.length) { 718 for (int i = 1; i < states.length; i++) { 719 if (phis[valueIndex] == null) { 720 ValueNode field = states[i].getObjectState(getObject.apply(i)).getEntry(valueIndex); 721 if (values[valueIndex] != field) { 722 phis[valueIndex] = createValuePhi(values[valueIndex].stamp().unrestricted()); 723 } 724 } 725 } 726 if (phis[valueIndex] != null && !phis[valueIndex].stamp().isCompatible(values[valueIndex].stamp())) { 727 phis[valueIndex] = createValuePhi(values[valueIndex].stamp().unrestricted()); 728 } 729 if (twoSlotKinds != null && twoSlotKinds[valueIndex] != null) { 730 // skip an entry after a long/double value that occupies two int slots 731 valueIndex++; 732 phis[valueIndex] = null; 733 values[valueIndex] = ConstantNode.forConstant(JavaConstant.forIllegal(), tool.getMetaAccessProvider(), graph()); 734 } 735 valueIndex++; 736 } 737 738 boolean materialized = false; 739 for (int i = 0; i < values.length; i++) { 740 PhiNode phi = phis[i]; 741 if (phi != null) { 742 mergeEffects.addFloatingNode(phi, "virtualMergePhi"); 743 if (virtual.entryKind(i) == Kind.Object) { 744 materialized |= mergeObjectEntry(getObject, states, phi, i); 745 } else { 746 for (int i2 = 0; i2 < states.length; i2++) { 747 ObjectState state = states[i2].getObjectState(getObject.apply(i2)); 748 if (!state.isVirtual()) { 749 break; 750 } 751 setPhiInput(phi, i2, state.getEntry(i)); 752 } 753 } 754 values[i] = phi; 755 } 756 } 757 newState.addObject(resultObject, new ObjectState(values, states[0].getObjectState(getObject.apply(0)).getLocks(), ensureVirtual)); 758 return materialized; 759 } else { 760 // not compatible: materialize in all predecessors 761 PhiNode materializedValuePhi = getPhi(resultObject, StampFactory.forKind(Kind.Object)); 762 for (int i = 0; i < states.length; i++) { 763 Block predecessor = getPredecessor(i); 764 ensureMaterialized(states[i], getObject.apply(i), predecessor.getEndNode(), blockEffects.get(predecessor), METRIC_MATERIALIZATIONS_MERGE); 765 setPhiInput(materializedValuePhi, i, states[i].getObjectState(getObject.apply(i)).getMaterializedValue()); 766 } 767 newState.addObject(resultObject, new ObjectState(materializedValuePhi, null, ensureVirtual)); 768 return true; 769 } 770 } 771 772 /** 773 * Fill the inputs of the PhiNode corresponding to one {@link Kind#Object} entry in the 774 * virtual object. 775 * 776 * @return true if materialization happened during the merge, false otherwise 777 */ 778 private boolean mergeObjectEntry(IntFunction<Integer> objectIdFunc, PartialEscapeBlockState<?>[] states, PhiNode phi, int entryIndex) { 779 boolean materialized = false; 780 for (int i = 0; i < states.length; i++) { 781 int object = objectIdFunc.apply(i); 782 ObjectState objectState = states[i].getObjectState(object); 783 if (!objectState.isVirtual()) { 784 break; 785 } 786 ValueNode entry = objectState.getEntry(entryIndex); 787 if (entry instanceof VirtualObjectNode) { 788 VirtualObjectNode entryVirtual = (VirtualObjectNode) entry; 789 Block predecessor = getPredecessor(i); 790 materialized |= ensureMaterialized(states[i], entryVirtual.getObjectId(), predecessor.getEndNode(), blockEffects.get(predecessor), METRIC_MATERIALIZATIONS_MERGE); 791 objectState = states[i].getObjectState(object); 792 if (objectState.isVirtual()) { 793 states[i].setEntry(object, entryIndex, entry = states[i].getObjectState(entryVirtual.getObjectId()).getMaterializedValue()); 794 } 795 } 796 setPhiInput(phi, i, entry); 797 } 798 return materialized; 799 } 800 801 /** 802 * Examine a PhiNode and try to replace it with merging of virtual objects if all its inputs 803 * refer to virtual object states. In order for the merging to happen, all incoming object 804 * states need to be compatible and without object identity (meaning that their object 805 * identity if not used later on). 806 * 807 * @param phi the PhiNode that should be processed 808 * @param states the predecessor block states of the merge 809 * @param mergedVirtualObjects the set of virtual objects that exist in all incoming states, 810 * and therefore also exist in the merged state 811 * @return true if materialization happened during the merge, false otherwise 812 */ 813 private boolean processPhi(ValuePhiNode phi, PartialEscapeBlockState<?>[] states, int[] mergedVirtualObjects) { 814 aliases.set(phi, null); 815 816 // determine how many inputs are virtual and if they're all the same virtual object 817 int virtualInputs = 0; 818 boolean uniqueVirtualObject = true; 819 VirtualObjectNode[] virtualObjs = new VirtualObjectNode[states.length]; 820 for (int i = 0; i < states.length; i++) { 821 ValueNode alias = getAlias(getPhiValueAt(phi, i)); 822 if (alias instanceof VirtualObjectNode) { 823 VirtualObjectNode virtual = (VirtualObjectNode) alias; 824 virtualObjs[i] = virtual; 825 if (states[i].getObjectState(virtual).isVirtual()) { 826 if (virtualObjs[0] != alias) { 827 uniqueVirtualObject = false; 828 } 829 virtualInputs++; 830 } 831 } 832 } 833 if (virtualInputs == states.length) { 834 if (uniqueVirtualObject) { 835 // all inputs refer to the same object: just make the phi node an alias 836 addAndMarkAlias(virtualObjs[0], phi); 837 mergeEffects.deleteNode(phi); 838 return false; 839 } else { 840 // all inputs are virtual: check if they're compatible and without identity 841 boolean compatible = true; 842 boolean hasIdentity = false; 843 VirtualObjectNode firstVirtual = virtualObjs[0]; 844 for (int i = 0; i < states.length; i++) { 845 VirtualObjectNode virtual = virtualObjs[i]; 846 hasIdentity |= virtual.hasIdentity(); 847 boolean identitySurvives = virtual.hasIdentity() && Arrays.asList(mergedVirtualObjects).contains(virtual.getObjectId()); 848 if (identitySurvives || !firstVirtual.type().equals(virtual.type()) || firstVirtual.entryCount() != virtual.entryCount()) { 849 compatible = false; 850 break; 851 } 852 if (!states[0].getObjectState(firstVirtual).locksEqual(states[i].getObjectState(virtual))) { 853 compatible = false; 854 break; 855 } 856 } 857 if (compatible && hasIdentity) { 858 // we still need to check whether this value is referenced by any other phi 859 outer: for (PhiNode otherPhi : getPhis().filter(otherPhi -> otherPhi != phi)) { 860 for (int i = 0; i < states.length; i++) { 861 ValueNode alias = getAliasAndResolve(states[i], getPhiValueAt(otherPhi, i)); 862 if (alias instanceof VirtualObjectNode) { 863 VirtualObjectNode phiValueVirtual = (VirtualObjectNode) alias; 864 if (Arrays.asList(virtualObjs).contains(phiValueVirtual)) { 865 compatible = false; 866 break outer; 867 } 868 } 869 } 870 } 871 } 872 873 if (compatible) { 874 VirtualObjectNode virtual = getValueObjectVirtual(phi, virtualObjs[0]); 875 mergeEffects.addFloatingNode(virtual, "valueObjectNode"); 876 mergeEffects.deleteNode(phi); 877 if (virtual.getObjectId() == -1) { 878 int id = virtualObjects.size(); 879 virtualObjects.add(virtual); 880 virtual.setObjectId(id); 881 } 882 883 int[] virtualObjectIds = new int[states.length]; 884 for (int i = 0; i < states.length; i++) { 885 virtualObjectIds[i] = virtualObjs[i].getObjectId(); 886 } 887 boolean materialized = mergeObjectStates(virtual.getObjectId(), virtualObjectIds, states); 888 addAndMarkAlias(virtual, virtual); 889 addAndMarkAlias(virtual, phi); 890 return materialized; 891 } 892 } 893 } 894 895 // otherwise: materialize all phi inputs 896 boolean materialized = false; 897 for (int i = 0; i < states.length; i++) { 898 VirtualObjectNode virtual = virtualObjs[i]; 899 if (virtual != null) { 900 Block predecessor = getPredecessor(i); 901 materialized |= ensureMaterialized(states[i], virtual.getObjectId(), predecessor.getEndNode(), blockEffects.get(predecessor), METRIC_MATERIALIZATIONS_PHI); 902 setPhiInput(phi, i, getAliasAndResolve(states[i], virtual)); 903 } 904 } 905 return materialized; 906 } 907 } 908 909 public ObjectState getObjectState(PartialEscapeBlockState<?> state, ValueNode value) { 910 if (value == null) { 911 return null; 912 } 913 if (value.isAlive() && !aliases.isNew(value)) { 914 ValueNode object = aliases.get(value); 915 return object instanceof VirtualObjectNode ? state.getObjectStateOptional((VirtualObjectNode) object) : null; 916 } else { 917 if (value instanceof VirtualObjectNode) { 918 return state.getObjectStateOptional((VirtualObjectNode) value); 919 } 920 return null; 921 } 922 } 923 924 public ValueNode getAlias(ValueNode value) { 925 if (value != null && !(value instanceof VirtualObjectNode)) { 926 if (value.isAlive() && !aliases.isNew(value)) { 927 ValueNode result = aliases.get(value); 928 if (result != null) { 929 return result; 930 } 931 } 932 } 933 return value; 934 } 935 936 public ValueNode getAliasAndResolve(PartialEscapeBlockState<?> state, ValueNode value) { 937 ValueNode result = getAlias(value); 938 if (result instanceof VirtualObjectNode) { 939 int id = ((VirtualObjectNode) result).getObjectId(); 940 if (id != -1 && !state.getObjectState(id).isVirtual()) { 941 result = state.getObjectState(id).getMaterializedValue(); 942 } 943 } 944 return result; 945 } 946 947 void addAndMarkAlias(VirtualObjectNode virtual, ValueNode node) { 948 if (node.isAlive()) { 949 aliases.set(node, virtual); 950 for (Node usage : node.usages()) { 951 markVirtualUsages(usage); 952 } 953 } 954 } 955 956 private void markVirtualUsages(Node node) { 957 if (!hasVirtualInputs.isNew(node) && !hasVirtualInputs.isMarked(node)) { 958 hasVirtualInputs.mark(node); 959 if (node instanceof VirtualState) { 960 for (Node usage : node.usages()) { 961 markVirtualUsages(usage); 962 } 963 } 964 } 965 } 966}