Skip to content

Commit 3c28d85

Browse files
committed
Support vtables with offsets relative to the code base.
1 parent 4ec7008 commit 3c28d85

35 files changed

+632
-291
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/ReservedRegisters.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ protected ReservedRegisters(Register frameRegister, Register threadRegister, Reg
5252
this.frameRegister = frameRegister;
5353
this.threadRegister = threadRegister;
5454
this.heapBaseRegister = heapBaseRegister;
55-
this.codeBaseRegister = codeBaseRegisterCandidate;
55+
this.codeBaseRegister = SubstrateOptions.useRelativeCodePointers() ? codeBaseRegisterCandidate : null;
5656
}
5757

5858
/**

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import static jdk.graal.compiler.core.common.SpectrePHTMitigations.Options.SpectrePHTBarriers;
3232
import static jdk.graal.compiler.options.OptionType.Expert;
3333
import static jdk.graal.compiler.options.OptionType.User;
34+
import static org.graalvm.nativeimage.impl.InternalPlatform.NATIVE_ONLY;
35+
import static org.graalvm.nativeimage.impl.InternalPlatform.PLATFORM_JNI;
3436

3537
import java.nio.file.InvalidPathException;
3638
import java.nio.file.Path;
@@ -50,6 +52,7 @@
5052
import com.oracle.svm.core.c.libc.MuslLibC;
5153
import com.oracle.svm.core.config.ConfigurationValues;
5254
import com.oracle.svm.core.heap.ReferenceHandler;
55+
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
5356
import com.oracle.svm.core.option.APIOption;
5457
import com.oracle.svm.core.option.APIOptionGroup;
5558
import com.oracle.svm.core.option.AccumulatingLocatableMultiOptionValue;
@@ -62,6 +65,7 @@
6265
import com.oracle.svm.core.option.ReplacingLocatableMultiOptionValue;
6366
import com.oracle.svm.core.option.RuntimeOptionKey;
6467
import com.oracle.svm.core.option.SubstrateOptionsParser;
68+
import com.oracle.svm.core.pltgot.PLTGOTConfiguration;
6569
import com.oracle.svm.core.thread.VMOperationControl;
6670
import com.oracle.svm.core.util.UserError;
6771
import com.oracle.svm.core.util.VMError;
@@ -1435,4 +1439,32 @@ public static boolean printClosedArenaUponThrow() {
14351439
return PrintClosedArenaUponThrow.getValue();
14361440
}
14371441

1442+
@Option(help = "Avoid linker relocations for code and instead emit address computations.", type = OptionType.Expert) //
1443+
public static final HostedOptionKey<Boolean> RelativeCodePointers = new HostedOptionKey<>(false, SubstrateOptions::validateRelativeCodePointers);
1444+
1445+
@Fold
1446+
public static boolean useRelativeCodePointers() {
1447+
return RelativeCodePointers.getValue();
1448+
}
1449+
1450+
private static void validateRelativeCodePointers(HostedOptionKey<Boolean> optionKey) {
1451+
if (optionKey.getValue()) {
1452+
String enabledOption = SubstrateOptionsParser.commandArgument(optionKey, "+");
1453+
1454+
UserError.guarantee(Platform.includedIn(PLATFORM_JNI.class) || Platform.includedIn(NATIVE_ONLY.class), "%s is supported only with hardware target platforms.", enabledOption);
1455+
1456+
// Offsets need to be passed on between layer builds rather than using symbol names.
1457+
UserError.guarantee(!ImageLayerBuildingSupport.buildingImageLayer(), "%s is currently not supported with layered images.", enabledOption);
1458+
1459+
// The concept of a code base would need to be introduced in the LLVM backend first.
1460+
UserError.guarantee(!useLLVMBackend(), "%s is currently not supported with the LLVM backend.", enabledOption);
1461+
1462+
/*
1463+
* Code offsets of PLT stubs cannot be predetermined because the PLT is separate from
1464+
* the text section and has its own base address. It would need to become a part of the
1465+
* text section (e.g., by turning it into a compilation unit).
1466+
*/
1467+
UserError.guarantee(!PLTGOTConfiguration.isEnabled(), "%s cannot be used together with PLT/GOT.", enabledOption);
1468+
}
1469+
}
14381470
}

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

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

