1/*
2 * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23package org.graalvm.compiler.core.gen;
24
25import java.util.ArrayDeque;
26import java.util.Arrays;
27import java.util.Map;
28import java.util.Queue;
29
30import org.graalvm.compiler.debug.Debug;
31import org.graalvm.compiler.debug.DebugCounter;
32import org.graalvm.compiler.debug.GraalError;
33import org.graalvm.compiler.graph.Node;
34import org.graalvm.compiler.lir.ConstantValue;
35import org.graalvm.compiler.lir.LIRFrameState;
36import org.graalvm.compiler.lir.LabelRef;
37import org.graalvm.compiler.lir.Variable;
38import org.graalvm.compiler.nodes.ConstantNode;
39import org.graalvm.compiler.nodes.FrameState;
40import org.graalvm.compiler.nodes.ValueNode;
41import org.graalvm.compiler.nodes.spi.NodeValueMap;
42import org.graalvm.compiler.nodes.util.GraphUtil;
43import org.graalvm.compiler.nodes.virtual.EscapeObjectState;
44import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
45import org.graalvm.compiler.virtual.nodes.MaterializedObjectState;
46import org.graalvm.compiler.virtual.nodes.VirtualObjectState;
47
48import jdk.vm.ci.code.BytecodeFrame;
49import jdk.vm.ci.code.VirtualObject;
50import jdk.vm.ci.meta.JavaKind;
51import jdk.vm.ci.meta.JavaType;
52import jdk.vm.ci.meta.JavaValue;
53import jdk.vm.ci.meta.ResolvedJavaField;
54import jdk.vm.ci.meta.ResolvedJavaType;
55import jdk.vm.ci.meta.Value;
56
57/**
58 * Builds {@link LIRFrameState}s from {@link FrameState}s.
59 */
60public class DebugInfoBuilder {
61
62    protected final NodeValueMap nodeValueMap;
63
64    public DebugInfoBuilder(NodeValueMap nodeValueMap) {
65        this.nodeValueMap = nodeValueMap;
66    }
67
68    private static final JavaValue[] NO_JAVA_VALUES = {};
69    private static final JavaKind[] NO_JAVA_KINDS = {};
70
71    protected final Map<VirtualObjectNode, VirtualObject> virtualObjects = Node.newMap();
72    protected final Map<VirtualObjectNode, EscapeObjectState> objectStates = Node.newIdentityMap();
73
74    protected final Queue<VirtualObjectNode> pendingVirtualObjects = new ArrayDeque<>();
75
76    public LIRFrameState build(FrameState topState, LabelRef exceptionEdge) {
77        assert virtualObjects.size() == 0;
78        assert objectStates.size() == 0;
79        assert pendingVirtualObjects.size() == 0;
80
81        // collect all VirtualObjectField instances:
82        FrameState current = topState;
83        do {
84            if (current.virtualObjectMappingCount() > 0) {
85                for (EscapeObjectState state : current.virtualObjectMappings()) {
86                    if (!objectStates.containsKey(state.object())) {
87                        if (!(state instanceof MaterializedObjectState) || ((MaterializedObjectState) state).materializedValue() != state.object()) {
88                            objectStates.put(state.object(), state);
89                        }
90                    }
91                }
92            }
93            current = current.outerFrameState();
94        } while (current != null);
95
96        BytecodeFrame frame = computeFrameForState(topState);
97
98        VirtualObject[] virtualObjectsArray = null;
99        if (virtualObjects.size() != 0) {
100            // fill in the VirtualObject values
101            VirtualObjectNode vobjNode;
102            while ((vobjNode = pendingVirtualObjects.poll()) != null) {
103                VirtualObject vobjValue = virtualObjects.get(vobjNode);
104                assert vobjValue.getValues() == null;
105
106                JavaValue[] values;
107                JavaKind[] slotKinds;
108                int entryCount = vobjNode.entryCount();
109                if (entryCount == 0) {
110                    values = NO_JAVA_VALUES;
111                    slotKinds = NO_JAVA_KINDS;
112                } else {
113                    values = new JavaValue[entryCount];
114                    slotKinds = new JavaKind[entryCount];
115                }
116                if (values.length > 0) {
117                    VirtualObjectState currentField = (VirtualObjectState) objectStates.get(vobjNode);
118                    assert currentField != null;
119                    int pos = 0;
120                    for (int i = 0; i < entryCount; i++) {
121                        if (!currentField.values().get(i).isConstant() || currentField.values().get(i).asJavaConstant().getJavaKind() != JavaKind.Illegal) {
122                            ValueNode value = currentField.values().get(i);
123                            values[pos] = toJavaValue(value);
124                            slotKinds[pos] = toSlotKind(value);
125                            pos++;
126                        } else {
127                            assert currentField.values().get(i - 1).getStackKind() == JavaKind.Double || currentField.values().get(i - 1).getStackKind() == JavaKind.Long : vobjNode + " " + i + " " +
128                                            currentField.values().get(i - 1);
129                        }
130                    }
131                    if (pos != entryCount) {
132                        values = Arrays.copyOf(values, pos);
133                        slotKinds = Arrays.copyOf(slotKinds, pos);
134                    }
135                }
136                assert checkValues(vobjValue.getType(), values, slotKinds);
137                vobjValue.setValues(values, slotKinds);
138            }
139
140            virtualObjectsArray = virtualObjects.values().toArray(new VirtualObject[virtualObjects.size()]);
141            virtualObjects.clear();
142        }
143        objectStates.clear();
144
145        return newLIRFrameState(exceptionEdge, frame, virtualObjectsArray);
146    }
147
148    private boolean checkValues(ResolvedJavaType type, JavaValue[] values, JavaKind[] slotKinds) {
149        assert (values == null) == (slotKinds == null);
150        if (values != null) {
151            assert values.length == slotKinds.length;
152            if (!type.isArray()) {
153                ResolvedJavaField[] fields = type.getInstanceFields(true);
154                int fieldIndex = 0;
155                for (int i = 0; i < values.length; i++) {
156                    ResolvedJavaField field = fields[fieldIndex++];
157                    JavaKind valKind = slotKinds[i].getStackKind();
158                    JavaKind fieldKind = storageKind(field.getType());
159                    if (fieldKind == JavaKind.Object) {
160                        assert valKind.isObject() : field + ": " + valKind + " != " + fieldKind;
161                    } else {
162                        if ((valKind == JavaKind.Double || valKind == JavaKind.Long) && fieldKind == JavaKind.Int) {
163                            assert storageKind(fields[fieldIndex].getType()) == JavaKind.Int;
164                            fieldIndex++;
165                        } else {
166                            assert valKind == fieldKind.getStackKind() : field + ": " + valKind + " != " + fieldKind;
167                        }
168                    }
169                }
170                assert fields.length == fieldIndex : type + ": fields=" + Arrays.toString(fields) + ", field values=" + Arrays.toString(values);
171            } else {
172                JavaKind componentKind = storageKind(type.getComponentType()).getStackKind();
173                if (componentKind == JavaKind.Object) {
174                    for (int i = 0; i < values.length; i++) {
175                        assert slotKinds[i].isObject() : slotKinds[i] + " != " + componentKind;
176                    }
177                } else {
178                    for (int i = 0; i < values.length; i++) {
179                        assert slotKinds[i] == componentKind || componentKind.getBitCount() >= slotKinds[i].getBitCount() ||
180                                        (componentKind == JavaKind.Int && slotKinds[i].getBitCount() >= JavaKind.Int.getBitCount()) : slotKinds[i] + " != " + componentKind;
181                    }
182                }
183            }
184        }
185        return true;
186    }
187
188    /*
189     * Customization point for subclasses. For example, Word types have a kind Object, but are
190     * internally stored as a primitive value. We do not know about Word types here, but subclasses
191     * do know.
192     */
193    protected JavaKind storageKind(JavaType type) {
194        return type.getJavaKind();
195    }
196
197    protected LIRFrameState newLIRFrameState(LabelRef exceptionEdge, BytecodeFrame frame, VirtualObject[] virtualObjectsArray) {
198        return new LIRFrameState(frame, virtualObjectsArray, exceptionEdge);
199    }
200
201    protected BytecodeFrame computeFrameForState(FrameState state) {
202        try {
203            assert state.bci != BytecodeFrame.INVALID_FRAMESTATE_BCI;
204            assert state.bci != BytecodeFrame.UNKNOWN_BCI;
205            assert state.bci != BytecodeFrame.BEFORE_BCI || state.locksSize() == 0;
206            assert state.bci != BytecodeFrame.AFTER_BCI || state.locksSize() == 0;
207            assert state.bci != BytecodeFrame.AFTER_EXCEPTION_BCI || state.locksSize() == 0;
208            assert !(state.getMethod().isSynchronized() && state.bci != BytecodeFrame.BEFORE_BCI && state.bci != BytecodeFrame.AFTER_BCI && state.bci != BytecodeFrame.AFTER_EXCEPTION_BCI) ||
209                            state.locksSize() > 0;
210            assert state.verify();
211
212            int numLocals = state.localsSize();
213            int numStack = state.stackSize();
214            int numLocks = state.locksSize();
215
216            int numValues = numLocals + numStack + numLocks;
217            int numKinds = numLocals + numStack;
218
219            JavaValue[] values = numValues == 0 ? NO_JAVA_VALUES : new JavaValue[numValues];
220            JavaKind[] slotKinds = numKinds == 0 ? NO_JAVA_KINDS : new JavaKind[numKinds];
221            computeLocals(state, numLocals, values, slotKinds);
222            computeStack(state, numLocals, numStack, values, slotKinds);
223            computeLocks(state, values);
224
225            BytecodeFrame caller = null;
226            if (state.outerFrameState() != null) {
227                caller = computeFrameForState(state.outerFrameState());
228            }
229
230            if (!state.canProduceBytecodeFrame()) {
231                // This typically means a snippet or intrinsic frame state made it to the backend
232                StackTraceElement ste = state.getCode().asStackTraceElement(state.bci);
233                throw new GraalError("Frame state for %s cannot be converted to a BytecodeFrame since the frame state's code is " +
234                                "not the same as the frame state method's code", ste);
235            }
236
237            return new BytecodeFrame(caller, state.getMethod(), state.bci, state.rethrowException(), state.duringCall(), values, slotKinds, numLocals, numStack, numLocks);
238        } catch (GraalError e) {
239            throw e.addContext("FrameState: ", state);
240        }
241    }
242
243    protected void computeLocals(FrameState state, int numLocals, JavaValue[] values, JavaKind[] slotKinds) {
244        for (int i = 0; i < numLocals; i++) {
245            ValueNode local = state.localAt(i);
246            values[i] = toJavaValue(local);
247            slotKinds[i] = toSlotKind(local);
248        }
249    }
250
251    protected void computeStack(FrameState state, int numLocals, int numStack, JavaValue[] values, JavaKind[] slotKinds) {
252        for (int i = 0; i < numStack; i++) {
253            ValueNode stack = state.stackAt(i);
254            values[numLocals + i] = toJavaValue(stack);
255            slotKinds[numLocals + i] = toSlotKind(stack);
256        }
257    }
258
259    protected void computeLocks(FrameState state, JavaValue[] values) {
260        for (int i = 0; i < state.locksSize(); i++) {
261            values[state.localsSize() + state.stackSize() + i] = computeLockValue(state, i);
262        }
263    }
264
265    protected JavaValue computeLockValue(FrameState state, int i) {
266        return toJavaValue(state.lockAt(i));
267    }
268
269    private static final DebugCounter STATE_VIRTUAL_OBJECTS = Debug.counter("StateVirtualObjects");
270    private static final DebugCounter STATE_ILLEGALS = Debug.counter("StateIllegals");
271    private static final DebugCounter STATE_VARIABLES = Debug.counter("StateVariables");
272    private static final DebugCounter STATE_CONSTANTS = Debug.counter("StateConstants");
273
274    private static JavaKind toSlotKind(ValueNode value) {
275        if (value == null) {
276            return JavaKind.Illegal;
277        } else {
278            return value.getStackKind();
279        }
280    }
281
282    protected JavaValue toJavaValue(ValueNode value) {
283        try {
284            if (value instanceof VirtualObjectNode) {
285                VirtualObjectNode obj = (VirtualObjectNode) value;
286                EscapeObjectState state = objectStates.get(obj);
287                if (state == null && obj.entryCount() > 0) {
288                    // null states occur for objects with 0 fields
289                    throw new GraalError("no mapping found for virtual object %s", obj);
290                }
291                if (state instanceof MaterializedObjectState) {
292                    return toJavaValue(((MaterializedObjectState) state).materializedValue());
293                } else {
294                    assert obj.entryCount() == 0 || state instanceof VirtualObjectState;
295                    VirtualObject vobject = virtualObjects.get(obj);
296                    if (vobject == null) {
297                        vobject = VirtualObject.get(obj.type(), virtualObjects.size());
298                        virtualObjects.put(obj, vobject);
299                        pendingVirtualObjects.add(obj);
300                    }
301                    STATE_VIRTUAL_OBJECTS.increment();
302                    return vobject;
303                }
304            } else {
305                // Remove proxies from constants so the constant can be directly embedded.
306                ValueNode unproxied = GraphUtil.unproxify(value);
307                if (unproxied instanceof ConstantNode) {
308                    STATE_CONSTANTS.increment();
309                    return unproxied.asJavaConstant();
310
311                } else if (value != null) {
312                    STATE_VARIABLES.increment();
313                    Value operand = nodeValueMap.operand(value);
314                    if (operand instanceof ConstantValue && ((ConstantValue) operand).isJavaConstant()) {
315                        return ((ConstantValue) operand).getJavaConstant();
316                    } else {
317                        assert operand instanceof Variable : operand + " for " + value;
318                        return (JavaValue) operand;
319                    }
320
321                } else {
322                    // return a dummy value because real value not needed
323                    STATE_ILLEGALS.increment();
324                    return Value.ILLEGAL;
325                }
326            }
327        } catch (GraalError e) {
328            throw e.addContext("toValue: ", value);
329        }
330    }
331}
332