view graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/FrameStateBuilder.java @ 4678:a03f3fd16b22

Fix reexecute boolean in HotSpot debug information. Introduce "duringCall" flag in FrameState that indicates that the bci of the frame state denotes an invoke that should *not* be reexecuted.
author Thomas Wuerthinger <thomas.wuerthinger@oracle.com>
date Thu, 23 Feb 2012 21:43:59 +0100
parents 14a00ee82980
children b11561111585
line wrap: on
line source

/*
 * Copyright (c) 2012, 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.java;

import static com.oracle.max.graal.nodes.ValueUtil.*;
import static java.lang.reflect.Modifier.*;

import java.util.*;

import com.oracle.max.cri.ci.*;
import com.oracle.max.cri.ri.*;
import com.oracle.max.graal.nodes.*;
import com.oracle.max.graal.nodes.PhiNode.PhiType;
import com.oracle.max.graal.nodes.spi.*;
import com.oracle.max.graal.nodes.type.*;


public class FrameStateBuilder implements FrameStateAccess {

    private final StructuredGraph graph;

    private final ValueNode[] locals;
    private final ValueNode[] stack;

    private int stackIndex;
    private boolean rethrowException;

    private final RiResolvedMethod method;

    public FrameStateBuilder(RiResolvedMethod method, int maxLocals, int maxStackSize, StructuredGraph graph, boolean eagerResolve) {
        assert graph != null;
        this.method = method;
        this.graph = graph;
        this.locals = new ValueNode[maxLocals];
        // we always need at least one stack slot (for exceptions)
        int stackSize = Math.max(1, maxStackSize);
        this.stack = new ValueNode[stackSize];

        int javaIndex = 0;
        int index = 0;
        if (!isStatic(method.accessFlags())) {
            // add the receiver
            LocalNode local = graph.unique(new LocalNode(javaIndex, StampFactory.declaredNonNull(method.holder())));
            storeLocal(javaIndex, local);
            javaIndex = 1;
            index = 1;
        }
        RiSignature sig = method.signature();
        int max = sig.argumentCount(false);
        RiResolvedType accessingClass = method.holder();
        for (int i = 0; i < max; i++) {
            RiType type = sig.argumentTypeAt(i, accessingClass);
            if (eagerResolve) {
                type = type.resolve(accessingClass);
            }
            CiKind kind = type.kind(false).stackKind();
            Stamp stamp;
            if (kind == CiKind.Object && type instanceof RiResolvedType) {
                RiResolvedType resolvedType = (RiResolvedType) type;
                stamp = StampFactory.declared(resolvedType);
            } else {
                stamp = StampFactory.forKind(kind);
            }
            LocalNode local = graph.unique(new LocalNode(index, stamp));
            storeLocal(javaIndex, local);
            javaIndex += stackSlots(kind);
            index++;
        }
    }

    @Override
    public String toString() {
        return String.format("FrameStateBuilder[stackSize=%d]", stackIndex);
    }

    public void initializeFrom(FrameState other) {
        assert locals.length == other.localsSize() : "expected: " + locals.length + ", actual: " + other.localsSize();
        assert stack.length >= other.stackSize() : "expected: <=" + stack.length + ", actual: " + other.stackSize();

        this.stackIndex = other.stackSize();
        for (int i = 0; i < other.localsSize(); i++) {
            locals[i] = other.localAt(i);
        }
        for (int i = 0; i < other.stackSize(); i++) {
            stack[i] = other.stackAt(i);
        }
        this.rethrowException = other.rethrowException();
    }

    public FrameState create(int bci) {
        return graph.add(new FrameState(method, bci, locals, stack, stackIndex, rethrowException, false));
    }

    public FrameState duplicateWithException(int bci, ValueNode exceptionObject) {
        FrameState frameState = graph.add(new FrameState(method, bci, locals, new ValueNode[]{exceptionObject}, 1, true, false));
        frameState.setOuterFrameState(outerFrameState());
        return frameState;
    }

    /**
     * Pushes an instruction onto the stack with the expected type.
     * @param kind the type expected for this instruction
     * @param x the instruction to push onto the stack
     */
    public void push(CiKind kind, ValueNode x) {
        assert kind != CiKind.Void;
        xpush(assertKind(kind, x));
        if (isTwoSlot(kind)) {
            xpush(null);
        }
    }

    /**
     * Pushes a value onto the stack without checking the type.
     * @param x the instruction to push onto the stack
     */
    public void xpush(ValueNode x) {
        assert x == null || !x.isDeleted();
        assert x == null || (x.kind() != CiKind.Void && x.kind() != CiKind.Illegal) : "unexpected value: " + x;
        stack[stackIndex++] = x;
    }

    /**
     * Pushes a value onto the stack and checks that it is an int.
     * @param x the instruction to push onto the stack
     */
    public void ipush(ValueNode x) {
        xpush(assertInt(x));
    }

    /**
     * Pushes a value onto the stack and checks that it is a float.
     * @param x the instruction to push onto the stack
     */
    public void fpush(ValueNode x) {
        xpush(assertFloat(x));
    }

    /**
     * Pushes a value onto the stack and checks that it is an object.
     * @param x the instruction to push onto the stack
     */
    public void apush(ValueNode x) {
        xpush(assertObject(x));
    }

    /**
     * Pushes a value onto the stack and checks that it is a JSR return address.
     * @param x the instruction to push onto the stack
     */
    public void jpush(ValueNode x) {
        xpush(assertJsr(x));
    }

    /**
     * Pushes a value onto the stack and checks that it is a long.
     *
     * @param x the instruction to push onto the stack
     */
    public void lpush(ValueNode x) {
        xpush(assertLong(x));
        xpush(null);
    }

    /**
     * Pushes a value onto the stack and checks that it is a double.
     * @param x the instruction to push onto the stack
     */
    public void dpush(ValueNode x) {
        xpush(assertDouble(x));
        xpush(null);
    }

    public void pushReturn(CiKind kind, ValueNode x) {
        if (kind != CiKind.Void) {
            push(kind.stackKind(), x);
        }
    }

    /**
     * Pops an instruction off the stack with the expected type.
     * @param kind the expected type
     * @return the instruction on the top of the stack
     */
    public ValueNode pop(CiKind kind) {
        assert kind != CiKind.Void;
        if (isTwoSlot(kind)) {
            xpop();
        }
        return assertKind(kind, xpop());
    }

    /**
     * Pops a value off of the stack without checking the type.
     * @return x the instruction popped off the stack
     */
    public ValueNode xpop() {
        ValueNode result = stack[--stackIndex];
        assert result == null || !result.isDeleted();
        return result;
    }

    /**
     * Pops a value off of the stack and checks that it is an int.
     * @return x the instruction popped off the stack
     */
    public ValueNode ipop() {
        return assertInt(xpop());
    }

    /**
     * Pops a value off of the stack and checks that it is a float.
     * @return x the instruction popped off the stack
     */
    public ValueNode fpop() {
        return assertFloat(xpop());
    }

    /**
     * Pops a value off of the stack and checks that it is an object.
     * @return x the instruction popped off the stack
     */
    public ValueNode apop() {
        return assertObject(xpop());
    }

    /**
     * Pops a value off of the stack and checks that it is a JSR return address.
     * @return x the instruction popped off the stack
     */
    public ValueNode jpop() {
        return assertJsr(xpop());
    }

    /**
     * Pops a value off of the stack and checks that it is a long.
     * @return x the instruction popped off the stack
     */
    public ValueNode lpop() {
        assertHigh(xpop());
        return assertLong(xpop());
    }

    /**
     * Pops a value off of the stack and checks that it is a double.
     * @return x the instruction popped off the stack
     */
    public ValueNode dpop() {
        assertHigh(xpop());
        return assertDouble(xpop());
    }

    /**
     * Pop the specified number of slots off of this stack and return them as an array of instructions.
     * @param size the number of arguments off of the stack
     * @return an array containing the arguments off of the stack
     */
    public ValueNode[] popArguments(int slotSize, int argSize) {
        int base = stackIndex - slotSize;
        ValueNode[] r = new ValueNode[argSize];
        int argIndex = 0;
        int stackindex = 0;
        while (stackindex < slotSize) {
            ValueNode element = stack[base + stackindex];
            assert element != null;
            r[argIndex++] = element;
            stackindex += stackSlots(element.kind());
        }
        stackIndex = base;
        return r;
    }

    /**
     * Peeks an element from the operand stack.
     * @param argumentNumber The number of the argument, relative from the top of the stack (0 = top).
     *        Long and double arguments only count as one argument, i.e., null-slots are ignored.
     * @return The peeked argument.
     */
    public ValueNode peek(int argumentNumber) {
        int idx = stackSize() - 1;
        for (int i = 0; i < argumentNumber; i++) {
            if (stackAt(idx) == null) {
                idx--;
                assert isTwoSlot(stackAt(idx).kind());
            }
            idx--;
        }
        return stackAt(idx);
    }

    /**
     * Truncates this stack to the specified size.
     * @param size the size to truncate to
     */
    public void truncateStack(int size) {
        stackIndex = size;
        assert stackIndex >= 0;
    }

    /**
     * Clears all values on this stack.
     */
    public void clearStack() {
        stackIndex = 0;
    }

    /**
     * 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 ValueNode loadLocal(int i) {
        ValueNode x = locals[i];
        if (x != null) {
            if (x instanceof PhiNode) {
                assert ((PhiNode) x).type() == PhiType.Value;
                if (x.isDeleted()) {
                    return null;
                }
            }
            assert !isTwoSlot(x.kind()) || locals[i + 1] == null || locals[i + 1] instanceof PhiNode;
        }
        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, ValueNode x) {
        assert x.kind() != CiKind.Void && x.kind() != CiKind.Illegal : "unexpected value: " + x;
        locals[i] = x;
        if (isTwoSlot(x.kind())) {
            // (tw) if this was a double word then kill i+1
            locals[i + 1] = null;
        }
        if (i > 0) {
            // if there was a double word at i - 1, then kill it
            ValueNode p = locals[i - 1];
            if (p != null && isTwoSlot(p.kind())) {
                locals[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 ValueNode stackAt(int i) {
        return stack[i];
    }

    /**
     * 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 ValueNode localAt(int i) {
        return locals[i];
    }

    /**
     * Returns the size of the local variables.
     *
     * @return the size of the local variables
     */
    public int localsSize() {
        return locals.length;
    }

    /**
     * Gets the current size (height) of the stack.
     */
    public int stackSize() {
        return stackIndex;
    }

    public Iterator<ValueNode> locals() {
        return new ValueArrayIterator(locals);
    }

    public Iterator<ValueNode> stack() {
        return new ValueArrayIterator(locals);
    }


    private static class ValueArrayIterator implements Iterator<ValueNode> {
        private final ValueNode[] array;
        private int index;

        public ValueArrayIterator(ValueNode[] array, int length) {
            assert length <= array.length;
            this.array = array;
            this.index = 0;
        }

        public ValueArrayIterator(ValueNode[] array) {
            this(array, array.length);
        }

        @Override
        public boolean hasNext() {
            return index < array.length;
        }

        @Override
        public ValueNode next() {
            return array[index++];
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException("cannot remove from array");
        }

    }


    @Override
    public FrameState duplicate(int bci) {
        return create(bci);
    }

    @Override
    public ValueNode valueAt(int i) {
        if (i < locals.length) {
            return locals[i];
        } else {
            return stack[i - locals.length];
        }
    }

    @Override
    public FrameState outerFrameState() {
        return null;
    }

    public FrameState duplicateWithoutStack(int bci) {
        FrameState frameState = graph.add(new FrameState(method, bci, locals, new ValueNode[0], 0, false, false));
        frameState.setOuterFrameState(outerFrameState());
        return frameState;
    }

    @Override
    public boolean rethrowException() {
        return rethrowException;
    }

    public void setRethrowException(boolean b) {
        rethrowException = b;
    }

    public static int stackSlots(CiKind kind) {
        return isTwoSlot(kind) ? 2 : 1;
    }

    public static boolean isTwoSlot(CiKind kind) {
        assert kind != CiKind.Void && kind != CiKind.Illegal;
        return kind == CiKind.Long || kind == CiKind.Double;
    }
}