Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GR-14813] Remove unnecessary class initializations. #3595

Merged
merged 1 commit into from
Jul 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@
import org.graalvm.compiler.graph.Edges;
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.InputType;
import org.graalvm.compiler.nodeinfo.NodeInfo;
import org.graalvm.compiler.nodes.calc.FloatingNode;
Expand All @@ -49,6 +47,8 @@
import org.graalvm.compiler.nodes.java.ArrayLengthNode;
import org.graalvm.compiler.nodes.java.LoadFieldNode;
import org.graalvm.compiler.nodes.java.LoadIndexedNode;
import org.graalvm.compiler.nodes.spi.Canonicalizable;
import org.graalvm.compiler.nodes.spi.CanonicalizerTool;
import org.graalvm.compiler.nodes.spi.CoreProviders;
import org.graalvm.compiler.nodes.spi.CoreProvidersDelegate;
import org.graalvm.compiler.nodes.util.GraphUtil;
Expand Down Expand Up @@ -333,7 +333,11 @@ private void handleCanonicalization(LoopScope loopScope, int nodeOrderId, FixedN
}
}
if (!node.isDeleted()) {
GraphUtil.unlinkFixedNode((FixedWithNextNode) node);
if (node instanceof WithExceptionNode) {
GraphUtil.unlinkAndKillExceptionEdge((WithExceptionNode) node);
} else {
GraphUtil.unlinkFixedNode((FixedWithNextNode) node);
}
node.replaceAtUsagesAndDelete(canonical);
}
assert lookupNode(loopScope, nodeOrderId) == node;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.StructuredGraph.StageFlag;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.WithExceptionNode;
import org.graalvm.compiler.nodes.calc.FloatingNode;
import org.graalvm.compiler.nodes.spi.Canonicalizable;
import org.graalvm.compiler.nodes.spi.Canonicalizable.BinaryCommutative;
Expand Down Expand Up @@ -490,6 +491,49 @@ private boolean performReplacement(final Node node, Node newCanonical) {
fixed.replaceAtPredecessor(canonical);
GraphUtil.killCFG(fixed);
return true;

} else if (fixed instanceof WithExceptionNode) {
/*
* A fixed node with an exception edge is handled similarly to a
* FixedWithNextNode. The only difference is that the exception edge needs
* to be killed as part of canonicalization (unless the canonical node is a
* new WithExceptionNode too).
*/
WithExceptionNode withException = (WithExceptionNode) fixed;

// When removing a fixed node, new canonicalization
// opportunities for its successor may arise
assert withException.next() != null;
tool.addToWorkList(withException.next());
if (canonical == null) {
// case 3
node.replaceAtUsages(null);
GraphUtil.unlinkAndKillExceptionEdge(withException);
GraphUtil.killWithUnusedFloatingInputs(withException);
} else if (canonical instanceof FloatingNode) {
// case 4
withException.killExceptionEdge();
graph.replaceSplitWithFloating(withException, (FloatingNode) canonical, withException.next());
} else {
assert canonical instanceof FixedNode;
if (canonical.predecessor() == null) {
assert !canonical.cfgSuccessors().iterator().hasNext() : "replacement " + canonical + " shouldn't have successors";
// case 5
if (canonical instanceof WithExceptionNode) {
graph.replaceWithExceptionSplit(withException, (WithExceptionNode) canonical);
} else {
withException.killExceptionEdge();
graph.replaceSplitWithFixed(withException, (FixedWithNextNode) canonical, withException.next());
}
} else {
assert canonical.cfgSuccessors().iterator().hasNext() : "replacement " + canonical + " should have successors";
// case 6
node.replaceAtUsages(canonical);
GraphUtil.unlinkAndKillExceptionEdge(withException);
GraphUtil.killWithUnusedFloatingInputs(withException);
}
}

} else {
assert fixed instanceof FixedWithNextNode;
FixedWithNextNode fixedWithNext = (FixedWithNextNode) fixed;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
package com.oracle.svm.core.classinitialization;

import org.graalvm.compiler.core.common.type.StampFactory;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.graph.Node.NodeIntrinsicFactory;
import org.graalvm.compiler.graph.NodeClass;
import org.graalvm.compiler.nodeinfo.InputType;
Expand All @@ -37,17 +38,18 @@
import org.graalvm.compiler.nodes.WithExceptionNode;
import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
import org.graalvm.compiler.nodes.memory.SingleMemoryKill;
import org.graalvm.compiler.nodes.spi.Canonicalizable;
import org.graalvm.compiler.nodes.spi.CanonicalizerTool;
import org.graalvm.compiler.nodes.spi.CoreProviders;
import org.graalvm.compiler.nodes.spi.Lowerable;
import org.graalvm.compiler.nodes.spi.Simplifiable;
import org.graalvm.compiler.nodes.spi.SimplifierTool;
import org.graalvm.compiler.nodes.type.StampTool;
import org.graalvm.word.LocationIdentity;

import jdk.vm.ci.meta.ResolvedJavaType;

@NodeInfo(size = NodeSize.SIZE_16, cycles = NodeCycles.CYCLES_2, cyclesRationale = "Class initialization only runs at most once at run time, so the amortized cost is only the is-initialized check")
@NodeIntrinsicFactory
public class EnsureClassInitializedNode extends WithExceptionNode implements Simplifiable, StateSplit, SingleMemoryKill, Lowerable {
public class EnsureClassInitializedNode extends WithExceptionNode implements Canonicalizable, StateSplit, SingleMemoryKill, Lowerable {

public static final NodeClass<EnsureClassInitializedNode> TYPE = NodeClass.create(EnsureClassInitializedNode.class);

Expand Down Expand Up @@ -95,15 +97,61 @@ public boolean hasSideEffect() {
return true;
}

@Override
public void simplify(SimplifierTool tool) {
public ResolvedJavaType constantTypeOrNull(CoreProviders providers) {
if (hub.isConstant()) {
ResolvedJavaType type = tool.getConstantReflection().asJavaType(hub.asConstant());
if (type != null && type.isInitialized()) {
killExceptionEdge();
graph().removeSplit(this, next());
return;
return providers.getConstantReflection().asJavaType(hub.asConstant());
} else {
return null;
}
}

@Override
public Node canonical(CanonicalizerTool tool) {
ResolvedJavaType type = constantTypeOrNull(tool);
if (type != null) {
for (FrameState cur = stateAfter; cur != null; cur = cur.outerFrameState()) {
if (!needsRuntimeInitialization(cur.getMethod().getDeclaringClass(), type)) {
return null;
}
}
}
return this;
}

/**
* Return true if the type needs to be initialized at run time, i.e., it has not been already
* initialized during image generation or initialization is implied by the declaringClass.
*/
public static boolean needsRuntimeInitialization(ResolvedJavaType declaringClass, ResolvedJavaType type) {
if (type.isInitialized() || type.isArray() || type.equals(declaringClass)) {
/*
* The simple cases: the type is already initialized, or the type is an exact match with
* the declaring class.
*/
return false;

} else if (declaringClass.isInterface()) {
/*
* Initialization of an interface does not trigger initialization of superinterfaces.
* Regardless whether any of the involved interfaces declares default methods.
*/
return true;

} else if (type.isAssignableFrom(declaringClass)) {
if (type.isInterface()) {
/*
* Initialization of a class only triggers initialization of an implemented
* interface if that interface declares default methods.
*/
return !type.declaresDefaultMethods();
} else {
/* Initialization of a class triggers initialization of all superclasses. */
return false;
}

} else {
/* No relationship between the two types. */
return true;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@
import com.oracle.svm.hosted.substitute.UnsafeAutomaticSubstitutionProcessor;
import com.oracle.svm.util.ReflectionUtil;

import jdk.vm.ci.meta.Constant;
import jdk.vm.ci.meta.DeoptimizationReason;
import jdk.vm.ci.meta.ResolvedJavaField;
import jdk.vm.ci.meta.ResolvedJavaMethod;
Expand Down Expand Up @@ -652,10 +651,9 @@ private void checkClassInitializerSideEffect(BigBang bb, AnalysisMethod method,
*/
classInitializerSideEffect.put(method, true);
} else if (n instanceof EnsureClassInitializedNode) {
Constant constantHub = ((EnsureClassInitializedNode) n).getHub().asConstant();
if (constantHub != null) {
AnalysisType type = (AnalysisType) bb.getProviders().getConstantReflection().asJavaType(constantHub);
initializedClasses.computeIfAbsent(method, k -> new HashSet<>()).add(type);
ResolvedJavaType type = ((EnsureClassInitializedNode) n).constantTypeOrNull(bb.getProviders());
if (type != null) {
initializedClasses.computeIfAbsent(method, k -> new HashSet<>()).add((AnalysisType) type);
} else {
classInitializerSideEffect.put(method, true);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import org.graalvm.compiler.phases.util.Providers;

import com.oracle.svm.core.ParsingReason;
import com.oracle.svm.core.classinitialization.EnsureClassInitializedNode;
import com.oracle.svm.core.graal.thread.VMThreadLocalAccess;
import com.oracle.svm.core.option.HostedOptionValues;
import com.oracle.svm.core.util.VMError;
Expand Down Expand Up @@ -197,7 +198,7 @@ final class AbortOnUnitializedClassPlugin extends NoClassInitializationPlugin {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaType type, Supplier<FrameState> frameState, ValueNode[] classInit) {
ResolvedJavaMethod clinitMethod = b.getGraph().method();
if (type.isInitialized() || type.isArray() || type.equals(clinitMethod.getDeclaringClass())) {
if (!EnsureClassInitializedNode.needsRuntimeInitialization(clinitMethod.getDeclaringClass(), type)) {
return false;
}
if (classInitializationSupport.computeInitKindAndMaybeInitializeClass(ConfigurableClassInitialization.getJavaClass(type), true, analyzedClasses) != InitKind.RUN_TIME) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ protected Instance createGraphBuilderInstance(GraphBuilderConfiguration graphBui
}

public void emitEnsureInitializedCall(ResolvedJavaType type) {
if (SubstrateClassInitializationPlugin.needsRuntimeInitialization(graph.method().getDeclaringClass(), type)) {
if (EnsureClassInitializedNode.needsRuntimeInitialization(graph.method().getDeclaringClass(), type)) {
ValueNode hub = createConstant(getConstantReflection().asJavaClass(type), JavaKind.Object);
appendWithUnwind(new EnsureClassInitializedNode(hub));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public void loadReferencedType(GraphBuilderContext builder, ConstantPool constan

@Override
public boolean apply(GraphBuilderContext builder, ResolvedJavaType type, Supplier<FrameState> frameState, ValueNode[] classInit) {
if (needsRuntimeInitialization(builder.getMethod().getDeclaringClass(), type)) {
if (EnsureClassInitializedNode.needsRuntimeInitialization(builder.getMethod().getDeclaringClass(), type)) {
emitEnsureClassInitialized(builder, SubstrateObjectConstant.forObject(host.dynamicHub(type)), frameState.get());
/*
* The classInit value is only registered with Invoke nodes. Since we do not need that,
Expand All @@ -79,12 +79,4 @@ private static void emitEnsureClassInitialized(GraphBuilderContext builder, Java
EnsureClassInitializedNode node = new EnsureClassInitializedNode(hub, frameState);
builder.add(node);
}

/**
* Return true if the type needs to be initialized at run time, i.e., it has not been already
* initialized during image generation.
*/
static boolean needsRuntimeInitialization(ResolvedJavaType declaringClass, ResolvedJavaType type) {
return !declaringClass.equals(type) && !type.isInitialized() && !type.isArray();
}
}