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.compiler.gen;
024
025import java.util.*;
026import java.util.Map.*;
027
028import com.oracle.graal.graph.*;
029import com.oracle.graal.lir.*;
030import com.oracle.graal.nodes.*;
031import com.oracle.graal.nodes.spi.*;
032import com.oracle.graal.nodes.util.*;
033import com.oracle.graal.nodes.virtual.*;
034import com.oracle.graal.virtual.nodes.*;
035
036import jdk.internal.jvmci.code.*;
037import jdk.internal.jvmci.common.*;
038import com.oracle.graal.debug.*;
039import jdk.internal.jvmci.meta.*;
040
041/**
042 * Builds {@link LIRFrameState}s from {@link FrameState}s.
043 */
044public class DebugInfoBuilder {
045
046    protected final NodeValueMap nodeValueMap;
047
048    public DebugInfoBuilder(NodeValueMap nodeValueMap) {
049        this.nodeValueMap = nodeValueMap;
050    }
051
052    protected final Map<VirtualObjectNode, VirtualObject> virtualObjects = Node.newMap();
053    protected final Map<VirtualObjectNode, EscapeObjectState> objectStates = Node.newIdentityMap();
054
055    public LIRFrameState build(FrameState topState, LabelRef exceptionEdge) {
056        assert virtualObjects.size() == 0;
057        assert objectStates.size() == 0;
058
059        // collect all VirtualObjectField instances:
060        FrameState current = topState;
061        do {
062            if (current.virtualObjectMappingCount() > 0) {
063                for (EscapeObjectState state : current.virtualObjectMappings()) {
064                    if (!objectStates.containsKey(state.object())) {
065                        if (!(state instanceof MaterializedObjectState) || ((MaterializedObjectState) state).materializedValue() != state.object()) {
066                            objectStates.put(state.object(), state);
067                        }
068                    }
069                }
070            }
071            current = current.outerFrameState();
072        } while (current != null);
073
074        BytecodeFrame frame = computeFrameForState(topState);
075
076        VirtualObject[] virtualObjectsArray = null;
077        if (virtualObjects.size() != 0) {
078            // fill in the VirtualObject values:
079            // during this process new VirtualObjects might be discovered, so repeat until no more
080            // changes occur.
081            boolean changed;
082            do {
083                changed = false;
084                Map<VirtualObjectNode, VirtualObject> virtualObjectsCopy = Node.newIdentityMap(virtualObjects);
085                for (Entry<VirtualObjectNode, VirtualObject> entry : virtualObjectsCopy.entrySet()) {
086                    if (entry.getValue().getValues() == null) {
087                        VirtualObjectNode vobj = entry.getKey();
088                        Value[] values = new Value[vobj.entryCount()];
089                        if (values.length > 0) {
090                            changed = true;
091                            VirtualObjectState currentField = (VirtualObjectState) objectStates.get(vobj);
092                            assert currentField != null;
093                            int pos = 0;
094                            for (int i = 0; i < vobj.entryCount(); i++) {
095                                if (!currentField.values().get(i).isConstant() || currentField.values().get(i).asJavaConstant().getKind() != Kind.Illegal) {
096                                    values[pos++] = toValue(currentField.values().get(i));
097                                } else {
098                                    assert currentField.values().get(i - 1).getKind() == Kind.Double || currentField.values().get(i - 1).getKind() == Kind.Long : vobj + " " + i + " " +
099                                                    currentField.values().get(i - 1);
100                                }
101                            }
102                            if (pos != vobj.entryCount()) {
103                                Value[] newValues = new Value[pos];
104                                System.arraycopy(values, 0, newValues, 0, pos);
105                                values = newValues;
106                            }
107                        }
108                        entry.getValue().setValues(values);
109                    }
110                }
111            } while (changed);
112
113            virtualObjectsArray = virtualObjects.values().toArray(new VirtualObject[virtualObjects.size()]);
114            virtualObjects.clear();
115        }
116        objectStates.clear();
117
118        return newLIRFrameState(exceptionEdge, frame, virtualObjectsArray);
119    }
120
121    protected LIRFrameState newLIRFrameState(LabelRef exceptionEdge, BytecodeFrame frame, VirtualObject[] virtualObjectsArray) {
122        return new LIRFrameState(frame, virtualObjectsArray, exceptionEdge);
123    }
124
125    protected BytecodeFrame computeFrameForState(FrameState state) {
126        try {
127            assert state.bci != BytecodeFrame.INVALID_FRAMESTATE_BCI;
128            assert state.bci != BytecodeFrame.UNKNOWN_BCI;
129            assert state.bci != BytecodeFrame.BEFORE_BCI || state.locksSize() == 0;
130            assert state.bci != BytecodeFrame.AFTER_BCI || state.locksSize() == 0;
131            assert state.bci != BytecodeFrame.AFTER_EXCEPTION_BCI || state.locksSize() == 0;
132            assert !(state.method().isSynchronized() && state.bci != BytecodeFrame.BEFORE_BCI && state.bci != BytecodeFrame.AFTER_BCI && state.bci != BytecodeFrame.AFTER_EXCEPTION_BCI) ||
133                            state.locksSize() > 0;
134            assert state.verify();
135
136            int numLocals = state.localsSize();
137            int numStack = state.stackSize();
138            int numLocks = state.locksSize();
139
140            Value[] values = new Value[numLocals + numStack + numLocks];
141            computeLocals(state, numLocals, values);
142            computeStack(state, numLocals, numStack, values);
143            computeLocks(state, values);
144
145            BytecodeFrame caller = null;
146            if (state.outerFrameState() != null) {
147                caller = computeFrameForState(state.outerFrameState());
148            }
149            return new BytecodeFrame(caller, state.method(), state.bci, state.rethrowException(), state.duringCall(), values, numLocals, numStack, numLocks);
150        } catch (JVMCIError e) {
151            throw e.addContext("FrameState: ", state);
152        }
153    }
154
155    protected void computeLocals(FrameState state, int numLocals, Value[] values) {
156        for (int i = 0; i < numLocals; i++) {
157            values[i] = computeLocalValue(state, i);
158        }
159    }
160
161    protected Value computeLocalValue(FrameState state, int i) {
162        return toValue(state.localAt(i));
163    }
164
165    protected void computeStack(FrameState state, int numLocals, int numStack, Value[] values) {
166        for (int i = 0; i < numStack; i++) {
167            values[numLocals + i] = computeStackValue(state, i);
168        }
169    }
170
171    protected Value computeStackValue(FrameState state, int i) {
172        return toValue(state.stackAt(i));
173    }
174
175    protected void computeLocks(FrameState state, Value[] values) {
176        for (int i = 0; i < state.locksSize(); i++) {
177            values[state.localsSize() + state.stackSize() + i] = computeLockValue(state, i);
178        }
179    }
180
181    protected Value computeLockValue(FrameState state, int i) {
182        return toValue(state.lockAt(i));
183    }
184
185    private static final DebugMetric STATE_VIRTUAL_OBJECTS = Debug.metric("StateVirtualObjects");
186    private static final DebugMetric STATE_ILLEGALS = Debug.metric("StateIllegals");
187    private static final DebugMetric STATE_VARIABLES = Debug.metric("StateVariables");
188    private static final DebugMetric STATE_CONSTANTS = Debug.metric("StateConstants");
189
190    protected Value toValue(ValueNode value) {
191        try {
192            if (value instanceof VirtualObjectNode) {
193                VirtualObjectNode obj = (VirtualObjectNode) value;
194                EscapeObjectState state = objectStates.get(obj);
195                if (state == null && obj.entryCount() > 0) {
196                    // null states occur for objects with 0 fields
197                    throw new JVMCIError("no mapping found for virtual object %s", obj);
198                }
199                if (state instanceof MaterializedObjectState) {
200                    return toValue(((MaterializedObjectState) state).materializedValue());
201                } else {
202                    assert obj.entryCount() == 0 || state instanceof VirtualObjectState;
203                    VirtualObject vobject = virtualObjects.get(value);
204                    if (vobject == null) {
205                        vobject = VirtualObject.get(obj.type(), null, virtualObjects.size());
206                        virtualObjects.put(obj, vobject);
207                    }
208                    STATE_VIRTUAL_OBJECTS.increment();
209                    return vobject;
210                }
211            } else {
212                // Remove proxies from constants so the constant can be directly embedded.
213                ValueNode unproxied = GraphUtil.unproxify(value);
214                if (unproxied instanceof ConstantNode) {
215                    STATE_CONSTANTS.increment();
216                    return unproxied.asJavaConstant();
217
218                } else if (value != null) {
219                    STATE_VARIABLES.increment();
220                    Value operand = nodeValueMap.operand(value);
221                    assert operand != null && (operand instanceof Variable || operand instanceof JavaConstant) : operand + " for " + value;
222                    return operand;
223
224                } else {
225                    // return a dummy value because real value not needed
226                    STATE_ILLEGALS.increment();
227                    return Value.ILLEGAL;
228                }
229            }
230        } catch (JVMCIError e) {
231            throw e.addContext("toValue: ", value);
232        }
233    }
234}