Skip to content

Commit

Permalink
[GR-49708] Implement open type world virtual method dispatch
Browse files Browse the repository at this point in the history
PullRequest: graal/17065
  • Loading branch information
teshull committed Mar 13, 2024
2 parents 2700cc4 + fc4c523 commit ca59606
Show file tree
Hide file tree
Showing 27 changed files with 1,165 additions and 501 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,18 @@ private boolean isFullyInitialized() {
return vtableEntrySize > 0;
}

public int getVTableOffset(int vTableIndex) {
/**
* Returns of the offset of the index either relative to the start of the vtable
* ({@code fromDynamicHubStart} == false) or start of the dynamic hub
* ({@code fromDynamicHubStart} == true).
*/
public int getVTableOffset(int vTableIndex, boolean fromDynamicHubStart) {
assert isFullyInitialized();
return vtableBaseOffset + vTableIndex * vtableEntrySize;
if (fromDynamicHubStart) {
return vtableBaseOffset + vTableIndex * vtableEntrySize;
} else {
return vTableIndex * vtableEntrySize;
}
}

public int getTypeIDSlotsOffset() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@
import org.graalvm.nativeimage.Platforms;

import com.oracle.svm.core.StaticFieldsSupport;
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.config.ConfigurationValues;
import com.oracle.svm.core.config.ObjectLayout;
import com.oracle.svm.core.graal.code.SubstrateBackend;
import com.oracle.svm.core.graal.nodes.FloatingWordCastNode;
import com.oracle.svm.core.graal.nodes.LoadOpenTypeWorldDispatchTableStartingOffset;
import com.oracle.svm.core.graal.nodes.LoweredDeadEndNode;
import com.oracle.svm.core.graal.nodes.SubstrateCompressionNode;
import com.oracle.svm.core.graal.nodes.SubstrateFieldLocationIdentity;
Expand All @@ -46,7 +48,6 @@
import com.oracle.svm.core.identityhashcode.IdentityHashCodeSupport;
import com.oracle.svm.core.meta.SharedField;
import com.oracle.svm.core.meta.SharedMethod;
import com.oracle.svm.core.meta.SubstrateMethodPointerStamp;
import com.oracle.svm.core.snippets.SubstrateIsArraySnippets;

import jdk.graal.compiler.core.common.memory.BarrierType;
Expand All @@ -65,10 +66,12 @@
import jdk.graal.compiler.nodes.DeadEndNode;
import jdk.graal.compiler.nodes.FieldLocationIdentity;
import jdk.graal.compiler.nodes.FixedNode;
import jdk.graal.compiler.nodes.FixedWithNextNode;
import jdk.graal.compiler.nodes.NamedLocationIdentity;
import jdk.graal.compiler.nodes.NodeView;
import jdk.graal.compiler.nodes.StructuredGraph;
import jdk.graal.compiler.nodes.ValueNode;
import jdk.graal.compiler.nodes.calc.AddNode;
import jdk.graal.compiler.nodes.calc.AndNode;
import jdk.graal.compiler.nodes.calc.LeftShiftNode;
import jdk.graal.compiler.nodes.calc.UnsignedRightShiftNode;
Expand Down Expand Up @@ -144,29 +147,51 @@ public void lower(Node n, LoweringTool tool) {
} else if (n instanceof DeadEndNode) {
lowerDeadEnd((DeadEndNode) n);
} else if (n instanceof LoadMethodNode) {
lowerLoadMethodNode((LoadMethodNode) n);
lowerLoadMethodNode((LoadMethodNode) n, tool);
} else {
super.lower(n, tool);
}
}

