001/* 002 * Copyright (c) 2012, 2015, 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.java; 024 025import static com.oracle.graal.bytecode.Bytecodes.*; 026import static com.oracle.graal.graph.iterators.NodePredicates.*; 027import static com.oracle.graal.java.BytecodeParser.Options.*; 028import static com.oracle.graal.nodes.FrameState.*; 029import static jdk.internal.jvmci.common.JVMCIError.*; 030 031import java.util.*; 032import java.util.function.*; 033 034import jdk.internal.jvmci.code.*; 035import com.oracle.graal.debug.*; 036import jdk.internal.jvmci.meta.*; 037 038import com.oracle.graal.compiler.common.type.*; 039import com.oracle.graal.graphbuilderconf.IntrinsicContext.SideEffectsState; 040import com.oracle.graal.graphbuilderconf.*; 041import com.oracle.graal.java.BciBlockMapping.BciBlock; 042import com.oracle.graal.nodeinfo.*; 043import com.oracle.graal.nodes.*; 044import com.oracle.graal.nodes.calc.*; 045import com.oracle.graal.nodes.java.*; 046import com.oracle.graal.nodes.util.*; 047 048public final class FrameStateBuilder implements SideEffectsState { 049 050 private static final ValueNode[] EMPTY_ARRAY = new ValueNode[0]; 051 private static final MonitorIdNode[] EMPTY_MONITOR_ARRAY = new MonitorIdNode[0]; 052 053 private final BytecodeParser parser; 054 private final ResolvedJavaMethod method; 055 private int stackSize; 056 protected final ValueNode[] locals; 057 protected final ValueNode[] stack; 058 private ValueNode[] lockedObjects; 059 private boolean canVerifyKind; 060 061 /** 062 * @see BytecodeFrame#rethrowException 063 */ 064 private boolean rethrowException; 065 066 private MonitorIdNode[] monitorIds; 067 private final StructuredGraph graph; 068 private FrameState outerFrameState; 069 070 /** 071 * The closest {@link StateSplit#hasSideEffect() side-effect} predecessors. There will be more 072 * than one when the current block contains no side-effects but merging predecessor blocks do. 073 */ 074 private List<StateSplit> sideEffects; 075 076 /** 077 * Creates a new frame state builder for the given method and the given target graph. 078 * 079 * @param method the method whose frame is simulated 080 * @param graph the target graph of Graal nodes created by the builder 081 */ 082 public FrameStateBuilder(BytecodeParser parser, ResolvedJavaMethod method, StructuredGraph graph) { 083 this.parser = parser; 084 this.method = method; 085 this.locals = allocateArray(method.getMaxLocals()); 086 this.stack = allocateArray(Math.max(1, method.getMaxStackSize())); 087 this.lockedObjects = allocateArray(0); 088 089 assert graph != null; 090 091 this.monitorIds = EMPTY_MONITOR_ARRAY; 092 this.graph = graph; 093 this.canVerifyKind = true; 094 } 095 096 public void disableKindVerification() { 097 canVerifyKind = false; 098 } 099 100 public void initializeFromArgumentsArray(ValueNode[] arguments) { 101 102 int javaIndex = 0; 103 int index = 0; 104 if (!method.isStatic()) { 105 // set the receiver 106 locals[javaIndex] = arguments[index]; 107 javaIndex = 1; 108 index = 1; 109 } 110 Signature sig = method.getSignature(); 111 int max = sig.getParameterCount(false); 112 for (int i = 0; i < max; i++) { 113 Kind kind = sig.getParameterKind(i); 114 locals[javaIndex] = arguments[index]; 115 javaIndex++; 116 if (kind.needsTwoSlots()) { 117 locals[javaIndex] = TWO_SLOT_MARKER; 118 javaIndex++; 119 } 120 index++; 121 } 122 } 123 124 public void initializeForMethodStart(boolean eagerResolve, ParameterPlugin[] parameterPlugins) { 125 126 int javaIndex = 0; 127 int index = 0; 128 if (!method.isStatic()) { 129 // add the receiver 130 FloatingNode receiver = null; 131 Stamp receiverStamp = StampFactory.declaredNonNull(method.getDeclaringClass()); 132 for (ParameterPlugin plugin : parameterPlugins) { 133 receiver = plugin.interceptParameter(parser, index, receiverStamp); 134 if (receiver != null) { 135 break; 136 } 137 } 138 if (receiver == null) { 139 receiver = new ParameterNode(javaIndex, receiverStamp); 140 } 141 locals[javaIndex] = graph.unique(receiver); 142 javaIndex = 1; 143 index = 1; 144 } 145 Signature sig = method.getSignature(); 146 int max = sig.getParameterCount(false); 147 ResolvedJavaType accessingClass = method.getDeclaringClass(); 148 for (int i = 0; i < max; i++) { 149 JavaType type = sig.getParameterType(i, accessingClass); 150 if (eagerResolve) { 151 type = type.resolve(accessingClass); 152 } 153 Kind kind = type.getKind(); 154 Stamp stamp; 155 if (kind == Kind.Object && type instanceof ResolvedJavaType) { 156 stamp = StampFactory.declared((ResolvedJavaType) type); 157 } else { 158 stamp = StampFactory.forKind(kind); 159 } 160 FloatingNode param = null; 161 for (ParameterPlugin plugin : parameterPlugins) { 162 param = plugin.interceptParameter(parser, index, stamp); 163 if (param != null) { 164 break; 165 } 166 } 167 if (param == null) { 168 param = new ParameterNode(index, stamp); 169 } 170 locals[javaIndex] = graph.unique(param); 171 javaIndex++; 172 if (kind.needsTwoSlots()) { 173 locals[javaIndex] = TWO_SLOT_MARKER; 174 javaIndex++; 175 } 176 index++; 177 } 178 } 179 180 private FrameStateBuilder(FrameStateBuilder other) { 181 this.parser = other.parser; 182 this.method = other.method; 183 this.stackSize = other.stackSize; 184 this.locals = other.locals.clone(); 185 this.stack = other.stack.clone(); 186 this.lockedObjects = other.lockedObjects.length == 0 ? other.lockedObjects : other.lockedObjects.clone(); 187 this.rethrowException = other.rethrowException; 188 this.canVerifyKind = other.canVerifyKind; 189 190 assert locals.length == method.getMaxLocals(); 191 assert stack.length == Math.max(1, method.getMaxStackSize()); 192 193 assert other.graph != null; 194 graph = other.graph; 195 monitorIds = other.monitorIds.length == 0 ? other.monitorIds : other.monitorIds.clone(); 196 197 assert locals.length == method.getMaxLocals(); 198 assert stack.length == Math.max(1, method.getMaxStackSize()); 199 assert lockedObjects.length == monitorIds.length; 200 } 201 202 private static ValueNode[] allocateArray(int length) { 203 return length == 0 ? EMPTY_ARRAY : new ValueNode[length]; 204 } 205 206 public ResolvedJavaMethod getMethod() { 207 return method; 208 } 209 210 @Override 211 public String toString() { 212 StringBuilder sb = new StringBuilder(); 213 sb.append("[locals: ["); 214 for (int i = 0; i < locals.length; i++) { 215 sb.append(i == 0 ? "" : ",").append(locals[i] == null ? "_" : locals[i] == TWO_SLOT_MARKER ? "#" : locals[i].toString(Verbosity.Id)); 216 } 217 sb.append("] stack: ["); 218 for (int i = 0; i < stackSize; i++) { 219 sb.append(i == 0 ? "" : ",").append(stack[i] == null ? "_" : stack[i] == TWO_SLOT_MARKER ? "#" : stack[i].toString(Verbosity.Id)); 220 } 221 sb.append("] locks: ["); 222 for (int i = 0; i < lockedObjects.length; i++) { 223 sb.append(i == 0 ? "" : ",").append(lockedObjects[i].toString(Verbosity.Id)).append(" / ").append(monitorIds[i].toString(Verbosity.Id)); 224 } 225 sb.append("]"); 226 if (rethrowException) { 227 sb.append(" rethrowException"); 228 } 229 sb.append("]"); 230 return sb.toString(); 231 } 232 233 public FrameState create(int bci, StateSplit forStateSplit) { 234 if (parser != null && parser.parsingIntrinsic()) { 235 return parser.intrinsicContext.createFrameState(parser.getGraph(), this, forStateSplit); 236 } 237 238 // Skip intrinsic frames 239 return create(bci, parser != null ? parser.getNonIntrinsicAncestor() : null, false, null, null); 240 } 241 242 /** 243 * @param pushedValues if non-null, values to {@link #push(Kind, ValueNode)} to the stack before 244 * creating the {@link FrameState} 245 */ 246 public FrameState create(int bci, BytecodeParser parent, boolean duringCall, Kind[] pushedSlotKinds, ValueNode[] pushedValues) { 247 if (outerFrameState == null && parent != null) { 248 outerFrameState = parent.getFrameStateBuilder().create(parent.bci(), null); 249 } 250 if (bci == BytecodeFrame.AFTER_EXCEPTION_BCI && parent != null) { 251 FrameState newFrameState = outerFrameState.duplicateModified(outerFrameState.bci, true, Kind.Void, new Kind[]{Kind.Object}, new ValueNode[]{stack[0]}); 252 return newFrameState; 253 } 254 if (bci == BytecodeFrame.INVALID_FRAMESTATE_BCI) { 255 throw shouldNotReachHere(); 256 } 257 258 if (pushedValues != null) { 259 assert pushedSlotKinds.length == pushedValues.length; 260 int stackSizeToRestore = stackSize; 261 for (int i = 0; i < pushedValues.length; i++) { 262 push(pushedSlotKinds[i], pushedValues[i]); 263 } 264 FrameState res = graph.add(new FrameState(outerFrameState, method, bci, locals, stack, stackSize, lockedObjects, Arrays.asList(monitorIds), rethrowException, duringCall)); 265 stackSize = stackSizeToRestore; 266 return res; 267 } else { 268 return graph.add(new FrameState(outerFrameState, method, bci, locals, stack, stackSize, lockedObjects, Arrays.asList(monitorIds), rethrowException, duringCall)); 269 } 270 } 271 272 public BytecodePosition createBytecodePosition(int bci) { 273 BytecodeParser parent = parser.getParent(); 274 if (HideSubstitutionStates.getValue()) { 275 if (parser.parsingIntrinsic()) { 276 // Attribute to the method being replaced 277 return new BytecodePosition(parent.getFrameStateBuilder().createBytecodePosition(parent.bci()), parser.intrinsicContext.getOriginalMethod(), -1); 278 } 279 // Skip intrinsic frames 280 parent = parser.getNonIntrinsicAncestor(); 281 } 282 return create(null, bci, parent); 283 } 284 285 private BytecodePosition create(BytecodePosition o, int bci, BytecodeParser parent) { 286 BytecodePosition outer = o; 287 if (outer == null && parent != null) { 288 outer = parent.getFrameStateBuilder().createBytecodePosition(parent.bci()); 289 } 290 if (bci == BytecodeFrame.AFTER_EXCEPTION_BCI && parent != null) { 291 return FrameState.toBytecodePosition(outerFrameState); 292 } 293 if (bci == BytecodeFrame.INVALID_FRAMESTATE_BCI) { 294 throw shouldNotReachHere(); 295 } 296 return new BytecodePosition(outer, method, bci); 297 } 298 299 public FrameStateBuilder copy() { 300 return new FrameStateBuilder(this); 301 } 302 303 public boolean isCompatibleWith(FrameStateBuilder other) { 304 assert method.equals(other.method) && graph == other.graph && localsSize() == other.localsSize() : "Can only compare frame states of the same method"; 305 assert lockedObjects.length == monitorIds.length && other.lockedObjects.length == other.monitorIds.length : "mismatch between lockedObjects and monitorIds"; 306 307 if (stackSize() != other.stackSize()) { 308 return false; 309 } 310 for (int i = 0; i < stackSize(); i++) { 311 ValueNode x = stack[i]; 312 ValueNode y = other.stack[i]; 313 assert x != null && y != null; 314 if (x != y && (x == TWO_SLOT_MARKER || x.isDeleted() || y == TWO_SLOT_MARKER || y.isDeleted() || x.getKind() != y.getKind())) { 315 return false; 316 } 317 } 318 if (lockedObjects.length != other.lockedObjects.length) { 319 return false; 320 } 321 for (int i = 0; i < lockedObjects.length; i++) { 322 if (GraphUtil.originalValue(lockedObjects[i]) != GraphUtil.originalValue(other.lockedObjects[i]) || monitorIds[i] != other.monitorIds[i]) { 323 throw new BailoutException("unbalanced monitors"); 324 } 325 } 326 return true; 327 } 328 329 /** 330 * Phi nodes are recursively deleted in {@link #propagateDelete}. However, this does not cover 331 * frame state builder objects, since these are not nodes and not in the usage list of the phi 332 * node. Therefore, we clean the frame state builder manually here, before we parse a block. 333 */ 334 public void cleanDeletedNodes() { 335 for (int i = 0; i < localsSize(); i++) { 336 ValueNode node = locals[i]; 337 if (node != null && node.isDeleted()) { 338 assert node instanceof ValuePhiNode || node instanceof ValueProxyNode; 339 locals[i] = null; 340 } 341 } 342 for (int i = 0; i < stackSize(); i++) { 343 ValueNode node = stack[i]; 344 assert node == null || !node.isDeleted(); 345 } 346 for (int i = 0; i < lockedObjects.length; i++) { 347 ValueNode node = lockedObjects[i]; 348 assert !node.isDeleted(); 349 } 350 } 351 352 public void merge(AbstractMergeNode block, FrameStateBuilder other) { 353 assert isCompatibleWith(other); 354 355 for (int i = 0; i < localsSize(); i++) { 356 locals[i] = merge(locals[i], other.locals[i], block); 357 } 358 for (int i = 0; i < stackSize(); i++) { 359 stack[i] = merge(stack[i], other.stack[i], block); 360 } 361 for (int i = 0; i < lockedObjects.length; i++) { 362 lockedObjects[i] = merge(lockedObjects[i], other.lockedObjects[i], block); 363 assert monitorIds[i] == other.monitorIds[i]; 364 } 365 366 if (sideEffects == null) { 367 sideEffects = other.sideEffects; 368 } else { 369 if (other.sideEffects != null) { 370 sideEffects.addAll(other.sideEffects); 371 } 372 } 373 } 374 375 private ValueNode merge(ValueNode currentValue, ValueNode otherValue, AbstractMergeNode block) { 376 if (currentValue == null || currentValue.isDeleted()) { 377 return null; 378 } else if (block.isPhiAtMerge(currentValue)) { 379 if (otherValue == null || otherValue == TWO_SLOT_MARKER || otherValue.isDeleted() || currentValue.getKind() != otherValue.getKind()) { 380 propagateDelete((ValuePhiNode) currentValue); 381 return null; 382 } 383 ((PhiNode) currentValue).addInput(otherValue); 384 return currentValue; 385 } else if (currentValue != otherValue) { 386 assert !(block instanceof LoopBeginNode) : String.format("Phi functions for loop headers are create eagerly for changed locals and all stack slots: %s != %s", currentValue, otherValue); 387 if (currentValue == TWO_SLOT_MARKER || otherValue == TWO_SLOT_MARKER) { 388 return null; 389 } else if (otherValue == null || otherValue.isDeleted() || currentValue.getKind() != otherValue.getKind()) { 390 return null; 391 } 392 return createValuePhi(currentValue, otherValue, block); 393 } else { 394 return currentValue; 395 } 396 } 397 398 private ValuePhiNode createValuePhi(ValueNode currentValue, ValueNode otherValue, AbstractMergeNode block) { 399 ValuePhiNode phi = graph.addWithoutUnique(new ValuePhiNode(currentValue.stamp().unrestricted(), block)); 400 for (int i = 0; i < block.phiPredecessorCount(); i++) { 401 phi.addInput(currentValue); 402 } 403 phi.addInput(otherValue); 404 assert phi.valueCount() == block.phiPredecessorCount() + 1; 405 return phi; 406 } 407 408 private void propagateDelete(FloatingNode node) { 409 assert node instanceof ValuePhiNode || node instanceof ProxyNode; 410 if (node.isDeleted()) { 411 return; 412 } 413 // Collect all phi functions that use this phi so that we can delete them recursively (after 414 // we delete ourselves to avoid circles). 415 List<FloatingNode> propagateUsages = node.usages().filter(FloatingNode.class).filter(isA(ValuePhiNode.class).or(ProxyNode.class)).snapshot(); 416 417 // Remove the phi function from all FrameStates where it is used and then delete it. 418 assert node.usages().filter(isNotA(FrameState.class).nor(ValuePhiNode.class).nor(ProxyNode.class)).isEmpty() : "phi function that gets deletes must only be used in frame states"; 419 node.replaceAtUsages(null); 420 node.safeDelete(); 421 422 for (FloatingNode phiUsage : propagateUsages) { 423 propagateDelete(phiUsage); 424 } 425 } 426 427 public void insertLoopPhis(LocalLiveness liveness, int loopId, LoopBeginNode loopBegin, boolean forcePhis) { 428 for (int i = 0; i < localsSize(); i++) { 429 boolean needPhi = forcePhis || liveness.localIsChangedInLoop(loopId, i); 430 if (needPhi) { 431 locals[i] = createLoopPhi(loopBegin, locals[i]); 432 } 433 } 434 for (int i = 0; i < stackSize(); i++) { 435 stack[i] = createLoopPhi(loopBegin, stack[i]); 436 } 437 for (int i = 0; i < lockedObjects.length; i++) { 438 lockedObjects[i] = createLoopPhi(loopBegin, lockedObjects[i]); 439 } 440 } 441 442 public void insertLoopProxies(LoopExitNode loopExit, FrameStateBuilder loopEntryState) { 443 for (int i = 0; i < localsSize(); i++) { 444 ValueNode value = locals[i]; 445 if (value != null && value != TWO_SLOT_MARKER && (!loopEntryState.contains(value) || loopExit.loopBegin().isPhiAtMerge(value))) { 446 Debug.log(" inserting proxy for %s", value); 447 locals[i] = ProxyNode.forValue(value, loopExit, graph); 448 } 449 } 450 for (int i = 0; i < stackSize(); i++) { 451 ValueNode value = stack[i]; 452 if (value != null && value != TWO_SLOT_MARKER && (!loopEntryState.contains(value) || loopExit.loopBegin().isPhiAtMerge(value))) { 453 Debug.log(" inserting proxy for %s", value); 454 stack[i] = ProxyNode.forValue(value, loopExit, graph); 455 } 456 } 457 for (int i = 0; i < lockedObjects.length; i++) { 458 ValueNode value = lockedObjects[i]; 459 if (value != null && (!loopEntryState.contains(value) || loopExit.loopBegin().isPhiAtMerge(value))) { 460 Debug.log(" inserting proxy for %s", value); 461 lockedObjects[i] = ProxyNode.forValue(value, loopExit, graph); 462 } 463 } 464 } 465 466 public void insertProxies(Function<ValueNode, ValueNode> proxyFunction) { 467 for (int i = 0; i < localsSize(); i++) { 468 ValueNode value = locals[i]; 469 if (value != null && value != TWO_SLOT_MARKER) { 470 Debug.log(" inserting proxy for %s", value); 471 locals[i] = proxyFunction.apply(value); 472 } 473 } 474 for (int i = 0; i < stackSize(); i++) { 475 ValueNode value = stack[i]; 476 if (value != null && value != TWO_SLOT_MARKER) { 477 Debug.log(" inserting proxy for %s", value); 478 stack[i] = proxyFunction.apply(value); 479 } 480 } 481 for (int i = 0; i < lockedObjects.length; i++) { 482 ValueNode value = lockedObjects[i]; 483 if (value != null) { 484 Debug.log(" inserting proxy for %s", value); 485 lockedObjects[i] = proxyFunction.apply(value); 486 } 487 } 488 } 489 490 private ValueNode createLoopPhi(AbstractMergeNode block, ValueNode value) { 491 if (value == null || value == TWO_SLOT_MARKER) { 492 return value; 493 } 494 assert !block.isPhiAtMerge(value) : "phi function for this block already created"; 495 496 ValuePhiNode phi = graph.addWithoutUnique(new ValuePhiNode(value.stamp().unrestricted(), block)); 497 phi.addInput(value); 498 return phi; 499 } 500 501 /** 502 * Adds a locked monitor to this frame state. 503 * 504 * @param object the object whose monitor will be locked. 505 */ 506 public void pushLock(ValueNode object, MonitorIdNode monitorId) { 507 assert object.isAlive() && object.getKind() == Kind.Object : "unexpected value: " + object; 508 lockedObjects = Arrays.copyOf(lockedObjects, lockedObjects.length + 1); 509 monitorIds = Arrays.copyOf(monitorIds, monitorIds.length + 1); 510 lockedObjects[lockedObjects.length - 1] = object; 511 monitorIds[monitorIds.length - 1] = monitorId; 512 assert lockedObjects.length == monitorIds.length; 513 } 514 515 /** 516 * Removes a locked monitor from this frame state. 517 * 518 * @return the object whose monitor was removed from the locks list. 519 */ 520 public ValueNode popLock() { 521 try { 522 return lockedObjects[lockedObjects.length - 1]; 523 } finally { 524 lockedObjects = lockedObjects.length == 1 ? EMPTY_ARRAY : Arrays.copyOf(lockedObjects, lockedObjects.length - 1); 525 monitorIds = monitorIds.length == 1 ? EMPTY_MONITOR_ARRAY : Arrays.copyOf(monitorIds, monitorIds.length - 1); 526 assert lockedObjects.length == monitorIds.length; 527 } 528 } 529 530 public MonitorIdNode peekMonitorId() { 531 return monitorIds[monitorIds.length - 1]; 532 } 533 534 /** 535 * @return the current lock depth 536 */ 537 public int lockDepth(boolean includeParents) { 538 int depth = lockedObjects.length; 539 assert depth == monitorIds.length; 540 if (includeParents && parser.getParent() != null) { 541 depth += parser.getParent().frameState.lockDepth(true); 542 } 543 return depth; 544 } 545 546 public boolean contains(ValueNode value) { 547 for (int i = 0; i < localsSize(); i++) { 548 if (locals[i] == value) { 549 return true; 550 } 551 } 552 for (int i = 0; i < stackSize(); i++) { 553 if (stack[i] == value) { 554 return true; 555 } 556 } 557 assert lockedObjects.length == monitorIds.length; 558 for (int i = 0; i < lockedObjects.length; i++) { 559 if (lockedObjects[i] == value || monitorIds[i] == value) { 560 return true; 561 } 562 } 563 return false; 564 } 565 566 public void clearNonLiveLocals(BciBlock block, LocalLiveness liveness, boolean liveIn) { 567 /* 568 * (lstadler) if somebody is tempted to remove/disable this clearing code: it's possible to 569 * remove it for normal compilations, but not for OSR compilations - otherwise dead object 570 * slots at the OSR entry aren't cleared. it is also not enough to rely on PiNodes with 571 * Kind.Illegal, because the conflicting branch might not have been parsed. 572 */ 573 if (!parser.graphBuilderConfig.clearNonLiveLocals()) { 574 return; 575 } 576 if (liveIn) { 577 for (int i = 0; i < locals.length; i++) { 578 if (!liveness.localIsLiveIn(block, i)) { 579 assert locals[i] != TWO_SLOT_MARKER || locals[i - 1] == null : "Clearing of second slot must have cleared the first slot too"; 580 locals[i] = null; 581 } 582 } 583 } else { 584 for (int i = 0; i < locals.length; i++) { 585 if (!liveness.localIsLiveOut(block, i)) { 586 assert locals[i] != TWO_SLOT_MARKER || locals[i - 1] == null : "Clearing of second slot must have cleared the first slot too"; 587 locals[i] = null; 588 } 589 } 590 } 591 } 592 593 /** 594 * Clears all local variables. 595 */ 596 public void clearLocals() { 597 for (int i = 0; i < locals.length; i++) { 598 locals[i] = null; 599 } 600 } 601 602 /** 603 * @see BytecodeFrame#rethrowException 604 */ 605 public boolean rethrowException() { 606 return rethrowException; 607 } 608 609 /** 610 * @see BytecodeFrame#rethrowException 611 */ 612 public void setRethrowException(boolean b) { 613 rethrowException = b; 614 } 615 616 /** 617 * Returns the size of the local variables. 618 * 619 * @return the size of the local variables 620 */ 621 public int localsSize() { 622 return locals.length; 623 } 624 625 /** 626 * Gets the current size (height) of the stack. 627 */ 628 public int stackSize() { 629 return stackSize; 630 } 631 632 private boolean verifyKind(Kind slotKind, ValueNode x) { 633 assert x != null; 634 assert x != TWO_SLOT_MARKER; 635 assert slotKind.getSlotCount() > 0; 636 637 if (canVerifyKind) { 638 assert x.getKind().getStackKind() == slotKind.getStackKind(); 639 } 640 return true; 641 } 642 643 /** 644 * Loads the local variable at the specified index, checking that the returned value is non-null 645 * and that two-stack values are properly handled. 646 * 647 * @param i the index of the local variable to load 648 * @param slotKind the kind of the local variable from the point of view of the bytecodes 649 * @return the instruction that produced the specified local 650 */ 651 public ValueNode loadLocal(int i, Kind slotKind) { 652 ValueNode x = locals[i]; 653 assert verifyKind(slotKind, x); 654 assert slotKind.needsTwoSlots() ? locals[i + 1] == TWO_SLOT_MARKER : (i == locals.length - 1 || locals[i + 1] != TWO_SLOT_MARKER); 655 return x; 656 } 657 658 /** 659 * Stores a given local variable at the specified index. If the value occupies two slots, then 660 * the next local variable index is also overwritten. 661 * 662 * @param i the index at which to store 663 * @param slotKind the kind of the local variable from the point of view of the bytecodes 664 * @param x the instruction which produces the value for the local 665 */ 666 public void storeLocal(int i, Kind slotKind, ValueNode x) { 667 assert verifyKind(slotKind, x); 668 669 if (locals[i] == TWO_SLOT_MARKER) { 670 /* Writing the second slot of a two-slot value invalidates the first slot. */ 671 locals[i - 1] = null; 672 } 673 locals[i] = x; 674 if (slotKind.needsTwoSlots()) { 675 /* Writing a two-slot value: mark the second slot. */ 676 locals[i + 1] = TWO_SLOT_MARKER; 677 } else if (i < locals.length - 1 && locals[i + 1] == TWO_SLOT_MARKER) { 678 /* 679 * Writing a one-slot value to an index previously occupied by a two-slot value: clear 680 * the old marker of the second slot. 681 */ 682 locals[i + 1] = null; 683 } 684 } 685 686 /** 687 * Pushes an instruction onto the stack with the expected type. 688 * 689 * @param slotKind the kind of the stack element from the point of view of the bytecodes 690 * @param x the instruction to push onto the stack 691 */ 692 public void push(Kind slotKind, ValueNode x) { 693 assert verifyKind(slotKind, x); 694 695 xpush(x); 696 if (slotKind.needsTwoSlots()) { 697 xpush(TWO_SLOT_MARKER); 698 } 699 } 700 701 public void pushReturn(Kind slotKind, ValueNode x) { 702 if (slotKind != Kind.Void) { 703 push(slotKind, x); 704 } 705 } 706 707 /** 708 * Pops an instruction off the stack with the expected type. 709 * 710 * @param slotKind the kind of the stack element from the point of view of the bytecodes 711 * @return the instruction on the top of the stack 712 */ 713 public ValueNode pop(Kind slotKind) { 714 if (slotKind.needsTwoSlots()) { 715 ValueNode s = xpop(); 716 assert s == TWO_SLOT_MARKER; 717 } 718 ValueNode x = xpop(); 719 assert verifyKind(slotKind, x); 720 return x; 721 } 722 723 private void xpush(ValueNode x) { 724 assert x != null; 725 stack[stackSize++] = x; 726 } 727 728 private ValueNode xpop() { 729 ValueNode result = stack[--stackSize]; 730 assert result != null; 731 return result; 732 } 733 734 private ValueNode xpeek() { 735 ValueNode result = stack[stackSize - 1]; 736 assert result != null; 737 return result; 738 } 739 740 /** 741 * Pop the specified number of slots off of this stack and return them as an array of 742 * instructions. 743 * 744 * @return an array containing the arguments off of the stack 745 */ 746 public ValueNode[] popArguments(int argSize) { 747 ValueNode[] result = allocateArray(argSize); 748 for (int i = argSize - 1; i >= 0; i--) { 749 ValueNode x = xpop(); 750 if (x == TWO_SLOT_MARKER) { 751 /* Ignore second slot of two-slot value. */ 752 x = xpop(); 753 } 754 assert x != null && x != TWO_SLOT_MARKER; 755 result[i] = x; 756 } 757 return result; 758 } 759 760 /** 761 * Clears all values on this stack. 762 */ 763 public void clearStack() { 764 stackSize = 0; 765 } 766 767 /** 768 * Performs a raw stack operation as defined in the Java bytecode specification. 769 * 770 * @param opcode The Java bytecode. 771 */ 772 public void stackOp(int opcode) { 773 switch (opcode) { 774 case POP: { 775 ValueNode w1 = xpop(); 776 assert w1 != TWO_SLOT_MARKER; 777 break; 778 } 779 case POP2: { 780 xpop(); 781 ValueNode w2 = xpop(); 782 assert w2 != TWO_SLOT_MARKER; 783 break; 784 } 785 case DUP: { 786 ValueNode w1 = xpeek(); 787 assert w1 != TWO_SLOT_MARKER; 788 xpush(w1); 789 break; 790 } 791 case DUP_X1: { 792 ValueNode w1 = xpop(); 793 ValueNode w2 = xpop(); 794 assert w1 != TWO_SLOT_MARKER; 795 xpush(w1); 796 xpush(w2); 797 xpush(w1); 798 break; 799 } 800 case DUP_X2: { 801 ValueNode w1 = xpop(); 802 ValueNode w2 = xpop(); 803 ValueNode w3 = xpop(); 804 assert w1 != TWO_SLOT_MARKER; 805 xpush(w1); 806 xpush(w3); 807 xpush(w2); 808 xpush(w1); 809 break; 810 } 811 case DUP2: { 812 ValueNode w1 = xpop(); 813 ValueNode w2 = xpop(); 814 xpush(w2); 815 xpush(w1); 816 xpush(w2); 817 xpush(w1); 818 break; 819 } 820 case DUP2_X1: { 821 ValueNode w1 = xpop(); 822 ValueNode w2 = xpop(); 823 ValueNode w3 = xpop(); 824 xpush(w2); 825 xpush(w1); 826 xpush(w3); 827 xpush(w2); 828 xpush(w1); 829 break; 830 } 831 case DUP2_X2: { 832 ValueNode w1 = xpop(); 833 ValueNode w2 = xpop(); 834 ValueNode w3 = xpop(); 835 ValueNode w4 = xpop(); 836 xpush(w2); 837 xpush(w1); 838 xpush(w4); 839 xpush(w3); 840 xpush(w2); 841 xpush(w1); 842 break; 843 } 844 case SWAP: { 845 ValueNode w1 = xpop(); 846 ValueNode w2 = xpop(); 847 assert w1 != TWO_SLOT_MARKER; 848 assert w2 != TWO_SLOT_MARKER; 849 xpush(w1); 850 xpush(w2); 851 break; 852 } 853 default: 854 throw shouldNotReachHere(); 855 } 856 } 857 858 @Override 859 public int hashCode() { 860 int result = hashCode(locals, locals.length); 861 result *= 13; 862 result += hashCode(stack, this.stackSize); 863 return result; 864 } 865 866 private static int hashCode(Object[] a, int length) { 867 int result = 1; 868 for (int i = 0; i < length; ++i) { 869 Object element = a[i]; 870 result = 31 * result + (element == null ? 0 : System.identityHashCode(element)); 871 } 872 return result; 873 } 874 875 private static boolean equals(ValueNode[] a, ValueNode[] b, int length) { 876 for (int i = 0; i < length; ++i) { 877 if (a[i] != b[i]) { 878 return false; 879 } 880 } 881 return true; 882 } 883 884 @Override 885 public boolean equals(Object otherObject) { 886 if (otherObject instanceof FrameStateBuilder) { 887 FrameStateBuilder other = (FrameStateBuilder) otherObject; 888 if (!other.method.equals(method)) { 889 return false; 890 } 891 if (other.stackSize != stackSize) { 892 return false; 893 } 894 if (other.parser != parser) { 895 return false; 896 } 897 if (other.rethrowException != rethrowException) { 898 return false; 899 } 900 if (other.graph != graph) { 901 return false; 902 } 903 if (other.locals.length != locals.length) { 904 return false; 905 } 906 return equals(other.locals, locals, locals.length) && equals(other.stack, stack, stackSize) && equals(other.lockedObjects, lockedObjects, lockedObjects.length) && 907 equals(other.monitorIds, monitorIds, monitorIds.length); 908 } 909 return false; 910 } 911 912 @Override 913 public boolean isAfterSideEffect() { 914 return sideEffects != null; 915 } 916 917 @Override 918 public Iterable<StateSplit> sideEffects() { 919 return sideEffects; 920 } 921 922 @Override 923 public void addSideEffect(StateSplit sideEffect) { 924 assert sideEffect != null; 925 assert sideEffect.hasSideEffect(); 926 if (sideEffects == null) { 927 sideEffects = new ArrayList<>(4); 928 } 929 sideEffects.add(sideEffect); 930 } 931 932 public void traceState() { 933 Debug.log(String.format("| state [nr locals = %d, stack depth = %d, method = %s]", localsSize(), stackSize(), method)); 934 for (int i = 0; i < localsSize(); ++i) { 935 ValueNode value = locals[i]; 936 Debug.log(String.format("| local[%d] = %-8s : %s", i, value == null ? "bogus" : value == TWO_SLOT_MARKER ? "second" : value.getKind().getJavaName(), value)); 937 } 938 for (int i = 0; i < stackSize(); ++i) { 939 ValueNode value = stack[i]; 940 Debug.log(String.format("| stack[%d] = %-8s : %s", i, value == null ? "bogus" : value == TWO_SLOT_MARKER ? "second" : value.getKind().getJavaName(), value)); 941 } 942 } 943}