Skip to content

Commit e01ba82

Browse files
author
Christian Wimmer
committed
Remove unnecessary class initializations
1 parent 88b60a5 commit e01ba82

File tree

7 files changed

+116
-29
lines changed

7 files changed

+116
-29
lines changed

compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/SimplifyingGraphDecoder.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@
3838
import org.graalvm.compiler.graph.Edges;
3939
import org.graalvm.compiler.graph.Node;
4040
import org.graalvm.compiler.graph.NodeClass;
41-
import org.graalvm.compiler.nodes.spi.Canonicalizable;
42-
import org.graalvm.compiler.nodes.spi.CanonicalizerTool;
4341
import org.graalvm.compiler.nodeinfo.InputType;
4442
import org.graalvm.compiler.nodeinfo.NodeInfo;
4543
import org.graalvm.compiler.nodes.calc.FloatingNode;
@@ -49,6 +47,8 @@
4947
import org.graalvm.compiler.nodes.java.ArrayLengthNode;
5048
import org.graalvm.compiler.nodes.java.LoadFieldNode;
5149
import org.graalvm.compiler.nodes.java.LoadIndexedNode;
50+
import org.graalvm.compiler.nodes.spi.Canonicalizable;
51+
import org.graalvm.compiler.nodes.spi.CanonicalizerTool;
5252
import org.graalvm.compiler.nodes.spi.CoreProviders;
5353
import org.graalvm.compiler.nodes.spi.CoreProvidersDelegate;
5454
import org.graalvm.compiler.nodes.util.GraphUtil;
@@ -333,7 +333,11 @@ private void handleCanonicalization(LoopScope loopScope, int nodeOrderId, FixedN
333333
}
334334
}
335335
if (!node.isDeleted()) {
336-
GraphUtil.unlinkFixedNode((FixedWithNextNode) node);
336+
if (node instanceof WithExceptionNode) {
337+
GraphUtil.unlinkAndKillExceptionEdge((WithExceptionNode) node);
338+
} else {
339+
GraphUtil.unlinkFixedNode((FixedWithNextNode) node);
340+
}
337341
node.replaceAtUsagesAndDelete(canonical);
338342
}
339343
assert lookupNode(loopScope, nodeOrderId) == node;

