blob: 4449ecc3e7df62f6632e0211eb19a5160a21952e [file] [log] [blame]
/*
* Copyright (c) 2011, 2015, 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 org.graalvm.compiler.core.gen;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Map;
import java.util.Queue;
import org.graalvm.compiler.debug.Debug;
import org.graalvm.compiler.debug.DebugCounter;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.lir.ConstantValue;
import org.graalvm.compiler.lir.LIRFrameState;
import org.graalvm.compiler.lir.LabelRef;
import org.graalvm.compiler.lir.Variable;
import org.graalvm.compiler.nodes.ConstantNode;
import org.graalvm.compiler.nodes.FrameState;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.spi.NodeValueMap;
import org.graalvm.compiler.nodes.util.GraphUtil;
import org.graalvm.compiler.nodes.virtual.EscapeObjectState;
import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
import org.graalvm.compiler.virtual.nodes.MaterializedObjectState;
import org.graalvm.compiler.virtual.nodes.VirtualObjectState;
import jdk.vm.ci.code.BytecodeFrame;
import jdk.vm.ci.code.VirtualObject;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.JavaType;
import jdk.vm.ci.meta.JavaValue;
import jdk.vm.ci.meta.ResolvedJavaField;
import jdk.vm.ci.meta.ResolvedJavaType;
import jdk.vm.ci.meta.Value;
/**
* Builds {@link LIRFrameState}s from {@link FrameState}s.
*/
public class DebugInfoBuilder {
protected final NodeValueMap nodeValueMap;
public DebugInfoBuilder(NodeValueMap nodeValueMap) {
this.nodeValueMap = nodeValueMap;
}
private static final JavaValue[] NO_JAVA_VALUES = {};
private static final JavaKind[] NO_JAVA_KINDS = {};
protected final Map<VirtualObjectNode, VirtualObject> virtualObjects = Node.newMap();
protected final Map<VirtualObjectNode, EscapeObjectState> objectStates = Node.newIdentityMap();
protected final Queue<VirtualObjectNode> pendingVirtualObjects = new ArrayDeque<>();
public LIRFrameState build(FrameState topState, LabelRef exceptionEdge) {
assert virtualObjects.size() == 0;
assert objectStates.size() == 0;
assert pendingVirtualObjects.size() == 0;
// collect all VirtualObjectField instances:
FrameState current = topState;
do {
if (current.virtualObjectMappingCount() > 0) {
for (EscapeObjectState state : current.virtualObjectMappings()) {
if (!objectStates.containsKey(state.object())) {
if (!(state instanceof MaterializedObjectState) || ((MaterializedObjectState) state).materializedValue() != state.object()) {
objectStates.put(state.object(), state);
}
}
}
}
current = current.outerFrameState();
} while (current != null);
BytecodeFrame frame = computeFrameForState(topState);
VirtualObject[] virtualObjectsArray = null;
if (virtualObjects.size() != 0) {
// fill in the VirtualObject values
VirtualObjectNode vobjNode;
while ((vobjNode = pendingVirtualObjects.poll()) != null) {
VirtualObject vobjValue = virtualObjects.get(vobjNode);
assert vobjValue.getValues() == null;
JavaValue[] values;
JavaKind[] slotKinds;
int entryCount = vobjNode.entryCount();
if (entryCount == 0) {
values = NO_JAVA_VALUES;
slotKinds = NO_JAVA_KINDS;
} else {
values = new JavaValue[entryCount];
slotKinds = new JavaKind[entryCount];
}
if (values.length > 0) {
VirtualObjectState currentField = (VirtualObjectState) objectStates.get(vobjNode);
assert currentField != null;
int pos = 0;
for (int i = 0; i < entryCount; i++) {
if (!currentField.values().get(i).isConstant() || currentField.values().get(i).asJavaConstant().getJavaKind() != JavaKind.Illegal) {
ValueNode value = currentField.values().get(i);
values[pos] = toJavaValue(value);
slotKinds[pos] = toSlotKind(value);
pos++;
} else {
assert currentField.values().get(i - 1).getStackKind() == JavaKind.Double || currentField.values().get(i - 1).getStackKind() == JavaKind.Long : vobjNode + " " + i + " " +
currentField.values().get(i - 1);
}
}
if (pos != entryCount) {
values = Arrays.copyOf(values, pos);
slotKinds = Arrays.copyOf(slotKinds, pos);
}
}
assert checkValues(vobjValue.getType(), values, slotKinds);
vobjValue.setValues(values, slotKinds);
}
virtualObjectsArray = virtualObjects.values().toArray(new VirtualObject[virtualObjects.size()]);
virtualObjects.clear();
}
objectStates.clear();
return newLIRFrameState(exceptionEdge, frame, virtualObjectsArray);
}
private boolean checkValues(ResolvedJavaType type, JavaValue[] values, JavaKind[] slotKinds) {
assert (values == null) == (slotKinds == null);
if (values != null) {
assert values.length == slotKinds.length;
if (!type.isArray()) {
ResolvedJavaField[] fields = type.getInstanceFields(true);
int fieldIndex = 0;
for (int i = 0; i < values.length; i++) {
ResolvedJavaField field = fields[fieldIndex++];
JavaKind valKind = slotKinds[i].getStackKind();
JavaKind fieldKind = storageKind(field.getType());
if (fieldKind == JavaKind.Object) {
assert valKind.isObject() : field + ": " + valKind + " != " + fieldKind;
} else {
if ((valKind == JavaKind.Double || valKind == JavaKind.Long) && fieldKind == JavaKind.Int) {
assert storageKind(fields[fieldIndex].getType()) == JavaKind.Int;
fieldIndex++;
} else {
assert valKind == fieldKind.getStackKind() : field + ": " + valKind + " != " + fieldKind;
}
}
}
assert fields.length == fieldIndex : type + ": fields=" + Arrays.toString(fields) + ", field values=" + Arrays.toString(values);
} else {
JavaKind componentKind = storageKind(type.getComponentType()).getStackKind();
if (componentKind == JavaKind.Object) {
for (int i = 0; i < values.length; i++) {
assert slotKinds[i].isObject() : slotKinds[i] + " != " + componentKind;
}
} else {
for (int i = 0; i < values.length; i++) {
assert slotKinds[i] == componentKind || componentKind.getBitCount() >= slotKinds[i].getBitCount() ||
(componentKind == JavaKind.Int && slotKinds[i].getBitCount() >= JavaKind.Int.getBitCount()) : slotKinds[i] + " != " + componentKind;
}
}
}
}
return true;
}
/*
* Customization point for subclasses. For example, Word types have a kind Object, but are
* internally stored as a primitive value. We do not know about Word types here, but subclasses
* do know.
*/
protected JavaKind storageKind(JavaType type) {
return type.getJavaKind();
}
protected LIRFrameState newLIRFrameState(LabelRef exceptionEdge, BytecodeFrame frame, VirtualObject[] virtualObjectsArray) {
return new LIRFrameState(frame, virtualObjectsArray, exceptionEdge);
}
protected BytecodeFrame computeFrameForState(FrameState state) {
try {
assert state.bci != BytecodeFrame.INVALID_FRAMESTATE_BCI;
assert state.bci != BytecodeFrame.UNKNOWN_BCI;
assert state.bci != BytecodeFrame.BEFORE_BCI || state.locksSize() == 0;
assert state.bci != BytecodeFrame.AFTER_BCI || state.locksSize() == 0;
assert state.bci != BytecodeFrame.AFTER_EXCEPTION_BCI || state.locksSize() == 0;
assert !(state.getMethod().isSynchronized() && state.bci != BytecodeFrame.BEFORE_BCI && state.bci != BytecodeFrame.AFTER_BCI && state.bci != BytecodeFrame.AFTER_EXCEPTION_BCI) ||
state.locksSize() > 0;
assert state.verify();
int numLocals = state.localsSize();
int numStack = state.stackSize();
int numLocks = state.locksSize();
int numValues = numLocals + numStack + numLocks;
int numKinds = numLocals + numStack;
JavaValue[] values = numValues == 0 ? NO_JAVA_VALUES : new JavaValue[numValues];
JavaKind[] slotKinds = numKinds == 0 ? NO_JAVA_KINDS : new JavaKind[numKinds];
computeLocals(state, numLocals, values, slotKinds);
computeStack(state, numLocals, numStack, values, slotKinds);
computeLocks(state, values);
BytecodeFrame caller = null;
if (state.outerFrameState() != null) {
caller = computeFrameForState(state.outerFrameState());
}
if (!state.canProduceBytecodeFrame()) {
// This typically means a snippet or intrinsic frame state made it to the backend
StackTraceElement ste = state.getCode().asStackTraceElement(state.bci);
throw new GraalError("Frame state for %s cannot be converted to a BytecodeFrame since the frame state's code is " +
"not the same as the frame state method's code", ste);
}
return new BytecodeFrame(caller, state.getMethod(), state.bci, state.rethrowException(), state.duringCall(), values, slotKinds, numLocals, numStack, numLocks);
} catch (GraalError e) {
throw e.addContext("FrameState: ", state);
}
}
protected void computeLocals(FrameState state, int numLocals, JavaValue[] values, JavaKind[] slotKinds) {
for (int i = 0; i < numLocals; i++) {
ValueNode local = state.localAt(i);
values[i] = toJavaValue(local);
slotKinds[i] = toSlotKind(local);
}
}
protected void computeStack(FrameState state, int numLocals, int numStack, JavaValue[] values, JavaKind[] slotKinds) {
for (int i = 0; i < numStack; i++) {
ValueNode stack = state.stackAt(i);
values[numLocals + i] = toJavaValue(stack);
slotKinds[numLocals + i] = toSlotKind(stack);
}
}
protected void computeLocks(FrameState state, JavaValue[] values) {
for (int i = 0; i < state.locksSize(); i++) {
values[state.localsSize() + state.stackSize() + i] = computeLockValue(state, i);
}
}
protected JavaValue computeLockValue(FrameState state, int i) {
return toJavaValue(state.lockAt(i));
}
private static final DebugCounter STATE_VIRTUAL_OBJECTS = Debug.counter("StateVirtualObjects");
private static final DebugCounter STATE_ILLEGALS = Debug.counter("StateIllegals");
private static final DebugCounter STATE_VARIABLES = Debug.counter("StateVariables");
private static final DebugCounter STATE_CONSTANTS = Debug.counter("StateConstants");
private static JavaKind toSlotKind(ValueNode value) {
if (value == null) {
return JavaKind.Illegal;
} else {
return value.getStackKind();
}
}
protected JavaValue toJavaValue(ValueNode value) {
try {
if (value instanceof VirtualObjectNode) {
VirtualObjectNode obj = (VirtualObjectNode) value;
EscapeObjectState state = objectStates.get(obj);
if (state == null && obj.entryCount() > 0) {
// null states occur for objects with 0 fields
throw new GraalError("no mapping found for virtual object %s", obj);
}
if (state instanceof MaterializedObjectState) {
return toJavaValue(((MaterializedObjectState) state).materializedValue());
} else {
assert obj.entryCount() == 0 || state instanceof VirtualObjectState;
VirtualObject vobject = virtualObjects.get(obj);
if (vobject == null) {
vobject = VirtualObject.get(obj.type(), virtualObjects.size());
virtualObjects.put(obj, vobject);
pendingVirtualObjects.add(obj);
}
STATE_VIRTUAL_OBJECTS.increment();
return vobject;
}
} else {
// Remove proxies from constants so the constant can be directly embedded.
ValueNode unproxied = GraphUtil.unproxify(value);
if (unproxied instanceof ConstantNode) {
STATE_CONSTANTS.increment();
return unproxied.asJavaConstant();
} else if (value != null) {
STATE_VARIABLES.increment();
Value operand = nodeValueMap.operand(value);
if (operand instanceof ConstantValue && ((ConstantValue) operand).isJavaConstant()) {
return ((ConstantValue) operand).getJavaConstant();
} else {
assert operand instanceof Variable : operand + " for " + value;
return (JavaValue) operand;
}
} else {
// return a dummy value because real value not needed
STATE_ILLEGALS.increment();
return Value.ILLEGAL;
}
}
} catch (GraalError e) {
throw e.addContext("toValue: ", value);
}
}
}