Skip to content

Commit b0328b6

Browse files
committed
[GR-54987] Share the NFI errno between all NFI backends
PullRequest: graal/19154
2 parents f89d28d + d7639de commit b0328b6

File tree

15 files changed

+203
-83
lines changed

15 files changed

+203
-83
lines changed

substratevm/src/com.oracle.svm.truffle.nfi/src/com/oracle/svm/truffle/nfi/Target_com_oracle_truffle_nfi_backend_spi_NFIState.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,52 @@
2525
package com.oracle.svm.truffle.nfi;
2626

2727
import com.oracle.svm.core.annotate.Alias;
28+
import com.oracle.svm.core.annotate.Substitute;
2829
import com.oracle.svm.core.annotate.TargetClass;
30+
import com.oracle.svm.core.heap.VMOperationInfos;
31+
import com.oracle.svm.core.thread.JavaVMOperation;
32+
import com.oracle.svm.core.thread.PlatformThreads;
33+
import com.oracle.truffle.api.CompilerDirectives;
34+
import org.graalvm.nativeimage.IsolateThread;
2935

3036
@TargetClass(className = "com.oracle.truffle.nfi.backend.spi.NFIState", onlyWith = TruffleNFIFeature.IsEnabled.class)
3137
final class Target_com_oracle_truffle_nfi_backend_spi_NFIState {
3238

3339
@Alias boolean hasPendingException;
3440

41+
@Substitute
42+
private static long initNfiErrnoAddress(Thread thread) {
43+
var op = new GetErrnoMirrorAddressOperation(thread);
44+
op.enqueue();
45+
long address = op.result;
46+
if (address == 0) {
47+
throw CompilerDirectives.shouldNotReachHere("Could not find the IsolateThread for " + thread);
48+
}
49+
return address;
50+
}
51+
52+
@Substitute
53+
private void freeNfiErrnoAddress() {
54+
}
55+
56+
private static class GetErrnoMirrorAddressOperation extends JavaVMOperation {
57+
private final Thread thread;
58+
private long result = 0;
59+
60+
GetErrnoMirrorAddressOperation(Thread thread) {
61+
super(VMOperationInfos.get(GetErrnoMirrorAddressOperation.class, "Get ErrnoMirror address", SystemEffect.SAFEPOINT));
62+
this.thread = thread;
63+
}
64+
65+
@Override
66+
protected void operate() {
67+
IsolateThread isolateThread = PlatformThreads.getIsolateThread(thread);
68+
if (isolateThread.isNonNull()) {
69+
this.result = ErrnoMirror.errnoMirror.getAddress(isolateThread).rawValue();
70+
}
71+
}
72+
}
73+
3574
@Alias
3675
native void setPendingException(Throwable t);
3776
}

truffle/mx.truffle/suite.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,9 @@
932932
"dependencies" : [
933933
"TRUFFLE_API",
934934
],
935+
"requires" : [
936+
"jdk.unsupported", # sun.misc.Unsafe
937+
],
935938
"checkstyle" : "com.oracle.truffle.api",
936939
"javaCompliance" : "17+",
937940
"annotationProcessors" : ["TRUFFLE_DSL_PROCESSOR"],

truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4047,19 +4047,14 @@ protected LanguageReference() {
40474047
* node should be provided as parameter if available, otherwise <code>null</code> may be
40484048
* provided. This method is designed to be called safely from compiled code paths. In order
40494049
* to maximize efficiency in compiled code paths, a partial evaluation constant and adopted
4050-
* node should be passed as parameter. If this is the case then the return context will get
4051-
* constant folded in compiled code paths if there is a only single context instance for the
4052-
* enclosing engine or lookup location/node.
4050+
* node should be passed as parameter. If this is the case then the return language will get
4051+
* constant folded in compiled code paths.
40534052
* <p>
4054-
* The current language will not change for {@link RootNode#execute(VirtualFrame)
4055-
* executions} of {@link RootNode roots} of the current language. For roots of other
4056-
* languages, e.g. if invoked through the interoperability protocol, the language might
4057-
* change between consecutive executions. It is recommended to *not* cache values of the
4058-
* language in the AST to reduce footprint. Getting it through a language reference will
4059-
* either constant fold or be very efficient in compiled code paths.
4053+
* The current language is fixed per {@link RootNode}. It is recommended to *not* cache
4054+
* values of the language in the AST to reduce footprint. Getting it through a language
4055+
* reference will either constant fold or be very efficient in compiled code paths.
40604056
* <p>
4061-
* If a context is accessed during {@link TruffleLanguage#createContext(Env) context
4062-
* creation}, on an unknown Thread, or in the language class constructor an
4057+
* If a language is accessed on an unknown Thread, or in the language class constructor an
40634058
* {@link IllegalStateException} is thrown.
40644059
*
40654060
* @see ContextReference for a full usage example
@@ -4166,8 +4161,8 @@ protected ContextReference() {
41664161
* provided. This method is designed to be called safely from compiled code paths. In order
41674162
* to maximize efficiency in compiled code paths, a partial evaluation constant and adopted
41684163
* node should be passed as parameter. If this is the case then the return context will get
4169-
* constant folded in compiled code paths if there is a only single language instance for
4170-
* the enclosing engine or lookup location/node.
4164+
* constant folded in compiled code paths if there is only a single context instance for the
4165+
* enclosing engine or lookup location/node.
41714166
* <p>
41724167
* The current context might vary between {@link RootNode#execute(VirtualFrame) executions}
41734168
* if resources or code is {@link ContextPolicy#SHARED shared} between multiple contexts or

truffle/src/com.oracle.truffle.nfi.backend.panama/src/com/oracle/truffle/nfi/backend/panama/ErrorContext.java

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -61,21 +61,8 @@ public class ErrorContext {
6161
}
6262

6363
@SuppressWarnings("preview") private MemorySegment errnoLocation;
64-
private Integer nativeErrno = null;
6564
final PanamaNFIContext ctx;
6665

67-
public boolean nativeErrnoSet() {
68-
return (nativeErrno != null);
69-
}
70-
71-
public int getNativeErrno() {
72-
return nativeErrno;
73-
}
74-
75-
public void setNativeErrno(int nativeErrno) {
76-
this.nativeErrno = nativeErrno;
77-
}
78-
7966
@SuppressWarnings({"preview", "restricted"})
8067
MemorySegment lookupErrnoLocation() {
8168
try {
@@ -118,12 +105,12 @@ private MemorySegment getErrnoLocation() {
118105
}
119106

120107
@SuppressWarnings("preview")
121-
int getErrno() {
108+
int getNativeErrno() {
122109
return getErrnoLocation().get(ValueLayout.JAVA_INT, 0);
123110
}
124111

125112
@SuppressWarnings("preview")
126-
void setErrno(int newErrno) {
113+
void setNativeErrno(int newErrno) {
127114
getErrnoLocation().set(ValueLayout.JAVA_INT, 0, newErrno);
128115
}
129116

truffle/src/com.oracle.truffle.nfi.backend.panama/src/com/oracle/truffle/nfi/backend/panama/FunctionExecuteNode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ public Object doGeneric(VirtualFrame frame) {
159159
throw silenceException(RuntimeException.class, ex);
160160
}
161161

162-
return signatureInfo.execute(signature, args, address);
162+
return signatureInfo.execute(signature, args, address, this);
163163
}
164164

165165
@SuppressWarnings({"unchecked"})

truffle/src/com.oracle.truffle.nfi.backend.panama/src/com/oracle/truffle/nfi/backend/panama/PanamaClosure.java

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,12 @@
5757
import com.oracle.truffle.api.nodes.ExplodeLoop;
5858
import com.oracle.truffle.api.nodes.Node;
5959
import com.oracle.truffle.api.nodes.RootNode;
60+
import com.oracle.truffle.api.profiles.BranchProfile;
6061
import com.oracle.truffle.nfi.backend.panama.PanamaSignature.CachedSignatureInfo;
6162
import com.oracle.truffle.nfi.backend.panama.ClosureArgumentNode.GetArgumentNode;
6263
import com.oracle.truffle.nfi.backend.panama.ClosureArgumentNode.ConstArgumentNode;
6364
import com.oracle.truffle.nfi.backend.panama.PanamaClosureFactory.CallClosureNodeGen;
65+
import com.oracle.truffle.nfi.backend.spi.NFIState;
6466
import com.oracle.truffle.nfi.backend.spi.types.NativeSimpleType;
6567

6668
import java.lang.foreign.MemorySegment;
@@ -141,11 +143,10 @@ abstract static class PanamaClosureRootNode extends RootNode {
141143
// Object closure(Object receiver, Object[] args)
142144
static final MethodType METHOD_TYPE = MethodType.methodType(Object.class, Object.class, Object[].class);
143145

144-
@Child InteropLibrary interop;
146+
final BranchProfile exceptionProfile = BranchProfile.create();
145147

146148
PanamaClosureRootNode(PanamaNFILanguage language) {
147149
super(language);
148-
this.interop = InteropLibrary.getFactory().createDispatched(3);
149150
}
150151

151152
static final MethodHandle handle_CallTarget_call;
@@ -200,17 +201,7 @@ Object doCall(VirtualFrame frame, Object receiver,
200201
}
201202

202203
try {
203-
ErrorContext ctx = PanamaNFILanguage.get(null).errorContext.get();
204-
int errnoMirror = ctx.getErrno();
205-
if (ctx.nativeErrnoSet()) {
206-
ctx.setErrno(ctx.getNativeErrno());
207-
}
208-
Object result = interop.execute(receiver, args);
209-
210-
ctx.setNativeErrno(ctx.getErrno());
211-
ctx.setErrno(errnoMirror);
212-
213-
return result;
204+
return interop.execute(receiver, args);
214205
} catch (InteropException ex) {
215206
throw CompilerDirectives.shouldNotReachHere(ex);
216207
}
@@ -244,21 +235,30 @@ private StringRetClosureRootNode(PanamaNFILanguage lang, CachedSignatureInfo sig
244235

245236
@Override
246237
public Object execute(VirtualFrame frame) {
238+
// We need to capture native errno as early as possible and set it as late as possible
239+
// to avoid the JVM clobbering it
240+
PanamaNFILanguage language = PanamaNFILanguage.get(this);
241+
NFIState nfiState = language.getNFIState();
242+
ErrorContext ctx = language.errorContext.get();
243+
nfiState.setNFIErrno(ctx.getNativeErrno());
247244
try {
248245
Object ret = callClosure.execute(frame);
249246
if (interopLibrary.isNull(ret)) {
250247
return null;
251248
}
252249
return toJavaRet.execute(ret);
253250
} catch (Throwable t) {
251+
exceptionProfile.enter();
254252
TruffleStackTrace.fillIn(t);
255-
PanamaNFILanguage.get(this).getNFIState().setPendingException(t);
253+
nfiState.setPendingException(t);
256254
try {
257255
return toJavaRet.execute("");
258256
} catch (UnsupportedTypeException ex) {
259257
// toJavaRet expects a string, so this should always work
260258
throw CompilerDirectives.shouldNotReachHere();
261259
}
260+
} finally {
261+
ctx.setNativeErrno(nfiState.getNFIErrno());
262262
}
263263
}
264264
}
@@ -286,11 +286,20 @@ private VoidRetClosureRootNode(PanamaNFILanguage lang, CachedSignatureInfo signa
286286

287287
@Override
288288
public Object execute(VirtualFrame frame) {
289+
// We need to capture native errno as early as possible and set it as late as possible
290+
// to avoid the JVM clobbering it
291+
PanamaNFILanguage language = PanamaNFILanguage.get(this);
292+
NFIState nfiState = language.getNFIState();
293+
ErrorContext ctx = language.errorContext.get();
294+
nfiState.setNFIErrno(ctx.getNativeErrno());
289295
try {
290296
callClosure.execute(frame);
291297
} catch (Throwable t) {
298+
exceptionProfile.enter();
292299
TruffleStackTrace.fillIn(t);
293-
PanamaNFILanguage.get(this).getNFIState().setPendingException(t);
300+
nfiState.setPendingException(t);
301+
} finally {
302+
ctx.setNativeErrno(nfiState.getNFIErrno());
294303
}
295304
return null;
296305
}
@@ -324,21 +333,30 @@ private GenericRetClosureRootNode(PanamaNFILanguage lang, CachedSignatureInfo si
324333

325334
@Override
326335
public Object execute(VirtualFrame frame) {
336+
// We need to capture native errno as early as possible and set it as late as possible
337+
// to avoid the JVM clobbering it
338+
PanamaNFILanguage language = PanamaNFILanguage.get(this);
339+
NFIState nfiState = language.getNFIState();
340+
ErrorContext ctx = language.errorContext.get();
341+
nfiState.setNFIErrno(ctx.getNativeErrno());
327342
try {
328343
Object ret = callClosure.execute(frame);
329344
if (interopLibrary.isNull(ret)) {
330345
return toJavaRet.execute(0);
331346
}
332347
return toJavaRet.execute(ret);
333348
} catch (Throwable t) {
349+
exceptionProfile.enter();
334350
TruffleStackTrace.fillIn(t);
335-
PanamaNFILanguage.get(this).getNFIState().setPendingException(t);
351+
nfiState.setPendingException(t);
336352
try {
337353
return toJavaRet.execute(0);
338354
} catch (UnsupportedTypeException e) {
339355
// we expect 0 to be convertible to every type
340356
throw CompilerDirectives.shouldNotReachHere();
341357
}
358+
} finally {
359+
ctx.setNativeErrno(nfiState.getNFIErrno());
342360
}
343361
}
344362
}

truffle/src/com.oracle.truffle.nfi.backend.panama/src/com/oracle/truffle/nfi/backend/panama/PanamaSignature.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
import com.oracle.truffle.nfi.backend.panama.FunctionExecuteNodeGen.SignatureExecuteNodeGen;
6262
import com.oracle.truffle.nfi.backend.spi.NFIBackendSignatureBuilderLibrary;
6363
import com.oracle.truffle.nfi.backend.spi.NFIBackendSignatureLibrary;
64+
import com.oracle.truffle.nfi.backend.spi.NFIState;
6465
import com.oracle.truffle.nfi.backend.spi.types.NativeSimpleType;
6566
import com.oracle.truffle.nfi.backend.spi.util.ProfiledArrayBuilder;
6667
import com.oracle.truffle.nfi.backend.spi.util.ProfiledArrayBuilder.ArrayBuilderFactory;
@@ -373,20 +374,19 @@ PanamaType getRetType() {
373374
return retType;
374375
}
375376

376-
Object execute(PanamaSignature signature, Object[] args, @SuppressWarnings("preview") MemorySegment segment) {
377+
Object execute(PanamaSignature signature, Object[] args, @SuppressWarnings("preview") MemorySegment segment, Node node) {
377378
assert signature.signatureInfo == this;
378379
CompilerAsserts.partialEvaluationConstant(retType);
379380

380-
ErrorContext ctx = PanamaNFILanguage.get(null).errorContext.get();
381+
PanamaNFILanguage language = PanamaNFILanguage.get(node);
382+
NFIState nfiState = language.getNFIState();
383+
ErrorContext ctx = language.errorContext.get();
381384
try {
382-
int errnoMirror = ctx.getErrno();
383-
if (ctx.nativeErrnoSet()) {
384-
ctx.setErrno(ctx.getNativeErrno());
385-
}
385+
// We need to set and capture native errno as close as possible to the native call
386+
// to avoid the JVM clobbering it
387+
ctx.setNativeErrno(nfiState.getNFIErrno());
386388
Object result = (Object) downcallHandle.invokeExact(segment, args);
387-
388-
ctx.setNativeErrno(ctx.getErrno());
389-
ctx.setErrno(errnoMirror);
389+
nfiState.setNFIErrno(ctx.getNativeErrno());
390390

391391
if (result == null) {
392392
return NativePointer.NULL;

truffle/src/com.oracle.truffle.nfi.backend.spi/src/com/oracle/truffle/nfi/backend/spi/NFIState.java

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,59 @@
4040
*/
4141
package com.oracle.truffle.nfi.backend.spi;
4242

43+
import sun.misc.Unsafe;
44+
45+
import java.lang.reflect.Field;
46+
4347
/**
44-
* Thread-local state of the NFI that is shared between different backends.
48+
* Thread-local state of the NFI that is shared between different backends. All methods should only
49+
* be called from the corresponding thread to which this state belongs (for thread-safety reasons
50+
* and because the SVM NFI backend relies on it by using a FastThreadLocalBytes for the errno
51+
* pointer, which means the errno pointer points inside the IsolateThread), except the constructor
52+
* and {@link #dispose()} which may be called from another thread, as Truffle hooks which call those
53+
* are not guaranteed to be called from the corresponding thread.
4554
*/
4655
public final class NFIState {
4756

57+
private static final Unsafe UNSAFE;
58+
59+
static {
60+
Field unsafeField;
61+
try {
62+
unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
63+
unsafeField.setAccessible(true);
64+
} catch (NoSuchFieldException e) {
65+
throw new RuntimeException(e);
66+
}
67+
68+
try {
69+
UNSAFE = (Unsafe) unsafeField.get(null);
70+
} catch (IllegalAccessException e) {
71+
throw new RuntimeException(e);
72+
}
73+
}
74+
75+
// We cannot just store the errno as an int here, because it must be accessible as an int* in
76+
// native code for Sulong
77+
private final long nfiErrnoAddress;
78+
4879
private Throwable pendingException;
4980

5081
// for faster query from JNI
5182
boolean hasPendingException;
5283

84+
public NFIState(Thread thread) {
85+
this.nfiErrnoAddress = initNfiErrnoAddress(thread);
86+
}
87+
88+
public int getNFIErrno() {
89+
return UNSAFE.getInt(nfiErrnoAddress);
90+
}
91+
92+
public void setNFIErrno(int nfiErrno) {
93+
UNSAFE.putInt(nfiErrnoAddress, nfiErrno);
94+
}
95+
5396
public Throwable getPendingException() {
5497
return pendingException;
5598
}
@@ -64,4 +107,18 @@ public void clearPendingException() {
64107
pendingException = null;
65108
hasPendingException = false;
66109
}
110+
111+
private static long initNfiErrnoAddress(@SuppressWarnings("unused") Thread thread) {
112+
long address = UNSAFE.allocateMemory(Integer.BYTES);
113+
UNSAFE.putInt(address, 0);
114+
return address;
115+
}
116+
117+
private void freeNfiErrnoAddress() {
118+
UNSAFE.freeMemory(this.nfiErrnoAddress);
119+
}
120+
121+
public void dispose() {
122+
freeNfiErrnoAddress();
123+
}
67124
}

0 commit comments

Comments
 (0)