diff --git a/compiler/src/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/DebugInfoBuilder.java b/compiler/src/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/DebugInfoBuilder.java index e24305c67e47..c9728c723cd0 100644 --- a/compiler/src/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/DebugInfoBuilder.java +++ b/compiler/src/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/DebugInfoBuilder.java @@ -46,11 +46,11 @@ import org.graalvm.compiler.nodes.spi.NodeWithState; import org.graalvm.compiler.nodes.util.GraphUtil; import org.graalvm.compiler.nodes.virtual.EscapeObjectState; +import org.graalvm.compiler.nodes.virtual.MaterializedObjectState; import org.graalvm.compiler.nodes.virtual.VirtualBoxingNode; import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; +import org.graalvm.compiler.nodes.virtual.VirtualObjectState; import org.graalvm.compiler.serviceprovider.GraalServices; -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.RegisterValue; diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java index c84c6f49dbd4..ede8cc4ff846 100644 --- a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java @@ -115,7 +115,6 @@ import org.graalvm.compiler.nodes.java.DynamicNewInstanceNode; import org.graalvm.compiler.nodes.java.InstanceOfNode; import org.graalvm.compiler.nodes.java.NewArrayNode; -import org.graalvm.compiler.nodes.java.ValidateNewInstanceClassNode; import org.graalvm.compiler.nodes.memory.OnHeapMemoryAccess.BarrierType; import org.graalvm.compiler.nodes.memory.address.AddressNode; import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode; @@ -141,7 +140,6 @@ import jdk.vm.ci.code.TargetDescription; import jdk.vm.ci.hotspot.VMIntrinsicMethod; import jdk.vm.ci.meta.ConstantReflectionProvider; -import jdk.vm.ci.meta.DeoptimizationAction; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaField; @@ -523,8 +521,7 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec * check. Such a DynamicNewInstanceNode is also never constant folded to a * NewInstanceNode. */ - ValueNode clazzLegal = b.add(new ValidateNewInstanceClassNode(clazz)); - b.addPush(JavaKind.Object, new DynamicNewInstanceNode(b.nullCheckedValue(clazzLegal, DeoptimizationAction.None), true)); + DynamicNewInstanceNode.createAndPush(b, clazz); return true; } }); diff --git a/compiler/src/org.graalvm.compiler.java/src/org/graalvm/compiler/java/FrameStateBuilder.java b/compiler/src/org.graalvm.compiler.java/src/org/graalvm/compiler/java/FrameStateBuilder.java index abd855990443..1165373c2ba1 100644 --- a/compiler/src/org.graalvm.compiler.java/src/org/graalvm/compiler/java/FrameStateBuilder.java +++ b/compiler/src/org.graalvm.compiler.java/src/org/graalvm/compiler/java/FrameStateBuilder.java @@ -334,7 +334,7 @@ public FrameState create(int bci, BytecodeParser parent, boolean duringCall, Jav outerFrameState = parent.getFrameStateBuilder().create(parent.bci(), parent.getNonIntrinsicAncestor(), true, null, null); } if (bci == BytecodeFrame.AFTER_EXCEPTION_BCI && parent != null) { - return outerFrameState.duplicateModified(graph, outerFrameState.bci, true, false, JavaKind.Void, new JavaKind[]{JavaKind.Object}, new ValueNode[]{stack[0]}); + return outerFrameState.duplicateModified(graph, outerFrameState.bci, true, false, JavaKind.Void, new JavaKind[]{JavaKind.Object}, new ValueNode[]{stack[0]}, null); } if (bci == BytecodeFrame.INVALID_FRAMESTATE_BCI) { throw shouldNotReachHere(); diff --git a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/FrameState.java b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/FrameState.java index 9134d04c84b0..5bfc6e6bcae7 100644 --- a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/FrameState.java +++ b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/FrameState.java @@ -58,6 +58,7 @@ import org.graalvm.compiler.nodes.java.ExceptionObjectNode; import org.graalvm.compiler.nodes.java.MonitorIdNode; import org.graalvm.compiler.nodes.virtual.EscapeObjectState; +import org.graalvm.compiler.nodes.virtual.MaterializedObjectState; import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; import jdk.vm.ci.code.BytecodeFrame; @@ -365,24 +366,20 @@ public FrameState duplicateWithVirtualState() { * the stack. */ public FrameState duplicateModifiedDuringCall(int newBci, JavaKind popKind) { - return duplicateModified(graph(), newBci, rethrowException, true, popKind, null, null); + return duplicateModified(graph(), newBci, rethrowException, true, popKind, null, null, null); } - public FrameState duplicateModifiedBeforeCall(int newBci, JavaKind popKind, JavaKind[] pushedSlotKinds, ValueNode[] pushedValues) { - return duplicateModified(graph(), newBci, rethrowException, false, popKind, pushedSlotKinds, pushedValues); + public FrameState duplicateModifiedBeforeCall(int newBci, JavaKind popKind, JavaKind[] pushedSlotKinds, ValueNode[] pushedValues, List pushedVirtualObjectMappings) { + return duplicateModified(graph(), newBci, rethrowException, false, popKind, pushedSlotKinds, pushedValues, pushedVirtualObjectMappings); } /** * Creates a copy of this frame state with the top of stack replaced with with * {@code pushedValue} which must be of type {@code popKind}. */ - public FrameState duplicateModified(JavaKind popKind, JavaKind pushedSlotKind, ValueNode pushedValue) { + public FrameState duplicateModified(JavaKind popKind, JavaKind pushedSlotKind, ValueNode pushedValue, List pushedVirtualObjectMappings) { assert pushedValue != null && pushedValue.getStackKind() == popKind; - return duplicateModified(graph(), bci, rethrowException, duringCall, popKind, new JavaKind[]{pushedSlotKind}, new ValueNode[]{pushedValue}); - } - - public FrameState duplicateRethrow(ValueNode exceptionObject) { - return duplicateModified(graph(), bci, true, duringCall, JavaKind.Void, new JavaKind[]{JavaKind.Object}, new ValueNode[]{exceptionObject}); + return duplicateModified(graph(), bci, rethrowException, duringCall, popKind, new JavaKind[]{pushedSlotKind}, new ValueNode[]{pushedValue}, pushedVirtualObjectMappings); } /** @@ -391,7 +388,9 @@ public FrameState duplicateRethrow(ValueNode exceptionObject) { * correctly in slot encoding: a long or double will be followed by a null slot. The bci will be * changed to newBci. */ - public FrameState duplicateModified(StructuredGraph graph, int newBci, boolean newRethrowException, boolean newDuringCall, JavaKind popKind, JavaKind[] pushedSlotKinds, ValueNode[] pushedValues) { + public FrameState duplicateModified(StructuredGraph graph, int newBci, boolean newRethrowException, boolean newDuringCall, JavaKind popKind, JavaKind[] pushedSlotKinds, ValueNode[] pushedValues, + List pushedVirtualObjectMappings) { + List copiedVirtualObjectMappings = null; ArrayList copy; if (newRethrowException && !rethrowException && popKind == JavaKind.Void) { assert popKind == JavaKind.Void; @@ -410,7 +409,11 @@ public FrameState duplicateModified(StructuredGraph graph, int newBci, boolean n if (pushedValues != null) { assert pushedSlotKinds.length == pushedValues.length; for (int i = 0; i < pushedValues.length; i++) { - copy.add(pushedValues[i]); + ValueNode pushedValue = pushedValues[i]; + if (pushedValue instanceof VirtualObjectNode) { + copiedVirtualObjectMappings = ensureHasVirtualObjectMapping((VirtualObjectNode) pushedValue, pushedVirtualObjectMappings, copiedVirtualObjectMappings); + } + copy.add(pushedValue); if (pushedSlotKinds[i].needsTwoSlots()) { copy.add(null); } @@ -420,7 +423,56 @@ public FrameState duplicateModified(StructuredGraph graph, int newBci, boolean n copy.addAll(values.subList(localsSize + stackSize, values.size())); assert checkStackDepth(bci, stackSize, duringCall, rethrowException, newBci, newStackSize, newDuringCall, newRethrowException); - return graph.add(new FrameState(outerFrameState(), code, newBci, copy, localsSize, newStackSize, newRethrowException, newDuringCall, monitorIds, virtualObjectMappings)); + return graph.add(new FrameState(outerFrameState(), code, newBci, copy, localsSize, newStackSize, newRethrowException, newDuringCall, monitorIds, + copiedVirtualObjectMappings != null ? copiedVirtualObjectMappings : virtualObjectMappings)); + } + + /** + * A {@link VirtualObjectNode} in a frame state requires a corresponding + * {@link EscapeObjectState} entry in {@link FrameState#virtualObjectMappings}. So when a + * {@link VirtualObjectNode} is pushed as part of a frame state modification, the + * {@link EscapeObjectState} must either be already there, or it must be passed in explicitly + * from another frame state where the pushed value is coming from. + */ + private List ensureHasVirtualObjectMapping(VirtualObjectNode pushedValue, List pushedVirtualObjectMappings, + List copiedVirtualObjectMappings) { + if (virtualObjectMappings != null) { + for (EscapeObjectState existingEscapeObjectState : virtualObjectMappings) { + if (existingEscapeObjectState.object() == pushedValue) { + /* Found a matching EscapeObjectState, nothing needs to be added. */ + return copiedVirtualObjectMappings; + } + } + } + + if (pushedVirtualObjectMappings == null) { + throw GraalError.shouldNotReachHere("Pushing a virtual object, but no virtual object mapping provided: " + pushedValue); + } + for (EscapeObjectState pushedEscapeObjectState : pushedVirtualObjectMappings) { + if (pushedEscapeObjectState.object() == pushedValue) { + /* + * A VirtualObjectState could have transitive dependencies on other object states + * that are would also need to be added. For now, we do not have a case where a + * FrameState with a VirtualObjectState is duplicated, therefore this case is not + * implemented yet. + */ + GraalError.guarantee(pushedEscapeObjectState instanceof MaterializedObjectState, "A VirtualObjectState could have transitive dependencies"); + /* + * Found a new EscapeObjectState that needs to be added to the + * virtualObjectMappings. + */ + List result = copiedVirtualObjectMappings; + if (result == null) { + result = new ArrayList<>(); + if (virtualObjectMappings != null) { + result.addAll(virtualObjectMappings); + } + } + result.add(pushedEscapeObjectState); + return result; + } + } + throw GraalError.shouldNotReachHere("Did not find a virtual object mapping: " + pushedValue); } /** diff --git a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BytecodeExceptionNode.java b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BytecodeExceptionNode.java index e5b93a5e1da9..6902f4edb42d 100644 --- a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BytecodeExceptionNode.java +++ b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BytecodeExceptionNode.java @@ -216,7 +216,7 @@ public FrameState createStateDuring() { boolean rethrowException = false; boolean duringCall = true; return stateAfter.duplicateModified(graph(), stateAfter.bci, rethrowException, duringCall, - JavaKind.Object, null, null); + JavaKind.Object, null, null, null); } } diff --git a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/ForeignCall.java b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/ForeignCall.java index e37c36b40d9e..59d884a193f6 100644 --- a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/ForeignCall.java +++ b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/ForeignCall.java @@ -73,7 +73,7 @@ default void computeStateDuring(FrameState currentStateAfter) { (currentStateAfter.stackSize() > 1 && currentStateAfter.stackAt(currentStateAfter.stackSize() - 2) == this)) { // The result of this call is on the top of stack, so roll back to the previous bci. assert bci() != BytecodeFrame.UNKNOWN_BCI : this; - newStateDuring = currentStateAfter.duplicateModified(currentStateAfter.graph(), bci(), false, true, this.asNode().getStackKind(), null, null); + newStateDuring = currentStateAfter.duplicateModified(currentStateAfter.graph(), bci(), false, true, this.asNode().getStackKind(), null, null, null); } else { newStateDuring = currentStateAfter; } diff --git a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawLoadNode.java b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawLoadNode.java index 230b97db4c15..2b744277dd86 100644 --- a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawLoadNode.java +++ b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawLoadNode.java @@ -86,12 +86,10 @@ static Stamp computeStampForArrayAccess(ValueNode object, JavaKind accessKind, S // precise stamp but array accesses will not, so manually compute a better stamp from // the underlying object. if (accessKind.isObject() && type != null && type.getType().isArray() && type.getType().getComponentType().getJavaKind().isObject()) { - TypeReference oldType = StampTool.typeReferenceOrNull(oldStamp); TypeReference componentType = TypeReference.create(object.graph().getAssumptions(), type.getType().getComponentType()); + Stamp newStamp = StampFactory.object(componentType); // Don't allow the type to get worse - if (oldType == null || oldType.getType().isAssignableFrom(componentType.getType())) { - return StampFactory.object(componentType); - } + return oldStamp == null ? newStamp : oldStamp.improveWith(newStamp); } if (oldStamp != null) { return oldStamp; diff --git a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/DynamicNewInstanceNode.java b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/DynamicNewInstanceNode.java index b8b47e310c48..3efec40773bd 100644 --- a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/DynamicNewInstanceNode.java +++ b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/DynamicNewInstanceNode.java @@ -30,28 +30,36 @@ import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.NodeClass; -import org.graalvm.compiler.nodes.spi.Canonicalizable; -import org.graalvm.compiler.nodes.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; -import org.graalvm.compiler.nodes.FrameState; import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; +import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext; +import org.graalvm.compiler.nodes.spi.Canonicalizable; +import org.graalvm.compiler.nodes.spi.CanonicalizerTool; +import org.graalvm.compiler.nodes.spi.CoreProviders; +import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaType; @NodeInfo -public class DynamicNewInstanceNode extends AbstractNewObjectNode implements Canonicalizable { +public final class DynamicNewInstanceNode extends AbstractNewObjectNode implements Canonicalizable { public static final NodeClass TYPE = NodeClass.create(DynamicNewInstanceNode.class); @Input ValueNode clazz; - public DynamicNewInstanceNode(ValueNode clazz, boolean fillContents) { - this(TYPE, clazz, fillContents, null); + public static void createAndPush(GraphBuilderContext b, ValueNode clazz) { + ResolvedJavaType constantType = tryConvertToNonDynamic(clazz, b); + if (constantType != null) { + b.addPush(JavaKind.Object, new NewInstanceNode(constantType, true)); + } else { + ValueNode clazzLegal = b.add(new ValidateNewInstanceClassNode(clazz)); + b.addPush(JavaKind.Object, new DynamicNewInstanceNode(clazzLegal, true)); + } } - protected DynamicNewInstanceNode(NodeClass c, ValueNode clazz, boolean fillContents, FrameState stateBefore) { - super(c, StampFactory.objectNonNull(), fillContents, stateBefore); + protected DynamicNewInstanceNode(ValueNode clazz, boolean fillContents) { + super(TYPE, StampFactory.objectNonNull(), fillContents, null); this.clazz = clazz; assert ((ObjectStamp) clazz.stamp(NodeView.DEFAULT)).nonNull(); } @@ -60,20 +68,20 @@ public ValueNode getInstanceType() { return clazz; } - public static boolean canConvertToNonDynamic(ValueNode clazz, CanonicalizerTool tool) { + static ResolvedJavaType tryConvertToNonDynamic(ValueNode clazz, CoreProviders tool) { if (clazz.isConstant()) { ResolvedJavaType type = tool.getConstantReflection().asJavaType(clazz.asConstant()); if (type != null && !throwsInstantiationException(type, tool.getMetaAccess()) && tool.getMetaAccessExtensionProvider().canConstantFoldDynamicAllocation(type)) { - return true; + return type; } } - return false; + return null; } @Override public Node canonical(CanonicalizerTool tool) { - if (canConvertToNonDynamic(clazz, tool)) { - ResolvedJavaType type = tool.getConstantReflection().asJavaType(clazz.asConstant()); + ResolvedJavaType type = tryConvertToNonDynamic(clazz, tool); + if (type != null) { return new NewInstanceNode(type, fillContents(), stateBefore()); } return this; diff --git a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ValidateNewInstanceClassNode.java b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ValidateNewInstanceClassNode.java index 25481e97aa6f..1574e08fc630 100644 --- a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ValidateNewInstanceClassNode.java +++ b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ValidateNewInstanceClassNode.java @@ -43,7 +43,7 @@ * or {@link Class} type. */ @NodeInfo(size = NodeSize.SIZE_8, cycles = NodeCycles.CYCLES_8, cyclesRationale = "Performs multiple checks.") -public class ValidateNewInstanceClassNode extends WithExceptionNode implements Lowerable, Simplifiable { +public final class ValidateNewInstanceClassNode extends WithExceptionNode implements Lowerable, Simplifiable { @Input ValueNode clazz; @@ -56,7 +56,7 @@ public class ValidateNewInstanceClassNode extends WithExceptionNode implements L public static final NodeClass TYPE = NodeClass.create(ValidateNewInstanceClassNode.class); - public ValidateNewInstanceClassNode(ValueNode clazz) { + protected ValidateNewInstanceClassNode(ValueNode clazz) { super(TYPE, AbstractPointerStamp.pointerNonNull(clazz.stamp(NodeView.DEFAULT))); this.clazz = clazz; } @@ -76,7 +76,7 @@ public void setClassClass(ValueNode newClassClass) { @Override public void simplify(SimplifierTool tool) { - if (DynamicNewInstanceNode.canConvertToNonDynamic(clazz, tool)) { + if (DynamicNewInstanceNode.tryConvertToNonDynamic(clazz, tool) != null) { killExceptionEdge(); tool.addToWorkList(usages()); replaceAtUsages(clazz); diff --git a/compiler/src/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/nodes/MaterializedObjectState.java b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/MaterializedObjectState.java similarity index 92% rename from compiler/src/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/nodes/MaterializedObjectState.java rename to compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/MaterializedObjectState.java index b960bbe8f8bf..330cbf8b8662 100644 --- a/compiler/src/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/nodes/MaterializedObjectState.java +++ b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/MaterializedObjectState.java @@ -22,14 +22,12 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package org.graalvm.compiler.virtual.nodes; +package org.graalvm.compiler.nodes.virtual; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ValueNode; -import org.graalvm.compiler.nodes.virtual.EscapeObjectState; -import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; /** * This class encapsulated the materialized state of an escape analyzed object. diff --git a/compiler/src/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/nodes/VirtualObjectState.java b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/VirtualObjectState.java similarity index 93% rename from compiler/src/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/nodes/VirtualObjectState.java rename to compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/VirtualObjectState.java index 5916e3c0b24b..440638c597be 100644 --- a/compiler/src/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/nodes/VirtualObjectState.java +++ b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/VirtualObjectState.java @@ -22,7 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package org.graalvm.compiler.virtual.nodes; +package org.graalvm.compiler.nodes.virtual; import java.util.List; @@ -31,8 +31,6 @@ import org.graalvm.compiler.graph.NodeInputList; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ValueNode; -import org.graalvm.compiler.nodes.virtual.EscapeObjectState; -import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; /** * This class encapsulated the virtual state of an escape analyzed object. diff --git a/compiler/src/org.graalvm.compiler.options/src/org/graalvm/compiler/options/OptionKey.java b/compiler/src/org.graalvm.compiler.options/src/org/graalvm/compiler/options/OptionKey.java index b6017b44e308..053ed46fa25b 100644 --- a/compiler/src/org.graalvm.compiler.options/src/org/graalvm/compiler/options/OptionKey.java +++ b/compiler/src/org.graalvm.compiler.options/src/org/graalvm/compiler/options/OptionKey.java @@ -24,8 +24,6 @@ */ package org.graalvm.compiler.options; -import java.util.Formatter; - import org.graalvm.collections.EconomicMap; import org.graalvm.collections.UnmodifiableEconomicMap; @@ -64,19 +62,19 @@ public final OptionDescriptor getDescriptor() { protected boolean checkDescriptorExists() { OptionKey.Lazy.init(); if (descriptor == null) { - Formatter buf = new Formatter(); - buf.format("Could not find a descriptor for an option key. The most likely cause is " + - "a dependency on the %s annotation without a dependency on the " + - "org.graalvm.compiler.options.processor.OptionProcessor annotation processor.", Option.class.getName()); + StringBuilder result = new StringBuilder(); + result.append("Could not find a descriptor for an option key. The most likely cause is a dependency on the "); + result.append(Option.class.getName()); + result.append(" annotation without a dependency on the org.graalvm.compiler.options.processor.OptionProcessor annotation processor."); StackTraceElement[] stackTrace = new Exception().getStackTrace(); if (stackTrace.length > 2 && stackTrace[1].getClassName().equals(OptionKey.class.getName()) && stackTrace[1].getMethodName().equals("getValue")) { String caller = stackTrace[2].getClassName(); - buf.format(" In suite.py, add GRAAL_OPTIONS_PROCESSOR to the \"annotationProcessors\" attribute of the project " + - "containing %s.", caller); + result.append(" In suite.py, add GRAAL_OPTIONS_PROCESSOR to the \"annotationProcessors\" attribute of the project containing "); + result.append(caller); } - throw new AssertionError(buf.toString()); + throw new AssertionError(result.toString()); } return true; } diff --git a/compiler/src/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/InliningUtil.java b/compiler/src/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/InliningUtil.java index b51cd76a7312..30d702cc5b03 100644 --- a/compiler/src/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/InliningUtil.java +++ b/compiler/src/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/InliningUtil.java @@ -786,7 +786,7 @@ public static FrameState processFrameState(FrameState frameState, Invoke invoke, // exception object (top of stack) FrameState stateAfterException = stateAtExceptionEdge; if (frameState.stackSize() > 0 && stateAtExceptionEdge.stackAt(0) != frameState.stackAt(0)) { - stateAfterException = stateAtExceptionEdge.duplicateModified(JavaKind.Object, JavaKind.Object, frameState.stackAt(0)); + stateAfterException = stateAtExceptionEdge.duplicateModified(JavaKind.Object, JavaKind.Object, frameState.stackAt(0), frameState.virtualObjectMappings()); } frameState.replaceAndDelete(stateAfterException); return stateAfterException; @@ -807,7 +807,7 @@ public static FrameState processFrameState(FrameState frameState, Invoke invoke, assert frameState.outerFrameState() == null; ValueNode[] invokeArgs = invokeArgsList.isEmpty() ? NO_ARGS : invokeArgsList.toArray(new ValueNode[invokeArgsList.size()]); FrameState stateBeforeCall = stateAtReturn.duplicateModifiedBeforeCall(invoke.bci(), invokeReturnKind, invokeTargetMethod.getSignature().toParameterKinds(!invokeTargetMethod.isStatic()), - invokeArgs); + invokeArgs, frameState.virtualObjectMappings()); frameState.replaceAndDelete(stateBeforeCall); return stateBeforeCall; } else { @@ -852,7 +852,7 @@ private static FrameState handleAfterBciFrameState(FrameState frameState, Invoke assert !frameState.rethrowException() : frameState; if (frameState.stackSize() > 0 && (alwaysDuplicateStateAfter || stateAfterReturn.stackAt(0) != frameState.stackAt(0))) { // A non-void return value. - stateAfterReturn = stateAtReturn.duplicateModified(invokeReturnKind, invokeReturnKind, frameState.stackAt(0)); + stateAfterReturn = stateAtReturn.duplicateModified(invokeReturnKind, invokeReturnKind, frameState.stackAt(0), frameState.virtualObjectMappings()); } else { // A void return value. stateAfterReturn = stateAtReturn.duplicate(); diff --git a/compiler/src/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/MultiTypeGuardInlineInfo.java b/compiler/src/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/MultiTypeGuardInlineInfo.java index eb33785bd66b..f42e515d0952 100644 --- a/compiler/src/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/MultiTypeGuardInlineInfo.java +++ b/compiler/src/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/MultiTypeGuardInlineInfo.java @@ -219,7 +219,7 @@ private EconomicSet inlineMultipleMethods(StructuredGraph graph, CoreProvi assert exceptionEdge.stateAfter().bci == invoke.bci(); assert exceptionEdge.stateAfter().rethrowException(); - exceptionMerge.setStateAfter(exceptionEdge.stateAfter().duplicateModified(JavaKind.Object, JavaKind.Object, exceptionObjectPhi)); + exceptionMerge.setStateAfter(exceptionEdge.stateAfter().duplicateModified(JavaKind.Object, JavaKind.Object, exceptionObjectPhi, null)); } // create one separate block for each invoked method @@ -420,7 +420,7 @@ private static Invoke duplicateInvokeForInlining(StructuredGraph graph, Invoke i ExceptionObjectNode newExceptionEdge = (ExceptionObjectNode) exceptionEdge.copyWithInputs(); // set new state (pop old exception object, push new one) - newExceptionEdge.setStateAfter(stateAfterException.duplicateModified(JavaKind.Object, JavaKind.Object, newExceptionEdge)); + newExceptionEdge.setStateAfter(stateAfterException.duplicateModified(JavaKind.Object, JavaKind.Object, newExceptionEdge, null)); EndNode endNode = graph.add(new EndNode()); newExceptionEdge.setNext(endNode); diff --git a/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/PEGraphDecoder.java b/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/PEGraphDecoder.java index 92267995f86b..d81c787236b9 100644 --- a/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/PEGraphDecoder.java +++ b/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/PEGraphDecoder.java @@ -1267,7 +1267,7 @@ protected void finishInlining(MethodScope is) { null, unwindNode -> unwindNode.exception()); unwindMergeNode.setNext(unwindReplacement); ensureExceptionStateDecoded(inlineScope); - unwindMergeNode.setStateAfter(inlineScope.exceptionState.duplicateModified(JavaKind.Object, JavaKind.Object, exceptionValue)); + unwindMergeNode.setStateAfter(inlineScope.exceptionState.duplicateModified(JavaKind.Object, JavaKind.Object, exceptionValue, null)); } if (invoke instanceof InvokeWithExceptionNode) { /* @@ -1576,7 +1576,7 @@ protected void ensureOuterStateDecoded(PEMethodScope methodScope) { } JavaKind invokeReturnKind = methodScope.invokeData.invoke.asNode().getStackKind(); - FrameState outerState = stateAtReturn.duplicateModified(graph, methodScope.invokeData.invoke.bci(), stateAtReturn.rethrowException(), true, invokeReturnKind, null, null); + FrameState outerState = stateAtReturn.duplicateModified(graph, methodScope.invokeData.invoke.bci(), stateAtReturn.rethrowException(), true, invokeReturnKind, null, null, null); /* * When the encoded graph has methods inlining, we can already have a proper caller diff --git a/compiler/src/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ObjectState.java b/compiler/src/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ObjectState.java index c075cca921a9..5b384f4cfe00 100644 --- a/compiler/src/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ObjectState.java +++ b/compiler/src/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ObjectState.java @@ -34,9 +34,9 @@ import org.graalvm.compiler.nodes.java.MonitorIdNode; import org.graalvm.compiler.nodes.virtual.EscapeObjectState; import org.graalvm.compiler.nodes.virtual.LockState; +import org.graalvm.compiler.nodes.virtual.MaterializedObjectState; import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; -import org.graalvm.compiler.virtual.nodes.MaterializedObjectState; -import org.graalvm.compiler.virtual.nodes.VirtualObjectState; +import org.graalvm.compiler.nodes.virtual.VirtualObjectState; import jdk.vm.ci.meta.JavaConstant; diff --git a/compiler/src/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java b/compiler/src/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java index 52039a722733..d4bd8c14b7e4 100644 --- a/compiler/src/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java +++ b/compiler/src/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java @@ -44,7 +44,6 @@ import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.NodeBitMap; import org.graalvm.compiler.graph.Position; -import org.graalvm.compiler.nodes.spi.Canonicalizable; import org.graalvm.compiler.nodes.AbstractEndNode; import org.graalvm.compiler.nodes.CallTargetNode; import org.graalvm.compiler.nodes.ConstantNode; @@ -67,6 +66,7 @@ import org.graalvm.compiler.nodes.ValueProxyNode; import org.graalvm.compiler.nodes.VirtualState; import org.graalvm.compiler.nodes.cfg.Block; +import org.graalvm.compiler.nodes.spi.Canonicalizable; import org.graalvm.compiler.nodes.spi.CoreProviders; import org.graalvm.compiler.nodes.spi.NodeWithState; import org.graalvm.compiler.nodes.spi.Virtualizable; @@ -75,7 +75,7 @@ import org.graalvm.compiler.nodes.virtual.AllocatedObjectNode; import org.graalvm.compiler.nodes.virtual.EnsureVirtualizedNode; import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; -import org.graalvm.compiler.virtual.nodes.VirtualObjectState; +import org.graalvm.compiler.nodes.virtual.VirtualObjectState; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index 7540b0b54b48..8a948785a054 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -10,5 +10,6 @@ This changelog summarizes major changes to GraalVM Native Image. * (GR-29957) Removed the option -H:SubstitutionFiles= to register substitutions via a JSON file. This was an early experiment and is no longer necessary. * (GR-32403) Use more compressed encoding for stack frame metadata. * (GR-35152) Add -H:DisableURLProtocols to allow specifying URL protocols that must never be included in the image. -* (GR-35085) Custom prologue/epilogue/handleException customizations of @CEntryPoint must be annotated with @Uninterruptible. The entry points synthetic methods are now implicilty annotated with @Uninterruptible too. +* (GR-35085) Custom prologue/epilogue/handleException customizations of @CEntryPoint must be annotated with @Uninterruptible. The synthetic methods created for entry points are now implicitly annotated with @Uninterruptible too. +* (GR-34935) More compiler optimization phases are run before static analysis: Conditional Elimination (to remove redundant conditions) and Escape Analysis. * (GR-33602) Enable new user-friendly build output mode. The old output can be restored with `-H:-BuildOutputUseNewStyle`. Run `native-image --expert-options-all | grep "BuildOutput` to see all options for the new output. diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/AccessFieldTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/AccessFieldTypeFlow.java index 511b453a5a9c..bc9c705aed50 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/AccessFieldTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/AccessFieldTypeFlow.java @@ -24,7 +24,7 @@ */ package com.oracle.graal.pointsto.flow; -import org.graalvm.compiler.nodes.java.AccessFieldNode; +import org.graalvm.compiler.nodes.ValueNode; import com.oracle.graal.pointsto.PointsToAnalysis; import com.oracle.graal.pointsto.meta.AnalysisField; @@ -38,10 +38,10 @@ public abstract class AccessFieldTypeFlow extends TypeFlow { /** The field that this flow stores into or loads from. */ protected final AnalysisField field; - protected AccessFieldTypeFlow(AccessFieldNode node) { + protected AccessFieldTypeFlow(ValueNode node, AnalysisField field) { /* The declared type of a field access node is the field declared type. */ - super(node.getNodeSourcePosition(), ((AnalysisField) node.field()).getType()); - this.field = (AnalysisField) node.field(); + super(node.getNodeSourcePosition(), field.getType()); + this.field = field; } protected AccessFieldTypeFlow(AccessFieldTypeFlow original, MethodFlowsGraph methodFlows) { diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/AnalysisParsedGraph.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/AnalysisParsedGraph.java index ed6fa1e57089..8665d25fab08 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/AnalysisParsedGraph.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/AnalysisParsedGraph.java @@ -141,14 +141,19 @@ public static AnalysisParsedGraph parseBytecode(BigBang bb, AnalysisMethod metho } } + @SuppressWarnings("try") private static AnalysisParsedGraph optimizeAndEncode(BigBang bb, AnalysisMethod method, StructuredGraph graph, boolean isIntrinsic) { - /* - * Must be called before any other thread can access the graph, i.e., before the graph is - * published. - */ - bb.getHostVM().methodAfterParsingHook(bb, method, graph); - - EncodedGraph encodedGraph = GraphEncoder.encodeSingleGraph(graph, HOST_ARCHITECTURE); - return new AnalysisParsedGraph(encodedGraph, isIntrinsic); + try (DebugContext.Scope s = graph.getDebug().scope("ClosedWorldAnalysis", graph, method)) { + /* + * Must be called before any other thread can access the graph, i.e., before the graph + * is published. + */ + bb.getHostVM().methodAfterParsingHook(bb, method, graph); + + EncodedGraph encodedGraph = GraphEncoder.encodeSingleGraph(graph, HOST_ARCHITECTURE); + return new AnalysisParsedGraph(encodedGraph, isIntrinsic); + } catch (Throwable e) { + throw graph.getDebug().handle(e); + } } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/LoadFieldTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/LoadFieldTypeFlow.java index 566e81963183..f20f9d90097a 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/LoadFieldTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/LoadFieldTypeFlow.java @@ -28,6 +28,7 @@ import com.oracle.graal.pointsto.PointsToAnalysis; import com.oracle.graal.pointsto.flow.context.object.AnalysisObject; +import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.typestate.TypeState; import jdk.vm.ci.code.BytecodePosition; @@ -38,7 +39,7 @@ public abstract class LoadFieldTypeFlow extends AccessFieldTypeFlow { protected LoadFieldTypeFlow(LoadFieldNode node) { - super(node); + super(node, (AnalysisField) node.field()); } protected LoadFieldTypeFlow(MethodFlowsGraph methodFlows, LoadFieldTypeFlow original) { diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java index 788f22287a9c..556826cf068e 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java @@ -90,6 +90,10 @@ import org.graalvm.compiler.nodes.java.UnsafeCompareAndSwapNode; import org.graalvm.compiler.nodes.type.StampTool; import org.graalvm.compiler.nodes.util.GraphUtil; +import org.graalvm.compiler.nodes.virtual.AllocatedObjectNode; +import org.graalvm.compiler.nodes.virtual.CommitAllocationNode; +import org.graalvm.compiler.nodes.virtual.VirtualInstanceNode; +import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; import org.graalvm.compiler.phases.common.CanonicalizerPhase; import org.graalvm.compiler.phases.common.IterativeConditionalEliminationPhase; import org.graalvm.compiler.phases.graph.MergeableState; @@ -222,6 +226,29 @@ public void registerUsedElements(boolean registerEmbeddedRoots) { AnalysisType type = (AnalysisType) node.instanceClass(); type.registerAsAllocated(node); + } else if (n instanceof VirtualObjectNode) { + VirtualObjectNode node = (VirtualObjectNode) n; + AnalysisType type = (AnalysisType) node.type(); + type.registerAsAllocated(node); + + } else if (n instanceof CommitAllocationNode) { + CommitAllocationNode node = (CommitAllocationNode) n; + List values = node.getValues(); + int objectStartIndex = 0; + for (VirtualObjectNode virtualObject : node.getVirtualObjects()) { + AnalysisType type = (AnalysisType) virtualObject.type(); + if (!type.isArray()) { + for (int i = 0; i < virtualObject.entryCount(); i++) { + ValueNode value = values.get(objectStartIndex + i); + if (!value.isJavaConstant() || !value.asJavaConstant().isDefaultForKind()) { + AnalysisField field = (AnalysisField) ((VirtualInstanceNode) virtualObject).field(i); + field.registerAsWritten(methodFlow); + } + } + } + objectStartIndex += virtualObject.entryCount(); + } + } else if (n instanceof NewArrayNode) { NewArrayNode node = (NewArrayNode) n; AnalysisType type = ((AnalysisType) node.elementType()).getArrayClass(); @@ -766,6 +793,8 @@ protected void node(FixedNode n) { if (node.result() != null && node.result().getStackKind() == JavaKind.Object) { returnFlowBuilder.addUseDependency(state.lookup(node.result())); } + } else if (n instanceof CommitAllocationNode) { + processCommitAllocation((CommitAllocationNode) n, state); } else if (n instanceof NewInstanceNode) { processNewInstance((NewInstanceNode) n, state); } else if (n instanceof DynamicNewInstanceNode) { @@ -883,34 +912,7 @@ protected void node(FixedNode n) { } } else if (n instanceof StoreFieldNode) { // object.field = value - StoreFieldNode node = (StoreFieldNode) n; - AnalysisField field = (AnalysisField) node.field(); - assert field.isWritten(); - if (node.value().getStackKind() == JavaKind.Object) { - TypeFlowBuilder valueBuilder = state.lookup(node.value()); - - TypeFlowBuilder storeFieldBuilder; - if (node.isStatic()) { - storeFieldBuilder = TypeFlowBuilder.create(bb, node, StoreFieldTypeFlow.class, () -> { - FieldTypeFlow fieldFlow = field.getStaticFieldFlow(); - StoreStaticFieldTypeFlow storeFieldFlow = new StoreStaticFieldTypeFlow(node, valueBuilder.get(), fieldFlow); - methodFlow.addMiscEntry(storeFieldFlow); - return storeFieldFlow; - }); - storeFieldBuilder.addUseDependency(valueBuilder); - } else { - TypeFlowBuilder objectBuilder = state.lookup(node.object()); - storeFieldBuilder = TypeFlowBuilder.create(bb, node, StoreFieldTypeFlow.class, () -> { - StoreInstanceFieldTypeFlow storeFieldFlow = new StoreInstanceFieldTypeFlow(node, valueBuilder.get(), objectBuilder.get()); - methodFlow.addMiscEntry(storeFieldFlow); - return storeFieldFlow; - }); - storeFieldBuilder.addUseDependency(valueBuilder); - storeFieldBuilder.addObserverDependency(objectBuilder); - } - /* Field stores must not be removed. */ - typeFlowGraphBuilder.registerSinkBuilder(storeFieldBuilder); - } + processStoreField((StoreFieldNode) n, state); } else if (n instanceof LoadIndexedNode) { LoadIndexedNode node = (LoadIndexedNode) n; @@ -933,23 +935,7 @@ protected void node(FixedNode n) { } } else if (n instanceof StoreIndexedNode) { - StoreIndexedNode node = (StoreIndexedNode) n; - if (node.value().getStackKind() == JavaKind.Object) { - AnalysisType arrayType = (AnalysisType) StampTool.typeOrNull(node.array()); - AnalysisType nonNullArrayType = Optional.ofNullable(arrayType).orElseGet(bb::getObjectArrayType); - TypeFlowBuilder arrayBuilder = state.lookup(node.array()); - TypeFlowBuilder valueBuilder = state.lookup(node.value()); - TypeFlowBuilder storeIndexedBuilder = TypeFlowBuilder.create(bb, node, StoreIndexedTypeFlow.class, () -> { - StoreIndexedTypeFlow storeIndexedFlow = new StoreIndexedTypeFlow(node, nonNullArrayType, arrayBuilder.get(), valueBuilder.get()); - methodFlow.addMiscEntry(storeIndexedFlow); - return storeIndexedFlow; - }); - storeIndexedBuilder.addUseDependency(valueBuilder); - storeIndexedBuilder.addObserverDependency(arrayBuilder); - - /* Index stores must not be removed. */ - typeFlowGraphBuilder.registerSinkBuilder(storeIndexedBuilder); - } + processStoreIndexed((StoreIndexedNode) n, state); } else if (n instanceof UnsafePartitionLoadNode) { UnsafePartitionLoadNode node = (UnsafePartitionLoadNode) n; @@ -1474,43 +1460,120 @@ protected Object uniqueKey(Node node) { return new Object(); } + protected void processCommitAllocation(CommitAllocationNode commitAllocationNode, TypeFlowsOfNodes state) { + Map allocatedObjects = new HashMap<>(); + for (AllocatedObjectNode allocatedObjectNode : commitAllocationNode.usages().filter(AllocatedObjectNode.class)) { + AnalysisType type = (AnalysisType) allocatedObjectNode.getVirtualObject().type(); + processNewInstance(allocatedObjectNode, type, state); + allocatedObjects.put(allocatedObjectNode.getVirtualObject(), allocatedObjectNode); + } + + List values = commitAllocationNode.getValues(); + int objectStartIndex = 0; + for (VirtualObjectNode virtualObject : commitAllocationNode.getVirtualObjects()) { + AnalysisType type = (AnalysisType) virtualObject.type(); + ValueNode object = allocatedObjects.get(virtualObject); + if (object == null) { + /* + * The AllocatedObjectNode itself is not used directly, so it got removed from the + * graph. We still need to register field/array stores because otherwise we can miss + * types that flow into the field/array. We use the VirtualObjectNode as the + * placeholder for the stores. + */ + object = virtualObject; + } + for (int i = 0; i < virtualObject.entryCount(); i++) { + ValueNode value = values.get(objectStartIndex + i); + if (!value.isJavaConstant() || !value.asJavaConstant().isDefaultForKind()) { + if (type.isArray()) { + processStoreIndexed(commitAllocationNode, object, value, state); + } else { + AnalysisField field = (AnalysisField) ((VirtualInstanceNode) virtualObject).field(i); + processStoreField(commitAllocationNode, field, object, value, state); + } + } + } + objectStartIndex += virtualObject.entryCount(); + } + assert values.size() == objectStartIndex; + } + protected void processNewInstance(NewInstanceNode node, TypeFlowsOfNodes state) { + /* Instance fields of a new object are initialized to null state in AnalysisField. */ + processNewInstance(node, (AnalysisType) node.instanceClass(), state); + } - AnalysisType type = (AnalysisType) node.instanceClass(); + protected void processNewArray(NewArrayNode node, TypeFlowsOfNodes state) { + processNewInstance(node, ((AnalysisType) node.elementType()).getArrayClass(), state); + } + + protected void processNewInstance(ValueNode node, AnalysisType type, TypeFlowsOfNodes state) { assert type.isInstantiated(); Object key = uniqueKey(node); BytecodeLocation allocationLabel = bb.analysisPolicy().createAllocationSite(bb, key, method); TypeFlowBuilder newInstanceBuilder = TypeFlowBuilder.create(bb, node, NewInstanceTypeFlow.class, () -> { - NewInstanceTypeFlow newInstance = createNewInstanceTypeFlow(node, type, allocationLabel); - /* Instance fields of a new object are initialized to null state in AnalysisField. */ + NewInstanceTypeFlow newInstance = new NewInstanceTypeFlow(node, type, allocationLabel); methodFlow.addMiscEntry(newInstance); return newInstance; }); state.add(node, newInstanceBuilder); } - protected NewInstanceTypeFlow createNewInstanceTypeFlow(NewInstanceNode node, AnalysisType type, BytecodeLocation allocationLabel) { - return new NewInstanceTypeFlow(node, type, allocationLabel); + protected void processStoreField(StoreFieldNode node, TypeFlowsOfNodes state) { + processStoreField(node, (AnalysisField) node.field(), node.object(), node.value(), state); } - protected void processNewArray(NewArrayNode node, TypeFlowsOfNodes state) { - AnalysisType type = ((AnalysisType) node.elementType()).getArrayClass(); - assert type.isInstantiated(); - - Object key = uniqueKey(node); - BytecodeLocation allocationLabel = bb.analysisPolicy().createAllocationSite(bb, key, method); + protected void processStoreField(ValueNode node, AnalysisField field, ValueNode object, ValueNode value, TypeFlowsOfNodes state) { + assert field.isWritten(); + if (value.getStackKind() == JavaKind.Object) { + TypeFlowBuilder valueBuilder = state.lookup(value); + + TypeFlowBuilder storeFieldBuilder; + if (field.isStatic()) { + storeFieldBuilder = TypeFlowBuilder.create(bb, node, StoreFieldTypeFlow.class, () -> { + FieldTypeFlow fieldFlow = field.getStaticFieldFlow(); + StoreStaticFieldTypeFlow storeFieldFlow = new StoreStaticFieldTypeFlow(node, field, valueBuilder.get(), fieldFlow); + methodFlow.addMiscEntry(storeFieldFlow); + return storeFieldFlow; + }); + storeFieldBuilder.addUseDependency(valueBuilder); + } else { + TypeFlowBuilder objectBuilder = state.lookup(object); + storeFieldBuilder = TypeFlowBuilder.create(bb, node, StoreFieldTypeFlow.class, () -> { + StoreInstanceFieldTypeFlow storeFieldFlow = new StoreInstanceFieldTypeFlow(node, field, valueBuilder.get(), objectBuilder.get()); + methodFlow.addMiscEntry(storeFieldFlow); + return storeFieldFlow; + }); + storeFieldBuilder.addUseDependency(valueBuilder); + storeFieldBuilder.addObserverDependency(objectBuilder); + } + /* Field stores must not be removed. */ + typeFlowGraphBuilder.registerSinkBuilder(storeFieldBuilder); + } + } - TypeFlowBuilder newArrayBuilder = TypeFlowBuilder.create(bb, node, NewInstanceTypeFlow.class, () -> { - NewInstanceTypeFlow newArray = createNewArrayTypeFlow(node, type, allocationLabel); - methodFlow.addMiscEntry(newArray); - return newArray; - }); - state.add(node, newArrayBuilder); + private void processStoreIndexed(StoreIndexedNode node, TypeFlowsOfNodes state) { + processStoreIndexed(node, node.array(), node.value(), state); } - protected NewInstanceTypeFlow createNewArrayTypeFlow(NewArrayNode node, AnalysisType type, BytecodeLocation allocationLabel) { - return new NewInstanceTypeFlow(node, type, allocationLabel); + private void processStoreIndexed(ValueNode node, ValueNode array, ValueNode value, TypeFlowsOfNodes state) { + if (value.getStackKind() == JavaKind.Object) { + AnalysisType arrayType = (AnalysisType) StampTool.typeOrNull(array); + AnalysisType nonNullArrayType = Optional.ofNullable(arrayType).orElseGet(bb::getObjectArrayType); + TypeFlowBuilder arrayBuilder = state.lookup(array); + TypeFlowBuilder valueBuilder = state.lookup(value); + TypeFlowBuilder storeIndexedBuilder = TypeFlowBuilder.create(bb, node, StoreIndexedTypeFlow.class, () -> { + StoreIndexedTypeFlow storeIndexedFlow = new StoreIndexedTypeFlow(node, nonNullArrayType, arrayBuilder.get(), valueBuilder.get()); + methodFlow.addMiscEntry(storeIndexedFlow); + return storeIndexedFlow; + }); + storeIndexedBuilder.addUseDependency(valueBuilder); + storeIndexedBuilder.addObserverDependency(arrayBuilder); + + /* Index stores must not be removed. */ + typeFlowGraphBuilder.registerSinkBuilder(storeIndexedBuilder); + } } /** Hook for unsafe offset value checks. */ diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/StoreFieldTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/StoreFieldTypeFlow.java index a6cd6eafafe2..c8af1fbd0bd3 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/StoreFieldTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/StoreFieldTypeFlow.java @@ -24,10 +24,11 @@ */ package com.oracle.graal.pointsto.flow; -import org.graalvm.compiler.nodes.java.StoreFieldNode; +import org.graalvm.compiler.nodes.ValueNode; import com.oracle.graal.pointsto.PointsToAnalysis; import com.oracle.graal.pointsto.flow.context.object.AnalysisObject; +import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.typestate.TypeState; /** @@ -35,8 +36,8 @@ */ public abstract class StoreFieldTypeFlow extends AccessFieldTypeFlow { - protected StoreFieldTypeFlow(StoreFieldNode node) { - super(node); + protected StoreFieldTypeFlow(ValueNode node, AnalysisField field) { + super(node, field); } protected StoreFieldTypeFlow(StoreFieldTypeFlow original, MethodFlowsGraph methodFlows) { @@ -60,8 +61,8 @@ public static class StoreStaticFieldTypeFlow extends StoreFieldTypeFlow { /** The flow of the input value. */ private final TypeFlow valueFlow; - StoreStaticFieldTypeFlow(StoreFieldNode node, TypeFlow valueFlow, FieldTypeFlow fieldFlow) { - super(node); + StoreStaticFieldTypeFlow(ValueNode node, AnalysisField field, TypeFlow valueFlow, FieldTypeFlow fieldFlow) { + super(node, field); this.valueFlow = valueFlow; this.fieldFlow = fieldFlow; } @@ -107,8 +108,8 @@ public static class StoreInstanceFieldTypeFlow extends StoreFieldTypeFlow { /** The flow of the store operation receiver object. */ private TypeFlow objectFlow; - StoreInstanceFieldTypeFlow(StoreFieldNode node, TypeFlow valueFlow, TypeFlow objectFlow) { - super(node); + StoreInstanceFieldTypeFlow(ValueNode node, AnalysisField field, TypeFlow valueFlow, TypeFlow objectFlow) { + super(node, field); this.valueFlow = valueFlow; this.objectFlow = objectFlow; } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMetaAccess.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMetaAccess.java index 8eddbc6d8b47..1e2704ce11f4 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMetaAccess.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMetaAccess.java @@ -40,6 +40,11 @@ public class AnalysisMetaAccess extends UniverseMetaAccess { public AnalysisMetaAccess(AnalysisUniverse analysisUniverse, MetaAccessProvider originalMetaAccess) { super(analysisUniverse, originalMetaAccess); + + /* Make sure that Object type is added to the universe before any other types. */ + lookupJavaType(Object.class); + /* Cloneable is needed before any other instance class can be created. */ + lookupJavaType(Cloneable.class); } @Override diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java index ca169a1ccfe7..00dedf487478 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java @@ -61,7 +61,6 @@ import com.oracle.graal.pointsto.util.ConcurrentLightHashSet; import com.oracle.svm.util.UnsafePartitionKind; -import jdk.vm.ci.common.JVMCIError; import jdk.vm.ci.meta.Assumptions.AssumptionResult; import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.JavaConstant; @@ -110,6 +109,7 @@ public class AnalysisType implements WrappedJavaType, OriginalClassProvider, Com private final int id; private final JavaKind storageKind; + private final boolean isCloneableWithAllocation; /** The unique context insensitive analysis object for this type. */ private AnalysisObject contextInsensitiveAnalysisObject; @@ -156,7 +156,7 @@ public enum UsageKind { private final AnalysisFuture initializationTask; - AnalysisType(AnalysisUniverse universe, ResolvedJavaType javaType, JavaKind storageKind, AnalysisType objectType) { + AnalysisType(AnalysisUniverse universe, ResolvedJavaType javaType, JavaKind storageKind, AnalysisType objectType, AnalysisType cloneableType) { this.universe = universe; this.wrapped = javaType; isArray = wrapped.isArray(); @@ -230,6 +230,12 @@ public enum UsageKind { assert getSuperclass() == null || getId() > getSuperclass().getId(); + if (isJavaLangObject() || isInterface()) { + this.isCloneableWithAllocation = false; + } else { + this.isCloneableWithAllocation = cloneableType.isAssignableFrom(this); + } + /* The registration task initializes the type. */ this.initializationTask = new AnalysisFuture<>(() -> universe.hostVM.initializeType(this), null); } @@ -1070,7 +1076,7 @@ public boolean declaresDefaultMethods() { @Override public boolean isCloneableWithAllocation() { - throw JVMCIError.unimplemented(); + return isCloneableWithAllocation; } @SuppressWarnings("deprecation") diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java index cf380199738b..a2c56c2a4870 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java @@ -111,6 +111,7 @@ public class AnalysisUniverse implements Universe { private final SnippetReflectionProvider snippetReflection; private AnalysisType objectClass; + private AnalysisType cloneableClass; private final JavaKind wordKind; private AnalysisPolicy analysisPolicy; private BigBang bb; @@ -260,7 +261,7 @@ private AnalysisType createType(ResolvedJavaType type) { try { JavaKind storageKind = getStorageKind(type, originalMetaAccess); - AnalysisType newValue = new AnalysisType(this, type, storageKind, objectClass); + AnalysisType newValue = new AnalysisType(this, type, storageKind, objectClass, cloneableClass); synchronized (this) { /* @@ -277,9 +278,10 @@ private AnalysisType createType(ResolvedJavaType type) { assert typesById[newValue.getId()] == null; typesById[newValue.getId()] = newValue; - if (newValue.isJavaLangObject()) { - assert objectClass == null; + if (objectClass == null && newValue.isJavaLangObject()) { objectClass = newValue; + } else if (cloneableClass == null && newValue.toJavaName(true).equals(Cloneable.class.getName())) { + cloneableClass = newValue; } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/compression/GzipBundleCompression.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/compression/GzipBundleCompression.java index d1db05b2af7f..d6b9c63192ca 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/compression/GzipBundleCompression.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/compression/GzipBundleCompression.java @@ -44,12 +44,12 @@ import java.util.zip.GZIPOutputStream; import org.graalvm.collections.Pair; -import org.graalvm.compiler.debug.GraalError; - -import com.oracle.svm.core.jdk.localization.bundles.CompressedBundle; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import com.oracle.svm.core.jdk.localization.bundles.CompressedBundle; +import com.oracle.svm.core.util.VMError; + /** * Class responsible for serialization and compression of resource bundles. Only bundles whose * values are strings or arrays of strings are supported. While in theory the bundles can contain @@ -90,7 +90,7 @@ public static CompressedBundle compress(ResourceBundle bundle) { out.finish(); return new CompressedBundle(byteStream.toByteArray(), GzipBundleCompression::decompressBundle); } catch (IOException ex) { - throw GraalError.shouldNotReachHere(ex, "Compression of a bundle " + bundle.getClass() + " failed. This is an internal error. Please open an issue and submit a reproducer."); + throw VMError.shouldNotReachHere("Compression of a bundle " + bundle.getClass() + " failed. This is an internal error. Please open an issue and submit a reproducer.", ex); } } @@ -100,7 +100,7 @@ private static Map decompressBundle(byte[] data) { String decompressed = readText(input); return deserializeContent(indices, decompressed); } catch (IOException e) { - throw GraalError.shouldNotReachHere(e, "Decompressing a resource bundle failed."); + throw VMError.shouldNotReachHere("Decompressing a resource bundle failed.", e); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java index ffa43252d57f..7391107ec7db 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java @@ -811,8 +811,6 @@ private void setupNativeImage(String imageName, Timer classlistTimer, OptionValu classInitializationSupport, Collections.singletonList(harnessSubstitutions)); AnalysisMetaAccess aMetaAccess = new SVMAnalysisMetaAccess(aUniverse, originalMetaAccess); - /* Make sure that Object type is added to the universe before any other types. */ - aMetaAccess.lookupJavaType(Object.class); AnalysisConstantReflectionProvider aConstantReflection = new AnalysisConstantReflectionProvider( aUniverse, aMetaAccess, originalProviders.getConstantReflection(), classInitializationSupport); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java index 6cda94905328..447ff9be7918 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java @@ -61,8 +61,10 @@ import org.graalvm.compiler.options.Option; import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.phases.OptimisticOptimizations; +import org.graalvm.compiler.phases.common.BoxNodeIdentityPhase; import org.graalvm.compiler.phases.common.CanonicalizerPhase; import org.graalvm.compiler.serviceprovider.JavaVersionUtil; +import org.graalvm.compiler.virtual.phases.ea.PartialEscapePhase; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.function.RelocatedPointer; @@ -553,12 +555,7 @@ public void methodAfterParsingHook(BigBang bb, AnalysisMethod method, Structured graph.setGuardsStage(StructuredGraph.GuardsStage.FIXED_DEOPTS); if (parseOnce) { - new ImplicitAssertionsPhase().apply(graph, bb.getProviders()); - /* - * Do a complete Canonicalizer run once before graph encoding, to clean up any - * leftover uncanonicalized nodes. - */ - CanonicalizerPhase.create().apply(graph, bb.getProviders()); + optimizeAfterParsing(bb, graph); } for (BiConsumer methodAfterParsingHook : methodAfterParsingHooks) { @@ -567,6 +564,17 @@ public void methodAfterParsingHook(BigBang bb, AnalysisMethod method, Structured } } + protected void optimizeAfterParsing(BigBang bb, StructuredGraph graph) { + new ImplicitAssertionsPhase().apply(graph, bb.getProviders()); + new BoxNodeIdentityPhase().apply(graph, bb.getProviders()); + new PartialEscapePhase(false, false, CanonicalizerPhase.create(), null, options).apply(graph, bb.getProviders()); + /* + * Do a complete Canonicalizer run once before graph encoding, to clean up any leftover + * uncanonicalized nodes. + */ + CanonicalizerPhase.create().apply(graph, bb.getProviders()); + } + @Override public void methodBeforeTypeFlowCreationHook(PointsToAnalysis bb, AnalysisMethod method, StructuredGraph graph) { if (method.isEntryPoint() && !Modifier.isStatic(graph.method().getModifiers())) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java index 39377b767663..0ed8b2199c55 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java @@ -52,6 +52,7 @@ import org.graalvm.compiler.core.GraalCompiler; import org.graalvm.compiler.core.common.CompilationIdentifier; import org.graalvm.compiler.core.common.CompilationIdentifier.Verbosity; +import org.graalvm.compiler.core.common.Fields; import org.graalvm.compiler.core.common.GraalOptions; import org.graalvm.compiler.core.common.spi.CodeGenProviders; import org.graalvm.compiler.core.common.type.AbstractObjectStamp; @@ -95,6 +96,10 @@ import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.BytecodeExceptionMode; import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin; import org.graalvm.compiler.nodes.java.MethodCallTargetNode; +import org.graalvm.compiler.nodes.virtual.CommitAllocationNode; +import org.graalvm.compiler.nodes.virtual.VirtualInstanceNode; +import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; +import org.graalvm.compiler.nodes.virtual.VirtualObjectState; import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.phases.OptimisticOptimizations; import org.graalvm.compiler.phases.PhaseSuite; @@ -116,7 +121,9 @@ import org.graalvm.nativeimage.ImageSingletons; import com.oracle.graal.pointsto.infrastructure.GraphProvider.Purpose; +import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMethod; +import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.HostedProviders; import com.oracle.graal.pointsto.phases.SubstrateIntrinsicGraphBuilder; import com.oracle.graal.pointsto.util.CompletionExecutor; @@ -150,7 +157,9 @@ import com.oracle.svm.hosted.FeatureHandler; import com.oracle.svm.hosted.NativeImageGenerator; import com.oracle.svm.hosted.NativeImageOptions; +import com.oracle.svm.hosted.meta.HostedField; import com.oracle.svm.hosted.meta.HostedMethod; +import com.oracle.svm.hosted.meta.HostedType; import com.oracle.svm.hosted.meta.HostedUniverse; import com.oracle.svm.hosted.phases.DevirtualizeCallsPhase; import com.oracle.svm.hosted.phases.HostedGraphBuilderPhase; @@ -170,10 +179,8 @@ import jdk.vm.ci.code.site.Infopoint; import jdk.vm.ci.code.site.InfopointReason; import jdk.vm.ci.meta.Constant; -import jdk.vm.ci.meta.JavaField; -import jdk.vm.ci.meta.JavaMethod; -import jdk.vm.ci.meta.JavaType; import jdk.vm.ci.meta.MetaAccessProvider; +import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; @@ -777,6 +784,8 @@ private StructuredGraph transplantGraph(DebugContext debug, HostedMethod hMethod boolean trackNodeSourcePosition = GraalOptions.TrackNodeSourcePosition.getValue(options); StructuredGraph graph = aGraph.copy(universe.lookup(aGraph.method()), options, debug, trackNodeSourcePosition); + transplantEscapeAnalysisState(graph); + IdentityHashMap replacements = new IdentityHashMap<>(); for (Node node : graph.getNodes()) { NodeClass nodeClass = node.getNodeClass(); @@ -816,12 +825,12 @@ public static Object replaceAnalysisObjects(Object obj, Node node, IdentityHashM if (obj instanceof Node) { throw VMError.shouldNotReachHere("Must not replace a Graal graph nodes, only data objects referenced from a node"); - } else if (obj instanceof JavaType) { - newReplacement = hUniverse.lookup((JavaType) obj); - } else if (obj instanceof JavaMethod) { - newReplacement = hUniverse.lookup((JavaMethod) obj); - } else if (obj instanceof JavaField) { - newReplacement = hUniverse.lookup((JavaField) obj); + } else if (obj instanceof AnalysisType) { + newReplacement = hUniverse.lookup((AnalysisType) obj); + } else if (obj instanceof AnalysisMethod) { + newReplacement = hUniverse.lookup((AnalysisMethod) obj); + } else if (obj instanceof AnalysisField) { + newReplacement = hUniverse.lookup((AnalysisField) obj); } else if (obj.getClass() == ObjectStamp.class) { ObjectStamp stamp = (ObjectStamp) obj; @@ -905,6 +914,78 @@ public static Object replaceAnalysisObjects(Object obj, Node node, IdentityHashM return newReplacement; } + /** + * The nodes produced by escape analysis need some manual patching: escape analysis requires + * that {@link ResolvedJavaType#getInstanceFields} is stable and uses the index of a field in + * that array also to index its own data structures. But {@link AnalysisType} and + * {@link HostedType} cannot return fields in the same order: Fields that are not seen as + * reachable by the static analysis are removed from the hosted type; and the layout of objects, + * i.e., the field order, is only decided after static analysis. Therefore, we need to fix up + * all the nodes that implicitly use the field index. + */ + private void transplantEscapeAnalysisState(StructuredGraph graph) { + for (CommitAllocationNode node : graph.getNodes().filter(CommitAllocationNode.class)) { + List values = node.getValues(); + List aValues = new ArrayList<>(values); + values.clear(); + + int aObjectStartIndex = 0; + for (VirtualObjectNode virtualObject : node.getVirtualObjects()) { + transplantVirtualObjectState(virtualObject, aValues, values, aObjectStartIndex); + aObjectStartIndex += virtualObject.entryCount(); + } + assert aValues.size() == aObjectStartIndex; + } + + for (VirtualObjectState node : graph.getNodes().filter(VirtualObjectState.class)) { + List values = node.values(); + List aValues = new ArrayList<>(values); + values.clear(); + + transplantVirtualObjectState(node.object(), aValues, values, 0); + } + + for (VirtualInstanceNode node : graph.getNodes(VirtualInstanceNode.TYPE)) { + AnalysisType aType = (AnalysisType) node.type(); + ResolvedJavaField[] aFields = node.getFields(); + assert Arrays.equals(aFields, aType.getInstanceFields(true)); + HostedField[] hFields = universe.lookup(aType).getInstanceFields(true); + /* + * We cannot directly write the final field `VirtualInstanceNode.fields`. So we rely on + * the NodeClass mechanism, which is also used to transplant all other fields. + */ + Fields nodeClassDataFields = node.getNodeClass().getData(); + for (int i = 0; i < nodeClassDataFields.getCount(); i++) { + if (nodeClassDataFields.get(node, i) == aFields) { + nodeClassDataFields.putObjectChecked(node, i, hFields); + } + } + } + } + + private void transplantVirtualObjectState(VirtualObjectNode virtualObject, List aValues, List hValues, int aObjectStartIndex) { + AnalysisType aType = (AnalysisType) virtualObject.type(); + if (aType.isArray()) { + /* For arrays, there is no change between analysis and hosted elements. */ + for (int i = 0; i < virtualObject.entryCount(); i++) { + hValues.add(aValues.get(aObjectStartIndex + i)); + } + } else { + /* + * For instance fields, we need to add fields in the order of the hosted fields. + * `AnalysisField.getPosition` gives us the index of the field in the analysis-level + * list of field values. + */ + assert virtualObject.entryCount() == aType.getInstanceFields(true).length; + HostedField[] hFields = universe.lookup(aType).getInstanceFields(true); + for (HostedField hField : hFields) { + int aPosition = hField.wrapped.getPosition(); + assert hField.wrapped.equals(aType.getInstanceFields(true)[aPosition]); + hValues.add(aValues.get(aObjectStartIndex + aPosition)); + } + } + } + private final boolean parseOnce = SubstrateOptions.parseOnce(); @SuppressWarnings("try") diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedArrayClass.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedArrayClass.java index 2720e082784c..30a96a7a0609 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedArrayClass.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedArrayClass.java @@ -35,7 +35,7 @@ public class HostedArrayClass extends HostedClass { private final int arrayDepth; public HostedArrayClass(HostedUniverse universe, AnalysisType wrapped, JavaKind kind, JavaKind storageKind, HostedClass superClass, HostedInterface[] interfaces, HostedType componentType) { - super(universe, wrapped, kind, storageKind, superClass, interfaces, true); + super(universe, wrapped, kind, storageKind, superClass, interfaces); this.componentType = componentType; HostedType cur = this; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedClass.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedClass.java index 36eeba43efad..845aa6ce1182 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedClass.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedClass.java @@ -30,8 +30,8 @@ public abstract class HostedClass extends HostedType { - public HostedClass(HostedUniverse universe, AnalysisType wrapped, JavaKind kind, JavaKind storageKind, HostedClass superClass, HostedInterface[] interfaces, boolean isCloneable) { - super(universe, wrapped, kind, storageKind, superClass, interfaces, isCloneable); + public HostedClass(HostedUniverse universe, AnalysisType wrapped, JavaKind kind, JavaKind storageKind, HostedClass superClass, HostedInterface[] interfaces) { + super(universe, wrapped, kind, storageKind, superClass, interfaces); } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedInstanceClass.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedInstanceClass.java index c52b2161a786..782442bcb3a3 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedInstanceClass.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedInstanceClass.java @@ -24,10 +24,6 @@ */ package com.oracle.svm.hosted.meta; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - import com.oracle.graal.pointsto.meta.AnalysisType; import jdk.vm.ci.meta.JavaKind; @@ -35,14 +31,15 @@ public class HostedInstanceClass extends HostedClass { - protected HostedField[] instanceFields; + protected HostedField[] instanceFieldsWithoutSuper; + protected HostedField[] instanceFieldsWithSuper; protected int afterFieldsOffset; protected int instanceSize; protected boolean monitorFieldNeeded = false; protected int monitorFieldOffset = 0; - public HostedInstanceClass(HostedUniverse universe, AnalysisType wrapped, JavaKind kind, JavaKind storageKind, HostedClass superClass, HostedInterface[] interfaces, boolean isCloneable) { - super(universe, wrapped, kind, storageKind, superClass, interfaces, isCloneable); + public HostedInstanceClass(HostedUniverse universe, AnalysisType wrapped, JavaKind kind, JavaKind storageKind, HostedClass superClass, HostedInterface[] interfaces) { + super(universe, wrapped, kind, storageKind, superClass, interfaces); } @Override @@ -74,28 +71,17 @@ public int getArrayDimension() { @Override public HostedField[] getInstanceFields(boolean includeSuperclasses) { - assert instanceFields != null; - - if (includeSuperclasses && getSuperclass() != null) { - List fields = new ArrayList<>(); - fields.addAll(Arrays.asList(getSuperclass().getInstanceFields(true))); - fields.addAll(Arrays.asList(instanceFields)); - return fields.toArray(new HostedField[fields.size()]); - } - return instanceFields; + return includeSuperclasses ? instanceFieldsWithSuper : instanceFieldsWithoutSuper; } @Override public ResolvedJavaField findInstanceFieldWithOffset(long offset, JavaKind expectedKind) { assert offset >= 0; - for (HostedField field : instanceFields) { + for (HostedField field : instanceFieldsWithSuper) { if (field.getLocation() == offset && (expectedKind == null || field.getStorageKind() == expectedKind)) { return field; } } - if (getSuperclass() != null) { - return getSuperclass().findInstanceFieldWithOffset(offset, expectedKind); - } return null; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedInterface.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedInterface.java index f7b6a87e1759..0147510fc8b0 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedInterface.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedInterface.java @@ -37,7 +37,7 @@ public class HostedInterface extends HostedType { public HostedInterface(HostedUniverse universe, AnalysisType wrapped, JavaKind kind, JavaKind storageKind, HostedInterface[] interfaces) { - super(universe, wrapped, kind, storageKind, null, interfaces, false); + super(universe, wrapped, kind, storageKind, null, interfaces); } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedPrimitiveType.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedPrimitiveType.java index 86bcb479754c..256628ae8952 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedPrimitiveType.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedPrimitiveType.java @@ -31,7 +31,7 @@ public class HostedPrimitiveType extends HostedType { public HostedPrimitiveType(HostedUniverse universe, AnalysisType wrapped, JavaKind kind, JavaKind storageKind) { - super(universe, wrapped, kind, storageKind, null, new HostedInterface[0], false); + super(universe, wrapped, kind, storageKind, null, new HostedInterface[0]); } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java index 57142b89ec45..3c079e52d2fd 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java @@ -96,9 +96,7 @@ public abstract class HostedType implements SharedType, WrappedJavaType, Compara */ protected HostedType strengthenStampType; - private final boolean isCloneable; - - public HostedType(HostedUniverse universe, AnalysisType wrapped, JavaKind kind, JavaKind storageKind, HostedClass superClass, HostedInterface[] interfaces, boolean isCloneable) { + public HostedType(HostedUniverse universe, AnalysisType wrapped, JavaKind kind, JavaKind storageKind, HostedClass superClass, HostedInterface[] interfaces) { this.universe = universe; this.wrapped = wrapped; this.kind = kind; @@ -106,7 +104,6 @@ public HostedType(HostedUniverse universe, AnalysisType wrapped, JavaKind kind, this.superClass = superClass; this.interfaces = interfaces; this.typeID = -1; - this.isCloneable = isCloneable; } public HostedType getStrengthenStampType() { @@ -433,7 +430,7 @@ public boolean declaresDefaultMethods() { @Override public boolean isCloneableWithAllocation() { - return isCloneable; + return wrapped.isCloneableWithAllocation(); } @SuppressWarnings("deprecation") diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java index c68f556a5959..15537f1d5eb2 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java @@ -227,8 +227,7 @@ private HostedType makeType(AnalysisType aType) { } else if (aType.isInstanceClass()) { assert !aType.isInterface() && !aType.isArray(); HostedInstanceClass superClass = (HostedInstanceClass) makeType(aType.getSuperclass()); - boolean isCloneable = aMetaAccess.lookupJavaType(Cloneable.class).isAssignableFrom(aType); - hType = new HostedInstanceClass(hUniverse, aType, kind, storageKind, superClass, sInterfaces, isCloneable); + hType = new HostedInstanceClass(hUniverse, aType, kind, storageKind, superClass, sInterfaces); if (superClass == null) { hUniverse.kindToType.put(JavaKind.Object, hType); @@ -408,10 +407,10 @@ public static boolean isKnownImmutableType(Class clazz) { } private void layoutInstanceFields() { - layoutInstanceFields(hUniverse.getObjectClass(), ConfigurationValues.getObjectLayout().getFirstFieldOffset()); + layoutInstanceFields(hUniverse.getObjectClass(), ConfigurationValues.getObjectLayout().getFirstFieldOffset(), new HostedField[0]); } - private void layoutInstanceFields(HostedInstanceClass clazz, int superSize) { + private void layoutInstanceFields(HostedInstanceClass clazz, int superSize, HostedField[] superFields) { ArrayList rawFields = new ArrayList<>(); ArrayList orderedFields = new ArrayList<>(); ObjectLayout layout = ConfigurationValues.getObjectLayout(); @@ -485,10 +484,19 @@ private void layoutInstanceFields(HostedInstanceClass clazz, int superSize) { nextOffset += referenceFieldAlignmentAndSize; } - clazz.instanceFields = orderedFields.toArray(new HostedField[orderedFields.size()]); + clazz.instanceFieldsWithoutSuper = orderedFields.toArray(new HostedField[orderedFields.size()]); clazz.instanceSize = layout.alignUp(nextOffset); clazz.afterFieldsOffset = nextOffset; + if (clazz.instanceFieldsWithoutSuper.length == 0) { + clazz.instanceFieldsWithSuper = superFields; + } else if (superFields.length == 0) { + clazz.instanceFieldsWithSuper = clazz.instanceFieldsWithoutSuper; + } else { + clazz.instanceFieldsWithSuper = Arrays.copyOf(superFields, superFields.length + clazz.instanceFieldsWithoutSuper.length); + System.arraycopy(clazz.instanceFieldsWithoutSuper, 0, clazz.instanceFieldsWithSuper, superFields.length, clazz.instanceFieldsWithoutSuper.length); + } + for (HostedType subClass : clazz.subTypes) { if (subClass.isInstanceClass()) { /* @@ -497,7 +505,7 @@ private void layoutInstanceFields(HostedInstanceClass clazz, int superSize) { * possible because each class that needs a synthetic field gets its own synthetic * field at the end of its instance fields. */ - layoutInstanceFields((HostedInstanceClass) subClass, endOfFieldsOffset); + layoutInstanceFields((HostedInstanceClass) subClass, endOfFieldsOffset, clazz.instanceFieldsWithSuper); } } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java index 51d43b845b1f..f6d4f5e89176 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java @@ -29,9 +29,7 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; import java.util.Objects; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicLongFieldUpdater; @@ -69,12 +67,16 @@ import org.graalvm.compiler.nodes.java.InstanceOfDynamicNode; import org.graalvm.compiler.nodes.java.NewArrayNode; import org.graalvm.compiler.nodes.java.StoreIndexedNode; -import org.graalvm.compiler.nodes.java.ValidateNewInstanceClassNode; import org.graalvm.compiler.nodes.spi.ArrayLengthProvider; import org.graalvm.compiler.nodes.spi.CoreProviders; import org.graalvm.compiler.nodes.spi.Replacements; import org.graalvm.compiler.nodes.type.NarrowOopStamp; +import org.graalvm.compiler.nodes.type.StampTool; import org.graalvm.compiler.nodes.util.GraphUtil; +import org.graalvm.compiler.nodes.virtual.AllocatedObjectNode; +import org.graalvm.compiler.nodes.virtual.CommitAllocationNode; +import org.graalvm.compiler.nodes.virtual.VirtualArrayNode; +import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; import org.graalvm.compiler.options.Option; import org.graalvm.compiler.replacements.nodes.MacroNode.MacroParams; import org.graalvm.compiler.serviceprovider.JavaVersionUtil; @@ -342,6 +344,40 @@ static Class[] extractClassArray(AnnotationSubstitutionProcessor annotationSu */ return classes == null ? null : Stream.of(classes).allMatch(Objects::nonNull) ? classes : null; + } else if (originalArrayNode instanceof AllocatedObjectNode && StampTool.isAlwaysArray(originalArrayNode)) { + AllocatedObjectNode allocatedObjectNode = (AllocatedObjectNode) originalArrayNode; + CommitAllocationNode commitAllocationNode = allocatedObjectNode.getCommit(); + if (commitAllocationNode.next() != null) { + /* Nodes after the array materialization could interfere with the array. */ + return null; + } + + int objectStartIndex = 0; + for (VirtualObjectNode virtualObject : commitAllocationNode.getVirtualObjects()) { + if (virtualObject == allocatedObjectNode.getVirtualObject()) { + /* We found the begin of the object we were looking for. */ + assert virtualObject instanceof VirtualArrayNode : virtualObject; + + Class[] result = new Class[virtualObject.entryCount()]; + for (int i = 0; i < result.length; i++) { + ValueNode valueNode = commitAllocationNode.getValues().get(objectStartIndex + i); + if (!valueNode.isJavaConstant()) { + return null; + } + Class clazz = snippetReflection.asObject(Class.class, valueNode.asJavaConstant()); + /* + * It is possible that the returned class is a substitution class, e.g., + * DynamicHub returned for a Class.class constant. Get the target class of + * the substitution class. + */ + result[i] = annotationSubstitutions == null || clazz == null ? clazz : annotationSubstitutions.getTargetClass(clazz); + } + return result; + } + objectStartIndex += virtualObject.entryCount(); + } + throw VMError.shouldNotReachHere("Must have found the virtual object"); + } else if (originalArrayNode instanceof NewArrayNode) { /* * Find the elements written to the array. If the array length is a constant, all @@ -358,51 +394,41 @@ static Class[] extractClassArray(AnnotationSubstitutionProcessor annotationSu return null; } assert newArrayLengthNode.asJavaConstant().getJavaKind() == JavaKind.Int; + int newArrayLength = newArrayLengthNode.asJavaConstant().asInt(); /* * Walk down the control flow successor as long as we find StoreIndexedNode. Those are * values written in the array. */ - List> classList = new ArrayList<>(); + Class[] result = new Class[newArrayLength]; FixedNode successor = unwrapNode(newArray.next()); - /* - * In a case when we are creating a proxy, which contains a method with a class - * (initialized at runtime) as a parameter, a successor node will not be StoreIndexNode, - * so we need to skip initialization nodes. - */ - if (successor instanceof EnsureClassInitializedNode) { - AbstractBeginNode classInitializedNode = ((EnsureClassInitializedNode) successor).next(); - VMError.guarantee(classInitializedNode != null); - successor = classInitializedNode.next(); - } while (successor instanceof StoreIndexedNode) { StoreIndexedNode store = (StoreIndexedNode) successor; if (getDeoptProxyOriginalValue(store.array()).equals(newArray)) { + if (!store.index().isJavaConstant()) { + return null; + } + int index = store.index().asJavaConstant().asInt(); ValueNode valueNode = store.value(); - if (valueNode.isConstant() && !valueNode.isNullConstant()) { - Class clazz = snippetReflection.asObject(Class.class, valueNode.asJavaConstant()); - /* - * It is possible that the returned class is a substitution class, e.g., - * DynamicHub returned for a Class.class constant. Get the target class of - * the substitution class. - */ - classList.add(annotationSubstitutions == null ? clazz : annotationSubstitutions.getTargetClass(clazz)); - } else { - /* If not all classes are non-null constants we bail out. */ - classList = null; - break; + if (!valueNode.isJavaConstant()) { + return null; } + Class clazz = snippetReflection.asObject(Class.class, valueNode.asJavaConstant()); + /* + * It is possible that the returned class is a substitution class, e.g., + * DynamicHub returned for a Class.class constant. Get the target class of the + * substitution class. + */ + result[index] = annotationSubstitutions == null || clazz == null ? clazz : annotationSubstitutions.getTargetClass(clazz); } successor = unwrapNode(store.next()); } - /* - * Check that all array elements are filled, i.e., the number of writes matches the size - * of the array. - */ - int newArrayLength = newArrayLengthNode.asJavaConstant().asInt(); - - return classList != null && classList.size() == newArrayLength ? classList.toArray(new Class[0]) : null; + if (successor != null) { + /* Nodes after the array store could interfere with the array. */ + return null; + } + return result; } return null; } @@ -416,20 +442,26 @@ private static ValueNode getDeoptProxyOriginalValue(ValueNode node) { } /** - * Unwrap FullInfopointNode and DeoptEntryNode since they are not important for the Class[] - * elements analysis. + * Ignore nodes in the control flow graph that are not important for the Class[] elements + * analysis. */ private static FixedNode unwrapNode(FixedNode node) { FixedNode successor = node; - while (successor instanceof FullInfopointNode || successor instanceof DeoptEntryNode) { - assert !(successor instanceof DeoptEntryNode) || ((HostedMethod) successor.graph().method()).isDeoptTarget(); - if (successor instanceof FullInfopointNode) { + while (true) { + if (successor instanceof EnsureClassInitializedNode) { + successor = ((EnsureClassInitializedNode) successor).next(); + } else if (successor instanceof FullInfopointNode) { successor = ((FullInfopointNode) successor).next(); - } else { + } else if (successor instanceof DeoptEntryNode) { + assert ((HostedMethod) successor.graph().method()).isDeoptTarget(); successor = ((DeoptEntryNode) successor).next(); + } else if (successor instanceof AbstractBeginNode) { + /* Useless block begins can occur during parsing or graph decoding. */ + successor = ((AbstractBeginNode) successor).next(); + } else { + return successor; } } - return successor; } private static void registerAtomicUpdaterPlugins(MetaAccessProvider metaAccess, SnippetReflectionProvider snippetReflection, InvocationPlugins plugins, ParsingReason reason) { @@ -617,8 +649,7 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec */ ValueNode clazzNonNull = b.nullCheckedValue(clazz, DeoptimizationAction.None); b.add(new EnsureClassInitializedNode(clazzNonNull)); - ValueNode clazzLegal = b.add(new ValidateNewInstanceClassNode(clazzNonNull)); - b.addPush(JavaKind.Object, new DynamicNewInstanceNode(clazzLegal, true)); + DynamicNewInstanceNode.createAndPush(b, clazzNonNull); return true; } });