Skip to content

Commit e37732c

Browse files
committed
[GR-66310] Add support for arrays to Panama NFI backend
PullRequest: graal/21673
2 parents ea13540 + 69bd7c1 commit e37732c

File tree

10 files changed

+447
-124
lines changed

10 files changed

+447
-124
lines changed

truffle/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ This changelog summarizes major changes between Truffle versions relevant to lan
88
* GR-67702: Specialization DSL: For nodes annotated with `@GenerateInline`, inlining warnings emitted for `@Cached SomeNode helper` expressions are now suppressed if the helper node class is explicitly annotated with `@GenerateInline(false)`, or is not a DSL node. This avoids unnecessary warnings if inlining for a node was explicitly disabled, and makes it possible to remove `@Cached(inline = false)` in most cases.
99
* GR-68165: `DynamicObjectLibrary#setDynamicType` transitions are now weak, like `DynamicObjectLibrary#putConstant` transitions.
1010
* GR-67821: `TruffleLanguage.Env#createSystemThread` is now allowed to be be called from a system thread now without an explicitly entered context.
11+
* GR-67702: Specialization DSL: For nodes annotated with `@GenerateInline`, inlining warnings emitted for `@Cached` expressions are now suppressed if the inlined node is explicitly annotated with `@GenerateInline(false)`. This avoids unnecessary warnings if inlining for a node was explicitly disabled.
12+
* GR-66310: Added support for passing arrays of primitive types to native code through the Truffle NFI Panama backend.
1113

1214
## Version 25.0
1315
* GR-31495 Added ability to specify language and instrument specific options using `Source.Builder.option(String, String)`. Languages may describe available source options by implementing `TruffleLanguage.getSourceOptionDescriptors()` and `TruffleInstrument.getSourceOptionDescriptors()` respectively.

truffle/docs/NFI.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ Depending on the configuration of components you are running, available backends
194194
### Panama backend
195195

