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