3333
import com.oracle.svm.core.StaticFieldsSupport;
34-
import com.oracle.svm.core.SubstrateOptions;
3534
import com.oracle.svm.core.config.ConfigurationValues;
3635
import com.oracle.svm.core.config.ObjectLayout;
37-
import com.oracle.svm.core.graal.code.SubstrateBackend;
3836
import com.oracle.svm.core.graal.nodes.FloatingWordCastNode;
39-
import com.oracle.svm.core.graal.nodes.LoadOpenTypeWorldDispatchTableStartingOffset;
4037
import com.oracle.svm.core.graal.nodes.LoweredDeadEndNode;
4138
import com.oracle.svm.core.graal.nodes.SubstrateCompressionNode;
4239
import com.oracle.svm.core.graal.nodes.SubstrateFieldLocationIdentity;
@@ -48,11 +45,9 @@
4845
import com.oracle.svm.core.hub.DynamicHub;
4946
import com.oracle.svm.core.identityhashcode.IdentityHashCodeSupport;
5047
import com.oracle.svm.core.meta.SharedField;
51-
import com.oracle.svm.core.meta.SharedMethod;
5248
import com.oracle.svm.core.snippets.SubstrateIsArraySnippets;
5349

5450
import jdk.graal.compiler.core.common.memory.BarrierType;
55-
import jdk.graal.compiler.core.common.memory.MemoryOrderMode;
5651
import jdk.graal.compiler.core.common.spi.ForeignCallsProvider;
5752
import jdk.graal.compiler.core.common.spi.MetaAccessExtensionProvider;
5853
import jdk.graal.compiler.core.common.type.AbstractObjectStamp;
@@ -68,21 +63,17 @@
6863
import jdk.graal.compiler.nodes.DeadEndNode;
6964
import jdk.graal.compiler.nodes.FieldLocationIdentity;
7065
import jdk.graal.compiler.nodes.FixedNode;
71-
import jdk.graal.compiler.nodes.FixedWithNextNode;
7266
import jdk.graal.compiler.nodes.NamedLocationIdentity;
7367
import jdk.graal.compiler.nodes.NodeView;
7468
import jdk.graal.compiler.nodes.StructuredGraph;
7569
import jdk.graal.compiler.nodes.ValueNode;
76-
import jdk.graal.compiler.nodes.calc.AddNode;
7770
import jdk.graal.compiler.nodes.calc.AndNode;
7871
import jdk.graal.compiler.nodes.calc.LeftShiftNode;
7972
import jdk.graal.compiler.nodes.calc.NarrowNode;
8073
import jdk.graal.compiler.nodes.calc.UnsignedRightShiftNode;
8174
import jdk.graal.compiler.nodes.calc.ZeroExtendNode;
8275
import jdk.graal.compiler.nodes.extended.LoadHubNode;
83-
import jdk.graal.compiler.nodes.extended.LoadMethodNode;
8476
import jdk.graal.compiler.nodes.memory.FloatingReadNode;
85-
import jdk.graal.compiler.nodes.memory.ReadNode;
8677
import jdk.graal.compiler.nodes.memory.address.AddressNode;
8778
import jdk.graal.compiler.nodes.memory.address.OffsetAddressNode;
8879
import jdk.graal.compiler.nodes.spi.LoweringTool;
@@ -105,7 +96,6 @@ public abstract class SubstrateBasicLoweringProvider extends DefaultJavaLowering
10596
private final Map<Class<? extends Node>, NodeLoweringProvider<?>> lowerings;
10697

10798
private RuntimeConfiguration runtimeConfig;
108-
private final KnownOffsets knownOffsets;
10999
private final DynamicHubOffsets dynamicHubOffsets;
110100
private final AbstractObjectStamp hubStamp;
111101

