Skip to content

Commit f8f0561

Browse files
committed
[GR-41301] Introduce MultiMethod concept.
PullRequest: graal/12753
2 parents c9c7ab7 + 16412b3 commit f8f0561

File tree

10 files changed

+270
-104
lines changed

10 files changed

+270
-104
lines changed

substratevm/mx.substratevm/suite.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,6 +1569,7 @@
15691569
"name" : "org.graalvm.nativeimage.base",
15701570
"exports" : [
15711571
"com.oracle.svm.util to org.graalvm.nativeimage.pointsto,org.graalvm.nativeimage.builder,org.graalvm.nativeimage.librarysupport,org.graalvm.nativeimage.driver,org.graalvm.nativeimage.llvm,org.graalvm.nativeimage.agent.jvmtibase,org.graalvm.nativeimage.agent.tracing,org.graalvm.nativeimage.agent.diagnostics,org.graalvm.nativeimage.junitsupport,com.oracle.svm.svm_enterprise",
1572+
"com.oracle.svm.common.meta to org.graalvm.nativeimage.pointsto,org.graalvm.nativeimage.builder",
15721573
"com.oracle.svm.common.option to org.graalvm.nativeimage.pointsto,org.graalvm.nativeimage.builder,org.graalvm.nativeimage.driver",
15731574
],
15741575
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.common.meta;
26+
27+
import java.util.Collection;
28+
29+
/**
30+
* Multi-methods can have multiple implementations of the original Java method. This functionality
31+
* is used to create the different variants needed for different compilation scenarios.
32+
*/
33+
public interface MultiMethod {
34+
35+
/**
36+
* Key for accessing a multi-method.
37+
*/
38+
interface MultiMethodKey {
39+
}
40+
41+
MultiMethodKey ORIGINAL_METHOD = new MultiMethodKey() {
42+
@Override
43+
public String toString() {
44+
return "Original_Method_Key";
45+
}
46+
};
47+
48+
/**
49+
* Each method is assigned a unique key which denotes the purpose of the method.
50+
*/
51+
MultiMethodKey getMultiMethodKey();
52+
53+
/**
54+
* @return method implementation with the requested key, creating it if necessary.
55+
*/
56+
MultiMethod getOrCreateMultiMethod(MultiMethodKey key);
57+
58+
/**
59+
* @return method implementation with the requested key, or null if it does not exist.
60+
*/
61+
MultiMethod getMultiMethod(MultiMethodKey key);
62+
63+
/**
64+
* @return all implementations of this method.
65+
*/
66+
Collection<MultiMethod> getAllMultiMethods();
67+
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompilationInfo.java

Lines changed: 5 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.graalvm.compiler.options.OptionValues;
3636

3737
import com.oracle.graal.pointsto.flow.AnalysisParsedGraph;
38+
import com.oracle.svm.common.meta.MultiMethod;
3839
import com.oracle.svm.core.deopt.DeoptTest;
3940
import com.oracle.svm.core.deopt.Specialize;
4041
import com.oracle.svm.hosted.code.CompileQueue.CompileFunction;
@@ -65,20 +66,6 @@ public class CompilationInfo {
6566
*/
6667
protected ConstantNode[] specializedArguments;
6768

68-
/**
69-
* A link to the deoptimization target method if this method can deoptimize.
70-
*/
71-
protected HostedMethod deoptTarget;
72-
73-
/**
74-
* A link to the regular compiled method if this method is a deoptimization target.
75-
*
76-
* Note that it is important that this field is final: the {@link HostedMethod#getName() method
77-
* name} depends on this field (to distinguish a regular method from a deoptimization target
78-
* method), so mutating this field would mutate the name of a method.
79-
*/
80-
protected final HostedMethod deoptOrigin;
81-
8269
/* Custom parsing and compilation code that is executed instead of that of CompileQueue */
8370
protected ParseFunction customParseFunction;
8471
protected CompileFunction customCompileFunction;
@@ -95,41 +82,28 @@ public class CompilationInfo {
9582
protected final AtomicLong numVirtualCalls = new AtomicLong();
9683
protected final AtomicLong numEntryPointCalls = new AtomicLong();
9784

98-
public CompilationInfo(HostedMethod method, HostedMethod deoptOrigin) {
85+
public CompilationInfo(HostedMethod method) {
9986
this.method = method;
100-
this.deoptOrigin = deoptOrigin;
101-
102-
if (deoptOrigin != null) {
103-
assert deoptOrigin.compilationInfo.deoptTarget == null;
104-
deoptOrigin.compilationInfo.deoptTarget = method;
105-
}
106-
}
107-
108-
public boolean isDeoptTarget() {
109-
return deoptOrigin != null;
11087
}
11188

11289
public boolean isDeoptEntry(int bci, boolean duringCall, boolean rethrowException) {
113-
return isDeoptTarget() && (deoptOrigin.compilationInfo.canDeoptForTesting || SubstrateCompilationDirectives.singleton().isDeoptEntry(method, bci, duringCall, rethrowException));
90+
return method.isDeoptTarget() && (method.getMultiMethod(MultiMethod.ORIGINAL_METHOD).compilationInfo.canDeoptForTesting ||
91+
SubstrateCompilationDirectives.singleton().isDeoptEntry(method, bci, duringCall, rethrowException));
11492
}
11593

11694
/**
11795
* Returns whether this bci was registered as a potential deoptimization entrypoint via
11896
* {@link SubstrateCompilationDirectives#registerDeoptEntry}.
11997
*/
12098
public boolean isRegisteredDeoptEntry(int bci, boolean duringCall, boolean rethrowException) {
121-
return isDeoptTarget() && SubstrateCompilationDirectives.singleton().isDeoptTarget(method) &&
99+
return method.isDeoptTarget() && SubstrateCompilationDirectives.singleton().isDeoptTarget(method) &&
122100
SubstrateCompilationDirectives.singleton().isDeoptEntry(method, bci, duringCall, rethrowException);
123101
}
124102

125103
public boolean canDeoptForTesting() {
126104
return canDeoptForTesting;
127105
}
128106

129-
public HostedMethod getDeoptTargetMethod() {
130-
return deoptTarget;
131-
}
132-
133107
public CompilationGraph getCompilationGraph() {
134108
return compilationGraph;
135109
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
*/
2525
package com.oracle.svm.hosted.code;
2626

27+
import static com.oracle.svm.hosted.code.SubstrateCompilationDirectives.DEOPT_TARGET_METHOD;
28+
2729
import java.lang.annotation.Annotation;
2830
import java.util.ArrayList;
2931
import java.util.Arrays;
@@ -32,7 +34,6 @@
3234
import java.util.List;
3335
import java.util.Map;
3436
import java.util.Map.Entry;
35-
import java.util.Objects;
3637
import java.util.TreeMap;
3738
import java.util.concurrent.ConcurrentHashMap;
3839
import java.util.concurrent.ConcurrentMap;
@@ -102,9 +103,10 @@
102103
import com.oracle.graal.pointsto.phases.SubstrateIntrinsicGraphBuilder;
103104
import com.oracle.graal.pointsto.util.CompletionExecutor;
104105
import com.oracle.graal.pointsto.util.CompletionExecutor.DebugContextRunnable;
106+
import com.oracle.svm.common.meta.MultiMethod;
105107
import com.oracle.svm.core.SubstrateOptions;
106-
import com.oracle.svm.core.Uninterruptible;
107108
import com.oracle.svm.core.SubstrateOptions.OptimizationLevel;
109+
import com.oracle.svm.core.Uninterruptible;
108110
import com.oracle.svm.core.deopt.DeoptTest;
109111
import com.oracle.svm.core.deopt.Specialize;
110112
import com.oracle.svm.core.graal.code.SubstrateBackend;
@@ -511,18 +513,19 @@ private void printMethodHistogram() {
511513
CompilationResult result = task.result;
512514

513515
CompilationInfo ci = method.compilationInfo;
514-
if (!ci.isDeoptTarget()) {
516+
if (!method.isDeoptTarget()) {
515517
numberOfMethods += 1;
516518
sizeAllMethods += result.getTargetCodeSize();
517519
System.out.format("%8d; %5d; %5d; %5d; %s;", result.getTargetCodeSize(), ci.numNodesAfterParsing, ci.numNodesBeforeCompilation, ci.numNodesAfterCompilation,
518520
ci.isTrivialMethod ? "T" : " ");
519521

520522
int deoptMethodSize = 0;
521-
if (ci.deoptTarget != null) {
522-
CompilationInfo dci = ci.deoptTarget.compilationInfo;
523+
HostedMethod deoptTargetMethod = method.getMultiMethod(DEOPT_TARGET_METHOD);
524+
if (deoptTargetMethod != null) {
525+
CompilationInfo dci = deoptTargetMethod.compilationInfo;
523526

524527
numberOfDeopt += 1;
525-
deoptMethodSize = compilations.get(ci.deoptTarget).result.getTargetCodeSize();
528+
deoptMethodSize = compilations.get(deoptTargetMethod).result.getTargetCodeSize();
526529
sizeDeoptMethods += deoptMethodSize;
527530
sizeDeoptMethodsInNonDeopt += result.getTargetCodeSize();
528531
totalNumDeoptEntryPoints += dci.numDeoptEntryPoints;
@@ -607,7 +610,7 @@ private void parseDeoptimizationTargetMethods() {
607610
*/
608611
universe.getMethods().stream()
609612
.filter(method -> SubstrateCompilationDirectives.singleton().isDeoptTarget(method))
610-
.forEach(method -> ensureParsed(universe.createDeoptTarget(method), null, new EntryPointReason()));
613+
.forEach(method -> ensureParsed(method.getOrCreateMultiMethod(DEOPT_TARGET_METHOD), null, new EntryPointReason()));
611614

612615
/*
613616
* Deoptimization target code for deoptimization testing: all methods that are not
@@ -616,12 +619,10 @@ private void parseDeoptimizationTargetMethods() {
616619
*/
617620
universe.getMethods().stream()
618621
.filter(method -> method.getWrapped().isImplementationInvoked() && DeoptimizationUtils.canDeoptForTesting(universe, method, deoptimizeAll))
619-
.forEach(this::ensureParsedForDeoptTesting);
620-
}
621-
622-
private void ensureParsedForDeoptTesting(HostedMethod method) {
623-
method.compilationInfo.canDeoptForTesting = true;
624-
ensureParsed(universe.createDeoptTarget(method), null, new EntryPointReason());
622+
.forEach(method -> {
623+
method.compilationInfo.canDeoptForTesting = true;
624+
ensureParsed(method.getOrCreateMultiMethod(DEOPT_TARGET_METHOD), null, new EntryPointReason());
625+
});
625626
}
626627

627628
private static boolean checkTrivial(HostedMethod method, StructuredGraph graph) {
@@ -643,12 +644,15 @@ protected void inlineTrivialMethods(DebugContext debug) throws InterruptedExcept
643644
try (Indent ignored = debug.logAndIndent("==== Trivial Inlining round %d\n", round)) {
644645

645646
executor.init();
646-
universe.getMethods().stream()
647-
.filter(method -> method.compilationInfo.getCompilationGraph() != null)
648-
.forEach(method -> executor.execute(new TrivialInlineTask(method)));
649-
universe.getMethods().stream()
650-
.map(method -> method.compilationInfo.getDeoptTargetMethod()).filter(Objects::nonNull)
651-
.forEach(deoptTargetMethod -> executor.execute(new TrivialInlineTask(deoptTargetMethod)));
647+
universe.getMethods().forEach(method -> {
648+
assert method.getMultiMethodKey() == MultiMethod.ORIGINAL_METHOD;
649+
for (MultiMethod multiMethod : method.getAllMultiMethods()) {
650+
HostedMethod hMethod = (HostedMethod) multiMethod;
651+
if (hMethod.compilationInfo.getCompilationGraph() != null) {
652+
executor.execute(new TrivialInlineTask(hMethod));
653+
}
654+
}
655+
});
652656
executor.start();
653657
executor.complete();
654658
executor.shutdown();
@@ -799,7 +803,7 @@ public void scheduleEntryPoints() {
799803
ensureCompiled(impl, new EntryPointReason());
800804
}
801805
}
802-
HostedMethod deoptTargetMethod = method.compilationInfo.getDeoptTargetMethod();
806+
HostedMethod deoptTargetMethod = method.getMultiMethod(DEOPT_TARGET_METHOD);
803807
if (deoptTargetMethod != null) {
804808
ensureCompiled(deoptTargetMethod, new EntryPointReason());
805809
}
@@ -1011,7 +1015,7 @@ protected boolean canBeUsedForInlining(Invoke invoke) {
10111015
}
10121016

10131017
private static void handleSpecialization(final HostedMethod method, CallTargetNode targetNode, HostedMethod invokeTarget, HostedMethod invokeImplementation) {
1014-
if (method.getAnnotation(Specialize.class) != null && !method.compilationInfo.isDeoptTarget() && invokeTarget.getAnnotation(DeoptTest.class) != null) {
1018+
if (method.getAnnotation(Specialize.class) != null && !method.isDeoptTarget() && invokeTarget.getAnnotation(DeoptTest.class) != null) {
10151019
/*
10161020
* Collect the constant arguments to a method which should be specialized.
10171021
*/
@@ -1138,8 +1142,8 @@ private CompilationResult defaultCompileFunction(DebugContext debug, HostedMetho
11381142
.filter(invoke -> method.compilationInfo.isDeoptEntry(invoke.bci(), true, false))
11391143
.count();
11401144

1141-
Suites suites = method.compilationInfo.isDeoptTarget() ? deoptTargetSuites : regularSuites;
1142-
LIRSuites lirSuites = method.compilationInfo.isDeoptTarget() ? deoptTargetLIRSuites : regularLIRSuites;
1145+
Suites suites = method.isDeoptTarget() ? deoptTargetSuites : regularSuites;
1146+
LIRSuites lirSuites = method.isDeoptTarget() ? deoptTargetLIRSuites : regularLIRSuites;
11431147

11441148
CompilationResult result = backend.newCompilationResult(compilationIdentifier, method.format("%H.%n(%p)"));
11451149

@@ -1149,7 +1153,7 @@ private CompilationResult defaultCompileFunction(DebugContext debug, HostedMetho
11491153
}
11501154
method.compilationInfo.numNodesAfterCompilation = graph.getNodeCount();
11511155

1152-
if (method.compilationInfo.isDeoptTarget()) {
1156+
if (method.isDeoptTarget()) {
11531157
assert DeoptimizationUtils.verifyDeoptTarget(method, graph, result);
11541158
}
11551159
ensureCalleesCompiled(method, reason, result);

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/SubstrateCompilationDirectives.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.graalvm.nativeimage.ImageSingletons;
3434

3535
import com.oracle.graal.pointsto.meta.AnalysisMethod;
36+
import com.oracle.svm.common.meta.MultiMethod;
3637
import com.oracle.svm.core.code.FrameInfoEncoder;
3738
import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton;
3839
import com.oracle.svm.core.util.VMError;
@@ -44,6 +45,20 @@
4445
@AutomaticallyRegisteredImageSingleton
4546
public class SubstrateCompilationDirectives {
4647

48+
public static final MultiMethod.MultiMethodKey DEOPT_TARGET_METHOD = new MultiMethod.MultiMethodKey() {
49+
@Override
50+
public String toString() {
51+
return "Deopt_Target_Method_Key";
52+
}
53+
};
54+
55+
public static final MultiMethod.MultiMethodKey RUNTIME_COMPILED_METHOD = new MultiMethod.MultiMethodKey() {
56+
@Override
57+
public String toString() {
58+
return "Runtime_Compiled_Method_Key";
59+
}
60+
};
61+
4762
/**
4863
* Stores the value kinds present at a deoptimization point's (deoptimization source)
4964
* FrameState. This information is used to validate the deoptimization point's target
@@ -180,7 +195,7 @@ public boolean isDeoptTarget(ResolvedJavaMethod method) {
180195
return deoptEntries.containsKey(toAnalysisMethod(method));
181196
}
182197

183-
protected boolean isDeoptEntry(ResolvedJavaMethod method, int bci, boolean duringCall, boolean rethrowException) {
198+
public boolean isDeoptEntry(ResolvedJavaMethod method, int bci, boolean duringCall, boolean rethrowException) {
184199
assert seal();
185200
Map<Long, DeoptSourceFrameInfo> bciMap = deoptEntries.get(toAnalysisMethod(method));
186201
assert bciMap != null : "can only query for deopt entries for methods registered as deopt targets";

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/LIRNativeImageCodeCache.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ public void layoutMethods(DebugContext debug, BigBang bb, ForkJoinPool threadPoo
190190
* Need to have snapshot of trampoline key set since we update their
191191
* positions.
192192
*/
193-
for (HostedMethod callTarget : trampolines.keySet().toArray(new HostedMethod[0])) {
193+
for (HostedMethod callTarget : trampolines.keySet().toArray(HostedMethod.EMPTY_ARRAY)) {
194194
position = NumUtil.roundUp(position, trampolineSupport.getTrampolineAlignment());
195195
trampolines.put(callTarget, position);
196196
sortedTrampolines.add(Pair.create(callTarget, position));

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1081,7 +1081,7 @@ public boolean isDeoptTarget() {
10811081
if (method instanceof HostedMethod) {
10821082
return ((HostedMethod) method).isDeoptTarget();
10831083
}
1084-
return name().endsWith(HostedMethod.METHOD_NAME_DEOPT_SUFFIX);
1084+
return name().endsWith(HostedMethod.MULTI_METHOD_KEY_SEPARATOR);
10851085
}
10861086

10871087
@Override

0 commit comments

Comments
 (0)