/* * 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); } } }