1 /* 2 * Copyright (c) 2017, 2018, 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 */ 23 24 25 package org.graalvm.compiler.core.gen; 26 27 import java.util.ArrayDeque; 28 import java.util.Arrays; 29 import java.util.Queue; 30 31 import jdk.internal.vm.compiler.collections.EconomicMap; 32 import jdk.internal.vm.compiler.collections.Equivalence; 33 import org.graalvm.compiler.debug.CounterKey; 34 import org.graalvm.compiler.debug.DebugContext; 35 import org.graalvm.compiler.debug.GraalError; 36 import org.graalvm.compiler.lir.ConstantValue; 37 import org.graalvm.compiler.lir.LIRFrameState; 38 import org.graalvm.compiler.lir.LabelRef; 39 import org.graalvm.compiler.lir.Variable; 40 import org.graalvm.compiler.nodes.ConstantNode; 41 import org.graalvm.compiler.nodes.FrameState; 42 import org.graalvm.compiler.nodes.ValueNode; 43 import org.graalvm.compiler.nodes.spi.NodeValueMap; 44 import org.graalvm.compiler.nodes.util.GraphUtil; 45 import org.graalvm.compiler.nodes.virtual.EscapeObjectState; 46 import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; 47 import org.graalvm.compiler.virtual.nodes.MaterializedObjectState; 48 import org.graalvm.compiler.virtual.nodes.VirtualObjectState; 49 50 import jdk.vm.ci.code.BytecodeFrame; 51 import jdk.vm.ci.code.VirtualObject; 52 import jdk.vm.ci.meta.JavaConstant; 53 import jdk.vm.ci.meta.JavaKind; 54 import jdk.vm.ci.meta.JavaType; 55 import jdk.vm.ci.meta.JavaValue; 56 import jdk.vm.ci.meta.ResolvedJavaField; 57 import jdk.vm.ci.meta.ResolvedJavaType; 58 import jdk.vm.ci.meta.Value; 59 60 /** 61 * Builds {@link LIRFrameState}s from {@link FrameState}s. 62 */ 63 public class DebugInfoBuilder { 64 65 protected final NodeValueMap nodeValueMap; 66 protected final DebugContext debug; 67 68 public DebugInfoBuilder(NodeValueMap nodeValueMap, DebugContext debug) { 69 this.nodeValueMap = nodeValueMap; 70 this.debug = debug; 71 } 72 73 private static final JavaValue[] NO_JAVA_VALUES = {}; 74 private static final JavaKind[] NO_JAVA_KINDS = {}; 75 76 protected final EconomicMap<VirtualObjectNode, VirtualObject> virtualObjects = EconomicMap.create(Equivalence.IDENTITY); 77 protected final EconomicMap<VirtualObjectNode, EscapeObjectState> objectStates = EconomicMap.create(Equivalence.IDENTITY); 78 79 protected final Queue<VirtualObjectNode> pendingVirtualObjects = new ArrayDeque<>(); 80 81 public LIRFrameState build(FrameState topState, LabelRef exceptionEdge) { 82 assert virtualObjects.size() == 0; 83 assert objectStates.size() == 0; 84 assert pendingVirtualObjects.size() == 0; 85 86 // collect all VirtualObjectField instances: 87 FrameState current = topState; 88 do { 89 if (current.virtualObjectMappingCount() > 0) { 90 for (EscapeObjectState state : current.virtualObjectMappings()) { 91 if (!objectStates.containsKey(state.object())) { 92 if (!(state instanceof MaterializedObjectState) || ((MaterializedObjectState) state).materializedValue() != state.object()) { 93 objectStates.put(state.object(), state); 94 } 95 } 96 } 97 } 98 current = current.outerFrameState(); 99 } while (current != null); 100 101 BytecodeFrame frame = computeFrameForState(topState); 102 103 VirtualObject[] virtualObjectsArray = null; 104 if (virtualObjects.size() != 0) { 105 // fill in the VirtualObject values 106 VirtualObjectNode vobjNode; 107 while ((vobjNode = pendingVirtualObjects.poll()) != null) { 108 VirtualObject vobjValue = virtualObjects.get(vobjNode); 109 assert vobjValue.getValues() == null; 110 111 JavaValue[] values; 112 JavaKind[] slotKinds; 113 int entryCount = vobjNode.entryCount(); 114 if (entryCount == 0) { 115 values = NO_JAVA_VALUES; 116 slotKinds = NO_JAVA_KINDS; 117 } else { 118 values = new JavaValue[entryCount]; 119 slotKinds = new JavaKind[entryCount]; 120 } 121 if (values.length > 0) { 122 VirtualObjectState currentField = (VirtualObjectState) objectStates.get(vobjNode); 123 assert currentField != null; 124 int pos = 0; 125 for (int i = 0; i < entryCount; i++) { 126 ValueNode value = currentField.values().get(i); 127 if (value == null) { 128 JavaKind entryKind = vobjNode.entryKind(i); 129 values[pos] = JavaConstant.defaultForKind(entryKind.getStackKind()); 130 slotKinds[pos] = entryKind.getStackKind(); 131 pos++; 132 } else if (!value.isConstant() || value.asJavaConstant().getJavaKind() != JavaKind.Illegal) { 133 values[pos] = toJavaValue(value); 134 slotKinds[pos] = toSlotKind(value); 135 pos++; 136 } else { 137 assert value.getStackKind() == JavaKind.Illegal; 138 ValueNode previousValue = currentField.values().get(i - 1); 139 assert (previousValue != null && previousValue.getStackKind().needsTwoSlots()) : vobjNode + " " + i + 140 " " + previousValue + " " + currentField.values().snapshot(); 141 if (previousValue == null || !previousValue.getStackKind().needsTwoSlots()) { 142 // Don't allow the IllegalConstant to leak into the debug info 143 JavaKind entryKind = vobjNode.entryKind(i); 144 values[pos] = JavaConstant.defaultForKind(entryKind.getStackKind()); 145 slotKinds[pos] = entryKind.getStackKind(); 146 pos++; 147 } 148 } 149 } 150 if (pos != entryCount) { 151 values = Arrays.copyOf(values, pos); 152 slotKinds = Arrays.copyOf(slotKinds, pos); 153 } 154 } 155 assert checkValues(vobjValue.getType(), values, slotKinds); 156 vobjValue.setValues(values, slotKinds); 157 } 158 159 virtualObjectsArray = new VirtualObject[virtualObjects.size()]; 160 int index = 0; 161 for (VirtualObject value : virtualObjects.getValues()) { 162 virtualObjectsArray[index++] = value; 163 } 164 virtualObjects.clear(); 165 } 166 objectStates.clear(); 167 168 return newLIRFrameState(exceptionEdge, frame, virtualObjectsArray); 169 } 170 171 private boolean checkValues(ResolvedJavaType type, JavaValue[] values, JavaKind[] slotKinds) { 172 assert (values == null) == (slotKinds == null); 173 if (values != null) { 174 assert values.length == slotKinds.length; 175 if (!type.isArray()) { 176 ResolvedJavaField[] fields = type.getInstanceFields(true); 177 int fieldIndex = 0; 178 for (int valueIndex = 0; valueIndex < values.length; valueIndex++, fieldIndex++) { 179 ResolvedJavaField field = fields[fieldIndex]; 180 JavaKind valKind = slotKinds[valueIndex].getStackKind(); 181 JavaKind fieldKind = storageKind(field.getType()); 182 if ((valKind == JavaKind.Double || valKind == JavaKind.Long) && fieldKind == JavaKind.Int) { 183 assert fieldIndex + 1 < fields.length : String.format("Not enough fields for fieldIndex = %d valueIndex = %d %s %s", fieldIndex, valueIndex, Arrays.toString(fields), 184 Arrays.toString(values)); 185 assert storageKind(fields[fieldIndex + 1].getType()) == JavaKind.Int : String.format("fieldIndex = %d valueIndex = %d %s %s %s", fieldIndex, valueIndex, 186 storageKind(fields[fieldIndex + 1].getType()), Arrays.toString(fields), 187 Arrays.toString(values)); 188 fieldIndex++; 189 } else { 190 assert valKind == fieldKind.getStackKind() : field + ": " + valKind + " != " + fieldKind; 191 } 192 } 193 assert fields.length == fieldIndex : type + ": fields=" + Arrays.toString(fields) + ", field values=" + Arrays.toString(values); 194 } else { 195 JavaKind componentKind = storageKind(type.getComponentType()).getStackKind(); 196 if (componentKind == JavaKind.Object) { 197 for (int i = 0; i < values.length; i++) { 198 assert slotKinds[i].isObject() : slotKinds[i] + " != " + componentKind; 199 } 200 } else { 201 for (int i = 0; i < values.length; i++) { 202 assert slotKinds[i] == componentKind || componentKind.getBitCount() >= slotKinds[i].getBitCount() || 203 (componentKind == JavaKind.Int && slotKinds[i].getBitCount() >= JavaKind.Int.getBitCount()) : slotKinds[i] + " != " + componentKind; 204 } 205 } 206 } 207 } 208 return true; 209 } 210 211 /* 212 * Customization point for subclasses. For example, Word types have a kind Object, but are 213 * internally stored as a primitive value. We do not know about Word types here, but subclasses 214 * do know. 215 */ 216 protected JavaKind storageKind(JavaType type) { 217 return type.getJavaKind(); 218 } 219 220 protected LIRFrameState newLIRFrameState(LabelRef exceptionEdge, BytecodeFrame frame, VirtualObject[] virtualObjectsArray) { 221 return new LIRFrameState(frame, virtualObjectsArray, exceptionEdge); 222 } 223 224 protected BytecodeFrame computeFrameForState(FrameState state) { 225 try { 226 assert state.bci != BytecodeFrame.INVALID_FRAMESTATE_BCI; 227 assert state.bci != BytecodeFrame.UNKNOWN_BCI; 228 assert state.bci != BytecodeFrame.BEFORE_BCI || state.locksSize() == 0; 229 assert state.bci != BytecodeFrame.AFTER_BCI || state.locksSize() == 0; 230 assert state.bci != BytecodeFrame.AFTER_EXCEPTION_BCI || state.locksSize() == 0; 231 assert !(state.getMethod().isSynchronized() && state.bci != BytecodeFrame.BEFORE_BCI && state.bci != BytecodeFrame.AFTER_BCI && state.bci != BytecodeFrame.AFTER_EXCEPTION_BCI) || 232 state.locksSize() > 0; 233 assert state.verify(); 234 235 int numLocals = state.localsSize(); 236 int numStack = state.stackSize(); 237 int numLocks = state.locksSize(); 238 239 int numValues = numLocals + numStack + numLocks; 240 int numKinds = numLocals + numStack; 241 242 JavaValue[] values = numValues == 0 ? NO_JAVA_VALUES : new JavaValue[numValues]; 243 JavaKind[] slotKinds = numKinds == 0 ? NO_JAVA_KINDS : new JavaKind[numKinds]; 244 computeLocals(state, numLocals, values, slotKinds); 245 computeStack(state, numLocals, numStack, values, slotKinds); 246 computeLocks(state, values); 247 248 BytecodeFrame caller = null; 249 if (state.outerFrameState() != null) { 250 caller = computeFrameForState(state.outerFrameState()); 251 } 252 253 if (!state.canProduceBytecodeFrame()) { 254 // This typically means a snippet or intrinsic frame state made it to the backend 255 StackTraceElement ste = state.getCode().asStackTraceElement(state.bci); 256 throw new GraalError("Frame state for %s cannot be converted to a BytecodeFrame since the frame state's code is " + 257 "not the same as the frame state method's code", ste); 258 } 259 260 return new BytecodeFrame(caller, state.getMethod(), state.bci, state.rethrowException(), state.duringCall(), values, slotKinds, numLocals, numStack, numLocks); 261 } catch (GraalError e) { 262 throw e.addContext("FrameState: ", state); 263 } 264 } 265 266 protected void computeLocals(FrameState state, int numLocals, JavaValue[] values, JavaKind[] slotKinds) { 267 for (int i = 0; i < numLocals; i++) { 268 ValueNode local = state.localAt(i); 269 values[i] = toJavaValue(local); 270 slotKinds[i] = toSlotKind(local); 271 } 272 } 273 274 protected void computeStack(FrameState state, int numLocals, int numStack, JavaValue[] values, JavaKind[] slotKinds) { 275 for (int i = 0; i < numStack; i++) { 276 ValueNode stack = state.stackAt(i); 277 values[numLocals + i] = toJavaValue(stack); 278 slotKinds[numLocals + i] = toSlotKind(stack); 279 } 280 } 281 282 protected void computeLocks(FrameState state, JavaValue[] values) { 283 for (int i = 0; i < state.locksSize(); i++) { 284 values[state.localsSize() + state.stackSize() + i] = computeLockValue(state, i); 285 } 286 } 287 288 protected JavaValue computeLockValue(FrameState state, int i) { 289 return toJavaValue(state.lockAt(i)); 290 } 291 292 private static final CounterKey STATE_VIRTUAL_OBJECTS = DebugContext.counter("StateVirtualObjects"); 293 private static final CounterKey STATE_ILLEGALS = DebugContext.counter("StateIllegals"); 294 private static final CounterKey STATE_VARIABLES = DebugContext.counter("StateVariables"); 295 private static final CounterKey STATE_CONSTANTS = DebugContext.counter("StateConstants"); 296 297 private static JavaKind toSlotKind(ValueNode value) { 298 if (value == null) { 299 return JavaKind.Illegal; 300 } else { 301 return value.getStackKind(); 302 } 303 } 304 305 protected JavaValue toJavaValue(ValueNode value) { 306 try { 307 if (value instanceof VirtualObjectNode) { 308 VirtualObjectNode obj = (VirtualObjectNode) value; 309 EscapeObjectState state = objectStates.get(obj); 310 if (state == null && obj.entryCount() > 0) { 311 // null states occur for objects with 0 fields 312 throw new GraalError("no mapping found for virtual object %s", obj); 313 } 314 if (state instanceof MaterializedObjectState) { 315 return toJavaValue(((MaterializedObjectState) state).materializedValue()); 316 } else { 317 assert obj.entryCount() == 0 || state instanceof VirtualObjectState; 318 VirtualObject vobject = virtualObjects.get(obj); 319 if (vobject == null) { 320 vobject = VirtualObject.get(obj.type(), virtualObjects.size()); 321 virtualObjects.put(obj, vobject); 322 pendingVirtualObjects.add(obj); 323 } 324 STATE_VIRTUAL_OBJECTS.increment(debug); 325 return vobject; 326 } 327 } else { 328 // Remove proxies from constants so the constant can be directly embedded. 329 ValueNode unproxied = GraphUtil.unproxify(value); 330 if (unproxied instanceof ConstantNode) { 331 STATE_CONSTANTS.increment(debug); 332 return unproxied.asJavaConstant(); 333 334 } else if (value != null) { 335 STATE_VARIABLES.increment(debug); 336 Value operand = nodeValueMap.operand(value); 337 if (operand instanceof ConstantValue && ((ConstantValue) operand).isJavaConstant()) { 338 return ((ConstantValue) operand).getJavaConstant(); 339 } else { 340 assert operand instanceof Variable : operand + " for " + value; 341 return (JavaValue) operand; 342 } 343 344 } else { 345 // return a dummy value because real value not needed 346 STATE_ILLEGALS.increment(debug); 347 return Value.ILLEGAL; 348 } 349 } 350 } catch (GraalError e) { 351 throw e.addContext("toValue: ", value); 352 } 353 } 354 }