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