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.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23 package jdk.internal.foreign.abi;
24
25 import jdk.incubator.foreign.FunctionDescriptor;
26 import jdk.incubator.foreign.MemoryAddress;
27 import jdk.incubator.foreign.MemoryHandles;
28 import jdk.incubator.foreign.MemorySegment;
29 import jdk.internal.foreign.MemoryAddressImpl;
30
31 import java.lang.invoke.MethodHandle;
32 import java.lang.invoke.MethodHandles;
33 import java.lang.invoke.MethodType;
34 import java.lang.invoke.VarHandle;
35 import java.nio.ByteOrder;
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.concurrent.ConcurrentHashMap;
40
41 import static sun.security.action.GetBooleanAction.privilegedGetProperty;
42
43 /**
44 * This class implements native call invocation through a so called 'universal adapter'. A universal adapter takes
45 * an array of longs together with a call 'recipe', which is used to move the arguments in the right places as
46 * expected by the system ABI.
47 */
48 public class ProgrammableInvoker {
49 private static final boolean DEBUG =
50 privilegedGetProperty("jdk.internal.foreign.ProgrammableInvoker.DEBUG");
51
52 private static final VarHandle VH_LONG = MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder());
53
54 // Unbound MH for the invoke() method
55 private static final MethodHandle INVOKE_MH;
56
57 private static final Map<ABIDescriptor, Long> adapterStubs = new ConcurrentHashMap<>();
58
59 static {
60 try {
61 INVOKE_MH = MethodHandles.lookup().findVirtual(ProgrammableInvoker.class, "invoke", MethodType.methodType(Object.class, Object[].class));
62 } catch (ReflectiveOperationException e) {
63 throw new RuntimeException(e);
64 }
65 }
66
67 private final ABIDescriptor abi;
68 private final BufferLayout layout;
69 private final long stackArgsBytes;
70
71 private final MethodType type;
72 private final FunctionDescriptor function;
73 private final CallingSequence callingSequence;
74
75 private final MemoryAddress addr;
76 private final long stubAddress;
77
78 public ProgrammableInvoker(ABIDescriptor abi, MemoryAddress addr, CallingSequence callingSequence) {
79 this.abi = abi;
80 this.layout = BufferLayout.of(abi);
81 this.stubAddress = adapterStubs.computeIfAbsent(abi, key -> generateAdapter(key, layout));
82
83 this.addr = addr;
84 this.callingSequence = callingSequence;
85 this.type = callingSequence.methodType();
86 this.function = callingSequence.functionDesc();
87
88 this.stackArgsBytes = callingSequence.moveBindings()
89 .map(Binding.Move::storage)
90 .filter(s -> abi.arch.isStackType(s.type()))
91 .count()
92 * abi.arch.typeSize(abi.arch.stackType());
93 }
94
95 public MethodHandle getBoundMethodHandle() {
96 return INVOKE_MH.bindTo(this).asCollector(Object[].class, type.parameterCount()).asType(type);
97 }
98
99 Object invoke(Object[] args) {
100 List<MemorySegment> tempBuffers = new ArrayList<>();
101 try (MemorySegment argBuffer = MemorySegment.allocateNative(layout.size, 64)) {
102 MemoryAddress argsPtr = argBuffer.baseAddress();
103 MemoryAddress stackArgs;
104 if (stackArgsBytes > 0) {
105 MemorySegment stackArgsSeg = MemorySegment.allocateNative(stackArgsBytes, 8);
106 tempBuffers.add(stackArgsSeg);
107 stackArgs = stackArgsSeg.baseAddress();
108 } else {
109 stackArgs = MemoryAddressImpl.NULL;
110 }
111
112 VH_LONG.set(argsPtr.addOffset(layout.arguments_next_pc), addr.toRawLongValue());
113 VH_LONG.set(argsPtr.addOffset(layout.stack_args_bytes), stackArgsBytes);
114 VH_LONG.set(argsPtr.addOffset(layout.stack_args), stackArgs.toRawLongValue());
115
116 for (int i = 0; i < args.length; i++) {
117 Object arg = args[i];
118 jdk.internal.foreign.abi.BindingInterpreter.unbox(arg, callingSequence.argumentBindings(i),
119 s -> {
120 if (abi.arch.isStackType(s.type())) {
121 return stackArgs.addOffset(s.index() * abi.arch.typeSize(abi.arch.stackType()));
122 }
123 return argsPtr.addOffset(layout.argOffset(s));
124 }, tempBuffers);
125 }
126
127 if (DEBUG) {
128 System.err.println("Buffer state before:");
129 layout.dump(abi.arch, argsPtr, System.err);
130 }
131
132 invokeNative(stubAddress, argsPtr.toRawLongValue());
133
134 if (DEBUG) {
135 System.err.println("Buffer state after:");
136 layout.dump(abi.arch, argsPtr, System.err);
137 }
138
139 return function.returnLayout().isEmpty()
140 ? null
141 : jdk.internal.foreign.abi.BindingInterpreter.box(callingSequence.returnBindings(),
142 s -> argsPtr.addOffset(layout.retOffset(s))); // buffers are leaked
143 } finally {
144 tempBuffers.forEach(MemorySegment::close);
145 }
146 }
147
148 //natives
149
150 static native void invokeNative(long adapterStub, long buff);
151 static native long generateAdapter(ABIDescriptor abi, BufferLayout layout);
152
153 private static native void registerNatives();
154 static {
155 registerNatives();
156 }
157 }
158
|
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.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23 package jdk.internal.foreign.abi;
24
25 import jdk.incubator.foreign.MemoryAddress;
26 import jdk.incubator.foreign.MemoryHandles;
27 import jdk.incubator.foreign.MemorySegment;
28 import jdk.incubator.foreign.NativeScope;
29 import jdk.internal.foreign.MemoryAddressImpl;
30 import jdk.internal.foreign.Utils;
31
32 import java.lang.invoke.MethodHandle;
33 import java.lang.invoke.MethodHandles;
34 import java.lang.invoke.MethodType;
35 import java.lang.invoke.VarHandle;
36 import java.nio.ByteOrder;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.concurrent.ConcurrentHashMap;
42 import java.util.stream.Collectors;
43 import java.util.stream.IntStream;
44
45 import static java.lang.invoke.MethodHandles.collectArguments;
46 import static java.lang.invoke.MethodHandles.dropArguments;
47 import static java.lang.invoke.MethodHandles.empty;
48 import static java.lang.invoke.MethodHandles.filterArguments;
49 import static java.lang.invoke.MethodHandles.identity;
50 import static java.lang.invoke.MethodHandles.insertArguments;
51 import static java.lang.invoke.MethodHandles.permuteArguments;
52 import static java.lang.invoke.MethodHandles.tryFinally;
53 import static java.lang.invoke.MethodType.methodType;
54 import static sun.security.action.GetBooleanAction.privilegedGetProperty;
55
56 /**
57 * This class implements native call invocation through a so called 'universal adapter'. A universal adapter takes
58 * an array of longs together with a call 'recipe', which is used to move the arguments in the right places as
59 * expected by the system ABI.
60 */
61 public class ProgrammableInvoker {
62 private static final boolean DEBUG =
63 privilegedGetProperty("jdk.internal.foreign.ProgrammableInvoker.DEBUG");
64 private static final boolean NO_SPEC =
65 privilegedGetProperty("jdk.internal.foreign.ProgrammableInvoker.NO_SPEC");
66
67 private static final VarHandle VH_LONG = MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder());
68
69 private static final MethodHandle MH_INVOKE_MOVES;
70 private static final MethodHandle MH_INVOKE_INTERP_BINDINGS;
71
72 private static final MethodHandle MH_MAKE_ALLOCATOR;
73 private static final MethodHandle MH_CLOSE_ALLOCATOR;
74
75 private static final Map<ABIDescriptor, Long> adapterStubs = new ConcurrentHashMap<>();
76
77 static {
78 try {
79 MethodHandles.Lookup lookup = MethodHandles.lookup();
80 MH_INVOKE_MOVES = lookup.findVirtual(ProgrammableInvoker.class, "invokeMoves",
81 methodType(Object.class, Object[].class, Binding.Move[].class, Binding.Move[].class));
82 MH_INVOKE_INTERP_BINDINGS = lookup.findVirtual(ProgrammableInvoker.class, "invokeInterpBindings",
83 methodType(Object.class, Object[].class, MethodHandle.class, Map.class, Map.class));
84 MH_MAKE_ALLOCATOR = lookup.findStatic(NativeScope.class, "boundedScope",
85 methodType(NativeScope.class, long.class));
86 MH_CLOSE_ALLOCATOR = lookup.findVirtual(NativeScope.class, "close",
87 methodType(void.class));
88 } catch (ReflectiveOperationException e) {
89 throw new RuntimeException(e);
90 }
91 }
92
93 private final ABIDescriptor abi;
94 private final BufferLayout layout;
95 private final long stackArgsBytes;
96
97 private final CallingSequence callingSequence;
98
99 private final MemoryAddress addr;
100 private final long stubAddress;
101
102 private final long bufferCopySize;
103
104 public ProgrammableInvoker(ABIDescriptor abi, MemoryAddress addr, CallingSequence callingSequence) {
105 this.abi = abi;
106 this.layout = BufferLayout.of(abi);
107 this.stubAddress = adapterStubs.computeIfAbsent(abi, key -> generateAdapter(key, layout));
108
109 this.addr = addr;
110 this.callingSequence = callingSequence;
111
112 this.stackArgsBytes = callingSequence.argMoveBindings()
113 .map(Binding.Move::storage)
114 .filter(s -> abi.arch.isStackType(s.type()))
115 .count()
116 * abi.arch.typeSize(abi.arch.stackType());
117
118 this.bufferCopySize = bufferCopySize(callingSequence);
119 }
120
121 private static long bufferCopySize(CallingSequence callingSequence) {
122 // FIXME: > 16 bytes alignment might need extra space since the
123 // starting address of the allocator might be un-aligned.
124 long size = 0;
125 for (int i = 0; i < callingSequence.argumentCount(); i++) {
126 List<Binding> bindings = callingSequence.argumentBindings(i);
127 for (Binding b : bindings) {
128 if (b instanceof Binding.Copy) {
129 Binding.Copy c = (Binding.Copy) b;
130 size = Utils.alignUp(size, c.alignment());
131 size += c.size();
132 }
133 }
134 }
135 return size;
136 }
137
138 public MethodHandle getBoundMethodHandle() {
139 Binding.Move[] argMoves = callingSequence.argMoveBindings().toArray(Binding.Move[]::new);
140 Class<?>[] argMoveTypes = Arrays.stream(argMoves).map(Binding.Move::type).toArray(Class<?>[]::new);
141
142 Binding.Move[] retMoves = callingSequence.retMoveBindings().toArray(Binding.Move[]::new);
143 Class<?> returnType = retMoves.length == 0
144 ? void.class
145 : retMoves.length == 1
146 ? retMoves[0].type()
147 : Object[].class;
148
149 MethodType leafType = methodType(returnType, argMoveTypes);
150
151 MethodHandle handle = insertArguments(MH_INVOKE_MOVES.bindTo(this), 1, argMoves, retMoves)
152 .asCollector(Object[].class, leafType.parameterCount())
153 .asType(leafType);
154
155 if (NO_SPEC || retMoves.length > 1) {
156 Map<VMStorage, Integer> argIndexMap = indexMap(argMoves);
157 Map<VMStorage, Integer> retIndexMap = indexMap(retMoves);
158
159 handle = insertArguments(MH_INVOKE_INTERP_BINDINGS.bindTo(this), 1, handle, argIndexMap, retIndexMap);
160 handle = handle.asCollector(Object[].class, callingSequence.methodType().parameterCount())
161 .asType(callingSequence.methodType());
162 } else {
163 handle = specialize(handle);
164 }
165
166 return handle;
167 }
168
169 private MethodHandle specialize(MethodHandle leafHandle) {
170 MethodType highLevelType = callingSequence.methodType();
171 MethodType leafType = leafHandle.type();
172
173 MethodHandle specializedHandle = leafHandle; // initial
174
175 int insertPos = -1;
176 if (bufferCopySize > 0) {
177 specializedHandle = dropArguments(specializedHandle, 0, NativeScope.class);
178 insertPos++;
179 }
180 for (int i = 0; i < highLevelType.parameterCount(); i++) {
181 List<Binding> bindings = callingSequence.argumentBindings(i);
182 insertPos += bindings.stream().filter(Binding.Move.class::isInstance).count() + 1;
183 // We interpret the bindings in reverse since we have to construct a MethodHandle from the bottom up
184 for (int j = bindings.size() - 1; j >= 0; j--) {
185 Binding binding = bindings.get(j);
186 if (binding.tag() == Binding.Tag.MOVE) {
187 insertPos--;
188 } else {
189 specializedHandle = binding.specializeUnbox(specializedHandle, insertPos);
190 }
191 }
192 }
193
194 if (highLevelType.returnType() != void.class) {
195 MethodHandle returnFilter = identity(highLevelType.returnType());
196 List<Binding> bindings = callingSequence.returnBindings();
197 for (int j = bindings.size() - 1; j >= 0; j--) {
198 Binding binding = bindings.get(j);
199 returnFilter = binding.specializeBox(returnFilter);
200 }
201 specializedHandle = MethodHandles.filterReturnValue(specializedHandle, returnFilter);
202 }
203
204 if (bufferCopySize > 0) {
205 // insert try-finally to close the NativeScope used for Binding.Copy
206 MethodHandle closer = leafType.returnType() == void.class
207 // (Throwable, NativeScope) -> void
208 ? collectArguments(empty(methodType(void.class, Throwable.class)), 1, MH_CLOSE_ALLOCATOR)
209 // (Throwable, V, NativeScope) -> V
210 : collectArguments(dropArguments(identity(specializedHandle.type().returnType()), 0, Throwable.class),
211 2, MH_CLOSE_ALLOCATOR);
212 specializedHandle = tryFinally(specializedHandle, closer);
213 specializedHandle = collectArguments(specializedHandle, 0, insertArguments(MH_MAKE_ALLOCATOR, 0, bufferCopySize));
214 }
215 return specializedHandle;
216 }
217
218 private Map<VMStorage, Integer> indexMap(Binding.Move[] moves) {
219 return IntStream.range(0, moves.length)
220 .boxed()
221 .collect(Collectors.toMap(i -> moves[i].storage(), i -> i));
222 }
223
224 /**
225 * Does a native invocation by moving primitive values from the arg array into an intermediate buffer
226 * and calling the assembly stub that forwards arguments from the buffer to the target function
227 *
228 * @param args an array of primitive values to be copied in to the buffer
229 * @param argBindings Binding.Move values describing how arguments should be copied
230 * @param returnBindings Binding.Move values describing how return values should be copied
231 * @return null, a single primitive value, or an Object[] of primitive values
232 */
233 Object invokeMoves(Object[] args, Binding.Move[] argBindings, Binding.Move[] returnBindings) {
234 MemorySegment stackArgsSeg = null;
235 try (MemorySegment argBuffer = MemorySegment.allocateNative(layout.size, 64)) {
236 MemoryAddress argsPtr = argBuffer.baseAddress();
237 MemoryAddress stackArgs;
238 if (stackArgsBytes > 0) {
239 stackArgsSeg = MemorySegment.allocateNative(stackArgsBytes, 8);
240 stackArgs = stackArgsSeg.baseAddress();
241 } else {
242 stackArgs = MemoryAddressImpl.NULL;
243 }
244
245 VH_LONG.set(argsPtr.addOffset(layout.arguments_next_pc), addr.toRawLongValue());
246 VH_LONG.set(argsPtr.addOffset(layout.stack_args_bytes), stackArgsBytes);
247 VH_LONG.set(argsPtr.addOffset(layout.stack_args), stackArgs.toRawLongValue());
248
249 for (int i = 0; i < argBindings.length; i++) {
250 Binding.Move binding = argBindings[i];
251 VMStorage storage = binding.storage();
252 MemoryAddress ptr = abi.arch.isStackType(storage.type())
253 ? stackArgs.addOffset(storage.index() * abi.arch.typeSize(abi.arch.stackType()))
254 : argsPtr.addOffset(layout.argOffset(storage));
255 SharedUtils.writeOverSized(ptr, binding.type(), args[i]);
256 }
257
258 if (DEBUG) {
259 System.err.println("Buffer state before:");
260 layout.dump(abi.arch, argsPtr, System.err);
261 }
262
263 invokeNative(stubAddress, argsPtr.toRawLongValue());
264
265 if (DEBUG) {
266 System.err.println("Buffer state after:");
267 layout.dump(abi.arch, argsPtr, System.err);
268 }
269
270 if (returnBindings.length == 0) {
271 return null;
272 } else if (returnBindings.length == 1) {
273 Binding.Move move = returnBindings[0];
274 VMStorage storage = move.storage();
275 return SharedUtils.read(argsPtr.addOffset(layout.retOffset(storage)), move.type());
276 } else { // length > 1
277 Object[] returns = new Object[returnBindings.length];
278 for (int i = 0; i < returnBindings.length; i++) {
279 Binding.Move move = returnBindings[i];
280 VMStorage storage = move.storage();
281 returns[i] = SharedUtils.read(argsPtr.addOffset(layout.retOffset(storage)), move.type());
282 }
283 return returns;
284 }
285 } finally {
286 if (stackArgsSeg != null) {
287 stackArgsSeg.close();
288 }
289 }
290 }
291
292 Object invokeInterpBindings(Object[] args, MethodHandle leaf,
293 Map<VMStorage, Integer> argIndexMap,
294 Map<VMStorage, Integer> retIndexMap) throws Throwable {
295 NativeScope scope = bufferCopySize != 0 ? NativeScope.boundedScope(bufferCopySize) : null;
296 try {
297 // do argument processing, get Object[] as result
298 Object[] moves = new Object[leaf.type().parameterCount()];
299 for (int i = 0; i < args.length; i++) {
300 Object arg = args[i];
301 BindingInterpreter.unbox(arg, callingSequence.argumentBindings(i),
302 (storage, type, value) -> {
303 moves[argIndexMap.get(storage)] = value;
304 }, scope);
305 }
306
307 // call leaf
308 Object o = leaf.invokeWithArguments(moves);
309
310 // return value processing
311 if (o == null) {
312 return null;
313 } else if (o instanceof Object[]) {
314 Object[] oArr = (Object[]) o;
315 return BindingInterpreter.box(callingSequence.returnBindings(),
316 (storage, type) -> oArr[retIndexMap.get(storage)]);
317 } else {
318 return BindingInterpreter.box(callingSequence.returnBindings(), (storage, type) -> o);
319 }
320 } finally {
321 if (scope != null) {
322 scope.close();
323 }
324 }
325 }
326
327 //natives
328
329 static native void invokeNative(long adapterStub, long buff);
330 static native long generateAdapter(ABIDescriptor abi, BufferLayout layout);
331
332 private static native void registerNatives();
333 static {
334 registerNatives();
335 }
336 }
337
|