compiler/src/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/CanonicalizerPhase.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import org.graalvm.compiler.nodes.StructuredGraph;
5858
import org.graalvm.compiler.nodes.StructuredGraph.StageFlag;
5959
import org.graalvm.compiler.nodes.ValueNode;
60+
import org.graalvm.compiler.nodes.WithExceptionNode;
6061
import org.graalvm.compiler.nodes.calc.FloatingNode;
6162
import org.graalvm.compiler.nodes.spi.Canonicalizable;
6263
import org.graalvm.compiler.nodes.spi.Canonicalizable.BinaryCommutative;
@@ -490,6 +491,49 @@ private boolean performReplacement(final Node node, Node newCanonical) {
490491
fixed.replaceAtPredecessor(canonical);
491492
GraphUtil.killCFG(fixed);
492493
return true;
494+
495+
} else if (fixed instanceof WithExceptionNode) {
496+
/*
497+
* A fixed node with an exception edge is handled similarly to a
498+
* FixedWithNextNode. The only difference is that the exception edge needs
499+
* to be killed as part of canonicalization (unless the canonical node is a
500+
* new WithExceptionNode too).
501+
*/
502+
WithExceptionNode withException = (WithExceptionNode) fixed;
503+
504+
// When removing a fixed node, new canonicalization
505+
// opportunities for its successor may arise
506+
assert withException.next() != null;
507+
tool.addToWorkList(withException.next());
508+
if (canonical == null) {
509+
// case 3
510+
node.replaceAtUsages(null);
511+
GraphUtil.unlinkAndKillExceptionEdge(withException);
512+
GraphUtil.killWithUnusedFloatingInputs(withException);
513+
} else if (canonical instanceof FloatingNode) {
514+
// case 4
515+
withException.killExceptionEdge();
516+
graph.replaceSplitWithFloating(withException, (FloatingNode) canonical, withException.next());
517+
} else {
518+
assert canonical instanceof FixedNode;
519+
if (canonical.predecessor() == null) {
520+
assert !canonical.cfgSuccessors().iterator().hasNext() : "replacement " + canonical + " shouldn't have successors";
521+
// case 5
522+
if (canonical instanceof WithExceptionNode) {
523+
graph.replaceWithExceptionSplit(withException, (WithExceptionNode) canonical);
524+
} else {
525+
withException.killExceptionEdge();
526+
graph.replaceSplitWithFixed(withException, (FixedWithNextNode) canonical, withException.next());
527+
}
528+
} else {
529+
assert canonical.cfgSuccessors().iterator().hasNext() : "replacement " + canonical + " should have successors";
530+
// case 6
531+
node.replaceAtUsages(canonical);
532+
GraphUtil.unlinkAndKillExceptionEdge(withException);
533+
GraphUtil.killWithUnusedFloatingInputs(withException);
534+
}
535+
}
536+
493537
} else {
494538
assert fixed instanceof FixedWithNextNode;
495539
FixedWithNextNode fixedWithNext = (FixedWithNextNode) fixed;

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/classinitialization/EnsureClassInitializedNode.java

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
package com.oracle.svm.core.classinitialization;
2626

2727
import org.graalvm.compiler.core.common.type.StampFactory;
28+
import org.graalvm.compiler.graph.Node;
2829
import org.graalvm.compiler.graph.Node.NodeIntrinsicFactory;
2930
import org.graalvm.compiler.graph.NodeClass;
3031
import org.graalvm.compiler.nodeinfo.InputType;
@@ -37,17 +38,18 @@
3738
import org.graalvm.compiler.nodes.WithExceptionNode;
3839
import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
3940
import org.graalvm.compiler.nodes.memory.SingleMemoryKill;
41+
import org.graalvm.compiler.nodes.spi.Canonicalizable;
42+
import org.graalvm.compiler.nodes.spi.CanonicalizerTool;
43+
import org.graalvm.compiler.nodes.spi.CoreProviders;
4044
import org.graalvm.compiler.nodes.spi.Lowerable;
41-
import org.graalvm.compiler.nodes.spi.Simplifiable;
42-
import org.graalvm.compiler.nodes.spi.SimplifierTool;
4345
import org.graalvm.compiler.nodes.type.StampTool;
4446
import org.graalvm.word.LocationIdentity;
4547

4648
import jdk.vm.ci.meta.ResolvedJavaType;
4749

4850
@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")
4951
@NodeIntrinsicFactory
50-
public class EnsureClassInitializedNode extends WithExceptionNode implements Simplifiable, StateSplit, SingleMemoryKill, Lowerable {
52+
public class EnsureClassInitializedNode extends WithExceptionNode implements Canonicalizable, StateSplit, SingleMemoryKill, Lowerable {
5153

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

@@ -95,15 +97,61 @@ public boolean hasSideEffect() {
9597
return true;
9698
}
9799

98-
@Override
99-
public void simplify(SimplifierTool tool) {
100+
public ResolvedJavaType constantTypeOrNull(CoreProviders providers) {
100101
if (hub.isConstant()) {
101-
ResolvedJavaType type = tool.getConstantReflection().asJavaType(hub.asConstant());
102-
if (type != null && type.isInitialized()) {
103-
killExceptionEdge();
104-
graph().removeSplit(this, next());
105-
return;
102+
return providers.getConstantReflection().asJavaType(hub.asConstant());
103+
} else {
104+
return null;
105+
}
106+
}
107+
108+
@Override
109+
public Node canonical(CanonicalizerTool tool) {
110+
ResolvedJavaType type = constantTypeOrNull(tool);
111+
if (type != null) {
112+
for (FrameState cur = stateAfter; cur != null; cur = cur.outerFrameState()) {
113+
if (!needsRuntimeInitialization(cur.getMethod().getDeclaringClass(), type)) {
114+
return null;
115+
}
116+
}
117+
}
118+
return this;
119+
}
120+
121+
/**
122+
* Return true if the type needs to be initialized at run time, i.e., it has not been already
123+
* initialized during image generation or initialization is implied by the declaringClass.
124+
*/
125+
public static boolean needsRuntimeInitialization(ResolvedJavaType declaringClass, ResolvedJavaType type) {
126+
if (type.isInitialized() || type.isArray() || type.equals(declaringClass)) {
127+
/*
128+
* The simple cases: the type is already initialized, or the type is an exact match with
129+
* the declaring class.
130+
*/
131+
return false;
132+
133+
} else if (declaringClass.isInterface()) {
134+
/*
135+
* Initialization of an interface does not trigger initialization of superinterfaces.
136+
* Regardless whether any of the involved interfaces declares default methods.
137+
*/
138+
return true;
139+
140+
} else if (type.isAssignableFrom(declaringClass)) {
141+
if (type.isInterface()) {
142+
/*
143+
* Initialization of a class only triggers initialization of an implemented
144+
* interface if that interface declares default methods.
145+
*/
146+
return !type.declaresDefaultMethods();
147+
} else {
148+
/* Initialization of a class triggers initialization of all superclasses. */
149+
return false;
106150
}
151+
152+
} else {
153+
/* No relationship between the two types. */
154+
return true;
107155
}
108156
}
109157

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@
109109
import com.oracle.svm.hosted.substitute.UnsafeAutomaticSubstitutionProcessor;
110110
import com.oracle.svm.util.ReflectionUtil;
111111

112-
import jdk.vm.ci.meta.Constant;
113112
import jdk.vm.ci.meta.DeoptimizationReason;
114113
import jdk.vm.ci.meta.ResolvedJavaField;
115114
import jdk.vm.ci.meta.ResolvedJavaMethod;
@@ -652,10 +651,9 @@ private void checkClassInitializerSideEffect(BigBang bb, AnalysisMethod method,
652651
*/
653652
classInitializerSideEffect.put(method, true);
654653
} else if (n instanceof EnsureClassInitializedNode) {
655-
Constant constantHub = ((EnsureClassInitializedNode) n).getHub().asConstant();
656-
if (constantHub != null) {
657-
AnalysisType type = (AnalysisType) bb.getProviders().getConstantReflection().asJavaType(constantHub);
658-
initializedClasses.computeIfAbsent(method, k -> new HashSet<>()).add(type);
654+
ResolvedJavaType type = ((EnsureClassInitializedNode) n).constantTypeOrNull(bb.getProviders());
655+
if (type != null) {
656+
initializedClasses.computeIfAbsent(method, k -> new HashSet<>()).add((AnalysisType) type);
659657
} else {
660658
classInitializerSideEffect.put(method, true);
661659
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/EarlyClassInitializerAnalysis.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import org.graalvm.compiler.phases.util.Providers;
5656

5757
import com.oracle.svm.core.ParsingReason;
58+
import com.oracle.svm.core.classinitialization.EnsureClassInitializedNode;
5859
import com.oracle.svm.core.graal.thread.VMThreadLocalAccess;
5960
import com.oracle.svm.core.option.HostedOptionValues;
6061
import com.oracle.svm.core.util.VMError;
@@ -197,7 +198,7 @@ final class AbortOnUnitializedClassPlugin extends NoClassInitializationPlugin {
197198
@Override
198199
public boolean apply(GraphBuilderContext b, ResolvedJavaType type, Supplier<FrameState> frameState, ValueNode[] classInit) {
199200
ResolvedJavaMethod clinitMethod = b.getGraph().method();
200-
if (type.isInitialized() || type.isArray() || type.equals(clinitMethod.getDeclaringClass())) {
201+
if (!EnsureClassInitializedNode.needsRuntimeInitialization(clinitMethod.getDeclaringClass(), type)) {
201202
return false;
202203
}
203204
if (classInitializationSupport.computeInitKindAndMaybeInitializeClass(ConfigurableClassInitialization.getJavaClass(type), true, analyzedClasses) != InitKind.RUN_TIME) {

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/HostedGraphKit.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ protected Instance createGraphBuilderInstance(GraphBuilderConfiguration graphBui
101101
}
102102

103103
public void emitEnsureInitializedCall(ResolvedJavaType type) {
104-
if (SubstrateClassInitializationPlugin.needsRuntimeInitialization(graph.method().getDeclaringClass(), type)) {
104+
if (EnsureClassInitializedNode.needsRuntimeInitialization(graph.method().getDeclaringClass(), type)) {
105105
ValueNode hub = createConstant(getConstantReflection().asJavaClass(type), JavaKind.Object);
106106
appendWithUnwind(new EnsureClassInitializedNode(hub));
107107
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SubstrateClassInitializationPlugin.java

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public void loadReferencedType(GraphBuilderContext builder, ConstantPool constan
6060

6161
@Override
6262
public boolean apply(GraphBuilderContext builder, ResolvedJavaType type, Supplier<FrameState> frameState, ValueNode[] classInit) {
63-
if (needsRuntimeInitialization(builder.getMethod().getDeclaringClass(), type)) {
63+
if (EnsureClassInitializedNode.needsRuntimeInitialization(builder.getMethod().getDeclaringClass(), type)) {
6464
emitEnsureClassInitialized(builder, SubstrateObjectConstant.forObject(host.dynamicHub(type)), frameState.get());
6565
/*
6666
* The classInit value is only registered with Invoke nodes. Since we do not need that,
@@ -79,12 +79,4 @@ private static void emitEnsureClassInitialized(GraphBuilderContext builder, Java
7979
EnsureClassInitializedNode node = new EnsureClassInitializedNode(hub, frameState);
8080
builder.add(node);
8181
}
82-
83-
/**
84-
* Return true if the type needs to be initialized at run time, i.e., it has not been already
85-
* initialized during image generation.
86-
*/
87-
static boolean needsRuntimeInitialization(ResolvedJavaType declaringClass, ResolvedJavaType type) {
88-
return !declaringClass.equals(type) && !type.isInitialized() && !type.isArray();
89-
}
9082
}

0 commit comments

Comments
 (0)