Skip to content

Commit 9f64dc7

Browse files
committed
Allow to delay methods to the application layer
1 parent fb9ade3 commit 9f64dc7

File tree

20 files changed

+386
-98
lines changed

20 files changed

+386
-98
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: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,16 @@ public boolean hasNeverInlineDirective(ResolvedJavaMethod method) {
284284
return true;
285285
}
286286

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+
287297
public InlineBeforeAnalysisGraphDecoder createInlineBeforeAnalysisGraphDecoder(BigBang bb, AnalysisMethod method, StructuredGraph resultGraph) {
288298
/* No inlining by the static analysis unless explicitly overwritten by the VM. */
289299
return new InlineBeforeAnalysisGraphDecoder(bb, InlineBeforeAnalysisPolicy.NO_INLINING, resultGraph, bb.getProviders(method), null);

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/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().buildingExtensionLayer() && 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/results/StrengthenGraphs.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040

4141
import com.oracle.graal.pointsto.BigBang;
4242
import com.oracle.graal.pointsto.PointsToAnalysis;
43+
import com.oracle.graal.pointsto.api.HostVM;
4344
import com.oracle.graal.pointsto.api.PointstoOptions;
4445
import com.oracle.graal.pointsto.flow.AnalysisParsedGraph;
4546
import com.oracle.graal.pointsto.flow.InvokeTypeFlow;
@@ -193,6 +194,8 @@ public boolean equals(Object obj) {
193194
/** Used to avoid aggressive optimizations for open type world analysis. */
194195
protected final boolean isClosedTypeWorld;
195196

197+
protected final boolean buildingSharedLayer;
198+
196199
public StrengthenGraphs(BigBang bb, Universe converter) {
197200
this.bb = bb;
198201
this.converter = converter;
@@ -208,7 +211,9 @@ public StrengthenGraphs(BigBang bb, Universe converter) {
208211
beforeCounters = null;
209212
afterCounters = null;
210213
}
211-
this.isClosedTypeWorld = converter.hostVM().isClosedTypeWorld();
214+
HostVM hostVM = converter.hostVM();
215+
this.isClosedTypeWorld = hostVM.isClosedTypeWorld();
216+
this.buildingSharedLayer = hostVM.buildingSharedLayer();
212217
}
213218

214219
private static void reportNeverNullInstanceFields(BigBang bb) {

substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/SubstrateAMD64Backend.java

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -687,15 +687,22 @@ protected Value emitIndirectForeignCallAddress(ForeignCallLinkage linkage) {
687687
SubstrateForeignCallLinkage callTarget = (SubstrateForeignCallLinkage) linkage;
688688
SharedMethod targetMethod = (SharedMethod) callTarget.getMethod();
689689
if (SubstrateUtil.HOSTED && targetMethod.forceIndirectCall()) {
690-
/*
691-
* Load the address for the start of the text section and then add in the offset for
692-
* this specific method.
693-
*/
694-
var methodLocation = DynamicImageLayerInfo.singleton().getPriorLayerMethodLocation(targetMethod);
695-
AllocatableValue basePointerAddress = newVariable(getLIRKindTool().getWordKind());
696-
append(new AMD64CGlobalDataLoadAddressOp(methodLocation.base(), basePointerAddress));
697-
Value codeOffsetInSection = emitConstant(getLIRKindTool().getWordKind(), JavaConstant.forLong(methodLocation.offset()));
698-
return getArithmetic().emitAdd(basePointerAddress, codeOffsetInSection, false);
690+
DynamicImageLayerInfo dynamicImageLayerInfo = DynamicImageLayerInfo.singleton();
691+
if (dynamicImageLayerInfo.isMethodCompilationDelayed(targetMethod)) {
692+
AllocatableValue methodAddress = newVariable(getLIRKindTool().getWordKind());
693+
append(new AMD64CGlobalDataLoadAddressOp(dynamicImageLayerInfo.getSymbolForDelayedMethod(targetMethod), methodAddress));
694+
return methodAddress;
695+
} else {
696+
/*
697+
* Load the address for the start of the text section and then add in the offset
698+
* for this specific method.
699+
*/
700+
var methodLocation = dynamicImageLayerInfo.getPriorLayerMethodLocation(targetMethod);
701+
AllocatableValue basePointerAddress = newVariable(getLIRKindTool().getWordKind());
702+
append(new AMD64CGlobalDataLoadAddressOp(methodLocation.base(), basePointerAddress));
703+
Value codeOffsetInSection = emitConstant(getLIRKindTool().getWordKind(), JavaConstant.forLong(methodLocation.offset()));
704+
return getArithmetic().emitAdd(basePointerAddress, codeOffsetInSection, false);
705+
}
699706
}
700707
if (!shouldEmitOnlyIndirectCalls()) {
701708
return null;
@@ -1497,13 +1504,18 @@ private static JavaConstant getZeroConstant(AllocatableValue dst) {
14971504
public AMD64LIRInstruction createLoadMethodPointerConstant(AllocatableValue dst, SubstrateMethodPointerConstant constant) {
14981505
if (ImageLayerBuildingSupport.buildingExtensionLayer()) {
14991506
if (constant.pointer().getMethod() instanceof SharedMethod sharedMethod && sharedMethod.forceIndirectCall()) {
1500-
/*
1501-
* AMD64LoadMethodPointerConstantOp retrieves the address via a PC-relative
1502-
* load. This is not possible to do in extension layers when referring to
1503-
* methods defined in prior layers.
1504-
*/
1505-
var methodLocation = DynamicImageLayerInfo.singleton().getPriorLayerMethodLocation(sharedMethod);
1506-
return new AMD64CGlobalDataLoadAddressOp(methodLocation.base(), dst, methodLocation.offset());
1507+
DynamicImageLayerInfo dynamicImageLayerInfo = DynamicImageLayerInfo.singleton();
1508+
if (dynamicImageLayerInfo.isMethodCompilationDelayed(sharedMethod)) {
1509+
return new AMD64CGlobalDataLoadAddressOp(dynamicImageLayerInfo.getSymbolForDelayedMethod(sharedMethod), dst);
1510+
} else {
1511+
/*
1512+
* AMD64LoadMethodPointerConstantOp retrieves the address via a PC-relative
1513+
* load. This is not possible to do in extension layers when referring to
1514+
* methods defined in prior layers.
1515+
*/
1516+
var methodLocation = dynamicImageLayerInfo.getPriorLayerMethodLocation(sharedMethod);
1517+
return new AMD64CGlobalDataLoadAddressOp(methodLocation.base(), dst, methodLocation.offset());
1518+
}
15071519
}
15081520
}
15091521

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/NonSnippetLowerings.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -402,11 +402,17 @@ public void lower(FixedNode node, LoweringTool tool) {
402402
* address offset of the text section start and then add in the offset for
403403
* this specific method.
404404
*/
405-
var methodLocation = DynamicImageLayerInfo.singleton().getPriorLayerMethodLocation(targetMethod);
406-
AddressNode methodPointerAddress = graph.addOrUniqueWithInputs(
407-
new OffsetAddressNode(new CGlobalDataLoadAddressNode(methodLocation.base()),
408-
ConstantNode.forIntegerKind(ConfigurationValues.getWordKind(), methodLocation.offset())));
409-
loweredCallTarget = createIndirectCall(graph, callTarget, parameters, method, signature, callType, invokeKind, methodPointerAddress);
405+
DynamicImageLayerInfo dynamicImageLayerInfo = DynamicImageLayerInfo.singleton();
406+
if (dynamicImageLayerInfo.isMethodCompilationDelayed(targetMethod)) {
407+
loweredCallTarget = createIndirectCall(graph, callTarget, parameters, method, signature, callType, invokeKind,
408+
graph.addOrUniqueWithInputs(new CGlobalDataLoadAddressNode(dynamicImageLayerInfo.getSymbolForDelayedMethod(targetMethod))));
409+
} else {
410+
var methodLocation = dynamicImageLayerInfo.getPriorLayerMethodLocation(targetMethod);
411+
AddressNode methodPointerAddress = graph.addOrUniqueWithInputs(
412+
new OffsetAddressNode(new CGlobalDataLoadAddressNode(methodLocation.base()),
413+
ConstantNode.forIntegerKind(ConfigurationValues.getWordKind(), methodLocation.offset())));
414+
loweredCallTarget = createIndirectCall(graph, callTarget, parameters, method, signature, callType, invokeKind, methodPointerAddress);
415+
}
410416
} else if (!SubstrateBackend.shouldEmitOnlyIndirectCalls()) {
411417
loweredCallTarget = createDirectCall(graph, callTarget, parameters, signature, callType, invokeKind, targetMethod, node);
412418
} else if (!targetMethod.hasImageCodeOffset()) {

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/imagelayer/DynamicImageLayerInfo.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ public static DynamicImageLayerInfo singleton() {
5050
return ImageSingletons.lookup(DynamicImageLayerInfo.class);
5151
}
5252

53+
public abstract boolean isMethodCompilationDelayed(SharedMethod method);
54+
55+
public abstract CGlobalDataInfo getSymbolForDelayedMethod(SharedMethod targetMethod);
56+
5357
public record PriorLayerMethodLocation(CGlobalDataInfo base, int offset) {
5458
}
5559

0 commit comments

Comments
 (0)