@@ -121,7 +111,6 @@ public SubstrateBasicLoweringProvider(MetaAccessProvider metaAccess, ForeignCall
121111
hubRefStamp = SubstrateNarrowOopStamp.compressed(hubRefStamp, ReferenceAccess.singleton().getCompressEncoding());
122112
}
123113
hubStamp = hubRefStamp;
124-
knownOffsets = KnownOffsets.singleton();
125114
dynamicHubOffsets = DynamicHubOffsets.singleton();
126115
}
127116

@@ -156,54 +145,11 @@ public void lower(Node n, LoweringTool tool) {
156145
lowerAssertionNode((AssertionNode) n);
157146
} else if (n instanceof DeadEndNode) {
158147
lowerDeadEnd((DeadEndNode) n);
159-
} else if (n instanceof LoadMethodNode) {
160-
lowerLoadMethodNode((LoadMethodNode) n, tool);
161148
} else {
162149
super.lower(n, tool);
163150
}
164151
}
165152

166-
private void lowerLoadMethodNode(LoadMethodNode loadMethodNode, LoweringTool tool) {
167-
StructuredGraph graph = loadMethodNode.graph();
168-
SharedMethod method = (SharedMethod) loadMethodNode.getMethod();
169-
ValueNode hub = loadMethodNode.getHub();
170-
171-
if (SubstrateOptions.useClosedTypeWorldHubLayout()) {
172-
173-
int vtableEntryOffset = knownOffsets.getVTableOffset(method.getVTableIndex(), true);
174-
assert vtableEntryOffset > 0;
175-
/*
176-
* Method pointer will always exist in the vtable due to the fact that all reachable
177-
* methods through method pointer constant references will be compiled.
178-
*/
179-
AddressNode address = createOffsetAddress(graph, hub, vtableEntryOffset);
180-
ReadNode virtualMethod = graph.add(new ReadNode(address, SubstrateBackend.getVTableIdentity(), loadMethodNode.stamp(NodeView.DEFAULT), BarrierType.NONE, MemoryOrderMode.PLAIN));
181-
graph.replaceFixed(loadMethodNode, virtualMethod);
182-
183-
} else {
184-
// First compute the dispatch table starting offset
185-
LoadOpenTypeWorldDispatchTableStartingOffset tableStartingOffset = graph.add(new LoadOpenTypeWorldDispatchTableStartingOffset(hub, method));
186-
187-
// Add together table starting offset and index offset
188-
ValueNode methodAddress = graph.unique(
189-
new AddNode(tableStartingOffset, ConstantNode.forIntegerKind(ConfigurationValues.getWordKind(), knownOffsets.getVTableOffset(method.getVTableIndex(), false), graph)));
190-
191-
// The load the method address for the dispatch table
192-
AddressNode dispatchTableAddress = graph.unique(new OffsetAddressNode(hub, methodAddress));
193-
ReadNode virtualMethod = graph
194-
.add(new ReadNode(dispatchTableAddress, SubstrateBackend.getVTableIdentity(), loadMethodNode.stamp(NodeView.DEFAULT), BarrierType.NONE, MemoryOrderMode.PLAIN));
195-
196-
// wire in the new nodes
197-
FixedWithNextNode predecessor = (FixedWithNextNode) loadMethodNode.predecessor();
198-
predecessor.setNext(tableStartingOffset);
199-
tableStartingOffset.setNext(virtualMethod);
200-
graph.replaceFixed(loadMethodNode, virtualMethod);
201-
202-
// Lower logic associated with loading starting offset
203-
tableStartingOffset.lower(tool);
204-
}
205-
}
206-
207153
@Override
208154
public int arrayLengthOffset() {
209155
return getObjectLayout().getArrayLengthOffset();
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright (c) 2025, 2025, 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 static jdk.graal.compiler.nodeinfo.NodeCycles.CYCLES_2;
28+
import static jdk.graal.compiler.nodeinfo.NodeSize.SIZE_1;
29+
30+
import org.graalvm.nativeimage.c.function.CodePointer;
31+
32+
import jdk.graal.compiler.core.common.type.StampFactory;
33+
import jdk.graal.compiler.graph.NodeClass;
34+
import jdk.graal.compiler.nodeinfo.NodeInfo;
35+
import jdk.graal.compiler.nodes.FixedWithNextNode;
36+
import jdk.graal.compiler.nodes.ValueNode;
37+
import jdk.graal.compiler.nodes.spi.Lowerable;
38+
import jdk.graal.compiler.word.WordTypes;
39+
40+
@NodeInfo(cycles = CYCLES_2, size = SIZE_1)
41+
public final class LoadMethodByIndexNode extends FixedWithNextNode implements Lowerable {
42+
public static final NodeClass<LoadMethodByIndexNode> TYPE = NodeClass.create(LoadMethodByIndexNode.class);
43+
44+
@Input protected ValueNode hub;
45+
@Input protected ValueNode vtableIndex;
46+
@OptionalInput protected ValueNode interfaceTypeID;
47+
48+
protected LoadMethodByIndexNode(@InjectedNodeParameter WordTypes wordTypes, ValueNode hub, ValueNode vtableIndex, ValueNode interfaceTypeID) {
49+
super(TYPE, StampFactory.forKind(wordTypes.getWordKind()));
50+
this.hub = hub;
51+
this.vtableIndex = vtableIndex;
52+
this.interfaceTypeID = interfaceTypeID;
53+
}
54+
55+
public ValueNode getHub() {
56+
return hub;
57+
}
58+
59+
public ValueNode getVTableIndex() {
60+
return vtableIndex;
61+
}
62+
63+
public ValueNode getInterfaceTypeID() {
64+
return interfaceTypeID;
65+
}
66+
67+
@NodeIntrinsic
68+
public static native CodePointer loadMethodByIndex(Object hub, int vtableIndex, int interfaceTypeID);
69+
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/LoadOpenTypeWorldDispatchTableStartingOffset.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public LoadOpenTypeWorldDispatchTableStartingOffset(ValueNode hub, SharedMethod
5858
this.interfaceTypeID = null;
5959
}
6060

61-
protected LoadOpenTypeWorldDispatchTableStartingOffset(ValueNode hub, ValueNode interfaceTypeID) {
61+
public LoadOpenTypeWorldDispatchTableStartingOffset(ValueNode hub, ValueNode interfaceTypeID) {
6262
super(TYPE, StampFactory.forInteger(64));
6363
this.hub = hub;
6464
this.target = null;
@@ -76,7 +76,4 @@ public ValueNode getInterfaceTypeID() {
7676
public SharedMethod getTarget() {
7777
return target;
7878
}
79-
80-
@NodeIntrinsic
81-
public static native long createOpenTypeWorldLoadDispatchTableStartingOffset(Object hub, int interfaceTypeID);
8279
}

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,21 +217,30 @@ private static void initCodeBase() {
217217
writeCurrentVMCodeBase(codeBase);
218218
}
219219

220+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
221+
public static void earlyInitHeapBase(PointerBase heapBase) {
222+
setHeapBase(heapBase);
223+
}
224+
220225
/**
221226
* Sets the heap base register to the provided value. If the code base register is in use,
222227
* initializes it to contain the code base address.
223228
*/
224229
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
225230
public static void initBaseRegisters(PointerBase heapBase) {
226231
setHeapBase(heapBase);
227-
initCodeBase();
232+
if (SubstrateOptions.useRelativeCodePointers()) {
233+
initCodeBase();
234+
}
228235
}
229236

230237
/** Sets the heap base register, and if in use, the code base register, to the given values. */
231238
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
232239
public static void initBaseRegisters(PointerBase heapBase, PointerBase codeBase) {
233240
setHeapBase(heapBase);
234-
writeCurrentVMCodeBase(codeBase);
241+
if (SubstrateOptions.useRelativeCodePointers()) {
242+
writeCurrentVMCodeBase(codeBase);
243+
}
235244
}
236245

237246
@Snippet(allowMissingProbabilities = true)

0 commit comments

Comments
 (0)