private void lowerLoadMethodNode(LoadMethodNode loadMethodNode) {
private void lowerLoadMethodNode(LoadMethodNode loadMethodNode, LoweringTool tool) {
StructuredGraph graph = loadMethodNode.graph();
SharedMethod method = (SharedMethod) loadMethodNode.getMethod();
ReadNode methodPointer = createReadVirtualMethod(graph, loadMethodNode.getHub(), method);
graph.replaceFixed(loadMethodNode, methodPointer);
}
ValueNode hub = loadMethodNode.getHub();

if (SubstrateOptions.closedTypeWorld()) {

int vtableEntryOffset = knownOffsets.getVTableOffset(method.getVTableIndex(), true);
assert vtableEntryOffset > 0;
/*
* Method pointer will always exist in the vtable due to the fact that all reachable
* methods through method pointer constant references will be compiled.
*/
AddressNode address = createOffsetAddress(graph, hub, vtableEntryOffset);
ReadNode virtualMethod = graph.add(new ReadNode(address, SubstrateBackend.getVTableIdentity(), loadMethodNode.stamp(NodeView.DEFAULT), BarrierType.NONE, MemoryOrderMode.PLAIN));
graph.replaceFixed(loadMethodNode, virtualMethod);

private ReadNode createReadVirtualMethod(StructuredGraph graph, ValueNode hub, SharedMethod method) {
int vtableEntryOffset = knownOffsets.getVTableOffset(method.getVTableIndex());
assert vtableEntryOffset > 0;
/*
* Method pointer will always exist in the vtable due to the fact that all reachable methods
* through method pointer constant references will be compiled.
*/
Stamp methodStamp = SubstrateMethodPointerStamp.methodNonNull();
AddressNode address = createOffsetAddress(graph, hub, vtableEntryOffset);
return graph.add(new ReadNode(address, SubstrateBackend.getVTableIdentity(), methodStamp, BarrierType.NONE, MemoryOrderMode.PLAIN));
} else {
// First compute the dispatch table starting offset
LoadOpenTypeWorldDispatchTableStartingOffset tableStartingOffset = graph.add(new LoadOpenTypeWorldDispatchTableStartingOffset(hub, method));

// Add together table starting offset and index offset
ValueNode methodAddress = graph.unique(
new AddNode(tableStartingOffset, ConstantNode.forIntegerKind(ConfigurationValues.getWordKind(), knownOffsets.getVTableOffset(method.getVTableIndex(), false), graph)));

// The load the method address for the dispatch table
AddressNode dispatchTableAddress = graph.unique(new OffsetAddressNode(hub, methodAddress));
ReadNode virtualMethod = graph
.add(new ReadNode(dispatchTableAddress, SubstrateBackend.getVTableIdentity(), loadMethodNode.stamp(NodeView.DEFAULT), BarrierType.NONE, MemoryOrderMode.PLAIN));

// wire in the new nodes
FixedWithNextNode predecessor = (FixedWithNextNode) loadMethodNode.predecessor();
predecessor.setNext(tableStartingOffset);
tableStartingOffset.setNext(virtualMethod);
graph.replaceFixed(loadMethodNode, virtualMethod);

// Lower logic associated with loading starting offset
tableStartingOffset.lower(tool);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.core.graal.nodes;

import com.oracle.svm.core.graal.snippets.OpenTypeWorldDispatchTableSnippets;
import com.oracle.svm.core.meta.SharedMethod;

import jdk.graal.compiler.core.common.type.StampFactory;
import jdk.graal.compiler.graph.NodeClass;
import jdk.graal.compiler.nodeinfo.NodeCycles;
import jdk.graal.compiler.nodeinfo.NodeInfo;
import jdk.graal.compiler.nodeinfo.NodeSize;
import jdk.graal.compiler.nodes.FixedWithNextNode;
import jdk.graal.compiler.nodes.ValueNode;
import jdk.graal.compiler.nodes.spi.Lowerable;

/**
* When using the open type world, each interface a type implements has a unique dispatch table. For
* a given type, all the tables are stored together with the DynamicHubs' vtable slots. The logic
* for determining the starting offset of the dispatch table is contained in
* {@link OpenTypeWorldDispatchTableSnippets}.
*/
@NodeInfo(size = NodeSize.SIZE_UNKNOWN, cycles = NodeCycles.CYCLES_UNKNOWN)
public class LoadOpenTypeWorldDispatchTableStartingOffset extends FixedWithNextNode implements Lowerable {
public static final NodeClass<LoadOpenTypeWorldDispatchTableStartingOffset> TYPE = NodeClass.create(LoadOpenTypeWorldDispatchTableStartingOffset.class);

@Input protected ValueNode hub;
@OptionalInput protected ValueNode interfaceTypeID;

protected final SharedMethod target;

public LoadOpenTypeWorldDispatchTableStartingOffset(ValueNode hub, SharedMethod target) {
super(TYPE, StampFactory.forInteger(64));
this.hub = hub;
this.target = target;
this.interfaceTypeID = null;
}

protected LoadOpenTypeWorldDispatchTableStartingOffset(ValueNode hub, ValueNode interfaceTypeID) {
super(TYPE, StampFactory.forInteger(64));
this.hub = hub;
this.target = null;
this.interfaceTypeID = interfaceTypeID;
}

public ValueNode getHub() {
return hub;
}

public ValueNode getInterfaceTypeID() {
return interfaceTypeID;
}

public SharedMethod getTarget() {
return target;
}

@NodeIntrinsic
public static native long createOpenTypeWorldLoadDispatchTableStartingOffset(Object hub, int interfaceTypeID);
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import com.oracle.svm.core.graal.code.SubstrateBackend;
import com.oracle.svm.core.graal.meta.KnownOffsets;
import com.oracle.svm.core.graal.meta.RuntimeConfiguration;
import com.oracle.svm.core.graal.nodes.LoadOpenTypeWorldDispatchTableStartingOffset;
import com.oracle.svm.core.graal.nodes.LoweredDeadEndNode;
import com.oracle.svm.core.graal.nodes.ThrowBytecodeExceptionNode;
import com.oracle.svm.core.meta.SharedMethod;
Expand Down Expand Up @@ -78,6 +79,7 @@
import jdk.graal.compiler.nodes.PiNode;
import jdk.graal.compiler.nodes.StructuredGraph;
import jdk.graal.compiler.nodes.ValueNode;
import jdk.graal.compiler.nodes.calc.AddNode;
import jdk.graal.compiler.nodes.calc.IsNullNode;
import jdk.graal.compiler.nodes.extended.BranchProbabilityNode;
import jdk.graal.compiler.nodes.extended.BytecodeExceptionNode;
Expand All @@ -92,6 +94,7 @@
import jdk.graal.compiler.nodes.memory.ReadNode;
import jdk.graal.compiler.nodes.memory.address.AddressNode;
import jdk.graal.compiler.nodes.memory.address.OffsetAddressNode;
import jdk.graal.compiler.nodes.spi.Lowerable;
import jdk.graal.compiler.nodes.spi.LoweringTool;
import jdk.graal.compiler.nodes.spi.LoweringTool.StandardLoweringStage;
import jdk.graal.compiler.nodes.spi.StampProvider;
Expand Down Expand Up @@ -295,10 +298,12 @@ public void lower(FixedNode node, LoweringTool tool) {
NodeInputList<ValueNode> parameters = callTarget.arguments();
ValueNode receiver = parameters.size() <= 0 ? null : parameters.get(0);
FixedGuardNode nullCheck = null;
List<Lowerable> nodesToLower = new ArrayList<>(3);
if (!callTarget.isStatic() && receiver.getStackKind() == JavaKind.Object && !StampTool.isPointerNonNull(receiver)) {
LogicNode isNull = graph.unique(IsNullNode.create(receiver));
nullCheck = graph.add(new FixedGuardNode(isNull, DeoptimizationReason.NullCheckException, DeoptimizationAction.None, true));
graph.addBeforeFixed(node, nullCheck);
nodesToLower.add(nullCheck);
}
SharedMethod method = (SharedMethod) callTarget.targetMethod();
JavaType[] signature = method.getSignature().toParameterTypes(callTarget.isStatic() ? null : method.getDeclaringClass());
Expand Down Expand Up @@ -349,7 +354,6 @@ public void lower(FixedNode node, LoweringTool tool) {
reportError.setNext(graph.add(new LoweredDeadEndNode()));
}

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

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

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

graph.addBeforeFixed(node, entry);
AddressNode address = graph.unique(new OffsetAddressNode(hub, ConstantNode.forIntegerKind(ConfigurationValues.getWordKind(), vtableEntryOffset, graph)));
ReadNode entry = graph.add(new ReadNode(address, SubstrateBackend.getVTableIdentity(), FrameAccess.getWordStamp(), BarrierType.NONE, MemoryOrderMode.PLAIN));

loweredCallTarget = createIndirectCall(graph, callTarget, parameters, method, signature, callType, invokeKind, entry);

graph.addBeforeFixed(node, entry);
} else {

// Compute the dispatch table starting offset
LoadOpenTypeWorldDispatchTableStartingOffset tableStartingOffset = graph.add(new LoadOpenTypeWorldDispatchTableStartingOffset(hub, method));
nodesToLower.add(tableStartingOffset);

// Add together table starting offset and index offset
ValueNode methodAddressOffset = graph.unique(new AddNode(tableStartingOffset,
ConstantNode.forIntegerKind(ConfigurationValues.getWordKind(), knownOffsets.getVTableOffset(method.getVTableIndex(), false), graph)));

// The load the method address for the dispatch table
AddressNode dispatchTableAddress = graph.unique(new OffsetAddressNode(hub, methodAddressOffset));
ReadNode entry = graph.add(new ReadNode(dispatchTableAddress, SubstrateBackend.getVTableIdentity(), FrameAccess.getWordStamp(), BarrierType.NONE, MemoryOrderMode.PLAIN));

loweredCallTarget = createIndirectCall(graph, callTarget, parameters, method, signature, callType, invokeKind, entry);

// wire in the new nodes
FixedWithNextNode predecessor = (FixedWithNextNode) node.predecessor();
predecessor.setNext(tableStartingOffset);
tableStartingOffset.setNext(entry);
entry.setNext(node);

/*
* note here we don't delete the invoke because it remains in the graph,
* albeit with a different call target
*/
}
}

callTarget.replaceAndDelete(loweredCallTarget);

// Recursive lowering
if (nullCheck != null) {
nullCheck.lower(tool);
}
if (hub != null) {
hub.lower(tool);
}
nodesToLower.forEach(n -> n.lower(tool));
}
}

Expand Down
Loading

0 comments on commit ca59606

Please sign in to comment.