Skip to content

Commit ac282f9

Browse files
committed
[GR-63407] Flush instruction cache after trampoline preparation.
PullRequest: graal/20420
2 parents 39fd48e + c926b94 commit ac282f9

File tree

6 files changed

+88
-19
lines changed

6 files changed

+88
-19
lines changed

substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/TrampolineSet.java

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -29,19 +29,22 @@
2929
import java.util.BitSet;
3030
import java.util.List;
3131

32-
import jdk.graal.compiler.word.Word;
3332
import org.graalvm.nativeimage.CurrentIsolate;
3433
import org.graalvm.nativeimage.PinnedObject;
3534
import org.graalvm.nativeimage.c.function.CFunctionPointer;
3635
import org.graalvm.word.Pointer;
3736
import org.graalvm.word.PointerBase;
3837
import org.graalvm.word.UnsignedWord;
3938

39+
import com.oracle.svm.core.code.AbstractRuntimeCodeInstaller.RuntimeCodeInstallerPlatformHelper;
40+
import com.oracle.svm.core.heap.VMOperationInfos;
4041
import com.oracle.svm.core.os.VirtualMemoryProvider;
42+
import com.oracle.svm.core.thread.JavaVMOperation;
4143
import com.oracle.svm.core.util.UnsignedUtils;
4244
import com.oracle.svm.core.util.VMError;
4345

4446
import jdk.graal.compiler.core.common.NumUtil;
47+
import jdk.graal.compiler.word.Word;
4548

4649
/**
4750
* A set of trampolines that can be assigned to specific upcall stubs with specific method handles.
@@ -154,6 +157,15 @@ private Pointer prepareTrampolines(PinnedObject mhsArray, PinnedObject stubsArra
154157
VMError.guarantee(memoryProvider.protect(page, pageSize, VirtualMemoryProvider.Access.EXECUTE) == 0,
155158
"Error when making the trampoline allocation executable");
156159

160+
/*
161+
* On some architectures, it is necessary to flush the instruction cache if new code was
162+
* installed and/or to issue an instruction synchronization barrier on other cores currently
163+
* running a thread that may execute the newly installed code.
164+
*/
165+
if (RuntimeCodeInstallerPlatformHelper.singleton().needsInstructionCacheSynchronization()) {
166+
new InstructionCacheOperation(page.rawValue(), pageSize.rawValue()).enqueue();
167+
}
168+
157169
return page;
158170
}
159171

