1 /*
2 * Copyright (c) 2015, 2016, 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.
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.nicl;
24
25 import jdk.internal.nicl.abi.ArgumentBinding;
26 import jdk.internal.nicl.abi.CallingSequence;
27 import jdk.internal.nicl.abi.Storage;
28 import jdk.internal.nicl.abi.StorageClass;
29 import jdk.internal.nicl.abi.SystemABI;
30 import jdk.internal.nicl.abi.sysv.x64.Constants;
31 import jdk.internal.nicl.types.*;
32
33 import java.lang.invoke.MethodHandle;
34 import java.lang.invoke.MethodHandles;
35 import java.lang.invoke.MethodType;
36 import java.lang.reflect.Method;
37 import java.nicl.NativeTypes;
38 import java.nicl.Scope;
39 import java.nicl.layout.Function;
40 import java.nicl.layout.Layout;
41 import java.nicl.types.*;
42 import java.nicl.types.Pointer;
43 import java.util.ArrayList;
44 import static sun.security.action.GetPropertyAction.privilegedGetProperty;
45
46 public class UpcallHandler {
47
48 private static final boolean DEBUG = Boolean.parseBoolean(
49 privilegedGetProperty("jdk.internal.nicl.UpcallHandler.DEBUG"));
50 private static final LayoutType<Long> LONG_LAYOUT_TYPE = NativeTypes.UINT64;
51
52 private static final long MAX_STACK_ARG_BYTES = 64 * 1024; // FIXME: Arbitrary limitation for now...
53
54 private static final Object HANDLERS_LOCK = new Object();
55 private static final ArrayList<UpcallHandler> ID2HANDLER = new ArrayList<>();
56
57 private final MethodHandle mh;
58 private final Function ftype;
59
60 private final UpcallStub stub;
61
62 static {
63 long size = 8;
64 if (Constants.STACK_SLOT_SIZE != size) {
65 throw new Error("Invalid size: " + Constants.STACK_SLOT_SIZE);
66 }
67 if (Constants.INTEGER_REGISTER_SIZE != size) {
68 throw new Error("Invalid size: " + Constants.INTEGER_REGISTER_SIZE);
69 }
70 if ((Constants.VECTOR_REGISTER_SIZE % size) != 0) {
71 throw new Error("Invalid size: " + Constants.VECTOR_REGISTER_SIZE);
72 }
73 }
74
75 public static UpcallHandler make(Class<?> c, Object o) throws Throwable {
76 if (!Util.isFunctionalInterface(c)) {
77 throw new IllegalArgumentException("Class is not a @FunctionalInterface: " + c.getName());
78 }
79 if (o == null) {
80 throw new NullPointerException();
81 }
82
83 if (!c.isInstance(o)) {
84 throw new IllegalArgumentException("Object must implement FunctionalInterface class: " + c.getName());
85 }
86
87 Method ficMethod = Util.findFunctionalInterfaceMethod(c);
88 Function ftype = Util.functionof(ficMethod);
89
90 MethodType mt = MethodHandles.publicLookup().unreflect(ficMethod).type().dropParameterTypes(0, 1);
91
92 MethodHandle mh = MethodHandles.publicLookup().findVirtual(c, "fn", mt);
93
94 return UpcallHandler.make(mh.bindTo(o), ftype);
95 }
96
97 private static UpcallHandler make(MethodHandle mh, Function ftype) throws Throwable {
98 synchronized (HANDLERS_LOCK) {
99 int id = ID2HANDLER.size();
100 UpcallHandler handler = new UpcallHandler(mh, ftype, id);
101 ID2HANDLER.add(handler);
102
103 if (DEBUG) {
104 System.err.println("Allocated upcall handler with id " + id);
105 }
106
107 return handler;
108 }
109 }
110
111 public static void invoke(int id, long integers, long vectors, long stack, long integerReturn, long vectorReturn) {
112 UpcallHandler handler;
113
114 if (DEBUG) {
115 System.err.println("UpcallHandler.invoke(" + id + ", ...) with " + ID2HANDLER.size() + " stubs allocated");
116 }
117
118 synchronized (HANDLERS_LOCK) {
119 handler = ID2HANDLER.get(id);
120 }
121
122 try (Scope scope = Scope.newNativeScope()) {
123 UpcallContext context = new UpcallContext(scope, integers, vectors, stack, integerReturn, vectorReturn);
124 handler.invoke(context);
125 }
126 }
127
128 private UpcallHandler(MethodHandle mh, Function ftype, int id) throws Throwable {
129 this.mh = mh;
130 this.ftype = ftype;
131 this.stub = new UpcallStub(id);
132 }
133
134 public Pointer<?> getNativeEntryPoint() {
135 return stub.getEntryPoint();
136 }
137
138 static class UpcallContext {
139
140 private final Pointer<Long> integers;
141 private final Pointer<Long> vectors;
142 private final Pointer<Long> stack;
143 private final Pointer<Long> integerReturns;
144 private final Pointer<Long> vectorReturns;
145
146 UpcallContext(Scope scope, long integers, long vectors, long stack, long integerReturn, long vectorReturn) {
147 this.integers = new BoundedPointer<>(LONG_LAYOUT_TYPE, new BoundedMemoryRegion(integers, Constants.MAX_INTEGER_ARGUMENT_REGISTERS * Constants.INTEGER_REGISTER_SIZE, scope), 0, BoundedMemoryRegion.MODE_R);
148 this.vectors = new BoundedPointer<>(LONG_LAYOUT_TYPE, new BoundedMemoryRegion(vectors, Constants.MAX_VECTOR_ARGUMENT_REGISTERS * Constants.VECTOR_REGISTER_SIZE, scope), 0, BoundedMemoryRegion.MODE_R);
149 this.stack = new BoundedPointer<>(LONG_LAYOUT_TYPE, new BoundedMemoryRegion(stack, MAX_STACK_ARG_BYTES, scope), 0, BoundedMemoryRegion.MODE_R);
150 this.integerReturns = new BoundedPointer<>(LONG_LAYOUT_TYPE, new BoundedMemoryRegion(integerReturn, Constants.MAX_INTEGER_RETURN_REGISTERS * Constants.INTEGER_REGISTER_SIZE, scope), 0, BoundedMemoryRegion.MODE_W);
151 this.vectorReturns = new BoundedPointer<>(LONG_LAYOUT_TYPE, new BoundedMemoryRegion(vectorReturn, Constants.MAX_VECTOR_RETURN_REGISTERS * Constants.VECTOR_REGISTER_SIZE, scope), 0, BoundedMemoryRegion.MODE_W);
152 }
153
154 Pointer<Long> getPtr(Storage storage) {
155 switch (storage.getStorageClass()) {
156 case INTEGER_ARGUMENT_REGISTER:
157 return integers.offset(storage.getStorageIndex());
158 case VECTOR_ARGUMENT_REGISTER:
159 return vectors.offset(storage.getStorageIndex() * Constants.VECTOR_REGISTER_SIZE / 8);
160 case STACK_ARGUMENT_SLOT:
161 return stack.offset(storage.getStorageIndex());
162
163 case INTEGER_RETURN_REGISTER:
164 return integerReturns.offset(storage.getStorageIndex());
165 case VECTOR_RETURN_REGISTER:
166 return vectorReturns.offset(storage.getStorageIndex() * Constants.VECTOR_REGISTER_SIZE / 8);
167 default:
168 throw new Error("Unhandled storage: " + storage);
169 }
170 }
171 }
172
173 private Object boxArgument(Scope scope, UpcallContext context, Struct<?>[] structs, ArgumentBinding binding) throws IllegalAccessException {
174 Class<?> carrierType = binding.getMember().getCarrierType(mh.type());
175
176 Pointer<Long> src = context.getPtr(binding.getStorage());
177
178 if (DEBUG) {
179 System.err.println("boxArgument carrier type: " + carrierType);
180 }
181
182
183 if (Util.isCStruct(carrierType)) {
184 int index = binding.getMember().getArgumentIndex();
185 Struct<?> r = structs[index];
186 if (r == null) {
187 /*
188 * FIXME (STRUCT-LIFECYCLE):
189 *
190 * Leak memory for now
191 */
192 scope = Scope.newNativeScope();
193
194 @SuppressWarnings({"rawtypes", "unchecked"})
195 Struct<?> rtmp = scope.allocateStruct((Class)carrierType);
196
197 structs[index] = r = rtmp;
198 }
199
200 if (DEBUG) {
201 System.out.println("Populating struct at arg index " + index + " at offset 0x" + Long.toHexString(binding.getOffset()));
202 }
203
204 if ((binding.getOffset() % LONG_LAYOUT_TYPE.bytesSize()) != 0) {
205 throw new Error("Invalid offset: " + binding.getOffset());
206 }
207 Pointer<Long> dst = r.ptr().cast(LONG_LAYOUT_TYPE).offset(binding.getOffset() / LONG_LAYOUT_TYPE.bytesSize());
208
209 if (DEBUG) {
210 System.err.println("Copying struct data, value: 0x" + Long.toHexString(src.get()));
211 }
212
213 Util.copy(src, dst, binding.getStorage().getSize());
214
215 return r;
216 } else {
217 return src.cast(Util.makeType(carrierType, src.type().layout())).get();
218 }
219 }
220
221 private Object[] boxArguments(Scope scope, UpcallContext context, CallingSequence callingSequence) {
222 Object[] args = new Object[mh.type().parameterCount()];
223
224 Struct<?>[] structs = new Struct<?>[mh.type().parameterCount()];
225
226 if (DEBUG) {
227 System.out.println("boxArguments " + callingSequence.asString());
228 }
229
230 for (StorageClass c : Constants.ARGUMENT_STORAGE_CLASSES) {
231 int skip = (c == StorageClass.INTEGER_ARGUMENT_REGISTER && callingSequence.returnsInMemory()) ? 1 : 0;
232 callingSequence
233 .getBindings(c)
234 .stream()
235 .skip(skip)
236 .filter(binding -> binding != null)
237 .forEach(binding -> {
238 try {
239 args[binding.getMember().getArgumentIndex()] = boxArgument(scope, context, structs, binding);
240 } catch (IllegalAccessException e) {
241 throw new IllegalArgumentException("Failed to box argument", e);
242 }
243 });
244 }
245
246 return args;
247 }
248
249 private void unboxReturn(Class<?> c, UpcallContext context, ArgumentBinding binding, Object o) throws IllegalAccessException {
250 if (DEBUG) {
251 System.out.println("unboxReturn " + c.getName());
252 System.out.println(binding.toString());
253 }
254
255 Pointer<Long> dst = context.getPtr(binding.getStorage());
256
257 if (Util.isCStruct(c)) {
258 Function ft = Function.of(Util.layoutof(c), false, new Layout[0]);
259 boolean returnsInMemory = SystemABI.getInstance().arrangeCall(ft).returnsInMemory();
260
261 Struct<?> struct = (Struct<?>) o;
262
263 Pointer<Long> src = struct.ptr().cast(LONG_LAYOUT_TYPE);
264
265 if (returnsInMemory) {
266 // the first integer argument register contains a pointer to caller allocated struct
267 long structAddr = context.getPtr(new Storage(StorageClass.INTEGER_ARGUMENT_REGISTER, 0, Constants.INTEGER_REGISTER_SIZE)).get();
268 long size = Util.alignUp(ftype.returnLayout().get().bitsSize() / 8, 8);
269 Pointer<?> dstStructPtr = new BoundedPointer<>(Util.makeType(c, ftype.returnLayout().get()), new BoundedMemoryRegion(structAddr, size));
270 try {
271 ((BoundedPointer<?>) dstStructPtr).type.setter().invoke(dstStructPtr, o);
272 } catch (Throwable ex) {
273 throw new IllegalStateException(ex);
274 }
275 } else {
276 if ((binding.getOffset() % LONG_LAYOUT_TYPE.bytesSize()) != 0) {
277 throw new Error("Invalid offset: " + binding.getOffset());
278 }
279 Pointer<Long> srcPtr = src.offset(binding.getOffset() / LONG_LAYOUT_TYPE.bytesSize());
280 Util.copy(srcPtr, dst, binding.getStorage().getSize());
281 }
282 } else {
283 try {
284 dst.cast(Util.makeType(c, ftype.returnLayout().get())).type().setter().invoke(dst, o);
285 } catch (Throwable ex) {
286 throw new IllegalStateException(ex);
287 }
288 }
289 }
290
291 private void invoke(UpcallContext context) {
292 try (Scope scope = Scope.newNativeScope()) {
293 // FIXME: Handle varargs upcalls here
294 CallingSequence callingSequence = SystemABI.getInstance().arrangeCall(ftype);
295
296 if (DEBUG) {
297 System.err.println("=== UpcallHandler.invoke ===");
298 System.err.println(callingSequence.asString());
299 }
300
301 Object[] args = boxArguments(scope, context, callingSequence);
302
303 Object o = mh.asSpreader(Object[].class, args.length).invoke(args);
304
305 if (mh.type().returnType() != void.class) {
306 for (StorageClass c : Constants.RETURN_STORAGE_CLASSES) {
307 for (ArgumentBinding binding : callingSequence.getBindings(c)) {
308 unboxReturn(mh.type().returnType(), context, binding, o);
309 }
310 }
311 }
312 } catch (Throwable t) {
313 throw new RuntimeException(t);
314 }
315 }
316 }
--- EOF ---