Mercurial > hg > graal-compiler
diff graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/FrameState.java @ 3733:e233f5660da4
Added Java files from Maxine project.
author | Thomas Wuerthinger <thomas.wuerthinger@oracle.com> |
---|---|
date | Sat, 17 Dec 2011 19:59:18 +0100 |
parents | |
children | bc8527f3071c |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/FrameState.java Sat Dec 17 19:59:18 2011 +0100 @@ -0,0 +1,726 @@ +/* + * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.max.graal.nodes; + +import java.util.*; + +import com.oracle.max.graal.graph.*; +import com.oracle.max.graal.nodes.PhiNode.PhiType; +import com.oracle.max.graal.nodes.spi.*; +import com.oracle.max.graal.nodes.virtual.*; +import com.sun.cri.ci.*; +import com.sun.cri.ri.*; + +/** + * The {@code FrameState} class encapsulates the frame state (i.e. local variables and + * operand stack) at a particular point in the abstract interpretation. + */ +public final class FrameState extends Node implements FrameStateAccess, Node.IterableNodeType, LIRLowerable { + + protected final int localsSize; + + protected final int stackSize; + + protected final int locksSize; + + private boolean rethrowException; + + /** + * This BCI should be used for frame states that are built for code with no meaningful BCI. + */ + public static final int UNKNOWN_BCI = -4; + + /** + * When a node whose frame state has this BCI value is inlined, its frame state + * will be replaced with the frame state before the inlined invoke node. + */ + public static final int BEFORE_BCI = -1; + + /** + * When a node whose frame state has this BCI value is inlined, its frame state + * will be replaced with the frame state {@linkplain Invoke#stateAfter() after} + * the inlined invoke node. + */ + public static final int AFTER_BCI = -2; + + /** + * When a node whose frame state has this BCI value is inlined, its frame state + * will be replaced with the frame state at the exception edge of the inlined + * invoke node. + */ + public static final int AFTER_EXCEPTION_BCI = -3; + + @Input private FrameState outerFrameState; + + @Input private final NodeInputList<ValueNode> values; + + @Input private final NodeInputList<Node> virtualObjectMappings; + + public FrameState outerFrameState() { + return outerFrameState; + } + + public void setOuterFrameState(FrameState x) { + updateUsages(this.outerFrameState, x); + this.outerFrameState = x; + } + + public FrameState outermostFrameState() { + FrameState fs = this; + while (fs.outerFrameState() != null) { + fs = fs.outerFrameState(); + } + return fs; + } + + public void setValueAt(int i, ValueNode x) { + values.set(i, x); + } + + /** + * The bytecode index to which this frame state applies. This will be {@code -1} + * iff this state is mutable. + */ + public final int bci; + + private final RiResolvedMethod method; + + /** + * Creates a {@code FrameState} for the given scope and maximum number of stack and local variables. + * + * @param method the method for this frame state + * @param bci the bytecode index of the frame state + * @param localsSize number of locals + * @param stackSize size of the stack + * @param lockSize number of locks + * @param rethrowException if true the VM should re-throw the exception on top of the stack when deopt'ing using this framestate + */ + public FrameState(RiResolvedMethod method, int bci, int localsSize, int stackSize, int locksSize, boolean rethrowException) { + assert stackSize >= 0; + this.method = method; + this.bci = bci; + this.localsSize = localsSize; + this.stackSize = stackSize; + this.locksSize = locksSize; + this.values = new NodeInputList<ValueNode>(this, localsSize + stackSize + locksSize); + this.virtualObjectMappings = new NodeInputList<Node>(this); + this.rethrowException = rethrowException; + } + + public FrameState(RiResolvedMethod method, int bci, ValueNode[] locals, ValueNode[] stack, int stackSize, List<MonitorObject> locks, boolean rethrowException) { + this.method = method; + this.bci = bci; + this.localsSize = locals.length; + this.stackSize = stackSize; + this.locksSize = locks.size(); + final ValueNode[] values = new ValueNode[locals.length + stackSize + locks.size()]; + for (int i = 0; i < locals.length; i++) { + values[i] = locals[i]; + } + for (int i = 0; i < stackSize; i++) { + values[localsSize + i] = stack[i]; + } + for (int i = 0; i < locks.size(); i++) { + values[locals.length + stackSize + i] = locks.get(i); + } + this.values = new NodeInputList<ValueNode>(this, values); + this.virtualObjectMappings = new NodeInputList<Node>(this); + this.rethrowException = rethrowException; + } + + public boolean rethrowException() { + return rethrowException; + } + + public RiResolvedMethod method() { + return method; + } + + public void addVirtualObjectMapping(Node virtualObject) { + assert virtualObject instanceof VirtualObjectFieldNode || virtualObject instanceof PhiNode : virtualObject; + virtualObjectMappings.add(virtualObject); + } + + public int virtualObjectMappingCount() { + return virtualObjectMappings.size(); + } + + public Node virtualObjectMappingAt(int i) { + return virtualObjectMappings.get(i); + } + + public Iterable<Node> virtualObjectMappings() { + return virtualObjectMappings; + } + + /** + * Gets a copy of this frame state. + */ + public FrameState duplicate(int bci) { + return duplicate(bci, false); + } + + public FrameState duplicate(int bci, boolean duplicateOuter) { + FrameState other = graph().add(new FrameState(method, bci, localsSize, stackSize, locksSize, rethrowException)); + other.values.setAll(values); + other.virtualObjectMappings.setAll(virtualObjectMappings); + FrameState outerFrameState = outerFrameState(); + if (duplicateOuter && outerFrameState != null) { + outerFrameState = outerFrameState.duplicate(outerFrameState.bci, duplicateOuter); + } + other.setOuterFrameState(outerFrameState); + return other; + } + + @Override + public FrameState duplicateWithException(int bci, ValueNode exceptionObject) { + return duplicateModified(bci, true, CiKind.Void, exceptionObject); + } + + /** + * Creates a copy of this frame state with one stack element of type popKind popped from the stack and the + * values in pushedValues pushed on the stack. The pushedValues are expected to be in slot encoding: a long + * or double is followed by a null slot. + */ + public FrameState duplicateModified(int bci, boolean rethrowException, CiKind popKind, ValueNode... pushedValues) { + int popSlots = popKind == CiKind.Void ? 0 : isTwoSlot(popKind) ? 2 : 1; + int pushSlots = pushedValues.length; + FrameState other = graph().add(new FrameState(method, bci, localsSize, stackSize - popSlots + pushSlots, locksSize(), rethrowException)); + for (int i = 0; i < localsSize; i++) { + other.setValueAt(i, localAt(i)); + } + for (int i = 0; i < stackSize - popSlots; i++) { + other.setValueAt(localsSize + i, stackAt(i)); + } + int slot = localsSize + stackSize - popSlots; + for (int i = 0; i < pushSlots; i++) { + other.setValueAt(slot++, pushedValues[i]); + } + for (int i = 0; i < locksSize; i++) { + other.setValueAt(localsSize + other.stackSize + i, lockAt(i)); + } + other.virtualObjectMappings.setAll(virtualObjectMappings); + other.setOuterFrameState(outerFrameState()); + return other; + } + + public boolean isCompatibleWith(FrameStateAccess other) { + if (stackSize() != other.stackSize() || localsSize() != other.localsSize() || locksSize() != other.locksSize()) { + return false; + } + for (int i = 0; i < stackSize(); i++) { + ValueNode x = stackAt(i); + ValueNode y = other.stackAt(i); + if (x != y && ValueUtil.typeMismatch(x, y)) { + return false; + } + } + for (int i = 0; i < locksSize(); i++) { + if (lockAt(i) != other.lockAt(i)) { + return false; + } + } + if (other.outerFrameState() != outerFrameState()) { + return false; + } + return true; + } + + public boolean equals(FrameStateAccess other) { + if (stackSize() != other.stackSize() || localsSize() != other.localsSize() || locksSize() != other.locksSize()) { + return false; + } + for (int i = 0; i < stackSize(); i++) { + ValueNode x = stackAt(i); + ValueNode y = other.stackAt(i); + if (x != y) { + return false; + } + } + for (int i = 0; i < locksSize(); i++) { + if (lockAt(i) != other.lockAt(i)) { + return false; + } + } + if (other.outerFrameState() != outerFrameState()) { + return false; + } + return true; + } + + /** + * Gets the size of the local variables. + */ + public int localsSize() { + return localsSize; + } + + /** + * Gets the current size (height) of the stack. + */ + public int stackSize() { + return stackSize; + } + + /** + * Gets number of locks held by this frame state. + */ + public int locksSize() { + return locksSize; + } + + /** + * Invalidates the local variable at the specified index. If the specified index refers to a doubleword local, then + * invalidates the high word as well. + * + * @param i the index of the local to invalidate + */ + public void invalidateLocal(int i) { + // note that for double word locals, the high slot should already be null + // unless the local is actually dead and the high slot is being reused; + // in either case, it is not necessary to null the high slot + setValueAt(i, null); + } + + /** + * Stores a given local variable at the specified index. If the value is a {@linkplain CiKind#isDoubleWord() double word}, + * then the next local variable index is also overwritten. + * + * @param i the index at which to store + * @param x the instruction which produces the value for the local + */ + // TODO this duplicates code in FrameStateBuilder and needs to go away + public void storeLocal(int i, ValueNode x) { + assert i < localsSize : "local variable index out of range: " + i; + invalidateLocal(i); + setValueAt(i, x); + if (isTwoSlot(x.kind())) { + // (tw) if this was a double word then kill i+1 + setValueAt(i + 1, null); + } + if (i > 0) { + // if there was a double word at i - 1, then kill it + ValueNode p = localAt(i - 1); + if (p != null && isTwoSlot(p.kind())) { + setValueAt(i - 1, null); + } + } + } + + /** + * Gets the value in the local variables at the specified index. + * + * @param i the index into the locals + * @return the instruction that produced the value for the specified local + */ + public ValueNode localAt(int i) { + assert i < localsSize : "local variable index out of range: " + i; + return valueAt(i); + } + + /** + * Get the value on the stack at the specified stack index. + * + * @param i the index into the stack, with {@code 0} being the bottom of the stack + * @return the instruction at the specified position in the stack + */ + public ValueNode stackAt(int i) { + assert i >= 0 && i < (localsSize + stackSize); + return valueAt(localsSize + i); + } + + /** + * Retrieves the lock at the specified index in the lock stack. + * @param i the index into the lock stack + * @return the instruction which produced the object at the specified location in the lock stack + */ + public MonitorObject lockAt(int i) { + assert i >= 0; + return (MonitorObject) valueAt(localsSize + stackSize + i); + } + + /** + * Inserts a phi statement into the stack at the specified stack index. + * @param block the block begin for which we are creating the phi + * @param i the index into the stack for which to create a phi + */ + public PhiNode setupPhiForStack(MergeNode block, int i) { + ValueNode p = stackAt(i); + if (p != null) { + if (p instanceof PhiNode) { + PhiNode phi = (PhiNode) p; + if (phi.merge() == block) { + return phi; + } + } + PhiNode phi = graph().unique(new PhiNode(p.kind(), block, PhiType.Value)); + setValueAt(localsSize + i, phi); + return phi; + } + return null; + } + + /** + * Inserts a phi statement for the local at the specified index. + * @param block the block begin for which we are creating the phi + * @param i the index of the local variable for which to create the phi + */ + public PhiNode setupPhiForLocal(MergeNode block, int i) { + ValueNode p = localAt(i); + if (p instanceof PhiNode) { + PhiNode phi = (PhiNode) p; + if (phi.merge() == block) { + return phi; + } + } + PhiNode phi = graph().unique(new PhiNode(p.kind(), block, PhiType.Value)); + storeLocal(i, phi); + return phi; + } + + /** + * Gets the value at a specified index in the set of operand stack and local values represented by this frame. + * This method should only be used to iterate over all the values in this frame, irrespective of whether + * they are on the stack or in local variables. + * To iterate the stack slots, the {@link #stackAt(int)} and {@link #stackSize()} methods should be used. + * To iterate the local variables, the {@link #localAt(int)} and {@link #localsSize()} methods should be used. + * + * @param i a value in the range {@code [0 .. valuesSize()]} + * @return the value at index {@code i} which may be {@code null} + */ + public ValueNode valueAt(int i) { + assert i < (localsSize + stackSize + locksSize); + return values.isEmpty() ? null : values.get(i); + } + + /** + * The number of operand stack slots and local variables in this frame. + * This method should typically only be used in conjunction with {@link #valueAt(int)}. + * To iterate the stack slots, the {@link #stackAt(int)} and {@link #stackSize()} methods should be used. + * To iterate the local variables, the {@link #localAt(int)} and {@link #localsSize()} methods should be used. + * + * @return the number of local variables in this frame + */ + public int valuesSize() { + return localsSize + stackSize; + } + + private boolean checkSize(FrameStateAccess other) { + assert other.stackSize() == stackSize() : "stack sizes do not match"; + assert other.localsSize() == localsSize : "local sizes do not match"; + return true; + } + + public void merge(MergeNode block, FrameStateAccess other) { + assert checkSize(other); + for (int i = 0; i < valuesSize(); i++) { + ValueNode currentValue = valueAt(i); + ValueNode otherValue = other.valueAt(i); + if (currentValue != otherValue) { + if (block.isPhiAtMerge(currentValue)) { + addToPhi((PhiNode) currentValue, otherValue); + } else { + setValueAt(i, combineValues(currentValue, otherValue, block)); + } + } + } + } + + private ValueNode combineValues(ValueNode currentValue, ValueNode otherValue, MergeNode block) { + if (currentValue == null || otherValue == null || currentValue.kind() != otherValue.kind()) { + return null; + } + + PhiNode phi = graph().unique(new PhiNode(currentValue.kind(), block, PhiType.Value)); + for (int j = 0; j < block.phiPredecessorCount(); ++j) { + phi.addInput(currentValue); + } + phi.addInput(otherValue); + assert phi.valueCount() == block.phiPredecessorCount() + 1 : "valueCount=" + phi.valueCount() + " predSize= " + block.phiPredecessorCount(); + return phi; + } + + private void addToPhi(PhiNode phiNode, ValueNode otherValue) { + if (otherValue == null || otherValue.kind() != phiNode.kind()) { + phiNode.replaceAtUsages(null); + phiNode.safeDelete(); + } else { + phiNode.addInput(otherValue); + } + } + + public void mergeLoop(LoopBeginNode block, FrameStateAccess other) { + assert checkSize(other); + for (int i = 0; i < valuesSize(); i++) { + PhiNode currentValue = (PhiNode) valueAt(i); + if (currentValue != null) { + assert currentValue.merge() == block; + assert currentValue.valueCount() == 1; + ValueNode otherValue = other.valueAt(i); + if (otherValue == currentValue) { + deleteRedundantPhi(currentValue, currentValue.firstValue()); + } else if (otherValue == null || otherValue.kind() != currentValue.kind()) { + deleteInvalidPhi(currentValue); + } else { + currentValue.addInput(otherValue); + } + } + } + } + + public void deleteRedundantPhi(PhiNode redundantPhi, ValueNode phiValue) { + Collection<PhiNode> phiUsages = redundantPhi.usages().filter(PhiNode.class).snapshot(); + redundantPhi.replaceAndDelete(phiValue); + for (Node n : phiUsages) { + PhiNode phiNode = (PhiNode) n; + checkRedundantPhi(phiNode); + } + } + + private void checkRedundantPhi(PhiNode phiNode) { + if (phiNode.isDeleted() || phiNode.valueCount() == 1) { + return; + } + + ValueNode singleValue = phiNode.singleValue(); + if (singleValue != null) { + deleteRedundantPhi(phiNode, singleValue); + } + } + + private void deleteInvalidPhi(PhiNode phiNode) { + if (!phiNode.isDeleted()) { + Collection<PhiNode> phiUsages = phiNode.usages().filter(PhiNode.class).snapshot(); + phiNode.replaceAtUsages(null); + phiNode.delete(); + for (Node n : phiUsages) { + deleteInvalidPhi((PhiNode) n); + } + } + } + + public MergeNode block() { + return usages().filter(MergeNode.class).first(); + } + + public StateSplit stateSplit() { + for (Node n : usages()) { + if (n instanceof StateSplit) { + return (StateSplit) n; + } + } + return null; + } + + public Iterable<FrameState> innerFrameStates() { + final Iterator<Node> iterator = usages().iterator(); + return new Iterable<FrameState>() { + @Override + public Iterator<FrameState> iterator() { + return new Iterator<FrameState>() { + private Node next; + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + @Override + public FrameState next() { + forward(); + if (!hasNext()) { + throw new NoSuchElementException(); + } + FrameState res = (FrameState) next; + next = null; + return res; + } + @Override + public boolean hasNext() { + forward(); + return next != null; + } + private void forward() { + while (!(next instanceof FrameState) && iterator.hasNext()) { + next = iterator.next(); + } + } + }; + } + }; + } + + /** + * The interface implemented by a client of {@link FrameState#forEachPhi(MergeNode, PhiProcedure)} and + * {@link FrameState#forEachLivePhi(MergeNode, PhiProcedure)}. + */ + public interface PhiProcedure { + boolean doPhi(PhiNode phi); + } + + /** + * Checks whether this frame state has any {@linkplain PhiNode phi} statements. + */ + public boolean hasPhis() { + for (int i = 0; i < valuesSize(); i++) { + ValueNode value = valueAt(i); + if (value instanceof PhiNode) { + return true; + } + } + return false; + } + + public String toDetailedString() { + StringBuilder sb = new StringBuilder(); + String nl = String.format("%n"); + sb.append("[bci: ").append(bci).append("]"); + if (rethrowException()) { + sb.append(" rethrows Exception"); + } + sb.append(nl); + for (int i = 0; i < localsSize(); ++i) { + ValueNode value = localAt(i); + sb.append(String.format(" local[%d] = %-8s : %s%n", i, value == null ? "bogus" : value.kind().javaName, value)); + } + for (int i = 0; i < stackSize(); ++i) { + ValueNode value = stackAt(i); + sb.append(String.format(" stack[%d] = %-8s : %s%n", i, value == null ? "bogus" : value.kind().javaName, value)); + } + for (int i = 0; i < locksSize(); ++i) { + ValueNode value = lockAt(i); + sb.append(String.format(" lock[%d] = %-8s : %s%n", i, value == null ? "bogus" : value.kind().javaName, value)); + } + return sb.toString(); + } + + @Override + public void generate(LIRGeneratorTool gen) { + // Nothing to do, frame states are processed as part of the handling of AbstractStateSplit nodes. + } + + public static String toString(FrameState fs) { + StringBuilder sb = new StringBuilder(); + String nl = CiUtil.NEW_LINE; + while (fs != null) { + CiUtil.appendLocation(sb, fs.method, fs.bci).append(nl); + for (int i = 0; i < fs.localsSize(); ++i) { + ValueNode value = fs.localAt(i); + sb.append(String.format(" local[%d] = %-8s : %s%n", i, value == null ? "bogus" : value.kind().javaName, value)); + } + for (int i = 0; i < fs.stackSize(); ++i) { + ValueNode value = fs.stackAt(i); + sb.append(String.format(" stack[%d] = %-8s : %s%n", i, value == null ? "bogus" : value.kind().javaName, value)); + } + for (int i = 0; i < fs.locksSize(); ++i) { + ValueNode value = fs.lockAt(i); + sb.append(String.format(" lock[%d] = %-8s : %s%n", i, value == null ? "bogus" : value.kind().javaName, value)); + } + fs = fs.outerFrameState(); + } + return sb.toString(); + } + + @Override + public String toString(Verbosity verbosity) { + if (verbosity == Verbosity.Debugger) { + return toString(this); + } else if (verbosity == Verbosity.Name) { + return super.toString(Verbosity.Name) + "@" + bci; + } else { + return super.toString(verbosity); + } + } + + public void visitFrameState(FrameState i) { + // nothing to do for now + } + + public void insertLoopPhis(LoopBeginNode loopBegin) { + int stackSize = stackSize(); + for (int i = 0; i < stackSize; i++) { + // always insert phis for the stack + ValueNode x = stackAt(i); + if (x != null) { + setupPhiForStack(loopBegin, i).addInput(x); + } + } + int localsSize = localsSize(); + for (int i = 0; i < localsSize; i++) { + ValueNode x = localAt(i); + if (x != null) { + setupPhiForLocal(loopBegin, i).addInput(x); + } + } + } + + @Override + public Map<Object, Object> getDebugProperties() { + Map<Object, Object> properties = super.getDebugProperties(); + properties.put("bci", bci); + properties.put("method", CiUtil.format("%H.%n(%p):%r", method, false)); + StringBuilder str = new StringBuilder(); + for (int i = 0; i < localsSize(); i++) { + str.append(i == 0 ? "" : ", ").append(localAt(i) == null ? "_" : localAt(i).toString(Verbosity.Id)); + } + properties.put("locals", str.toString()); + str = new StringBuilder(); + for (int i = 0; i < stackSize(); i++) { + str.append(i == 0 ? "" : ", ").append(stackAt(i) == null ? "_" : stackAt(i).toString(Verbosity.Id)); + } + properties.put("stack", str.toString()); + str = new StringBuilder(); + for (int i = 0; i < locksSize(); i++) { + str.append(i == 0 ? "" : ", ").append(lockAt(i) == null ? "_" : lockAt(i).toString(Verbosity.Id)); + } + properties.put("locks", str.toString()); + properties.put("rethrowException", rethrowException); + return properties; + } + + @Override + public void setRethrowException(boolean b) { + rethrowException = b; + } + + public CiCodePos toCodePos() { + FrameState caller = outerFrameState(); + CiCodePos callerCodePos = null; + if (caller != null) { + callerCodePos = caller.toCodePos(); + } + return new CiCodePos(callerCodePos, method, bci); + } + + @Override + public boolean verify() { + for (ValueNode value : values) { + assert assertTrue(value == null || value instanceof VirtualObjectNode || (value.kind() != CiKind.Void && value.kind() != CiKind.Illegal), "unexpected value: %s", value); + } + return super.verify(); + } + + // TODO this duplicates code in FrameStateBuilder and needs to go away + public static boolean isTwoSlot(CiKind kind) { + assert kind != CiKind.Void && kind != CiKind.Illegal; + return kind == CiKind.Long || kind == CiKind.Double; + } +}