1 /*
  2  * Copyright (c) 2020, 2021, 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 jdk.internal.foreign.abi;
 26 
 27 import jdk.incubator.foreign.Addressable;

 28 import jdk.incubator.foreign.MemorySegment;
 29 import jdk.incubator.foreign.NativeSymbol;
 30 import jdk.incubator.foreign.ResourceScope;
 31 import jdk.incubator.foreign.SegmentAllocator;
 32 import jdk.incubator.foreign.ValueLayout;
 33 import jdk.internal.access.JavaLangInvokeAccess;
 34 import jdk.internal.access.SharedSecrets;
 35 import jdk.internal.invoke.NativeEntryPoint;
 36 import jdk.internal.invoke.VMStorageProxy;
 37 import sun.security.action.GetPropertyAction;
 38 
 39 import java.lang.invoke.MethodHandle;
 40 import java.lang.invoke.MethodHandles;
 41 import java.lang.invoke.MethodType;
 42 import java.lang.invoke.VarHandle;

 43 import java.util.Arrays;
 44 import java.util.List;
 45 import java.util.Map;
 46 import java.util.concurrent.ConcurrentHashMap;
 47 import java.util.stream.Stream;
 48 
 49 import static java.lang.invoke.MethodHandles.collectArguments;
 50 import static java.lang.invoke.MethodHandles.dropArguments;
 51 import static java.lang.invoke.MethodHandles.filterArguments;
 52 import static java.lang.invoke.MethodHandles.identity;
 53 import static java.lang.invoke.MethodHandles.insertArguments;
 54 import static java.lang.invoke.MethodType.methodType;
 55 import static sun.security.action.GetBooleanAction.privilegedGetProperty;
 56 
 57 /**
 58  * This class implements native call invocation through a so called 'universal adapter'. A universal adapter takes
 59  * an array of longs together with a call 'recipe', which is used to move the arguments in the right places as
 60  * expected by the system ABI.
 61  */
 62 public class ProgrammableInvoker {
 63     private static final boolean DEBUG =
 64         privilegedGetProperty("jdk.internal.foreign.ProgrammableInvoker.DEBUG");
 65     private static final boolean USE_SPEC = Boolean.parseBoolean(
 66         GetPropertyAction.privilegedGetProperty("jdk.internal.foreign.ProgrammableInvoker.USE_SPEC", "true"));
 67     private static final boolean USE_INTRINSICS = Boolean.parseBoolean(
 68         GetPropertyAction.privilegedGetProperty("jdk.internal.foreign.ProgrammableInvoker.USE_INTRINSICS", "true"));
 69 
 70     private static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess();
 71 
 72     private static final VarHandle VH_LONG = ValueLayout.JAVA_LONG.varHandle();
 73 
 74     private static final MethodHandle MH_INVOKE_MOVES;
 75     private static final MethodHandle MH_INVOKE_INTERP_BINDINGS;
 76     private static final MethodHandle MH_ADDR_TO_LONG;
 77     private static final MethodHandle MH_WRAP_ALLOCATOR;
 78 
 79     private static final Map<ABIDescriptor, Long> adapterStubs = new ConcurrentHashMap<>();
 80 
 81     private static final MethodHandle EMPTY_OBJECT_ARRAY_HANDLE = MethodHandles.constant(Object[].class, new Object[0]);
 82 
 83     static {
 84         try {
 85             MethodHandles.Lookup lookup = MethodHandles.lookup();
 86             MH_INVOKE_MOVES = lookup.findVirtual(ProgrammableInvoker.class, "invokeMoves",
 87                     methodType(Object.class, long.class, Object[].class, Binding.VMStore[].class, Binding.VMLoad[].class));
 88             MH_INVOKE_INTERP_BINDINGS = lookup.findVirtual(ProgrammableInvoker.class, "invokeInterpBindings",
 89                     methodType(Object.class, NativeSymbol.class, SegmentAllocator.class, Object[].class, MethodHandle.class, Map.class, Map.class));
 90             MH_WRAP_ALLOCATOR = lookup.findStatic(Binding.Context.class, "ofAllocator",
 91                     methodType(Binding.Context.class, SegmentAllocator.class));
 92             MH_ADDR_TO_LONG = lookup.findStatic(ProgrammableInvoker.class, "unboxTargetAddress", methodType(long.class, NativeSymbol.class));



 93         } catch (ReflectiveOperationException e) {
 94             throw new RuntimeException(e);
 95         }
 96     }
 97 
 98     private final ABIDescriptor abi;
 99     private final BufferLayout layout;
100     private final long stackArgsBytes;
101 
102     private final CallingSequence callingSequence;
103 
104     private final long stubAddress;
105 
106     private final long bufferCopySize;
107 
108     public ProgrammableInvoker(ABIDescriptor abi, CallingSequence callingSequence) {
109         this.abi = abi;
110         this.layout = BufferLayout.of(abi);
111         this.stubAddress = adapterStubs.computeIfAbsent(abi, key -> generateAdapter(key, layout));
112 
113         this.callingSequence = callingSequence;
114 
115         this.stackArgsBytes = argMoveBindingsStream(callingSequence)
116                 .map(Binding.VMStore::storage)
117                 .filter(s -> abi.arch.isStackType(s.type()))
118                 .count()
119                 * abi.arch.typeSize(abi.arch.stackType());
120 
121         this.bufferCopySize = SharedUtils.bufferCopySize(callingSequence);
122     }
123 
124     public MethodHandle getBoundMethodHandle() {
125         Binding.VMStore[] argMoves = argMoveBindingsStream(callingSequence).toArray(Binding.VMStore[]::new);
126         Class<?>[] argMoveTypes = Arrays.stream(argMoves).map(Binding.VMStore::type).toArray(Class<?>[]::new);
127 
128         Binding.VMLoad[] retMoves = retMoveBindings(callingSequence);
129         Class<?> returnType = retMoves.length == 0
130                 ? void.class
131                 : retMoves.length == 1
132                     ? retMoves[0].type()
133                     : Object[].class;
134 
135         MethodType leafType = methodType(returnType, argMoveTypes);
136         MethodType leafTypeWithAddress = leafType.insertParameterTypes(0, long.class);
137 
138         MethodHandle handle = insertArguments(MH_INVOKE_MOVES.bindTo(this), 2, argMoves, retMoves);
139         MethodHandle collector = makeCollectorHandle(leafType);
140         handle = collectArguments(handle, 1, collector);
141         handle = handle.asType(leafTypeWithAddress);
142 
143         boolean isSimple = !(retMoves.length > 1);
144         boolean usesStackArgs = stackArgsBytes != 0;
145         if (USE_INTRINSICS && isSimple && !usesStackArgs) {
146             NativeEntryPoint nep = NativeEntryPoint.make(
147                 "native_call",
148                 abi,
149                 toStorageArray(argMoves),
150                 toStorageArray(retMoves),
151                 !callingSequence.isTrivial(),
152                 leafTypeWithAddress
153             );
154 
155             handle = JLIA.nativeMethodHandle(nep, handle);
156         }
157         handle = filterArguments(handle, 0, MH_ADDR_TO_LONG);
158 
159         if (USE_SPEC && isSimple) {











160             handle = specialize(handle);
161          } else {
162             Map<VMStorage, Integer> argIndexMap = SharedUtils.indexMap(argMoves);
163             Map<VMStorage, Integer> retIndexMap = SharedUtils.indexMap(retMoves);
164 
165             handle = insertArguments(MH_INVOKE_INTERP_BINDINGS.bindTo(this), 3, handle, argIndexMap, retIndexMap);
166             MethodHandle collectorInterp = makeCollectorHandle(callingSequence.methodType());
167             handle = collectArguments(handle, 2, collectorInterp);
168             handle = handle.asType(handle.type().changeReturnType(callingSequence.methodType().returnType()));







169          }
170 






171         return handle;
172     }
173 
174     private static long unboxTargetAddress(NativeSymbol addr) {
175         SharedUtils.checkSymbol(addr);
176         return addr.address().toRawLongValue();
177     }
178 
179     // Funnel from type to Object[]
180     private static MethodHandle makeCollectorHandle(MethodType type) {
181         return type.parameterCount() == 0
182             ? EMPTY_OBJECT_ARRAY_HANDLE
183             : identity(Object[].class)
184                 .asCollector(Object[].class, type.parameterCount())
185                 .asType(type.changeReturnType(Object[].class));
186     }
187 
188     private Stream<Binding.VMStore> argMoveBindingsStream(CallingSequence callingSequence) {
189         return callingSequence.argumentBindings()
190                 .filter(Binding.VMStore.class::isInstance)
191                 .map(Binding.VMStore.class::cast);
192     }
193 
194     private Binding.VMLoad[] retMoveBindings(CallingSequence callingSequence) {




195         return callingSequence.returnBindings().stream()
196                 .filter(Binding.VMLoad.class::isInstance)
197                 .map(Binding.VMLoad.class::cast)
198                 .toArray(Binding.VMLoad[]::new);
199     }
200 
201 
202     private VMStorageProxy[] toStorageArray(Binding.Move[] moves) {
203         return Arrays.stream(moves).map(Binding.Move::storage).toArray(VMStorage[]::new);
204     }
205 
206     private MethodHandle specialize(MethodHandle leafHandle) {
207         MethodType highLevelType = callingSequence.methodType();
208 
209         int argInsertPos = 1;
210         int argContextPos = 1;
211 
212         MethodHandle specializedHandle = dropArguments(leafHandle, argContextPos, Binding.Context.class);
213 
214         for (int i = 0; i < highLevelType.parameterCount(); i++) {
215             List<Binding> bindings = callingSequence.argumentBindings(i);
216             argInsertPos += bindings.stream().filter(Binding.VMStore.class::isInstance).count() + 1;
217             // We interpret the bindings in reverse since we have to construct a MethodHandle from the bottom up
218             for (int j = bindings.size() - 1; j >= 0; j--) {
219                 Binding binding = bindings.get(j);
220                 if (binding.tag() == Binding.Tag.VM_STORE) {
221                     argInsertPos--;
222                 } else {
223                     specializedHandle = binding.specialize(specializedHandle, argInsertPos, argContextPos);
224                 }
225             }
226         }
227 
228         if (highLevelType.returnType() != void.class) {
229             MethodHandle returnFilter = identity(highLevelType.returnType());


230             int retContextPos = 0;
231             int retInsertPos = 1;







232             returnFilter = dropArguments(returnFilter, retContextPos, Binding.Context.class);
233             List<Binding> bindings = callingSequence.returnBindings();
234             for (int j = bindings.size() - 1; j >= 0; j--) {
235                 Binding binding = bindings.get(j);
236                 returnFilter = binding.specialize(returnFilter, retInsertPos, retContextPos);

























237             }
238             returnFilter = MethodHandles.filterArguments(returnFilter, retContextPos, MH_WRAP_ALLOCATOR);
239             // (SegmentAllocator, Addressable, Context, ...) -> ...
240             specializedHandle = MethodHandles.collectArguments(returnFilter, retInsertPos, specializedHandle);
241             // (Addressable, SegmentAllocator, Context, ...) -> ...
242             specializedHandle = SharedUtils.swapArguments(specializedHandle, 0, 1); // normalize parameter order











243         } else {
244             specializedHandle = MethodHandles.dropArguments(specializedHandle, 1, SegmentAllocator.class);
245         }
246 
247         // now bind the internal context parameter
248 
249         argContextPos++; // skip over the return SegmentAllocator (inserted by the above code)
250         specializedHandle = SharedUtils.wrapWithAllocator(specializedHandle, argContextPos, bufferCopySize, false);
251         return specializedHandle;
252     }
253 
254     /**
255      * Does a native invocation by moving primitive values from the arg array into an intermediate buffer
256      * and calling the assembly stub that forwards arguments from the buffer to the target function
257      *
258      * @param args an array of primitive values to be copied in to the buffer
259      * @param argBindings Binding.Move values describing how arguments should be copied
260      * @param returnBindings Binding.Move values describing how return values should be copied
261      * @return null, a single primitive value, or an Object[] of primitive values
262      */
263     Object invokeMoves(long addr, Object[] args, Binding.VMStore[] argBindings, Binding.VMLoad[] returnBindings) {
264         MemorySegment stackArgsSeg = null;
265         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
266             MemorySegment argBuffer = MemorySegment.allocateNative(layout.size, 64, scope);
267             if (stackArgsBytes > 0) {
268                 stackArgsSeg = MemorySegment.allocateNative(stackArgsBytes, 8, scope);
269             }
270 
271             VH_LONG.set(argBuffer.asSlice(layout.arguments_next_pc), addr);
272             VH_LONG.set(argBuffer.asSlice(layout.stack_args_bytes), stackArgsBytes);
273             VH_LONG.set(argBuffer.asSlice(layout.stack_args), stackArgsSeg == null ? 0L : stackArgsSeg.address().toRawLongValue());
274 
275             for (int i = 0; i < argBindings.length; i++) {
276                 Binding.VMStore binding = argBindings[i];
277                 VMStorage storage = binding.storage();
278                 MemorySegment ptr = abi.arch.isStackType(storage.type())
279                     ? stackArgsSeg.asSlice(storage.index() * abi.arch.typeSize(abi.arch.stackType()))
280                     : argBuffer.asSlice(layout.argOffset(storage));
281                 SharedUtils.writeOverSized(ptr, binding.type(), args[i]);
282             }
283 
284             if (DEBUG) {
285                 System.err.println("Buffer state before:");
286                 layout.dump(abi.arch, argBuffer, System.err);
287             }
288 
289             invokeNative(stubAddress, argBuffer.address().toRawLongValue());
290 
291             if (DEBUG) {
292                 System.err.println("Buffer state after:");
293                 layout.dump(abi.arch, argBuffer, System.err);
294             }
295 
296             if (returnBindings.length == 0) {
297                 return null;
298             } else if (returnBindings.length == 1) {
299                 Binding.VMLoad move = returnBindings[0];
300                 VMStorage storage = move.storage();
301                 return SharedUtils.read(argBuffer.asSlice(layout.retOffset(storage)), move.type());
302             } else { // length > 1
303                 Object[] returns = new Object[returnBindings.length];
304                 for (int i = 0; i < returnBindings.length; i++) {
305                     Binding.VMLoad move = returnBindings[i];
306                     VMStorage storage = move.storage();
307                     returns[i] = SharedUtils.read(argBuffer.asSlice(layout.retOffset(storage)), move.type());
308                 }
309                 return returns;
310             }
311         }
312     }
313 
314     Object invokeInterpBindings(NativeSymbol symbol, SegmentAllocator allocator, Object[] args, MethodHandle leaf,
315                                 Map<VMStorage, Integer> argIndexMap,
316                                 Map<VMStorage, Integer> retIndexMap) throws Throwable {
317         Binding.Context unboxContext = bufferCopySize != 0
318                 ? Binding.Context.ofBoundedAllocator(bufferCopySize)
319                 : Binding.Context.DUMMY;
320         try (unboxContext) {


321             // do argument processing, get Object[] as result
322             Object[] leafArgs = new Object[leaf.type().parameterCount()];
323             leafArgs[0] = symbol; // symbol







324             for (int i = 0; i < args.length; i++) {
325                 Object arg = args[i];
326                 BindingInterpreter.unbox(arg, callingSequence.argumentBindings(i),
327                         (storage, type, value) -> {
328                             leafArgs[argIndexMap.get(storage) + 1] = value; // +1 to skip symbol
329                         }, unboxContext);
330             }
331 
332             // call leaf
333             Object o = leaf.invokeWithArguments(leafArgs);
334 
335             // return value processing
336             if (o == null) {
337                 return null;
338             } else if (o instanceof Object[]) {
339                 Object[] oArr = (Object[]) o;

340                 return BindingInterpreter.box(callingSequence.returnBindings(),
341                         (storage, type) -> oArr[retIndexMap.get(storage)], Binding.Context.ofAllocator(allocator));








342             } else {
343                 return BindingInterpreter.box(callingSequence.returnBindings(), (storage, type) -> o,
344                         Binding.Context.ofAllocator(allocator));
345             }
346         }
347     }
348 
349     //natives
350 
351     static native void invokeNative(long adapterStub, long buff);
352     static native long generateAdapter(ABIDescriptor abi, BufferLayout layout);
353 
354     private static native void registerNatives();
355     static {
356         registerNatives();
357     }
358 }
359 
--- EOF ---