196196
The Panama backend uses the Foreign Function and Memory APIs introduced by [project Panama](https://openjdk.org/projects/panama/).
197-
This backend only supports a subset of all the types. Specifically, it does not support `STRING`, `OBJECT`, `ENV`, `FP80` or array types.
197+
This backend only supports a subset of all the types. Specifically, it does not support `STRING`, `OBJECT`, `ENV`, or `FP80`.
198198
Although less feature-complete, the backend is typically more performant.
199199
It is available starting from JDK 22.
200200

truffle/src/com.oracle.truffle.nfi.backend.panama.jdk22/src/com/oracle/truffle/nfi/backend/panama/ArgumentNode.java

Lines changed: 57 additions & 18 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
* The Universal Permissive License (UPL), Version 1.0
@@ -40,9 +40,15 @@
4040
*/
4141
package com.oracle.truffle.nfi.backend.panama;
4242

43+
import java.lang.foreign.Arena;
44+
import java.lang.foreign.MemorySegment;
45+
import java.lang.foreign.ValueLayout;
46+
import java.lang.reflect.Array;
47+
4348
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
4449
import com.oracle.truffle.api.dsl.Bind;
4550
import com.oracle.truffle.api.dsl.Cached;
51+
import com.oracle.truffle.api.dsl.Fallback;
4652
import com.oracle.truffle.api.dsl.Specialization;
4753
import com.oracle.truffle.api.interop.InteropLibrary;
4854
import com.oracle.truffle.api.interop.UnsupportedMessageException;
@@ -51,17 +57,14 @@
5157
import com.oracle.truffle.api.nodes.Node;
5258
import com.oracle.truffle.api.profiles.InlinedBranchProfile;
5359

54-
import java.lang.foreign.Arena;
55-
import java.lang.foreign.MemorySegment;
56-
5760
abstract class ArgumentNode extends Node {
5861
final PanamaType type;
5962

6063
ArgumentNode(PanamaType type) {
6164
this.type = type;
6265
}
6366

64-
abstract Object execute(Object value) throws UnsupportedTypeException;
67+
abstract Object execute(Arena arena, Object value) throws UnsupportedTypeException;
6568

6669
abstract static class ToVOIDNode extends ArgumentNode {
6770

@@ -70,7 +73,7 @@ abstract static class ToVOIDNode extends ArgumentNode {
7073
}
7174

7275
@Specialization
73-
Object doConvert(@SuppressWarnings("unused") Object value) {
76+
Object doConvert(@SuppressWarnings("unused") Arena arena, @SuppressWarnings("unused") Object value) {
7477
return null;
7578
}
7679
}
@@ -82,7 +85,7 @@ abstract static class ToINT8Node extends ArgumentNode {
8285
}
8386

8487
@Specialization(limit = "3")
85-
byte doConvert(Object value,
88+
byte doConvert(@SuppressWarnings("unused") Arena arena, Object value,
8689
@CachedLibrary("value") InteropLibrary interop) throws UnsupportedTypeException {
8790
try {
8891
return interop.asByte(value);
@@ -99,7 +102,7 @@ abstract static class ToINT16Node extends ArgumentNode {
99102
}
100103

101104
@Specialization(limit = "3")
102-
short doConvert(Object value,
105+
short doConvert(@SuppressWarnings("unused") Arena arena, Object value,
103106
@CachedLibrary("value") InteropLibrary interop) throws UnsupportedTypeException {
104107
try {
105108
return interop.asShort(value);
@@ -116,7 +119,7 @@ abstract static class ToINT32Node extends ArgumentNode {
116119
}
117120

118121
@Specialization(limit = "3")
119-
int doConvert(Object value,
122+
int doConvert(@SuppressWarnings("unused") Arena arena, Object value,
120123
@CachedLibrary("value") InteropLibrary interop) throws UnsupportedTypeException {
121124
try {
122125
return interop.asInt(value);
@@ -133,7 +136,7 @@ abstract static class ToINT64Node extends ArgumentNode {
133136
}
134137

135138
@Specialization(limit = "3")
136-
long doConvert(Object value,
139+
long doConvert(@SuppressWarnings("unused") Arena arena, Object value,
137140
@CachedLibrary("value") InteropLibrary interop) throws UnsupportedTypeException {
138141
try {
139142
return interop.asLong(value);
@@ -145,24 +148,26 @@ long doConvert(Object value,
145148

146149
abstract static class ToPointerNode extends ArgumentNode {
147150

151+
abstract long executeLong(Arena arena, Object value) throws UnsupportedTypeException;
152+
148153
ToPointerNode(PanamaType type) {
149154
super(type);
150155
}
151156

152157
@Specialization(limit = "3", guards = "interop.isPointer(arg)", rewriteOn = UnsupportedMessageException.class)
153-
long putPointer(Object arg,
158+
long putPointer(@SuppressWarnings("unused") Arena arena, Object arg,
154159
@CachedLibrary("arg") InteropLibrary interop) throws UnsupportedMessageException {
155160
return interop.asPointer(arg);
156161
}
157162

158163
@Specialization(limit = "3", guards = {"!interop.isPointer(arg)", "interop.isNull(arg)"})
159-
long putNull(@SuppressWarnings("unused") Object arg,
164+
long putNull(@SuppressWarnings("unused") Arena arena, @SuppressWarnings("unused") Object arg,
160165
@SuppressWarnings("unused") @CachedLibrary("arg") InteropLibrary interop) {
161166
return NativePointer.NULL.asPointer();
162167
}
163168

164169
@Specialization(limit = "3", replaces = {"putPointer", "putNull"})
165-
static long putGeneric(Object arg,
170+
static long putGeneric(@SuppressWarnings("unused") Arena arena, Object arg,
166171
@Bind Node node,
167172
@CachedLibrary("arg") InteropLibrary interop,
168173
@Cached InlinedBranchProfile exception) throws UnsupportedTypeException {
@@ -199,7 +204,7 @@ abstract static class ToFLOATNode extends ArgumentNode {
199204
}
200205

201206
@Specialization(limit = "3")
202-
float doConvert(Object value,
207+
float doConvert(@SuppressWarnings("unused") Arena arena, Object value,
203208
@CachedLibrary("value") InteropLibrary interop) throws UnsupportedTypeException {
204209
try {
205210
return interop.asFloat(value);
@@ -216,7 +221,7 @@ abstract static class ToDOUBLENode extends ArgumentNode {
216221
}
217222

218223
@Specialization(limit = "3")
219-
double doConvert(Object value,
224+
double doConvert(@SuppressWarnings("unused") Arena arena, Object value,
220225
@CachedLibrary("value") InteropLibrary interop) throws UnsupportedTypeException {
221226
try {
222227
return interop.asDouble(value);
@@ -233,11 +238,10 @@ abstract static class ToSTRINGNode extends ArgumentNode {
233238
}
234239

235240
@Specialization(limit = "3")
236-
Object doConvert(Object value,
241+
Object doConvert(@SuppressWarnings("unused") Arena arena, Object value,
237242
@CachedLibrary("value") InteropLibrary interop) throws UnsupportedTypeException {
238-
PanamaNFIContext ctx = PanamaNFIContext.get(this);
239243
try {
240-
return allocateFrom(ctx.getContextArena(), interop.asString(value));
244+
return allocateFrom(arena, interop.asString(value));
241245
} catch (UnsupportedMessageException ex) {
242246
throw UnsupportedTypeException.create(new Object[]{value});
243247
}
@@ -248,4 +252,39 @@ private static MemorySegment allocateFrom(Arena arena, String str) {
248252
return arena.allocateFrom(str);
249253
}
250254
}
255+
256+
abstract static class ToArrayNode extends ArgumentNode {
257+
258+
@Child GetArrayElementLayoutNode getArrayElementLayoutNode;
259+
260+
ToArrayNode(PanamaType type) {
261+
super(type);
262+
getArrayElementLayoutNode = GetArrayElementLayoutNode.create(type.type);
263+
}
264+
265+
@Specialization(guards = "elementLayout != null")
266+
MemorySegment doArray(Arena arena, Object value,
267+
@Bind("getArrayElementLayoutNode.execute(value)") ValueLayout elementLayout) {
268+
return doCopy(arena, value, elementLayout);
269+
}
270+
271+
@Fallback
272+
MemorySegment doInteropObject(@SuppressWarnings("unused") Arena arena, Object value,
273+
@Cached(parameters = "type") ToPointerNode toPointer) throws UnsupportedTypeException {
274+
return wrapPointer(toPointer.executeLong(arena, value));
275+
}
276+
277+
@TruffleBoundary(allowInlining = true)
278+
private static MemorySegment doCopy(Arena arena, Object array, ValueLayout elementLayout) {
279+
int arrayLength = Array.getLength(array);
280+
MemorySegment dst = arena.allocate(elementLayout, arrayLength);
281+
MemorySegment.copy(array, 0, dst, elementLayout, 0, arrayLength);
282+
return dst;
283+
}
284+
285+
@TruffleBoundary(allowInlining = true)
286+
private static MemorySegment wrapPointer(long ptr) {
287+
return MemorySegment.ofAddress(ptr);
288+
}
289+
}
251290
}

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

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -40,6 +40,7 @@
4040
*/
4141
package com.oracle.truffle.nfi.backend.panama;
4242

43+
import java.lang.foreign.Arena;
4344
import java.lang.foreign.MemorySegment;
4445
import java.lang.ref.Reference;
4546

@@ -57,7 +58,6 @@
5758
import com.oracle.truffle.api.nodes.IndirectCallNode;
5859
import com.oracle.truffle.api.nodes.Node;
5960
import com.oracle.truffle.api.nodes.RootNode;
60-
6161
import com.oracle.truffle.nfi.backend.panama.PanamaSignature.CachedSignatureInfo;
6262

6363
@GenerateUncached
@@ -108,15 +108,28 @@ abstract static class SignatureExecuteNode extends RootNode {
108108

109109
final CachedSignatureInfo signatureInfo;
110110
@Children ArgumentNode[] argNodes;
111+
@Children PostCallArgumentNode[] postCallArgNodes;
112+
final boolean needsArena;
111113

112114
SignatureExecuteNode(PanamaNFILanguage language, CachedSignatureInfo signatureInfo) {
113115
super(language);
114116
this.signatureInfo = signatureInfo;
115117

116118
PanamaType[] argTypes = signatureInfo.getArgTypes();
117119
this.argNodes = new ArgumentNode[argTypes.length];
120+
boolean postCall = false;
121+
boolean arenaNeeded = false;
118122
for (int i = 0; i < argTypes.length; i++) {
119123
argNodes[i] = argTypes[i].createArgumentNode();
124+
postCall |= argTypes[i].needsPostCallProcessing();
125+
arenaNeeded |= argTypes[i].needsArena();
126+
}
127+
this.needsArena = arenaNeeded;
128+
if (postCall) {
129+
this.postCallArgNodes = new PostCallArgumentNode[argNodes.length];
130+
for (int i = 0; i < argNodes.length; i++) {
131+
postCallArgNodes[i] = argTypes[i].createPostCallArgumentNode();
132+
}
120133
}
121134
}
122135

@@ -146,18 +159,36 @@ public Object doGeneric(VirtualFrame frame) {
146159
throw silenceException(RuntimeException.class, ArityException.create(argNodes.length, argNodes.length, args.length));
147160
}
148161

162+
Object[] convertedArgs = postCallArgNodes == null ? args : new Object[args.length];
163+
Arena arena = needsArena ? Arena.ofConfined() : null;
149164
try {
150-
PanamaType[] types = signatureInfo.getArgTypes();
151-
assert argNodes.length == types.length;
165+
try {
166+
PanamaType[] types = signatureInfo.getArgTypes();
167+
assert argNodes.length == types.length;
168+
169+
for (int i = 0; i < argNodes.length; i++) {
170+
convertedArgs[i] = argNodes[i].execute(arena, args[i]);
171+
}
172+
} catch (UnsupportedTypeException ex) {
173+
throw silenceException(RuntimeException.class, ex);
174+
}
175+
try {
176+
return signatureInfo.execute(signature, convertedArgs, address, this);
177+
} finally {
178+
if (postCallArgNodes != null) {
179+
for (int i = 0; i < postCallArgNodes.length; i++) {
180+
if (postCallArgNodes[i] != null) {
181+
postCallArgNodes[i].execute(args[i], convertedArgs[i]);
182+
}
183+
}
184+
}
185+
}
152186

153-
for (int i = 0; i < argNodes.length; i++) {
154-
args[i] = argNodes[i].execute(args[i]);
187+
} finally {
188+
if (needsArena) {
189+
arena.close();
155190
}
156-
} catch (UnsupportedTypeException ex) {
157-
throw silenceException(RuntimeException.class, ex);
158191
}
159-
160-
return signatureInfo.execute(signature, args, address, this);
161192
}
162193

163194
@SuppressWarnings({"unchecked", "unused"})

0 commit comments

Comments
 (0)