Mercurial > hg > truffle
view graal/GraalCompiler/src/com/sun/c1x/value/FrameState.java @ 2610:39aa89baa165
cleanup: FrameState copy methods, ImmutableFrameState
author | Lukas Stadler <lukas.stadler@jku.at> |
---|---|
date | Fri, 06 May 2011 13:03:33 +0200 |
parents | 01c5c0443158 |
children | bd235cb4375a |
line wrap: on
line source
/* * 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.sun.c1x.value; import java.util.*; import com.oracle.graal.graph.*; import com.sun.c1x.*; import com.sun.c1x.graph.*; import com.sun.c1x.ir.*; import com.sun.c1x.util.*; import com.sun.cri.ci.*; /** * The {@code FrameState} class encapsulates the frame state (i.e. local variables and * operand stack) at a particular point in the abstract interpretation. */ public abstract class FrameState { /** * The operand stack and local variables. * The local variables occupy the index range {@code [0 .. maxLocals)}. * The operand stack occupies the index range {@code [maxLocals .. values.length)}. * The top of the operand stack is at index {@code maxLocals + stackIndex}. * This does not include the operand stack or local variables of parent frames. * * {@linkplain CiKind#isDoubleWord() Double-word} local variables and * operand stack values occupy 2 slots in this array with the second slot * being {@code null}. */ protected final Value[] values; /** * The depth of the operand stack. * The top of stack value is in {@code values[maxLocals + stackIndex]}. */ protected int stackIndex; /** * The number of local variables. */ protected final int maxLocals; /** * The bytecode index to which this frame state applies. This will be {@code -1} * iff this state is mutable. */ public final int bci; /** * The list of locks held by this frame state. * This does not include locks held by parent frames. */ protected ArrayList<Value> locks; /** * The number of minimum stack slots required for doing IR wrangling during * {@linkplain GraphBuilder bytecode parsing}. While this may hide stack * overflow issues in the original bytecode, the assumption is that such * issues must be caught by the verifier. */ private static final int MINIMUM_STACK_SLOTS = 1; /** * Creates a {@code FrameState} for the given scope and maximum number of stack and local variables. * * @param irScope the inlining context of the method * @param bci the bytecode index of the frame state * @param maxLocals maximum number of locals * @param maxStack maximum size of the stack */ public FrameState(int bci, int maxLocals, int maxStack) { this.bci = bci; this.values = new Value[maxLocals + Math.max(maxStack, MINIMUM_STACK_SLOTS)]; this.maxLocals = maxLocals; C1XMetrics.FrameStatesCreated++; C1XMetrics.FrameStateValuesCreated += this.values.length; } /** * Copies the contents of this frame state so that further updates to either stack aren't reflected in the other. * @param bci the bytecode index of the copy * @param withLocals indicates whether to copy the local state * @param withStack indicates whether to copy the stack state * @param withLocks indicates whether to copy the lock state * * @return a new frame state with the specified components */ public FrameState immutableCopy(int bci, boolean withLocals, boolean withStack, boolean withLocks) { final ImmutableFrameState other = new ImmutableFrameState(bci, localsSize(), maxStackSize()); if (withLocals && withStack) { // fast path: use array copy System.arraycopy(values, 0, other.values, 0, valuesSize()); other.stackIndex = stackIndex; } else { if (withLocals) { System.arraycopy(values, 0, other.values, 0, maxLocals); } if (withStack) { System.arraycopy(values, maxLocals, other.values, other.maxLocals, stackIndex); other.stackIndex = stackIndex; } } if (withLocks) { if (locks != null) { other.locks = new ArrayList<Value>(locks); } } return other; } /** * Gets a mutable copy ({@link MutableFrameState}) of this frame state. */ public MutableFrameState copy() { final MutableFrameState other = new MutableFrameState(bci, localsSize(), maxStackSize()); System.arraycopy(values, 0, other.values, 0, valuesSize()); other.stackIndex = stackIndex; if (locks != null) { other.locks = new ArrayList<Value>(locks); } return other; } /** * Gets a immutable copy ({@link MutableFrameState}) of this frame state. */ public FrameState immutableCopy() { return immutableCopy(bci, true, true, true); } /** * Gets an immutable copy of this frame state but without the stack. */ public FrameState immutableCopyWithEmptyStack() { return immutableCopy(bci, true, false, true); } /** * Gets an immutable copy of this frame state but without the frame info. */ public FrameState immutableCopyCodePosOnly() { return immutableCopy(bci, false, false, false); } public boolean isCompatibleWith(FrameState other) { if (stackSize() != other.stackSize() || localsSize() != other.localsSize() || locksSize() != other.locksSize()) { return false; } for (int i = 0; i < stackIndex; i++) { Value x = stackAt(i); Value y = other.stackAt(i); if (x != y && typeMismatch(x, y)) { return false; } } if (locks != null) { for (int i = 0; i < locks.size(); i++) { if (lockAt(i) != other.lockAt(i)) { return false; } } } return true; } /** * Returns the size of the local variables. * * @return the size of the local variables */ public int localsSize() { return maxLocals; } /** * Gets number of locks held by this frame state. */ public int locksSize() { return locks == null ? 0 : locks.size(); } /** * Gets the current size (height) of the stack. */ public int stackSize() { return stackIndex; } /** * Gets the maximum size (height) of the stack. ] */ public int maxStackSize() { return values.length - maxLocals; } /** * Checks whether the stack is empty. * * @return {@code true} the stack is currently empty */ public boolean stackEmpty() { return stackIndex == 0; } /** * 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 values[i] = null; } /** * Loads the local variable at the specified index. * * @param i the index of the local variable to load * @return the instruction that produced the specified local */ public Value loadLocal(int i) { assert i < maxLocals : "local variable index out of range: " + i; Value x = values[i]; if (x != null) { if (x.isIllegal()) { return null; } assert x.kind.isSingleWord() || values[i + 1] == null || values[i + 1] instanceof Phi; } return x; } /** * 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 */ public void storeLocal(int i, Value x) { assert i < maxLocals : "local variable index out of range: " + i; invalidateLocal(i); values[i] = x; if (isDoubleWord(x)) { // (tw) if this was a double word then kill i+1 values[i + 1] = null; } if (i > 0) { // if there was a double word at i - 1, then kill it Value p = values[i - 1]; if (isDoubleWord(p)) { values[i - 1] = null; } } } /** * 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 final Value stackAt(int i) { assert i < stackIndex; return values[i + maxLocals]; } /** * 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 final Value localAt(int i) { assert i < maxLocals : "local variable index out of range: " + i; return values[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 final Value lockAt(int i) { return locks.get(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 * @param graph */ public void setupPhiForStack(BlockBegin block, int i, Graph graph) { Value p = stackAt(i); if (p != null) { if (p instanceof Phi) { Phi phi = (Phi) p; if (phi.block() == block && phi.isOnStack() && phi.stackIndex() == i) { return; } } values[maxLocals + i] = new Phi(p.kind, block, -i - 1, graph); } } /** * 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 * @param graph */ public void setupPhiForLocal(BlockBegin block, int i, Graph graph) { Value p = values[i]; if (p instanceof Phi) { Phi phi = (Phi) p; if (phi.block() == block && phi.isLocal() && phi.localIndex() == i) { return; } } storeLocal(i, new Phi(p.kind, block, i, graph)); } /** * 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 final Value valueAt(int i) { return values[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 final int valuesSize() { return maxLocals + stackIndex; } public void checkPhis(BlockBegin block, FrameState other) { checkSize(other); final int max = valuesSize(); for (int i = 0; i < max; i++) { Value x = values[i]; Value y = other.values[i]; if (x != null && x != y) { if (x instanceof Phi) { Phi phi = (Phi) x; if (phi.block() == block) { for (int j = 0; j < phi.phiInputCount(); j++) { if (phi.inputIn(other) == null) { throw new CiBailout("phi " + phi + " has null operand at new predecessor"); } } continue; } } throw new CiBailout("instruction is not a phi or null at " + i); } } } private void checkSize(FrameState other) { if (other.stackIndex != stackIndex) { throw new CiBailout("stack sizes do not match"); } else if (other.maxLocals != maxLocals) { throw new CiBailout("local sizes do not match"); } } public void merge(BlockBegin block, FrameState other, Graph graph) { checkSize(other); for (int i = 0; i < valuesSize(); i++) { Value x = values[i]; if (x != null) { Value y = other.values[i]; if (x != y) { if (typeMismatch(x, y)) { if (x instanceof Phi) { Phi phi = (Phi) x; if (phi.block() == block) { phi.makeDead(); } } values[i] = null; continue; } if (i < maxLocals) { // this a local setupPhiForLocal(block, i, graph); } else { // this is a stack slot setupPhiForStack(block, i - maxLocals, graph); } } } } } private static boolean typeMismatch(Value x, Value y) { return y == null || x.kind != y.kind; } private static boolean isDoubleWord(Value x) { return x != null && x.kind.isDoubleWord(); } /** * The interface implemented by a client of {@link FrameState#forEachPhi(BlockBegin, PhiProcedure)} and * {@link FrameState#forEachLivePhi(BlockBegin, PhiProcedure)}. */ public static interface PhiProcedure { boolean doPhi(Phi phi); } /** * Traverses all live {@linkplain Phi phis} of a given block in this frame state. * * @param block only phis {@linkplain Phi#block() associated} with this block are traversed * @param proc the call back invoked for each live phi traversed */ public final boolean forEachLivePhi(BlockBegin block, PhiProcedure proc) { int max = this.valuesSize(); for (int i = 0; i < max; i++) { Value instr = values[i]; if (instr instanceof Phi && !instr.isDeadPhi()) { Phi phi = (Phi) instr; if (block == null || phi.block() == block) { if (!proc.doPhi(phi)) { return false; } } } } return true; } /** * Checks whether this frame state has any {@linkplain Phi phi} statements. */ public boolean hasPhis() { int max = valuesSize(); for (int i = 0; i < max; i++) { Value value = values[i]; if (value instanceof Phi) { return true; } } return false; } /** * Iterates over all the values in this frame state and its callers, including the stack, locals, and locks. * @param closure the closure to apply to each value */ public void valuesDo(ValueClosure closure) { valuesDo(this, closure); } /** * Iterates over all the values of a given frame state and its callers, including the stack, locals, and locks. * @param closure the closure to apply to each value */ public static void valuesDo(FrameState state, ValueClosure closure) { final int max = state.valuesSize(); for (int i = 0; i < max; i++) { if (state.values[i] != null) { Value newValue = closure.apply(state.values[i]); state.values[i] = newValue; } } if (state.locks != null) { for (int i = 0; i < state.locks.size(); i++) { Value instr = state.locks.get(i); if (instr != null) { state.locks.set(i, closure.apply(instr)); } } } } /** * The interface implemented by a client of {@link FrameState#forEachLiveStateValue(ValueProcedure)}. */ public static interface ValueProcedure { void doValue(Value value); } /** * Traverses all {@linkplain Value#isLive() live values} of this frame state. * * @param proc the call back called to process each live value traversed */ public final void forEachLiveStateValue(ValueProcedure proc) { final int max = this.valuesSize(); for (int i = 0; i < max; i++) { Value value = this.values[i]; if (value != null) { proc.doValue(value); } } } @Override public String toString() { StringBuilder sb = new StringBuilder(); String nl = String.format("%n"); sb.append("[bci: ").append(this.bci).append("]").append(nl); for (int i = 0; i < this.localsSize(); ++i) { Value value = this.localAt(i); sb.append(String.format(" local[%d] = %-8s : %s%n", i, value == null ? "bogus" : value.kind.javaName, value)); } for (int i = 0; i < this.stackSize(); ++i) { Value value = this.stackAt(i); sb.append(String.format(" stack[%d] = %-8s : %s%n", i, value == null ? "bogus" : value.kind.javaName, value)); } for (int i = 0; i < this.locksSize(); ++i) { Value value = this.lockAt(i); sb.append(String.format(" lock[%d] = %-8s : %s%n", i, value == null ? "bogus" : value.kind.javaName, value)); } return sb.toString(); } }