Skip to content

Commit ca59606

Browse files
committed
[GR-49708] Implement open type world virtual method dispatch
PullRequest: graal/17065
2 parents 2700cc4 + fc4c523 commit ca59606

27 files changed

+1165
-501
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/KnownOffsets.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,18 @@ private boolean isFullyInitialized() {
7878
return vtableEntrySize > 0;
7979
}
8080

81-
public int getVTableOffset(int vTableIndex) {
81+
/**
82+
* Returns of the offset of the index either relative to the start of the vtable
83+
* ({@code fromDynamicHubStart} == false) or start of the dynamic hub
84+
* ({@code fromDynamicHubStart} == true).
85+
*/
86+
public int getVTableOffset(int vTableIndex, boolean fromDynamicHubStart) {
8287
assert isFullyInitialized();
83-
return vtableBaseOffset + vTableIndex * vtableEntrySize;
88+
if (fromDynamicHubStart) {
89+
return vtableBaseOffset + vTableIndex * vtableEntrySize;
90+
} else {
91+
return vTableIndex * vtableEntrySize;
92+
}
8493
}
8594

8695
public int getTypeIDSlotsOffset() {

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/SubstrateBasicLoweringProvider.java

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,12 @@
3131
import org.graalvm.nativeimage.Platforms;
3232

3333
import com.oracle.svm.core.StaticFieldsSupport;
34+
import com.oracle.svm.core.SubstrateOptions;
3435
import com.oracle.svm.core.config.ConfigurationValues;
3536
import com.oracle.svm.core.config.ObjectLayout;
3637
import com.oracle.svm.core.graal.code.SubstrateBackend;
3738
import com.oracle.svm.core.graal.nodes.FloatingWordCastNode;
39+
import com.oracle.svm.core.graal.nodes.LoadOpenTypeWorldDispatchTableStartingOffset;
3840
import com.oracle.svm.core.graal.nodes.LoweredDeadEndNode;
3941
import com.oracle.svm.core.graal.nodes.SubstrateCompressionNode;
4042
import com.oracle.svm.core.graal.nodes.SubstrateFieldLocationIdentity;
@@ -46,7 +48,6 @@
4648
import com.oracle.svm.core.identityhashcode.IdentityHashCodeSupport;
4749
import com.oracle.svm.core.meta.SharedField;
4850
import com.oracle.svm.core.meta.SharedMethod;
49-
import com.oracle.svm.core.meta.SubstrateMethodPointerStamp;
5051
import com.oracle.svm.core.snippets.SubstrateIsArraySnippets;
5152

5253
import jdk.graal.compiler.core.common.memory.BarrierType;
@@ -65,10 +66,12 @@
6566
import jdk.graal.compiler.nodes.DeadEndNode;
6667
import jdk.graal.compiler.nodes.FieldLocationIdentity;
6768
import jdk.graal.compiler.nodes.FixedNode;
69+
import jdk.graal.compiler.nodes.FixedWithNextNode;
6870
import jdk.graal.compiler.nodes.NamedLocationIdentity;
6971
import jdk.graal.compiler.nodes.NodeView;
7072
import jdk.graal.compiler.nodes.StructuredGraph;
7173
import jdk.graal.compiler.nodes.ValueNode;
74+
import jdk.graal.compiler.nodes.calc.AddNode;
7275
import jdk.graal.compiler.nodes.calc.AndNode;
7376
import jdk.graal.compiler.nodes.calc.LeftShiftNode;
7477
import jdk.graal.compiler.nodes.calc.UnsignedRightShiftNode;
@@ -144,29 +147,51 @@ public void lower(Node n, LoweringTool tool) {
144147
} else if (n instanceof DeadEndNode) {
145148
lowerDeadEnd((DeadEndNode) n);
146149
} else if (n instanceof LoadMethodNode) {
147-
lowerLoadMethodNode((LoadMethodNode) n);
150+
lowerLoadMethodNode((LoadMethodNode) n, tool);
148151
} else {
149152
super.lower(n, tool);
150153
}
151154
}
152155

153-
private void lowerLoadMethodNode(LoadMethodNode loadMethodNode) {
156+
private void lowerLoadMethodNode(LoadMethodNode loadMethodNode, LoweringTool tool) {
154157
StructuredGraph graph = loadMethodNode.graph();
155158
SharedMethod method = (SharedMethod) loadMethodNode.getMethod();
156-
ReadNode methodPointer = createReadVirtualMethod(graph, loadMethodNode.getHub(), method);
157-
graph.replaceFixed(loadMethodNode, methodPointer);
158-
}
159+
ValueNode hub = loadMethodNode.getHub();
160+
161+
if (SubstrateOptions.closedTypeWorld()) {
162+
163+
int vtableEntryOffset = knownOffsets.getVTableOffset(method.getVTableIndex(), true);
164+
assert vtableEntryOffset > 0;
165+
/*
166+
* Method pointer will always exist in the vtable due to the fact that all reachable
167+
* methods through method pointer constant references will be compiled.
168+
*/
169+
AddressNode address = createOffsetAddress(graph, hub, vtableEntryOffset);
170+
ReadNode virtualMethod = graph.add(new ReadNode(address, SubstrateBackend.getVTableIdentity(), loadMethodNode.stamp(NodeView.DEFAULT), BarrierType.NONE, MemoryOrderMode.PLAIN));
171+
graph.replaceFixed(loadMethodNode, virtualMethod);
159172

160-
private ReadNode createReadVirtualMethod(StructuredGraph graph, ValueNode hub, SharedMethod method) {
161-
int vtableEntryOffset = knownOffsets.getVTableOffset(method.getVTableIndex());
162-
assert vtableEntryOffset > 0;
163-
/*
164-
* Method pointer will always exist in the vtable due to the fact that all reachable methods
165-
* through method pointer constant references will be compiled.
166-
*/
167-
Stamp methodStamp = SubstrateMethodPointerStamp.methodNonNull();
168-
AddressNode address = createOffsetAddress(graph, hub, vtableEntryOffset);
169-
return graph.add(new ReadNode(address, SubstrateBackend.getVTableIdentity(), methodStamp, BarrierType.NONE, MemoryOrderMode.PLAIN));
173+
} else {
174+
// First compute the dispatch table starting offset
175+
LoadOpenTypeWorldDispatchTableStartingOffset tableStartingOffset = graph.add(new LoadOpenTypeWorldDispatchTableStartingOffset(hub, method));
176+
177+
// Add together table starting offset and index offset
178+
ValueNode methodAddress = graph.unique(
179+
new AddNode(tableStartingOffset, ConstantNode.forIntegerKind(ConfigurationValues.getWordKind(), knownOffsets.getVTableOffset(method.getVTableIndex(), false), graph)));
180+
181+
// The load the method address for the dispatch table
182+
AddressNode dispatchTableAddress = graph.unique(new OffsetAddressNode(hub, methodAddress));
183+
ReadNode virtualMethod = graph
184+
.add(new ReadNode(dispatchTableAddress, SubstrateBackend.getVTableIdentity(), loadMethodNode.stamp(NodeView.DEFAULT), BarrierType.NONE, MemoryOrderMode.PLAIN));
185+
186+
// wire in the new nodes
187+
FixedWithNextNode predecessor = (FixedWithNextNode) loadMethodNode.predecessor();
188+
predecessor.setNext(tableStartingOffset);
189+
tableStartingOffset.setNext(virtualMethod);
190+
graph.replaceFixed(loadMethodNode, virtualMethod);
191+
192+
// Lower logic associated with loading starting offset
193+
tableStartingOffset.lower(tool);
194+
}
170195
}
171196

172197
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright (c) 2024, 2024, 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.core.graal.nodes;
26+
27+
import com.oracle.svm.core.graal.snippets.OpenTypeWorldDispatchTableSnippets;
28+
import com.oracle.svm.core.meta.SharedMethod;
29+
30+
import jdk.graal.compiler.core.common.type.StampFactory;
31+
import jdk.graal.compiler.graph.NodeClass;
32+
import jdk.graal.compiler.nodeinfo.NodeCycles;
33+
import jdk.graal.compiler.nodeinfo.NodeInfo;
34+
import jdk.graal.compiler.nodeinfo.NodeSize;
35+
import jdk.graal.compiler.nodes.FixedWithNextNode;
36+
import jdk.graal.compiler.nodes.ValueNode;
37+
import jdk.graal.compiler.nodes.spi.Lowerable;
38+
39+
/**
40+
* When using the open type world, each interface a type implements has a unique dispatch table. For
41+
* a given type, all the tables are stored together with the DynamicHubs' vtable slots. The logic
42+
* for determining the starting offset of the dispatch table is contained in
43+
* {@link OpenTypeWorldDispatchTableSnippets}.
44+
*/
45+
@NodeInfo(size = NodeSize.SIZE_UNKNOWN, cycles = NodeCycles.CYCLES_UNKNOWN)
46+
public class LoadOpenTypeWorldDispatchTableStartingOffset extends FixedWithNextNode implements Lowerable {
47+
public static final NodeClass<LoadOpenTypeWorldDispatchTableStartingOffset> TYPE = NodeClass.create(LoadOpenTypeWorldDispatchTableStartingOffset.class);
48+
49+
@Input protected ValueNode hub;
50+
@OptionalInput protected ValueNode interfaceTypeID;
51+
52+
protected final SharedMethod target;
53+
54+
public LoadOpenTypeWorldDispatchTableStartingOffset(ValueNode hub, SharedMethod target) {
55+
super(TYPE, StampFactory.forInteger(64));
56+
this.hub = hub;
57+
this.target = target;
58+
this.interfaceTypeID = null;
59+
}
60+
61+
protected LoadOpenTypeWorldDispatchTableStartingOffset(ValueNode hub, ValueNode interfaceTypeID) {
62+
super(TYPE, StampFactory.forInteger(64));
63+
this.hub = hub;
64+
this.target = null;
65+
this.interfaceTypeID = interfaceTypeID;
66+
}
67+
68+
public ValueNode getHub() {
69+
return hub;
70+
}
71+
72+
public ValueNode getInterfaceTypeID() {
73+
return interfaceTypeID;
74+
}
75+
76+
public SharedMethod getTarget() {
77+
return target;
78+
}
79+
80+
@NodeIntrinsic
81+
public static native long createOpenTypeWorldLoadDispatchTableStartingOffset(Object hub, int interfaceTypeID);
82+
}

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

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import com.oracle.svm.core.graal.code.SubstrateBackend;
4343
import com.oracle.svm.core.graal.meta.KnownOffsets;
4444
import com.oracle.svm.core.graal.meta.RuntimeConfiguration;
45+
import com.oracle.svm.core.graal.nodes.LoadOpenTypeWorldDispatchTableStartingOffset;
4546
import com.oracle.svm.core.graal.nodes.LoweredDeadEndNode;
4647
import com.oracle.svm.core.graal.nodes.ThrowBytecodeExceptionNode;
4748
import com.oracle.svm.core.meta.SharedMethod;
@@ -78,6 +79,7 @@
7879
import jdk.graal.compiler.nodes.PiNode;
7980
import jdk.graal.compiler.nodes.StructuredGraph;
8081
import jdk.graal.compiler.nodes.ValueNode;
82+
import jdk.graal.compiler.nodes.calc.AddNode;
8183
import jdk.graal.compiler.nodes.calc.IsNullNode;
8284
import jdk.graal.compiler.nodes.extended.BranchProbabilityNode;
8385
import jdk.graal.compiler.nodes.extended.BytecodeExceptionNode;
@@ -92,6 +94,7 @@
9294
import jdk.graal.compiler.nodes.memory.ReadNode;
9395
import jdk.graal.compiler.nodes.memory.address.AddressNode;
9496
import jdk.graal.compiler.nodes.memory.address.OffsetAddressNode;
97+
import jdk.graal.compiler.nodes.spi.Lowerable;
9598
import jdk.graal.compiler.nodes.spi.LoweringTool;
9699
import jdk.graal.compiler.nodes.spi.LoweringTool.StandardLoweringStage;
97100
import jdk.graal.compiler.nodes.spi.StampProvider;
@@ -295,10 +298,12 @@ public void lower(FixedNode node, LoweringTool tool) {
295298
NodeInputList<ValueNode> parameters = callTarget.arguments();
296299
ValueNode receiver = parameters.size() <= 0 ? null : parameters.get(0);
297300
FixedGuardNode nullCheck = null;
301+
List<Lowerable> nodesToLower = new ArrayList<>(3);
298302
if (!callTarget.isStatic() && receiver.getStackKind() == JavaKind.Object && !StampTool.isPointerNonNull(receiver)) {
299303
LogicNode isNull = graph.unique(IsNullNode.create(receiver));
300304
nullCheck = graph.add(new FixedGuardNode(isNull, DeoptimizationReason.NullCheckException, DeoptimizationAction.None, true));
301305
graph.addBeforeFixed(node, nullCheck);
306+
nodesToLower.add(nullCheck);
302307
}
303308
SharedMethod method = (SharedMethod) callTarget.targetMethod();
304309
JavaType[] signature = method.getSignature().toParameterTypes(callTarget.isStatic() ? null : method.getDeclaringClass());
@@ -349,7 +354,6 @@ public void lower(FixedNode node, LoweringTool tool) {
349354
reportError.setNext(graph.add(new LoweredDeadEndNode()));
350355
}
351356

352-
LoadHubNode hub = null;
353357
CallTargetNode loweredCallTarget;
354358
if (invokeKind.isDirect() || implementations.length == 1) {
355359
SharedMethod targetMethod = method;
@@ -408,25 +412,51 @@ public void lower(FixedNode node, LoweringTool tool) {
408412
loweredCallTarget = createUnreachableCallTarget(tool, node, parameters, callTarget.returnStamp(), signature, method, callType, invokeKind);
409413

410414
} else {
411-
int vtableEntryOffset = knownOffsets.getVTableOffset(method.getVTableIndex());
415+
LoadHubNode hub = graph.unique(new LoadHubNode(runtimeConfig.getProviders().getStampProvider(), graph.addOrUnique(PiNode.create(receiver, nullCheck))));
416+
nodesToLower.add(hub);
412417

413-
hub = graph.unique(new LoadHubNode(runtimeConfig.getProviders().getStampProvider(), graph.addOrUnique(PiNode.create(receiver, nullCheck))));
414-
AddressNode address = graph.unique(new OffsetAddressNode(hub, ConstantNode.forIntegerKind(ConfigurationValues.getWordKind(), vtableEntryOffset, graph)));
415-
ReadNode entry = graph.add(new ReadNode(address, SubstrateBackend.getVTableIdentity(), FrameAccess.getWordStamp(), BarrierType.NONE, MemoryOrderMode.PLAIN));
416-
loweredCallTarget = createIndirectCall(graph, callTarget, parameters, method, signature, callType, invokeKind, entry);
418+
if (SubstrateOptions.closedTypeWorld()) {
419+
int vtableEntryOffset = knownOffsets.getVTableOffset(method.getVTableIndex(), true);
417420

418-
graph.addBeforeFixed(node, entry);
421+
AddressNode address = graph.unique(new OffsetAddressNode(hub, ConstantNode.forIntegerKind(ConfigurationValues.getWordKind(), vtableEntryOffset, graph)));
422+
ReadNode entry = graph.add(new ReadNode(address, SubstrateBackend.getVTableIdentity(), FrameAccess.getWordStamp(), BarrierType.NONE, MemoryOrderMode.PLAIN));
423+
424+
loweredCallTarget = createIndirectCall(graph, callTarget, parameters, method, signature, callType, invokeKind, entry);
425+
426+
graph.addBeforeFixed(node, entry);
427+
} else {
428+
429+
// Compute the dispatch table starting offset
430+
LoadOpenTypeWorldDispatchTableStartingOffset tableStartingOffset = graph.add(new LoadOpenTypeWorldDispatchTableStartingOffset(hub, method));
431+
nodesToLower.add(tableStartingOffset);
432+
433+
// Add together table starting offset and index offset
434+
ValueNode methodAddressOffset = graph.unique(new AddNode(tableStartingOffset,
435+
ConstantNode.forIntegerKind(ConfigurationValues.getWordKind(), knownOffsets.getVTableOffset(method.getVTableIndex(), false), graph)));
436+
437+
// The load the method address for the dispatch table
438+
AddressNode dispatchTableAddress = graph.unique(new OffsetAddressNode(hub, methodAddressOffset));
439+
ReadNode entry = graph.add(new ReadNode(dispatchTableAddress, SubstrateBackend.getVTableIdentity(), FrameAccess.getWordStamp(), BarrierType.NONE, MemoryOrderMode.PLAIN));
440+
441+
loweredCallTarget = createIndirectCall(graph, callTarget, parameters, method, signature, callType, invokeKind, entry);
442+
443+
// wire in the new nodes
444+
FixedWithNextNode predecessor = (FixedWithNextNode) node.predecessor();
445+
predecessor.setNext(tableStartingOffset);
446+
tableStartingOffset.setNext(entry);
447+
entry.setNext(node);
448+
449+
/*
450+
* note here we don't delete the invoke because it remains in the graph,
451+
* albeit with a different call target
452+
*/
453+
}
419454
}
420455

421456
callTarget.replaceAndDelete(loweredCallTarget);
422457

423458
// Recursive lowering
424-
if (nullCheck != null) {
425-
nullCheck.lower(tool);
426-
}
427-
if (hub != null) {
428-
hub.lower(tool);
429-
}
459+
nodesToLower.forEach(n -> n.lower(tool));
430460
}
431461
}
432462

0 commit comments

Comments
 (0)