Skip to content

Commit 3fa330a

Browse files
committed
[GR-65719] Delay methods to the application layer
PullRequest: graal/21418
2 parents 60eaa92 + 9f64dc7 commit 3fa330a

27 files changed

+424
-132
lines changed

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/PointsToAnalysis.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ public AnalysisMethod addRootMethod(Executable method, boolean invokeSpecial, Ob
346346

347347
@Override
348348
public AnalysisMethod forcedAddRootMethod(AnalysisMethod method, boolean invokeSpecial, Object reason, MultiMethod.MultiMethodKey... otherRoots) {
349-
AnalysisError.guarantee(isBaseLayerAnalysisEnabled());
349+
AnalysisError.guarantee(isBaseLayerAnalysisEnabled() || hostVM.buildingImageLayer());
350350
registerDefaultMethod(method, reason);
351351
PointsToAnalysisMethod analysisMethod = assertPointsToAnalysisMethod(method);
352352
postTask(ignore -> {

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -184,10 +184,6 @@ public boolean isCoreType(@SuppressWarnings("unused") AnalysisType type) {
184184
return false;
185185
}
186186

187-
public boolean useBaseLayer() {
188-
return false;
189-
}
190-
191187
public boolean analyzedInPriorLayer(@SuppressWarnings("unused") AnalysisMethod method) {
192188
return false;
193189
}
@@ -288,6 +284,16 @@ public boolean hasNeverInlineDirective(ResolvedJavaMethod method) {
288284
return true;
289285
}
290286

287+
/**
288+
* Check if the method has to be inlined.
289+
*
290+
* @param method the target method
291+
*/
292+
public boolean hasAlwaysInlineDirective(ResolvedJavaMethod method) {
293+
/* No force inlining by the static analysis unless explicitly overwritten by the VM. */
294+
return false;
295+
}
296+
291297
public InlineBeforeAnalysisGraphDecoder createInlineBeforeAnalysisGraphDecoder(BigBang bb, AnalysisMethod method, StructuredGraph resultGraph) {
292298
/* No inlining by the static analysis unless explicitly overwritten by the VM. */
293299
return new InlineBeforeAnalysisGraphDecoder(bb, InlineBeforeAnalysisPolicy.NO_INLINING, resultGraph, bb.getProviders(method), null);
@@ -432,6 +438,14 @@ public boolean buildingImageLayer() {
432438
return false;
433439
}
434440

441+
public boolean buildingSharedLayer() {
442+
return false;
443+
}
444+
445+
public boolean buildingExtensionLayer() {
446+
return false;
447+
}
448+
435449
@SuppressWarnings("unused")
436450
public boolean installableInLayer(AnalysisField aField) {
437451
return true;

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/InvokeTypeFlow.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,10 @@ protected void linkCallee(PointsToAnalysis bb, boolean isStatic, MethodFlowsGrap
278278
if (!calleeFlows.isStub()) {
279279
bb.analysisPolicy().registerAsImplementationInvoked(this, calleeFlows.getMethod());
280280
}
281+
282+
if (calleeFlows.getMethod().isDelayed()) {
283+
saturateForOpenTypeWorld(bb);
284+
}
281285
}
282286

283287
/*
@@ -426,4 +430,15 @@ public boolean canBeStaticallyBound() {
426430
public MultiMethodKey getCallerMultiMethodKey() {
427431
return callerMultiMethodKey;
428432
}
433+
434+
/**
435+
* Saturates the actual return of the invoke type flow to ensure that the type state represents
436+
* all the types that could exist in the open world.
437+
*/
438+
public void saturateForOpenTypeWorld(PointsToAnalysis bb) {
439+
if (actualReturn != null) {
440+
actualReturn.enableFlow(bb);
441+
actualReturn.onSaturated(bb);
442+
}
443+
}
429444
}

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodFlowsGraph.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -490,10 +490,7 @@ void saturateForOpenTypeWorld(PointsToAnalysis bb) {
490490
*/
491491
for (InvokeTypeFlow invokeTypeFlow : getInvokes()) {
492492
if (!invokeTypeFlow.isDirectInvoke() && !bb.isClosed(invokeTypeFlow.getReceiverType())) {
493-
if (invokeTypeFlow.actualReturn != null) {
494-
invokeTypeFlow.actualReturn.enableFlow(bb);
495-
invokeTypeFlow.actualReturn.onSaturated(bb);
496-
}
493+
invokeTypeFlow.saturateForOpenTypeWorld(bb);
497494
}
498495
}
499496
}

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,11 @@ protected void apply(boolean forceReparse, Object reason) {
706706

707707
method.setReachableInCurrentLayer();
708708

709+
if (method.isDelayed()) {
710+
/* The method will be analyzed in the application layer */
711+
return;
712+
}
713+
709714
if (method.analyzedInPriorLayer()) {
710715
/*
711716
* We don't need to analyze this method. We already know its return type state from the

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,11 +333,11 @@ private ImageHeapConstant getSnapshot(JavaConstant constant, ScanReason reason)
333333
result = (ImageHeapConstant) constant;
334334
} else {
335335
Object task = imageHeap.getSnapshot(constant);
336-
if (task == null && bb.getUniverse().hostVM().useBaseLayer() && bb.getUniverse().getImageLayerLoader().hasValueForConstant(constant)) {
336+
if (task == null && bb.getUniverse().hostVM().buildingExtensionLayer() && bb.getUniverse().getImageLayerLoader().hasValueForConstant(constant)) {
337337
/* The constant might not have been accessed in the extension image yet */
338338
task = bb.getUniverse().getImageLayerLoader().getValueForConstant(constant);
339339
}
340-
if (task == null && bb.getUniverse().hostVM().useBaseLayer()) {
340+
if (task == null && bb.getUniverse().hostVM().buildingExtensionLayer()) {
341341
/*
342342
* This does not distinguish between base and extension layer constants at the
343343
* moment. Doing so would require some refactoring to determine earlier if the

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ protected ImageHeapConstant getOrCreateImageHeapConstant(JavaConstant javaConsta
266266
if (existingTask == null) {
267267
AnalysisFuture<ImageHeapConstant> newTask;
268268
ImageLayerLoader imageLayerLoader = universe.getImageLayerLoader();
269-
if (hostVM.useBaseLayer() && imageLayerLoader.hasValueForConstant(javaConstant)) {
269+
if (hostVM.buildingExtensionLayer() && imageLayerLoader.hasValueForConstant(javaConstant)) {
270270
ImageHeapConstant value = imageLayerLoader.getValueForConstant(javaConstant);
271271
ensureFieldPositionsComputed(value, nonNullReason);
272272
AnalysisError.guarantee(value.getHostedObject().equals(javaConstant));

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ public AnalysisField(AnalysisUniverse universe, ResolvedJavaField wrappedField)
141141
sinkFlow = new ContextInsensitiveFieldTypeFlow(this, getType());
142142
}
143143

144-
if (universe.hostVM().useBaseLayer() && declaringClass.isInBaseLayer()) {
144+
if (universe.hostVM().buildingExtensionLayer() && declaringClass.isInBaseLayer()) {
145145
int fid = universe.getImageLayerLoader().lookupHostedFieldInBaseLayer(this);
146146
if (fid != -1) {
147147
/*

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151

5252
import com.oracle.graal.pointsto.BigBang;
5353
import com.oracle.graal.pointsto.PointsToAnalysis;
54+
import com.oracle.graal.pointsto.api.HostVM;
5455
import com.oracle.graal.pointsto.api.ImageLayerLoader;
5556
import com.oracle.graal.pointsto.api.ImageLayerWriter;
5657
import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException;
@@ -126,6 +127,7 @@ public record Signature(String name, AnalysisType[] parameterTypes) {
126127
public final ResolvedJavaMethod wrapped;
127128

128129
private final int id;
130+
private final boolean buildingSharedLayer;
129131
/** Marks a method loaded from a base layer. */
130132
private final boolean isInBaseLayer;
131133
private final boolean analyzedInPriorLayer;
@@ -196,9 +198,12 @@ public record Signature(String name, AnalysisType[] parameterTypes) {
196198
*/
197199
private boolean hasOpaqueReturn;
198200

201+
private CompilationBehavior compilationBehavior = CompilationBehavior.DEFAULT;
202+
199203
@SuppressWarnings({"this-escape", "unchecked"})
200204
protected AnalysisMethod(AnalysisUniverse universe, ResolvedJavaMethod wrapped, MultiMethodKey multiMethodKey, Map<MultiMethodKey, MultiMethod> multiMethodMap) {
201205
super(universe.hostVM.enableTrackAcrossLayers());
206+
HostVM hostVM = universe.hostVM();
202207
this.wrapped = wrapped;
203208

204209
declaringClass = universe.lookup(wrapped.getDeclaringClass());
@@ -213,13 +218,14 @@ protected AnalysisMethod(AnalysisUniverse universe, ResolvedJavaMethod wrapped,
213218
} else {
214219
signature = getUniverse().lookup(wrappedSignature, wrapped.getDeclaringClass());
215220
}
216-
hasNeverInlineDirective = universe.hostVM().hasNeverInlineDirective(wrapped);
221+
hasNeverInlineDirective = hostVM.hasNeverInlineDirective(wrapped);
217222

218223
name = createName(wrapped, multiMethodKey);
219224
qualifiedName = format("%H.%n(%P)");
220225
modifiers = wrapped.getModifiers();
221226

222-
if (universe.hostVM().useBaseLayer() && declaringClass.isInBaseLayer()) {
227+
buildingSharedLayer = hostVM.buildingSharedLayer();
228+
if (hostVM.buildingExtensionLayer() && declaringClass.isInBaseLayer()) {
223229
int mid = universe.getImageLayerLoader().lookupHostedMethodInBaseLayer(this);
224230
if (mid != -1) {
225231
/*
@@ -236,7 +242,7 @@ protected AnalysisMethod(AnalysisUniverse universe, ResolvedJavaMethod wrapped,
236242
id = universe.computeNextMethodId();
237243
isInBaseLayer = false;
238244
}
239-
analyzedInPriorLayer = isInBaseLayer && universe.hostVM().analyzedInPriorLayer(this);
245+
analyzedInPriorLayer = isInBaseLayer && hostVM.analyzedInPriorLayer(this);
240246

241247
ExceptionHandler[] original = wrapped.getExceptionHandlers();
242248
exceptionHandlers = new ExceptionHandler[original.length];
@@ -281,6 +287,7 @@ protected AnalysisMethod(AnalysisMethod original, MultiMethodKey multiMethodKey)
281287
super(original.enableTrackAcrossLayers);
282288
wrapped = original.wrapped;
283289
id = original.id;
290+
buildingSharedLayer = original.buildingSharedLayer;
284291
isInBaseLayer = original.isInBaseLayer;
285292
analyzedInPriorLayer = original.analyzedInPriorLayer;
286293
declaringClass = original.declaringClass;
@@ -306,6 +313,35 @@ protected AnalysisMethod(AnalysisMethod original, MultiMethodKey multiMethodKey)
306313
this.enableReachableInCurrentLayer = original.enableReachableInCurrentLayer;
307314
}
308315

316+
public void setCompilationBehavior(CompilationBehavior compilationBehavior) {
317+
this.compilationBehavior = compilationBehavior;
318+
}
319+
320+
public CompilationBehavior getCompilationBehavior() {
321+
return compilationBehavior;
322+
}
323+
324+
/**
325+
* Delays this method to the application layer. This should not be called after the method was
326+
* already parsed to avoid analyzing all the method's callees.
327+
*/
328+
public void setFullyDelayedToApplicationLayer() {
329+
HostVM hostVM = getUniverse().getBigbang().getHostVM();
330+
AnalysisError.guarantee(hostVM.buildingImageLayer(), "Methods can only be delayed in layered images");
331+
AnalysisError.guarantee(parsedGraphCacheState.get() == GraphCacheEntry.UNPARSED, "The method %s was marked as delayed to the application layer but was already parsed", this);
332+
AnalysisError.guarantee(!hostVM.hasAlwaysInlineDirective(this), "Method %s with an always inline directive cannot be delayed to the application layer as such methods cannot be inlined", this);
333+
AnalysisError.guarantee(isConcrete(), "Method %s is not concrete and cannot be delayed to the application layer", this);
334+
this.compilationBehavior = CompilationBehavior.FULLY_DELAYED_TO_APPLICATION_LAYER;
335+
}
336+
337+
/**
338+
* Returns true if this method is marked as delayed to the application layer and the current
339+
* layer is a shared layer.
340+
*/
341+
public boolean isDelayed() {
342+
return compilationBehavior == CompilationBehavior.FULLY_DELAYED_TO_APPLICATION_LAYER && buildingSharedLayer;
343+
}
344+
309345
private static String createName(ResolvedJavaMethod wrapped, MultiMethodKey multiMethodKey) {
310346
String aName = wrapped.getName();
311347
if (multiMethodKey != ORIGINAL_METHOD) {
@@ -847,7 +883,8 @@ public Type[] getGenericParameterTypes() {
847883

848884
@Override
849885
public boolean canBeInlined() {
850-
return !hasNeverInlineDirective();
886+
/* Delayed methods should not be inlined in the current layer */
887+
return !hasNeverInlineDirective() && !isDelayed();
851888
}
852889

853890
@Override
@@ -1202,6 +1239,7 @@ private AnalysisParsedGraph setGraph(BigBang bb, Stage stage, GraphCacheEntry ex
12021239
/* We lost the race, another thread is doing the parsing. */
12031240
return null;
12041241
}
1242+
AnalysisError.guarantee(!isDelayed(), "The method %s was parsed even though it was marked as delayed to the application layer", this);
12051243

12061244
GraphCacheEntry newEntry = graphSupplier.get(bb, this, expectedValue);
12071245

@@ -1360,4 +1398,40 @@ public boolean hasOpaqueReturn() {
13601398
}
13611399

13621400
protected abstract AnalysisMethod createMultiMethod(AnalysisMethod analysisMethod, MultiMethodKey newMultiMethodKey);
1401+
1402+
/**
1403+
* This state represents how a method should be compiled in layered images. The state of a
1404+
* method can only be decided in the first layer if it is marked as tracked across layers. The
1405+
* state has to stay the same across all the extension layers. If not specified, the state of a
1406+
* method will be {@link CompilationBehavior#DEFAULT}.
1407+
*/
1408+
public enum CompilationBehavior {
1409+
1410+
/**
1411+
* Method remains unanalyzed until the application layer and any inlining in a shared layer
1412+
* is prevented. A call to the method in a shared layer will be replaced by an indirect
1413+
* call. The compilation of those methods is then forced in the application layer and the
1414+
* corresponding symbol is declared as global.
1415+
*
1416+
* A delayed method that is not referenced in any shared layer is treated as a
1417+
* {@link CompilationBehavior#DEFAULT} method in the application layer and does not have to
1418+
* be compiled. If it is only referenced in the application layer, it might be inlined and
1419+
* not compiled at all.
1420+
*/
1421+
FULLY_DELAYED_TO_APPLICATION_LAYER,
1422+
1423+
/**
1424+
* Method can be inlined into other methods, both before analysis and during compilation,
1425+
* and will be compiled as a distinct compilation unit as stipulated by the normal native
1426+
* image generation process (i.e., the method is installed as a root and/or a reference to
1427+
* the method exists via a call and/or an explicit MethodReference).
1428+
*/
1429+
DEFAULT,
1430+
1431+
/**
1432+
* Method is pinned to a specific shared layer, meaning it has to be analyzed and compiled
1433+
* in this specific layer. A method can only be pinned to a shared layer.
1434+
*/
1435+
PINNED_TO_SHARED_LAYER,
1436+
}
13631437
}

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ public AnalysisType(AnalysisUniverse universe, ResolvedJavaType javaType, JavaKi
287287
}
288288

289289
/* Set id after accessing super types, so that all these types get a lower id number. */
290-
if (universe.hostVM().useBaseLayer()) {
290+
if (universe.hostVM().buildingExtensionLayer()) {
291291
int tid = universe.getImageLayerLoader().lookupHostedTypeInBaseLayer(this);
292292
if (tid != -1) {
293293
/*

0 commit comments

Comments
 (0)