@@ -173,4 +185,20 @@ boolean tryFree() {
173185
}
174186
return true;
175187
}
188+
189+
private static class InstructionCacheOperation extends JavaVMOperation {
190+
private final long codeStart;
191+
private final long codeSize;
192+
193+
InstructionCacheOperation(long codeStart, long codeSize) {
194+
super(VMOperationInfos.get(InstructionCacheOperation.class, "Prepare FFM API trampoline set", SystemEffect.SAFEPOINT));
195+
this.codeStart = codeStart;
196+
this.codeSize = codeSize;
197+
}
198+
199+
@Override
200+
protected void operate() {
201+
RuntimeCodeInstallerPlatformHelper.singleton().performCodeSynchronization(codeStart, codeSize);
202+
}
203+
}
176204
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@
3232
@CLibrary(value = "libchelper", requireStatic = true)
3333
public class CodeSynchronizationOperations {
3434

35+
/**
36+
* Flushes the processor's instruction cache for the given region of memory if required.
37+
*
38+
* Some target architectures (e.g. aarch64) require that the instruction cache be flushed, after
39+
* modifying memory containing code, in order to obtain deterministic behavior.
40+
*
41+
* @param codeStart start address of the modified memory
42+
* @param codeSize size of the modified memory
43+
*/
3544
@CFunction(value = "codeSynchronization_clearCache", transition = Transition.NO_TRANSITION)
3645
public static native void clearCache(long codeStart, long codeSize);
3746
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/AbstractRuntimeCodeInstaller.java

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
*/
2525
package com.oracle.svm.core.code;
2626

27-
import jdk.graal.compiler.word.Word;
2827
import org.graalvm.nativeimage.ImageSingletons;
2928
import org.graalvm.nativeimage.c.function.CodePointer;
3029
import org.graalvm.word.Pointer;
@@ -37,6 +36,9 @@
3736
import com.oracle.svm.core.thread.JavaVMOperation;
3837
import com.oracle.svm.core.util.VMError;
3938

39+
import jdk.graal.compiler.api.replacements.Fold;
40+
import jdk.graal.compiler.word.Word;
41+
4042
public class AbstractRuntimeCodeInstaller {
4143
protected Pointer allocateCodeMemory(long size) {
4244
PointerBase result = RuntimeCodeInfoAccess.allocateCodeMemory(Word.unsigned(size));
@@ -77,10 +79,6 @@ protected static <E extends Throwable> RuntimeException rethrow(Throwable ex) th
7779
throw (E) ex;
7880
}
7981

80-
protected static RuntimeCodeInstallerPlatformHelper platformHelper() {
81-
return ImageSingletons.lookup(RuntimeCodeInstallerPlatformHelper.class);
82-
}
83-
8482
private static class InstallCodeOperation extends JavaVMOperation {
8583
private final SharedMethod method;
8684
private final CodeInfo codeInfo;
@@ -102,7 +100,7 @@ protected void operate() {
102100
UnsignedWord offset = CodeInfoAccess.getCodeEntryPointOffset(codeInfo);
103101
installedCode.setAddress(codeStart.rawValue(), codeStart.rawValue() + offset.rawValue(), method);
104102
CodeInfoTable.getRuntimeCodeCache().addMethod(codeInfo);
105-
platformHelper().performCodeSynchronization(codeInfo);
103+
RuntimeCodeInstallerPlatformHelper.singleton().performCodeSynchronization(codeStart.rawValue(), CodeInfoAccess.getCodeSize(codeInfo).rawValue());
106104
VMError.guarantee(CodeInfoAccess.getState(codeInfo) == CodeInfo.STATE_CODE_CONSTANTS_LIVE && installedCode.isValid(), "The code can't be invalidated before the VM operation finishes");
107105
} catch (Throwable e) {
108106
error = e;
@@ -113,11 +111,19 @@ protected void operate() {
113111
/** Methods which are platform specific. */
114112
public interface RuntimeCodeInstallerPlatformHelper {
115113

114+
@Fold
115+
static RuntimeCodeInstallerPlatformHelper singleton() {
116+
return ImageSingletons.lookup(RuntimeCodeInstallerPlatformHelper.class);
117+
}
118+
119+
boolean needsInstructionCacheSynchronization();
120+
116121
/**
117122
* Method to enable platforms to perform any needed operations before code becomes visible.
118123
*
119-
* @param codeInfo the new code to be installed
124+
* @param codeStart start address of the new code to be installed
125+
* @param codeSize size of the new code to be installed
120126
*/
121-
void performCodeSynchronization(CodeInfo codeInfo);
127+
void performCodeSynchronization(long codeStart, long codeSize);
122128
}
123129
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMThreads.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -973,8 +973,21 @@ public static String toString(int safepointBehavior) {
973973
}
974974

975975
/**
976-
* A thread-local enum conveying any actions needed before thread begins executing Java code.
977-
* Only used on aarch64 at the moment.
976+
* A thread-local enum conveying any actions needed before thread begins executing Java code. At
977+
* the moment, only used on aarch64 where it is necessary to issue an ISB (instruction
978+
* synchronization barrier) if new code was made executable (see
979+
* <a href="https://developer.arm.com/documentation/ddi0487/latest">ARM Architecture Reference
980+
* Manual</a> Section B2.2.5).
981+
*
982+
* <pre>
983+
* For example, assume there are 4 cores and 2 Java threads:
984+
* - Thread A runs on core 0
985+
* - Thread B runs on core 1 and installs code. We initiate a safepoint and force all Java
986+
* threads to call ISB
987+
* - After the safepoint, thread A still runs on core 0 and executes ISB
988+
* - If the OS switches thread A to core 2, the context switch also executes an appropriate
989+
* instruction
990+
* </pre>
978991
*/
979992
public static class ActionOnTransitionToJavaSupport {
980993

substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/aarch64/AArch64RuntimeCodeInstallerPlatformHelper.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
33
* Copyright (c) 2020, 2020, Arm Limited. All rights reserved.
44
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
55
*
@@ -30,8 +30,6 @@
3030

3131
import com.oracle.svm.core.CodeSynchronizationOperations;
3232
import com.oracle.svm.core.code.AbstractRuntimeCodeInstaller.RuntimeCodeInstallerPlatformHelper;
33-
import com.oracle.svm.core.code.CodeInfo;
34-
import com.oracle.svm.core.code.CodeInfoAccess;
3533
import com.oracle.svm.core.code.RuntimeCodeCache;
3634
import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton;
3735
import com.oracle.svm.core.option.RuntimeOptionKey;
@@ -48,9 +46,20 @@ public class AArch64RuntimeCodeInstallerPlatformHelper implements RuntimeCodeIns
4846
}
4947
}
5048

49+
/**
50+
* According to the <a href="https://developer.arm.com/documentation/ddi0487/latest">ARM
51+
* Architecture Reference Manual</a> (see Section B2.2.5), it is necessary to flush the
52+
* instruction cache and to issue an ISB (instruction synchronization barrier) if new code was
53+
* made executable.
54+
*/
5155
@Override
52-
public void performCodeSynchronization(CodeInfo codeInfo) {
53-
CodeSynchronizationOperations.clearCache(CodeInfoAccess.getCodeStart(codeInfo).rawValue(), CodeInfoAccess.getCodeSize(codeInfo).rawValue());
56+
public boolean needsInstructionCacheSynchronization() {
57+
return true;
58+
}
59+
60+
@Override
61+
public void performCodeSynchronization(long codeStart, long codeSize) {
62+
CodeSynchronizationOperations.clearCache(codeStart, codeSize);
5463
VMThreads.ActionOnTransitionToJavaSupport.requestAllThreadsSynchronizeCode();
5564
}
5665
}

substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/amd64/AMD64RuntimeCodeInstallerPlatformHelper.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,18 @@
2828
import org.graalvm.nativeimage.Platforms;
2929

3030
import com.oracle.svm.core.code.AbstractRuntimeCodeInstaller.RuntimeCodeInstallerPlatformHelper;
31-
import com.oracle.svm.core.code.CodeInfo;
3231
import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton;
3332

3433
@AutomaticallyRegisteredImageSingleton(RuntimeCodeInstallerPlatformHelper.class)
3534
@Platforms(Platform.AMD64.class)
3635
public class AMD64RuntimeCodeInstallerPlatformHelper implements RuntimeCodeInstallerPlatformHelper {
3736

3837
@Override
39-
public void performCodeSynchronization(CodeInfo codeInfo) {
38+
public boolean needsInstructionCacheSynchronization() {
39+
return false;
40+
}
41+
42+
@Override
43+
public void performCodeSynchronization(long codeStart, long codeSize) {
4044
}
4145
}

0 commit comments

Comments
 (0)