--- /dev/null 2017-01-22 10:16:57.869617664 -0800 +++ new/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/DebugInfoBuilder.java 2017-02-15 16:58:04.234207214 -0800 @@ -0,0 +1,331 @@ +/* + * 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 virtualObjects = Node.newMap(); + protected final Map objectStates = Node.newIdentityMap(); + + protected final Queue 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); + } + } +}