# HG changeset patch # User Lukas Stadler # Date 1382961676 -3600 # Node ID bca33c3135de125e9ae64669b065276540c7b361 # Parent b292dd6d02ac03141697baa948ed945680b8da60 PEA: support for unsafe stores of mismatching sizes, cleanup, documentation diff -r b292dd6d02ac -r bca33c3135de graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/VirtualObject.java --- a/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/VirtualObject.java Mon Oct 28 13:10:10 2013 +0200 +++ b/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/VirtualObject.java Mon Oct 28 13:01:16 2013 +0100 @@ -138,12 +138,18 @@ if (values != null) { if (!type.isArray()) { ResolvedJavaField[] fields = type.getInstanceFields(true); - assert fields.length == values.length : type + ": fields=" + Arrays.toString(fields) + ", field values=" + Arrays.toString(values); + int fieldIndex = 0; for (int i = 0; i < values.length; i++) { - ResolvedJavaField field = fields[i]; + ResolvedJavaField field = fields[fieldIndex++]; Kind valKind = values[i].getKind().getStackKind(); - assert valKind == field.getKind().getStackKind() : field + ": " + valKind + " != " + field.getKind().getStackKind(); + if ((valKind == Kind.Double || valKind == Kind.Long) && field.getKind() == Kind.Int) { + assert fields[fieldIndex].getKind() == Kind.Int; + fieldIndex++; + } else { + assert valKind == field.getKind().getStackKind() : field + ": " + valKind + " != " + field.getKind().getStackKind(); + } } + assert fields.length == fieldIndex : type + ": fields=" + Arrays.toString(fields) + ", field values=" + Arrays.toString(values); } else { Kind componentKind = type.getComponentType().getKind().getStackKind(); for (int i = 0; i < values.length; i++) { diff -r b292dd6d02ac -r bca33c3135de graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/Constant.java --- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/Constant.java Mon Oct 28 13:10:10 2013 +0200 +++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/Constant.java Mon Oct 28 13:01:16 2013 +0100 @@ -112,11 +112,15 @@ @Override public String toString() { - String annotationSuffix = ""; - if (getKind() != Kind.Object && getPrimitiveAnnotation() != null) { - annotationSuffix = "{" + getPrimitiveAnnotation() + "}"; + if (getKind() == Kind.Illegal) { + return "illegal"; + } else { + String annotationSuffix = ""; + if (getKind() != Kind.Object && getPrimitiveAnnotation() != null) { + annotationSuffix = "{" + getPrimitiveAnnotation() + "}"; + } + return getKind().getJavaName() + "[" + getKind().format(asBoxedValue()) + (getKind() != Kind.Object ? "|0x" + Long.toHexString(primitive) : "") + "]" + annotationSuffix; } - return getKind().getJavaName() + "[" + getKind().format(asBoxedValue()) + (getKind() != Kind.Object ? "|0x" + Long.toHexString(primitive) : "") + "]" + annotationSuffix; } /** @@ -144,6 +148,8 @@ return asDouble(); case Object: return object; + case Illegal: + return this; } throw new IllegalArgumentException(); } @@ -427,6 +433,10 @@ } } + public static Constant forIllegal() { + return new Constant(Kind.Illegal, null, 0); + } + /** * Returns a constant with the default value for the given kind. */ diff -r b292dd6d02ac -r bca33c3135de graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/DebugInfoBuilder.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/DebugInfoBuilder.java Mon Oct 28 13:10:10 2013 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/DebugInfoBuilder.java Mon Oct 28 13:01:16 2013 +0100 @@ -87,8 +87,17 @@ changed = true; VirtualObjectState currentField = (VirtualObjectState) objectStates.get(vobj); assert currentField != null; + int pos = 0; for (int i = 0; i < vobj.entryCount(); i++) { - values[i] = toValue(currentField.fieldValues().get(i)); + if (!currentField.fieldValues().get(i).isConstant() || currentField.fieldValues().get(i).asConstant().getKind() != Kind.Illegal) { + values[pos++] = toValue(currentField.fieldValues().get(i)); + } else { + assert currentField.fieldValues().get(i - 1).kind() == Kind.Double || currentField.fieldValues().get(i - 1).kind() == Kind.Long : vobj + " " + i + " " + + currentField.fieldValues().get(i - 1); + } + } + if (pos != vobj.entryCount()) { + values = Arrays.copyOf(values, pos); } } entry.getValue().setValues(values); diff -r b292dd6d02ac -r bca33c3135de graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotHostLoweringProvider.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotHostLoweringProvider.java Mon Oct 28 13:10:10 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotHostLoweringProvider.java Mon Oct 28 13:01:16 2013 +0100 @@ -317,10 +317,19 @@ if (value == null) { omittedValues.set(valuePos); } else if (!(value.isConstant() && value.asConstant().isDefaultForKind())) { + // Constant.illegal is always the defaultForKind, so it is skipped VirtualInstanceNode virtualInstance = (VirtualInstanceNode) virtual; - WriteNode write = new WriteNode(newObject, value, createFieldLocation(graph, (HotSpotResolvedJavaField) virtualInstance.field(i), true), - (virtualInstance.field(i).getKind() == Kind.Object && !useDeferredInitBarriers()) ? BarrierType.IMPRECISE : BarrierType.NONE, - virtualInstance.field(i).getKind() == Kind.Object); + Kind accessKind; + HotSpotResolvedJavaField field = (HotSpotResolvedJavaField) virtualInstance.field(i); + if (value.kind().getStackKind() != field.getKind().getStackKind()) { + assert value.kind() == Kind.Long || value.kind() == Kind.Double; + accessKind = value.kind(); + } else { + accessKind = field.getKind(); + } + ConstantLocationNode location = ConstantLocationNode.create(INIT_LOCATION, accessKind, field.offset(), graph); + BarrierType barrierType = (virtualInstance.field(i).getKind() == Kind.Object && !useDeferredInitBarriers()) ? BarrierType.IMPRECISE : BarrierType.NONE; + WriteNode write = new WriteNode(newObject, value, location, barrierType, virtualInstance.field(i).getKind() == Kind.Object); graph.addAfterFixed(newObject, graph.add(write)); } valuePos++; @@ -338,8 +347,20 @@ if (value == null) { omittedValues.set(valuePos); } else if (!(value.isConstant() && value.asConstant().isDefaultForKind())) { - WriteNode write = new WriteNode(newObject, value, createArrayLocation(graph, element.getKind(), ConstantNode.forInt(i, graph), true), - (value.kind() == Kind.Object && !useDeferredInitBarriers()) ? BarrierType.PRECISE : BarrierType.NONE, value.kind() == Kind.Object); + // Constant.illegal is always the defaultForKind, so it is skipped + Kind componentKind = element.getKind(); + Kind accessKind; + if (value.kind().getStackKind() != componentKind.getStackKind()) { + assert value.kind() == Kind.Long || value.kind() == Kind.Double; + accessKind = value.kind(); + } else { + accessKind = componentKind; + } + + int scale = getScalingFactor(componentKind); + ConstantLocationNode location = ConstantLocationNode.create(INIT_LOCATION, accessKind, getArrayBaseOffset(componentKind) + i * scale, graph); + BarrierType barrierType = (componentKind == Kind.Object && !useDeferredInitBarriers()) ? BarrierType.IMPRECISE : BarrierType.NONE; + WriteNode write = new WriteNode(newObject, value, location, barrierType, componentKind == Kind.Object); graph.addAfterFixed(newObject, graph.add(write)); } valuePos++; diff -r b292dd6d02ac -r bca33c3135de graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ArrayCopyNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ArrayCopyNode.java Mon Oct 28 13:10:10 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ArrayCopyNode.java Mon Oct 28 13:01:16 2013 +0100 @@ -187,7 +187,7 @@ return; } for (int i = 0; i < length; i++) { - tool.setVirtualEntry(destState, destPos + i, srcState.getEntry(srcPos + i)); + tool.setVirtualEntry(destState, destPos + i, srcState.getEntry(srcPos + i), false); } tool.delete(); if (Debug.isLogEnabled()) { diff -r b292dd6d02ac -r bca33c3135de graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/UnsafeStoreNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/UnsafeStoreNode.java Mon Oct 28 13:10:10 2013 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/UnsafeStoreNode.java Mon Oct 28 13:01:16 2013 +0100 @@ -77,10 +77,23 @@ long offset = indexValue.asConstant().asLong(); int entryIndex = state.getVirtualObject().entryIndexForOffset(offset); if (entryIndex != -1) { + Kind entryKind = state.getVirtualObject().entryKind(entryIndex); ValueNode entry = state.getEntry(entryIndex); - if (entry.kind() == value.kind() || state.getVirtualObject().entryKind(entryIndex) == accessKind()) { - tool.setVirtualEntry(state, entryIndex, value()); + if (entry.kind() == value.kind() || entryKind == accessKind()) { + tool.setVirtualEntry(state, entryIndex, value(), true); tool.delete(); + } else { + if ((accessKind() == Kind.Long || accessKind() == Kind.Double) && entryKind == Kind.Int) { + int nextIndex = state.getVirtualObject().entryIndexForOffset(offset + 4); + if (nextIndex != -1) { + Kind nextKind = state.getVirtualObject().entryKind(nextIndex); + if (nextKind == Kind.Int) { + tool.setVirtualEntry(state, entryIndex, value(), true); + tool.setVirtualEntry(state, nextIndex, ConstantNode.forConstant(Constant.forIllegal(), tool.getMetaAccessProvider(), graph()), true); + tool.delete(); + } + } + } } } } diff -r b292dd6d02ac -r bca33c3135de graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/WriteNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/WriteNode.java Mon Oct 28 13:10:10 2013 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/WriteNode.java Mon Oct 28 13:01:16 2013 +0100 @@ -98,7 +98,7 @@ VirtualObjectNode virtual = state.getVirtualObject(); int entryIndex = virtual.entryIndexForOffset(constantLocation.getDisplacement()); if (entryIndex != -1 && virtual.entryKind(entryIndex) == constantLocation.getValueKind()) { - tool.setVirtualEntry(state, entryIndex, value()); + tool.setVirtualEntry(state, entryIndex, value(), false); tool.delete(); } } diff -r b292dd6d02ac -r bca33c3135de graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/StoreFieldNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/StoreFieldNode.java Mon Oct 28 13:10:10 2013 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/StoreFieldNode.java Mon Oct 28 13:01:16 2013 +0100 @@ -74,7 +74,7 @@ if (state != null && state.getState() == EscapeState.Virtual) { int fieldIndex = ((VirtualInstanceNode) state.getVirtualObject()).fieldIndex(field()); if (fieldIndex != -1) { - tool.setVirtualEntry(state, fieldIndex, value()); + tool.setVirtualEntry(state, fieldIndex, value(), false); tool.delete(); } } diff -r b292dd6d02ac -r bca33c3135de graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/StoreIndexedNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/StoreIndexedNode.java Mon Oct 28 13:10:10 2013 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/StoreIndexedNode.java Mon Oct 28 13:01:16 2013 +0100 @@ -76,7 +76,7 @@ ResolvedJavaType componentType = arrayState.getVirtualObject().type().getComponentType(); if (componentType.isPrimitive() || ObjectStamp.isObjectAlwaysNull(value) || componentType.getSuperclass() == null || (ObjectStamp.typeOrNull(value) != null && componentType.isAssignableFrom(ObjectStamp.typeOrNull(value)))) { - tool.setVirtualEntry(arrayState, index, value()); + tool.setVirtualEntry(arrayState, index, value(), false); tool.delete(); } } diff -r b292dd6d02ac -r bca33c3135de graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/VirtualizerTool.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/VirtualizerTool.java Mon Oct 28 13:10:10 2013 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/VirtualizerTool.java Mon Oct 28 13:01:16 2013 +0100 @@ -84,8 +84,9 @@ * @param state the state. * @param index the index to be set. * @param value the new value for the given index. + * @param unsafe if true, then mismatching value {@link Kind}s will be accepted. */ - void setVirtualEntry(State state, int index, ValueNode value); + void setVirtualEntry(State state, int index, ValueNode value, boolean unsafe); // scalar replacement diff -r b292dd6d02ac -r bca33c3135de graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/type/GenericStamp.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/type/GenericStamp.java Mon Oct 28 13:10:10 2013 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/type/GenericStamp.java Mon Oct 28 13:01:16 2013 +0100 @@ -27,7 +27,7 @@ public final class GenericStamp extends Stamp { public enum GenericStampType { - Dependency, Extension, Virtual, Condition, Void + Dependency, Extension, Condition, Void } private final GenericStampType type; diff -r b292dd6d02ac -r bca33c3135de graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/type/StampFactory.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/type/StampFactory.java Mon Oct 28 13:10:10 2013 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/type/StampFactory.java Mon Oct 28 13:01:16 2013 +0100 @@ -37,7 +37,6 @@ private static final Stamp objectAlwaysNullStamp = new ObjectStamp(null, false, false, true); private static final Stamp dependencyStamp = new GenericStamp(GenericStampType.Dependency); private static final Stamp extensionStamp = new GenericStamp(GenericStampType.Extension); - private static final Stamp virtualStamp = new GenericStamp(GenericStampType.Virtual); private static final Stamp conditionStamp = new GenericStamp(GenericStampType.Condition); private static final Stamp voidStamp = new GenericStamp(GenericStampType.Void); private static final Stamp nodeIntrinsicStamp = new ObjectStamp(null, false, false, false); @@ -94,10 +93,6 @@ return extensionStamp; } - public static Stamp virtual() { - return virtualStamp; - } - public static Stamp condition() { return conditionStamp; } @@ -163,6 +158,8 @@ return forFloat(kind, value.asFloat(), value.asFloat(), !Float.isNaN(value.asFloat())); case Double: return forFloat(kind, value.asDouble(), value.asDouble(), !Double.isNaN(value.asDouble())); + case Illegal: + return illegal(Kind.Illegal); default: throw new GraalInternalError("unexpected kind: %s", kind); } diff -r b292dd6d02ac -r bca33c3135de graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualArrayNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualArrayNode.java Mon Oct 28 13:10:10 2013 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualArrayNode.java Mon Oct 28 13:01:16 2013 +0100 @@ -36,7 +36,7 @@ private final int length; public VirtualArrayNode(ResolvedJavaType componentType, int length) { - super(true); + super(componentType.getArrayClass(), true); this.componentType = componentType; this.length = length; } diff -r b292dd6d02ac -r bca33c3135de graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualInstanceNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualInstanceNode.java Mon Oct 28 13:10:10 2013 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualInstanceNode.java Mon Oct 28 13:01:16 2013 +0100 @@ -37,7 +37,7 @@ } public VirtualInstanceNode(ResolvedJavaType type, ResolvedJavaField[] fields, boolean hasIdentity) { - super(hasIdentity); + super(type, hasIdentity); this.type = type; this.fields = fields; } diff -r b292dd6d02ac -r bca33c3135de graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualObjectNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualObjectNode.java Mon Oct 28 13:10:10 2013 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualObjectNode.java Mon Oct 28 13:01:16 2013 +0100 @@ -32,8 +32,8 @@ private boolean hasIdentity; - public VirtualObjectNode(boolean hasIdentity) { - super(StampFactory.virtual()); + public VirtualObjectNode(ResolvedJavaType type, boolean hasIdentity) { + super(StampFactory.exactNonNull(type)); this.hasIdentity = hasIdentity; } diff -r b292dd6d02ac -r bca33c3135de graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java --- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java Mon Oct 28 13:10:10 2013 +0200 +++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java Mon Oct 28 13:01:16 2013 +0100 @@ -132,13 +132,14 @@ } /** - * Sets the phi node's input at the given index to the given value. + * Sets the phi node's input at the given index to the given value, adding new phi inputs as + * needed. * * @param node The phi node whose input should be changed. * @param index The index of the phi input to be changed. * @param value The new value for the phi input. */ - public void setPhiInput(final PhiNode node, final int index, final ValueNode value) { + public void initializePhiInput(final PhiNode node, final int index, final ValueNode value) { add(new Effect() { @Override @@ -149,7 +150,7 @@ @Override public void apply(StructuredGraph graph, ArrayList obsoleteNodes) { assert node.isAlive() && value.isAlive() && index >= 0; - node.setValueAt(index, value); + node.initializeValueAt(index, value); } }); } diff -r b292dd6d02ac -r bca33c3135de graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java --- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java Mon Oct 28 13:10:10 2013 +0200 +++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java Mon Oct 28 13:01:16 2013 +0100 @@ -208,20 +208,26 @@ } } - private void ensureMaterialized(BlockT state, ObjectState obj, FixedNode materializeBefore, GraphEffectList effects, DebugMetric metric) { + /** + * @return true if materialization happened, false if not. + */ + private boolean ensureMaterialized(BlockT state, ObjectState obj, FixedNode materializeBefore, GraphEffectList effects, DebugMetric metric) { assert obj != null; if (obj.getState() == EscapeState.Virtual) { metric.increment(); state.materializeBefore(materializeBefore, obj.virtual, EscapeState.Materialized, effects); + assert !obj.isVirtual(); + return true; } else { assert obj.getState() == EscapeState.Materialized; + return false; } - assert !obj.isVirtual(); } - private void replaceWithMaterialized(ValueNode value, Node usage, FixedNode materializeBefore, BlockT state, ObjectState obj, GraphEffectList effects, DebugMetric metric) { - ensureMaterialized(state, obj, materializeBefore, effects, metric); + private boolean replaceWithMaterialized(ValueNode value, Node usage, FixedNode materializeBefore, BlockT state, ObjectState obj, GraphEffectList effects, DebugMetric metric) { + boolean materialized = ensureMaterialized(state, obj, materializeBefore, effects, metric); effects.replaceFirstInput(usage, value, obj.getMaterializedValue()); + return materialized; } @Override @@ -275,8 +281,7 @@ protected class MergeProcessor extends EffectsClosure.MergeProcessor { private final HashMap materializedPhis = new HashMap<>(); - private final IdentityHashMap valuePhis = new IdentityHashMap<>(); - private final IdentityHashMap valueObjectMergePhis = new IdentityHashMap<>(); + private final IdentityHashMap valuePhis = new IdentityHashMap<>(); private final IdentityHashMap valueObjectVirtuals = new IdentityHashMap<>(); public MergeProcessor(Block mergeBlock) { @@ -292,21 +297,13 @@ return result; } - private PhiNode[] getValuePhis(VirtualObjectNode virtual) { - PhiNode[] result = valuePhis.get(virtual); - if (result == null) { - result = new PhiNode[virtual.entryCount()]; - valuePhis.put(virtual, result); - } - return result; - } - - private PhiNode[] getValueObjectMergePhis(PhiNode phi, int entryCount) { - PhiNode[] result = valueObjectMergePhis.get(phi); + private PhiNode[] getValuePhis(ValueNode key, int entryCount) { + PhiNode[] result = valuePhis.get(key); if (result == null) { result = new PhiNode[entryCount]; - valueObjectMergePhis.put(phi, result); + valuePhis.put(key, result); } + assert result.length == entryCount; return result; } @@ -319,194 +316,299 @@ return result; } + /** + * Merge all predecessor block states into one block state. This is an iterative process, + * because merging states can lead to materializations which make previous parts of the + * merging operation invalid. The merging process is executed until a stable state has been + * reached. This method needs to be careful to place the effects of the merging operation + * into the correct blocks. + * + * @param states the predecessor block states of the merge + */ @Override protected void merge(List states) { super.merge(states); - /* - * Iterative processing: Merging the materialized/virtual state of virtual objects can - * lead to new materializations, which can lead to new materializations because of phis, - * and so on. - */ - + // calculate the set of virtual objects that exist in all predecessors HashSet virtualObjTemp = new HashSet<>(states.get(0).getVirtualObjects()); for (int i = 1; i < states.size(); i++) { virtualObjTemp.retainAll(states.get(i).getVirtualObjects()); } + ObjectState[] objStates = new ObjectState[states.size()]; boolean materialized; do { mergeEffects.clear(); afterMergeEffects.clear(); materialized = false; for (VirtualObjectNode object : virtualObjTemp) { - ObjectState[] objStates = new ObjectState[states.size()]; - for (int i = 0; i < states.size(); i++) { - objStates[i] = states.get(i).getObjectStateOptional(object); - assert objStates[i] != null; + for (int i = 0; i < objStates.length; i++) { + objStates[i] = states.get(i).getObjectState(object); } + + // determine if all inputs are virtual or the same materialized value int virtual = 0; ObjectState startObj = objStates[0]; boolean locksMatch = true; - ValueNode singleValue = startObj.isVirtual() ? null : startObj.getMaterializedValue(); + ValueNode uniqueMaterializedValue = startObj.isVirtual() ? null : startObj.getMaterializedValue(); for (ObjectState obj : objStates) { if (obj.isVirtual()) { virtual++; - singleValue = null; - } else { - if (obj.getMaterializedValue() != singleValue) { - singleValue = null; - } + uniqueMaterializedValue = null; + locksMatch &= obj.locksEqual(startObj); + } else if (obj.getMaterializedValue() != uniqueMaterializedValue) { + uniqueMaterializedValue = null; } - locksMatch &= obj.locksEqual(startObj); } - if (virtual < states.size() || !locksMatch) { - if (singleValue == null) { + if (virtual == objStates.length && locksMatch) { + materialized |= mergeObjectStates(object, objStates, states); + } else { + if (uniqueMaterializedValue != null) { + newState.addObject(object, new ObjectState(object, uniqueMaterializedValue, EscapeState.Materialized, null)); + } else { PhiNode materializedValuePhi = getCachedPhi(object, Kind.Object); mergeEffects.addFloatingNode(materializedValuePhi, "materializedPhi"); - for (int i = 0; i < states.size(); i++) { - BlockT state = states.get(i); + for (int i = 0; i < objStates.length; i++) { ObjectState obj = objStates[i]; - materialized |= obj.isVirtual(); Block predecessor = mergeBlock.getPredecessors().get(i); - ensureMaterialized(state, obj, predecessor.getEndNode(), blockEffects.get(predecessor), METRIC_MATERIALIZATIONS_MERGE); + materialized |= ensureMaterialized(states.get(i), obj, predecessor.getEndNode(), blockEffects.get(predecessor), METRIC_MATERIALIZATIONS_MERGE); afterMergeEffects.addPhiInput(materializedValuePhi, obj.getMaterializedValue()); } newState.addObject(object, new ObjectState(object, materializedValuePhi, EscapeState.Materialized, null)); - } else { - newState.addObject(object, new ObjectState(object, singleValue, EscapeState.Materialized, null)); } - } else { - assert virtual == states.size(); - ValueNode[] values = startObj.getEntries().clone(); - PhiNode[] phis = getValuePhis(object); - for (int index = 0; index < values.length; index++) { - for (int i = 1; i < states.size(); i++) { - ValueNode[] fields = objStates[i].getEntries(); - if (phis[index] == null && values[index] != fields[index]) { - Kind kind = values[index].kind(); - if (kind == Kind.Illegal) { - // Can happen if one of the values is virtual and is only - // materialized in the following loop. - kind = Kind.Object; - } - phis[index] = new PhiNode(kind, merge); - } - } - } - outer: for (int index = 0; index < values.length; index++) { - if (phis[index] != null) { - mergeEffects.addFloatingNode(phis[index], "virtualMergePhi"); - for (int i = 0; i < states.size(); i++) { - if (!objStates[i].isVirtual()) { - break outer; - } - ValueNode[] fields = objStates[i].getEntries(); - if (fields[index] instanceof VirtualObjectNode) { - ObjectState obj = states.get(i).getObjectState((VirtualObjectNode) fields[index]); - materialized |= obj.isVirtual(); - Block predecessor = mergeBlock.getPredecessors().get(i); - ensureMaterialized(states.get(i), obj, predecessor.getEndNode(), blockEffects.get(predecessor), METRIC_MATERIALIZATIONS_MERGE); - fields[index] = obj.getMaterializedValue(); - } - afterMergeEffects.addPhiInput(phis[index], fields[index]); - } - values[index] = phis[index]; - } - } - newState.addObject(object, new ObjectState(object, values, EscapeState.Virtual, startObj.getLocks())); } } for (PhiNode phi : merge.phis()) { if (usages.isMarked(phi) && phi.type() == PhiType.Value) { - materialized |= processPhi(phi, states); + materialized |= processPhi(phi, states, virtualObjTemp); } } } while (materialized); } - private boolean processPhi(PhiNode phi, List states) { - aliases.set(phi, null); - assert states.size() == phi.valueCount(); - int virtualInputs = 0; - boolean materialized = false; - VirtualObjectNode sameObject = null; - ResolvedJavaType sameType = null; - int sameEntryCount = -1; - boolean hasIdentity = false; - for (int i = 0; i < phi.valueCount(); i++) { - ValueNode value = phi.valueAt(i); - ObjectState obj = getObjectState(states.get(i), value); - if (obj != null) { - if (obj.isVirtual()) { - virtualInputs++; - if (i == 0) { - sameObject = obj.virtual; - sameType = obj.virtual.type(); - sameEntryCount = obj.virtual.entryCount(); - } else { - if (sameObject != obj.virtual) { - sameObject = null; + /** + * Try to merge multiple virtual object states into a single object state. If the incoming + * object states are compatible, then this method will create PhiNodes for the object's + * entries where needed. If they are incompatible, then all incoming virtual objects will be + * materialized, and a PhiNode for the materialized values will be created. Object states + * can be incompatible if they contain {@code long} or {@code double} values occupying two + * {@code int} slots in such a way that that their values cannot be merged using PhiNodes. + * + * @param object the virtual object that should be associated with the merged object state + * @param objStates the incoming object states (all of which need to be virtual) + * @param blockStates the predecessor block states of the merge + * @return true if materialization happened during the merge, false otherwise + */ + private boolean mergeObjectStates(VirtualObjectNode object, ObjectState[] objStates, List blockStates) { + boolean compatible = true; + ValueNode[] values = objStates[0].getEntries().clone(); + + // determine all entries that have a two-slot value + Kind[] twoSlotKinds = null; + outer: for (int i = 0; i < objStates.length; i++) { + ValueNode[] entries = objStates[i].getEntries(); + int valueIndex = 0; + while (valueIndex < values.length) { + Kind otherKind = entries[valueIndex].kind(); + Kind entryKind = object.entryKind(valueIndex); + if (entryKind == Kind.Int && (otherKind == Kind.Long || otherKind == Kind.Double)) { + if (twoSlotKinds == null) { + twoSlotKinds = new Kind[values.length]; + } + if (twoSlotKinds[valueIndex] != null && twoSlotKinds[valueIndex] != otherKind) { + compatible = false; + break outer; + } + twoSlotKinds[valueIndex] = otherKind; + // skip the next entry + valueIndex++; + } else { + assert entryKind.getStackKind() == otherKind.getStackKind() : entryKind + " vs " + otherKind; + } + valueIndex++; + } + } + if (compatible && twoSlotKinds != null) { + // if there are two-slot values then make sure the incoming states can be merged + outer: for (int valueIndex = 0; valueIndex < values.length; valueIndex++) { + if (twoSlotKinds[valueIndex] != null) { + assert valueIndex < object.entryCount() - 1 && object.entryKind(valueIndex) == Kind.Int && object.entryKind(valueIndex + 1) == Kind.Int; + for (int i = 0; i < objStates.length; i++) { + ValueNode value = objStates[i].getEntry(valueIndex); + Kind valueKind = value.kind(); + if (valueKind != twoSlotKinds[valueIndex]) { + ValueNode nextValue = objStates[i].getEntry(valueIndex + 1); + if (value.isConstant() && value.asConstant().equals(Constant.INT_0) && nextValue.isConstant() && nextValue.asConstant().equals(Constant.INT_0)) { + // rewrite to a zero constant of the larger kind + objStates[i].setEntry(valueIndex, ConstantNode.defaultForKind(twoSlotKinds[valueIndex], merge.graph())); + objStates[i].setEntry(valueIndex + 1, ConstantNode.forConstant(Constant.forIllegal(), tool.getMetaAccessProvider(), merge.graph())); + } else { + compatible = false; + break outer; + } } - if (sameType != obj.virtual.type()) { - sameType = null; - } - if (sameEntryCount != obj.virtual.entryCount()) { - sameEntryCount = -1; - } - hasIdentity |= obj.virtual.hasIdentity(); } - } else { - afterMergeEffects.setPhiInput(phi, i, obj.getMaterializedValue()); } } } - boolean materialize = false; - if (virtualInputs == 0) { - // nothing to do... - } else if (virtualInputs == phi.valueCount()) { - if (sameObject != null) { - addAndMarkAlias(sameObject, phi); - } else if (sameType != null && sameEntryCount != -1) { - if (!hasIdentity) { - VirtualObjectNode virtual = getValueObjectVirtual(phi, getObjectState(states.get(0), phi.valueAt(0)).virtual); + + if (compatible) { + // virtual objects are compatible: create phis for all entries that need them + PhiNode[] phis = getValuePhis(object, object.entryCount()); + int valueIndex = 0; + while (valueIndex < values.length) { + for (int i = 1; i < objStates.length; i++) { + ValueNode[] fields = objStates[i].getEntries(); + if (phis[valueIndex] == null && values[valueIndex] != fields[valueIndex]) { + phis[valueIndex] = new PhiNode(values[valueIndex].kind(), merge); + } + } + if (twoSlotKinds != null && twoSlotKinds[valueIndex] != null) { + // skip an entry after a long/double value that occupies two int slots + valueIndex++; + phis[valueIndex] = null; + values[valueIndex] = ConstantNode.forConstant(Constant.forIllegal(), tool.getMetaAccessProvider(), merge.graph()); + } + valueIndex++; + } + + boolean materialized = false; + for (int i = 0; i < values.length; i++) { + PhiNode phi = phis[i]; + if (phi != null) { + mergeEffects.addFloatingNode(phi, "virtualMergePhi"); + if (object.entryKind(i) == Kind.Object) { + materialized |= mergeObjectEntry(objStates, blockStates, phi, i); + } else { + mergePrimitiveEntry(objStates, phi, i); + } + values[i] = phi; + } + } + newState.addObject(object, new ObjectState(object, values, EscapeState.Virtual, objStates[0].getLocks())); + return materialized; + } else { + // not compatible: materialize in all predecessors + PhiNode materializedValuePhi = getCachedPhi(object, Kind.Object); + for (int i = 0; i < blockStates.size(); i++) { + ObjectState obj = objStates[i]; + Block predecessor = mergeBlock.getPredecessors().get(i); + ensureMaterialized(blockStates.get(i), obj, predecessor.getEndNode(), blockEffects.get(predecessor), METRIC_MATERIALIZATIONS_MERGE); + afterMergeEffects.addPhiInput(materializedValuePhi, obj.getMaterializedValue()); + } + newState.addObject(object, new ObjectState(object, materializedValuePhi, EscapeState.Materialized, null)); + return true; + } + } - PhiNode[] phis = getValueObjectMergePhis(phi, virtual.entryCount()); - for (int i = 0; i < virtual.entryCount(); i++) { - assert virtual.entryKind(i) != Kind.Object; - if (phis[i] == null) { - phis[i] = new PhiNode(virtual.entryKind(i), merge); - } - mergeEffects.addFloatingNode(phis[i], "valueObjectPhi"); - for (int i2 = 0; i2 < phi.valueCount(); i2++) { - afterMergeEffects.addPhiInput(phis[i], getObjectState(states.get(i2), phi.valueAt(i2)).getEntry(i)); - } + /** + * Fill the inputs of the PhiNode corresponding to one {@link Kind#Object} entry in the + * virtual object. + * + * @return true if materialization happened during the merge, false otherwise + */ + private boolean mergeObjectEntry(ObjectState[] objStates, List blockStates, PhiNode phi, int entryIndex) { + boolean materialized = false; + for (int i = 0; i < objStates.length; i++) { + if (!objStates[i].isVirtual()) { + break; + } + ValueNode[] entries = objStates[i].getEntries(); + if (entries[entryIndex] instanceof VirtualObjectNode) { + ObjectState obj = blockStates.get(i).getObjectState((VirtualObjectNode) entries[entryIndex]); + Block predecessor = mergeBlock.getPredecessors().get(i); + materialized |= ensureMaterialized(blockStates.get(i), obj, predecessor.getEndNode(), blockEffects.get(predecessor), METRIC_MATERIALIZATIONS_MERGE); + entries[entryIndex] = obj.getMaterializedValue(); + } + afterMergeEffects.addPhiInput(phi, entries[entryIndex]); + } + return materialized; + } + + /** + * Fill the inputs of the PhiNode corresponding to one primitive entry in the virtual + * object. + */ + private void mergePrimitiveEntry(ObjectState[] objStates, PhiNode phi, int entryIndex) { + for (ObjectState state : objStates) { + if (!state.isVirtual()) { + break; + } + afterMergeEffects.addPhiInput(phi, state.getEntries()[entryIndex]); + } + } + + /** + * Examine a PhiNode and try to replace it with merging of virtual objects if all its inputs + * refer to virtual object states. In order for the merging to happen, all incoming object + * states need to be compatible and without object identity (meaning that their object + * identity if not used later on). + * + * @param phi the PhiNode that should be processed + * @param states the predecessor block states of the merge + * @param mergedVirtualObjects the set of virtual objects that exist in all incoming states, + * and therefore also exist in the merged state + * @return true if materialization happened during the merge, false otherwise + */ + private boolean processPhi(PhiNode phi, List states, Set mergedVirtualObjects) { + aliases.set(phi, null); + assert states.size() == phi.valueCount(); + + // determine how many inputs are virtual and if they're all the same virtual object + int virtualInputs = 0; + ObjectState[] objStates = new ObjectState[states.size()]; + boolean uniqueVirtualObject = true; + for (int i = 0; i < objStates.length; i++) { + ObjectState obj = objStates[i] = getObjectState(states.get(i), phi.valueAt(i)); + if (obj != null) { + if (obj.isVirtual()) { + if (objStates[0] == null || objStates[0].virtual != obj.virtual) { + uniqueVirtualObject = false; } + virtualInputs++; + } + } + } + if (virtualInputs == objStates.length) { + if (uniqueVirtualObject) { + // all inputs refer to the same object: just make the phi node an alias + addAndMarkAlias(objStates[0].virtual, phi); + return false; + } else { + // all inputs are virtual: check if they're compatible and without identity + boolean compatible = true; + ObjectState firstObj = objStates[0]; + for (int i = 0; i < objStates.length; i++) { + ObjectState obj = objStates[i]; + boolean hasIdentity = obj.virtual.hasIdentity() && mergedVirtualObjects.contains(obj.virtual); + if (hasIdentity || firstObj.virtual.type() != obj.virtual.type() || firstObj.virtual.entryCount() != obj.virtual.entryCount() || !firstObj.locksEqual(obj)) { + compatible = false; + break; + } + } + + if (compatible) { + VirtualObjectNode virtual = getValueObjectVirtual(phi, getObjectState(states.get(0), phi.valueAt(0)).virtual); mergeEffects.addFloatingNode(virtual, "valueObjectNode"); - newState.addObject(virtual, new ObjectState(virtual, Arrays.copyOf(phis, phis.length, ValueNode[].class), EscapeState.Virtual, null)); + + boolean materialized = mergeObjectStates(virtual, objStates, states); addAndMarkAlias(virtual, virtual); addAndMarkAlias(virtual, phi); - } else { - materialize = true; + return materialized; } - } else { - materialize = true; } - } else { - materialize = true; } - if (materialize) { - for (int i = 0; i < phi.valueCount(); i++) { - ValueNode value = phi.valueAt(i); - ObjectState obj = getObjectState(states.get(i), value); - if (obj != null) { - materialized |= obj.isVirtual(); - Block predecessor = mergeBlock.getPredecessors().get(i); - replaceWithMaterialized(value, phi, predecessor.getEndNode(), states.get(i), obj, blockEffects.get(predecessor), METRIC_MATERIALIZATIONS_PHI); - } + // otherwise: materialize all phi inputs + boolean materialized = false; + for (int i = 0; i < objStates.length; i++) { + ObjectState obj = objStates[i]; + if (obj != null) { + Block predecessor = mergeBlock.getPredecessors().get(i); + materialized |= ensureMaterialized(states.get(i), obj, predecessor.getEndNode(), blockEffects.get(predecessor), METRIC_MATERIALIZATIONS_PHI); + afterMergeEffects.initializePhiInput(phi, i, obj.getMaterializedValue()); } } return materialized; diff -r b292dd6d02ac -r bca33c3135de graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualizerToolImpl.java --- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualizerToolImpl.java Mon Oct 28 13:10:10 2013 +0200 +++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualizerToolImpl.java Mon Oct 28 13:01:16 2013 +0100 @@ -80,22 +80,26 @@ } @Override - public void setVirtualEntry(State objectState, int index, ValueNode value) { + public void setVirtualEntry(State objectState, int index, ValueNode value, boolean unsafe) { ObjectState obj = (ObjectState) objectState; assert obj != null && obj.isVirtual() : "not virtual: " + obj; - ObjectState valueState = closure.getObjectState(state, value); ValueNode newValue; - if (valueState == null) { - newValue = getReplacedValue(value); - assert obj.getEntry(index) == null || obj.getEntry(index).kind() == newValue.kind() || (isObjectEntry(obj.getEntry(index)) && isObjectEntry(newValue)); + if (value == null) { + newValue = null; } else { - if (valueState.getState() != EscapeState.Virtual) { - newValue = valueState.getMaterializedValue(); - assert newValue.kind() == Kind.Object; + ObjectState valueState = closure.getObjectState(state, value); + if (valueState == null) { + newValue = getReplacedValue(value); + assert unsafe || obj.getEntry(index) == null || obj.getEntry(index).kind() == newValue.kind() || (isObjectEntry(obj.getEntry(index)) && isObjectEntry(newValue)); } else { - newValue = valueState.getVirtualObject(); + if (valueState.getState() != EscapeState.Virtual) { + newValue = valueState.getMaterializedValue(); + assert newValue.kind() == Kind.Object; + } else { + newValue = valueState.getVirtualObject(); + } + assert obj.getEntry(index) == null || isObjectEntry(obj.getEntry(index)); } - assert obj.getEntry(index) == null || isObjectEntry(obj.getEntry(index)); } obj.setEntry(index, newValue); } diff -r b292dd6d02ac -r bca33c3135de src/share/vm/runtime/deoptimization.cpp --- a/src/share/vm/runtime/deoptimization.cpp Mon Oct 28 13:10:10 2013 +0200 +++ b/src/share/vm/runtime/deoptimization.cpp Mon Oct 28 13:01:16 2013 +0100 @@ -914,7 +914,8 @@ fields->sort(compare); for (int i = 0; i < fields->length(); i++) { intptr_t val; - StackValue* value = StackValue::create_stack_value(fr, reg_map, sv->field_at(svIndex)); + ScopeValue* scope_field = sv->field_at(svIndex); + StackValue* value = StackValue::create_stack_value(fr, reg_map, scope_field); int offset = fields->at(i)._offset; BasicType type = fields->at(i)._type; switch (type) { @@ -923,6 +924,37 @@ obj->obj_field_put(offset, value->get_obj()()); break; + // Have to cast to INT (32 bits) pointer to avoid little/big-endian problem. + case T_INT: case T_FLOAT: { // 4 bytes. + assert(value->type() == T_INT, "Agreement."); + bool big_value = false; + if (i+1 < fields->length() && fields->at(i+1)._type == T_INT) { + if (scope_field->is_location()) { + Location::Type type = ((LocationValue*) scope_field)->location().type(); + if (type == Location::dbl || type == Location::lng) { + big_value = true; + } + } + if (scope_field->is_constant_int()) { + ScopeValue* next_scope_field = sv->field_at(svIndex + 1); + if (next_scope_field->is_constant_long() || next_scope_field->is_constant_double()) { + big_value = true; + } + } + } + + if (big_value) { + i++; + assert(i < fields->length(), "second T_INT field needed"); + assert(fields->at(i)._type == T_INT, "T_INT field needed"); + } else { + val = value->get_int(); + obj->int_field_put(offset, (jint)*((jint*)&val)); + break; + } + } + /* no break */ + case T_LONG: case T_DOUBLE: { assert(value->type() == T_INT, "Agreement."); StackValue* low = StackValue::create_stack_value(fr, reg_map, sv->field_at(++svIndex)); @@ -939,12 +971,6 @@ obj->long_field_put(offset, res); break; } - // Have to cast to INT (32 bits) pointer to avoid little/big-endian problem. - case T_INT: case T_FLOAT: // 4 bytes. - assert(value->type() == T_INT, "Agreement."); - val = value->get_int(); - obj->int_field_put(offset, (jint)*((jint*)&val)); - break; case T_SHORT: case T_CHAR: // 2 bytes assert(value->type() == T_INT, "Agreement.");