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.MemoryLayout;
26
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.Objects;
30
31 /**
32 * The binding operators defined in the Binding class can be combined into argument and return value processing 'recipes'.
33 *
34 * The binding operators are interpreted using a stack-base interpreter. Operators can either consume operands from the
35 * stack, or push them onto the stack.
36 *
37 * In the description of each binding we talk about 'boxing' and 'unboxing'.
38 * - Unboxing is the process of taking a Java value and decomposing it, and storing components into machine
39 * storage locations. As such, the binding interpreter stack starts with the Java value on it, and should end empty.
40 * - Boxing is the process of re-composing a Java value by pulling components from machine storage locations.
41 * If a MemorySegment is needed to store the result, one should be allocated using the ALLOCATE_BUFFER operator.
42 * The binding interpreter stack starts off empty, and ends with the value to be returned as the only value on it.
43 * A binding operator can be interpreted differently based on whether we are boxing or unboxing a value. For example,
44 * the CONVERT_ADDRESS operator 'unboxes' a MemoryAddress to a long, but 'boxes' a long to a MemoryAddress.
45 *
46 * Here are some examples of binding recipes derived from C declarations, and according to the Windows ABI (recipes are
47 * ABI-specific). Note that each argument has it's own recipe, which is indicated by '[number]:' (though, the only
48 * example that has multiple arguments is the one using varargs).
49 *
50 * --------------------
165 *
166 * --------------------
167 *
168 * void f(int dummy, ...); // varargs
169 *
170 * f(0, 10f); // passing a float
171 *
172 * Argument bindings:
173 * 0: MOVE(rcx, int.class) // moves the 'int dummy' into the RCX register
174 *
175 * 1: DUP // duplicates the '10f' argument
176 * MOVE(rdx, float.class) // move one copy into the RDX register
177 * MOVE(xmm1, float.class) // moves the other copy into the xmm2 register
178 *
179 * Return bindings:
180 * none
181 *
182 * --------------------
183 */
184 public abstract class Binding {
185 enum Tag {
186 MOVE,
187 DEREFERENCE,
188 COPY_BUFFER,
189 ALLOC_BUFFER,
190 CONVERT_ADDRESS,
191 BASE_ADDRESS,
192 DUP
193 }
194
195 private final Tag tag;
196
197 private Binding(Tag tag) {
198 this.tag = tag;
199 }
200
201 public Tag tag() {
202 return tag;
203 }
204
205 private static void checkType(Class<?> type) {
206 if (!type.isPrimitive() || type == void.class || type == boolean.class)
207 throw new IllegalArgumentException("Illegal type: " + type);
208 }
209
210 public static Move move(VMStorage storage, Class<?> type) {
211 checkType(type);
212 return new Move(storage, type);
213 }
214
215 public static Dereference dereference(long offset, Class<?> type) {
216 checkType(type);
217 if (offset < 0)
218 throw new IllegalArgumentException("Negative offset: " + offset);
219 return new Dereference(offset, type);
220 }
221
222 public static Copy copy(MemoryLayout layout) {
223 return new Copy(layout.byteSize(), layout.byteAlignment());
224 }
225
226 public static Allocate allocate(MemoryLayout layout) {
227 return new Allocate(layout.byteSize(), layout.byteAlignment());
228 }
229
319 return "Move{" +
320 "tag=" + tag() +
321 ", storage=" + storage +
322 ", type=" + type +
323 '}';
324 }
325
326 @Override
327 public boolean equals(Object o) {
328 if (this == o) return true;
329 if (o == null || getClass() != o.getClass()) return false;
330 Move move = (Move) o;
331 return storage.equals(move.storage) &&
332 type.equals(move.type);
333 }
334
335 @Override
336 public int hashCode() {
337 return Objects.hash(tag(), storage, type);
338 }
339 }
340
341 /**
342 * DEREFERENCE([offset into memory region], [type])
343 * When unboxing: pops a MemorySegment from the operand stack,
344 * loads a [type] from [offset into memory region] from it, and pushes it onto the operand stack
345 * When boxing: pops a [type], and then a MemorySegment from the operand stack,
346 * and then stores [type] to [offset into memory region] of the MemorySegment
347 * The [type] must be one of byte, short, char, int, long, float, or double
348 */
349 public static class Dereference extends Binding {
350 private final long offset;
351 private final Class<?> type;
352
353 private Dereference(long offset, Class<?> type) {
354 super(Tag.DEREFERENCE);
355 this.offset = offset;
356 this.type = type;
357 }
358
369 return "Dereference{" +
370 "tag=" + tag() +
371 ", offset=" + offset +
372 ", type=" + type +
373 '}';
374 }
375
376 @Override
377 public boolean equals(Object o) {
378 if (this == o) return true;
379 if (o == null || getClass() != o.getClass()) return false;
380 Dereference that = (Dereference) o;
381 return offset == that.offset &&
382 type.equals(that.type);
383 }
384
385 @Override
386 public int hashCode() {
387 return Objects.hash(tag(), offset, type);
388 }
389 }
390
391 /**
392 * COPY([size], [alignment])
393 * Creates a new MemorySegment with the given [size] and [alignment],
394 * and copies contents from a MemorySegment popped from the top of the operand stack into this new buffer,
395 * and pushes the new buffer onto the operand stack
396 */
397 public static class Copy extends Binding {
398 private final long size;
399 private final long alignment;
400
401 private Copy(long size, long alignment) {
402 super(Tag.COPY_BUFFER);
403 this.size = size;
404 this.alignment = alignment;
405 }
406
407 public long size() {
408 return size;
417 return "Copy{" +
418 "tag=" + tag() +
419 ", size=" + size +
420 ", alignment=" + alignment +
421 '}';
422 }
423
424 @Override
425 public boolean equals(Object o) {
426 if (this == o) return true;
427 if (o == null || getClass() != o.getClass()) return false;
428 Copy copy = (Copy) o;
429 return size == copy.size &&
430 alignment == copy.alignment;
431 }
432
433 @Override
434 public int hashCode() {
435 return Objects.hash(tag(), size, alignment);
436 }
437 }
438
439 /**
440 * ALLOCATE([size], [alignment])
441 * Creates a new MemorySegment with the give [size] and [alignment], and pushes it onto the operand stack.
442 */
443 public static class Allocate extends Binding {
444 private final long size;
445 private final long alignment;
446
447 private Allocate(long size, long alignment) {
448 super(Tag.ALLOC_BUFFER);
449 this.size = size;
450 this.alignment = alignment;
451 }
452
453 public long size() {
454 return size;
455 }
456
463 return "AllocateBuffer{" +
464 "tag=" + tag() +
465 "size=" + size +
466 ", alignment=" + alignment +
467 '}';
468 }
469
470 @Override
471 public boolean equals(Object o) {
472 if (this == o) return true;
473 if (o == null || getClass() != o.getClass()) return false;
474 Allocate that = (Allocate) o;
475 return size == that.size &&
476 alignment == that.alignment;
477 }
478
479 @Override
480 public int hashCode() {
481 return Objects.hash(tag(), size, alignment);
482 }
483 }
484
485 /**
486 * CONVERT_ADDRESS()
487 * When unboxing: pops a 'MemoryAddress' from the operand stack, converts it to a 'long',
488 * and pushes that onto the operand stack
489 * When boxing: pops a 'long' from the operand stack, converts it to a 'MemoryAddress',
490 * and pushes that onto the operand stack
491 */
492 public static class ConvertAddress extends Binding {
493 private static final ConvertAddress INSTANCE = new ConvertAddress();
494 private ConvertAddress() {
495 super(Tag.CONVERT_ADDRESS);
496 }
497
498 @Override
499 public String toString() {
500 return "BoxAddress{" +
501 "tag=" + tag() +
502 "}";
503 }
504
505 @Override
506 public int hashCode() {
507 return tag().hashCode();
508 }
509
510 @Override
511 public boolean equals(Object o) {
512 if (this == o) return true;
513 return o != null && getClass() == o.getClass();
514 }
515 }
516
517 /**
518 * BASE_ADDRESS()
519 * Pops a MemorySegment from the operand stack, and takes the base address of the segment
520 * (the MemoryAddress that points to the start), and pushes that onto the operand stack
521 */
522 public static class BaseAddress extends Binding {
523 private static final BaseAddress INSTANCE = new BaseAddress();
524 private BaseAddress() {
525 super(Tag.BASE_ADDRESS);
526 }
527
528 @Override
529 public String toString() {
530 return "BaseAddress{" +
531 "tag=" + tag() +
532 "}";
533 }
534
535 @Override
536 public int hashCode() {
537 return tag().hashCode();
538 }
539
540 @Override
541 public boolean equals(Object o) {
542 if (this == o) return true;
543 return o != null && getClass() == o.getClass();
544 }
545 }
546
547 /**
548 * DUP()
549 * Duplicates the value on the top of the operand stack (without popping it!),
550 * and pushes the duplicate onto the operand stack
551 */
552 public static class Dup extends Binding {
553 private static final Dup INSTANCE = new Dup();
554 private Dup() {
555 super(Tag.DUP);
556 }
557
558 @Override
559 public String toString() {
560 return "Dup{" +
561 "tag=" + tag() +
562 "}";
563 }
564
565 @Override
566 public int hashCode() {
567 return tag().hashCode();
568 }
569
570 @Override
571 public boolean equals(Object o) {
572 if (this == o) return true;
573 return o != null && getClass() == o.getClass();
574 }
575 }
576 }
|
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.MemoryLayout;
28 import jdk.incubator.foreign.MemorySegment;
29 import jdk.incubator.foreign.NativeScope;
30 import jdk.internal.foreign.MemoryAddressImpl;
31
32 import java.lang.invoke.MethodHandle;
33 import java.lang.invoke.MethodHandles;
34 import java.lang.invoke.MethodType;
35 import java.util.ArrayList;
36 import java.util.Deque;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Objects;
40
41 import java.lang.invoke.VarHandle;
42 import java.nio.ByteOrder;
43 import java.util.concurrent.ConcurrentHashMap;
44
45 import static java.lang.invoke.MethodHandles.collectArguments;
46 import static java.lang.invoke.MethodHandles.filterArguments;
47 import static java.lang.invoke.MethodHandles.insertArguments;
48 import static java.lang.invoke.MethodHandles.permuteArguments;
49 import static java.lang.invoke.MethodType.methodType;
50
51 /**
52 * The binding operators defined in the Binding class can be combined into argument and return value processing 'recipes'.
53 *
54 * The binding operators are interpreted using a stack-base interpreter. Operators can either consume operands from the
55 * stack, or push them onto the stack.
56 *
57 * In the description of each binding we talk about 'boxing' and 'unboxing'.
58 * - Unboxing is the process of taking a Java value and decomposing it, and storing components into machine
59 * storage locations. As such, the binding interpreter stack starts with the Java value on it, and should end empty.
60 * - Boxing is the process of re-composing a Java value by pulling components from machine storage locations.
61 * If a MemorySegment is needed to store the result, one should be allocated using the ALLOCATE_BUFFER operator.
62 * The binding interpreter stack starts off empty, and ends with the value to be returned as the only value on it.
63 * A binding operator can be interpreted differently based on whether we are boxing or unboxing a value. For example,
64 * the CONVERT_ADDRESS operator 'unboxes' a MemoryAddress to a long, but 'boxes' a long to a MemoryAddress.
65 *
66 * Here are some examples of binding recipes derived from C declarations, and according to the Windows ABI (recipes are
67 * ABI-specific). Note that each argument has it's own recipe, which is indicated by '[number]:' (though, the only
68 * example that has multiple arguments is the one using varargs).
69 *
70 * --------------------
185 *
186 * --------------------
187 *
188 * void f(int dummy, ...); // varargs
189 *
190 * f(0, 10f); // passing a float
191 *
192 * Argument bindings:
193 * 0: MOVE(rcx, int.class) // moves the 'int dummy' into the RCX register
194 *
195 * 1: DUP // duplicates the '10f' argument
196 * MOVE(rdx, float.class) // move one copy into the RDX register
197 * MOVE(xmm1, float.class) // moves the other copy into the xmm2 register
198 *
199 * Return bindings:
200 * none
201 *
202 * --------------------
203 */
204 public abstract class Binding {
205 private static final MethodHandle MH_UNBOX_ADDRESS;
206 private static final MethodHandle MH_BOX_ADDRESS;
207 private static final MethodHandle MH_BASE_ADDRESS;
208 private static final MethodHandle MH_COPY_BUFFER;
209 private static final MethodHandle MH_ALLOCATE_BUFFER;
210
211 static {
212 try {
213 MethodHandles.Lookup lookup = MethodHandles.lookup();
214 MH_UNBOX_ADDRESS = lookup.findVirtual(MemoryAddress.class, "toRawLongValue",
215 methodType(long.class));
216 MH_BOX_ADDRESS = lookup.findStatic(MemoryAddress.class, "ofLong",
217 methodType(MemoryAddress.class, long.class));
218 MH_BASE_ADDRESS = lookup.findVirtual(MemorySegment.class, "baseAddress",
219 methodType(MemoryAddress.class));
220 MH_COPY_BUFFER = lookup.findStatic(Binding.class, "copyBuffer",
221 methodType(MemorySegment.class, MemorySegment.class, long.class, long.class, NativeScope.class));
222 MH_ALLOCATE_BUFFER = lookup.findStatic(MemorySegment.class, "allocateNative",
223 methodType(MemorySegment.class, long.class, long.class));
224 } catch (ReflectiveOperationException e) {
225 throw new RuntimeException(e);
226 }
227 }
228
229 enum Tag {
230 MOVE,
231 DEREFERENCE,
232 COPY_BUFFER,
233 ALLOC_BUFFER,
234 CONVERT_ADDRESS,
235 BASE_ADDRESS,
236 DUP
237 }
238
239 private final Tag tag;
240
241 private Binding(Tag tag) {
242 this.tag = tag;
243 }
244
245 public Tag tag() {
246 return tag;
247 }
248
249 public abstract void verifyUnbox(Deque<Class<?>> stack);
250 public abstract void verifyBox(Deque<Class<?>> stack);
251
252 public abstract void unbox(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc, NativeScope scope);
253 public abstract void box(Deque<Object> stack, BindingInterpreter.LoadFunc loadFunc);
254
255 public abstract MethodHandle specializeUnbox(MethodHandle specializedHandle, int insertPos);
256 public abstract MethodHandle specializeBox(MethodHandle returnFilter);
257
258 private static MethodHandle mergeArguments(MethodHandle mh, int sourceIndex, int destIndex) {
259 MethodType oldType = mh.type();
260 Class<?> sourceType = oldType.parameterType(sourceIndex);
261 Class<?> destType = oldType.parameterType(destIndex);
262 if (sourceType != destType) {
263 // TODO meet?
264 throw new IllegalArgumentException("Parameter types differ: " + sourceType + " != " + destType);
265 }
266 MethodType newType = oldType.dropParameterTypes(destIndex, destIndex + 1);
267 int[] reorder = new int[oldType.parameterCount()];
268 assert destIndex > sourceIndex;
269 for (int i = 0, index = 0; i < reorder.length; i++) {
270 if (i != destIndex) {
271 reorder[i] = index++;
272 } else {
273 reorder[i] = sourceIndex;
274 }
275 }
276 return permuteArguments(mh, newType, reorder);
277 }
278
279 private static void checkType(Class<?> type) {
280 if (!type.isPrimitive() || type == void.class || type == boolean.class)
281 throw new IllegalArgumentException("Illegal type: " + type);
282 }
283
284 private static MemorySegment copyBuffer(MemorySegment operand, long size, long alignment, NativeScope allocator) {
285 MemorySegment copy = allocator.allocate(size, alignment).segment();
286 copy.copyFrom(operand.asSlice(0, size));
287 return copy;
288 }
289
290 public static Move move(VMStorage storage, Class<?> type) {
291 checkType(type);
292 return new Move(storage, type);
293 }
294
295 public static Dereference dereference(long offset, Class<?> type) {
296 checkType(type);
297 if (offset < 0)
298 throw new IllegalArgumentException("Negative offset: " + offset);
299 return new Dereference(offset, type);
300 }
301
302 public static Copy copy(MemoryLayout layout) {
303 return new Copy(layout.byteSize(), layout.byteAlignment());
304 }
305
306 public static Allocate allocate(MemoryLayout layout) {
307 return new Allocate(layout.byteSize(), layout.byteAlignment());
308 }
309
399 return "Move{" +
400 "tag=" + tag() +
401 ", storage=" + storage +
402 ", type=" + type +
403 '}';
404 }
405
406 @Override
407 public boolean equals(Object o) {
408 if (this == o) return true;
409 if (o == null || getClass() != o.getClass()) return false;
410 Move move = (Move) o;
411 return storage.equals(move.storage) &&
412 type.equals(move.type);
413 }
414
415 @Override
416 public int hashCode() {
417 return Objects.hash(tag(), storage, type);
418 }
419
420 @Override
421 public void verifyUnbox(Deque<Class<?>> stack) {
422 Class<?> actualType = stack.pop();
423 Class<?> expectedType = type;
424 SharedUtils.checkType(actualType, expectedType);
425 }
426
427 @Override
428 public void verifyBox(Deque<Class<?>> stack) {
429 stack.push(type);
430 }
431
432 @Override
433 public void unbox(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc, NativeScope scope) {
434 storeFunc.store(storage, type, stack.pop());
435 }
436
437 @Override
438 public void box(Deque<Object> stack, BindingInterpreter.LoadFunc loadFunc) {
439 stack.push(loadFunc.load(storage, type));
440 }
441
442 @Override
443 public MethodHandle specializeUnbox(MethodHandle specializedHandle, int insertPos) {
444 return specializedHandle; // no-op
445 }
446
447 @Override
448 public MethodHandle specializeBox(MethodHandle returnFilter) {
449 return returnFilter; // no-op
450 }
451 }
452
453 /**
454 * DEREFERENCE([offset into memory region], [type])
455 * When unboxing: pops a MemorySegment from the operand stack,
456 * loads a [type] from [offset into memory region] from it, and pushes it onto the operand stack
457 * When boxing: pops a [type], and then a MemorySegment from the operand stack,
458 * and then stores [type] to [offset into memory region] of the MemorySegment
459 * The [type] must be one of byte, short, char, int, long, float, or double
460 */
461 public static class Dereference extends Binding {
462 private final long offset;
463 private final Class<?> type;
464
465 private Dereference(long offset, Class<?> type) {
466 super(Tag.DEREFERENCE);
467 this.offset = offset;
468 this.type = type;
469 }
470
481 return "Dereference{" +
482 "tag=" + tag() +
483 ", offset=" + offset +
484 ", type=" + type +
485 '}';
486 }
487
488 @Override
489 public boolean equals(Object o) {
490 if (this == o) return true;
491 if (o == null || getClass() != o.getClass()) return false;
492 Dereference that = (Dereference) o;
493 return offset == that.offset &&
494 type.equals(that.type);
495 }
496
497 @Override
498 public int hashCode() {
499 return Objects.hash(tag(), offset, type);
500 }
501
502 @Override
503 public void verifyUnbox(Deque<Class<?>> stack) {
504 Class<?> actualType = stack.pop();
505 SharedUtils.checkType(actualType, MemorySegment.class);
506 Class<?> newType = type;
507 stack.push(newType);
508 }
509
510 @Override
511 public void verifyBox(Deque<Class<?>> stack) {
512 Class<?> storeType = stack.pop();
513 SharedUtils.checkType(storeType, type);
514 Class<?> segmentType = stack.pop();
515 SharedUtils.checkType(segmentType, MemorySegment.class);
516 }
517
518 @Override
519 public void unbox(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc, NativeScope scope) {
520 MemorySegment operand = (MemorySegment) stack.pop();
521 MemoryAddress baseAddress = operand.baseAddress();
522 MemoryAddress readAddress = baseAddress.addOffset(offset);
523 stack.push(SharedUtils.read(readAddress, type));
524 }
525
526 @Override
527 public void box(Deque<Object> stack, BindingInterpreter.LoadFunc loadFunc) {
528 Object value = stack.pop();
529 MemorySegment operand = (MemorySegment) stack.pop();
530 MemoryAddress baseAddress = operand.baseAddress();
531 MemoryAddress writeAddress = baseAddress.addOffset(offset);
532 SharedUtils.write(writeAddress, type, value);
533 }
534
535 private VarHandle varHandle() {
536 return MemoryHandles.withOffset(MemoryHandles.varHandle(type, ByteOrder.nativeOrder()), offset);
537 }
538
539 @Override
540 public MethodHandle specializeUnbox(MethodHandle specializedHandle, int insertPos) {
541 MethodHandle filter = filterArguments(
542 varHandle()
543 .toMethodHandle(VarHandle.AccessMode.GET)
544 .asType(methodType(type, MemoryAddress.class)), 0, MH_BASE_ADDRESS);
545 return filterArguments(specializedHandle, insertPos, filter);
546 }
547
548 @Override
549 public MethodHandle specializeBox(MethodHandle returnFilter) {
550 MethodHandle setter = varHandle().toMethodHandle(VarHandle.AccessMode.SET);
551 setter = filterArguments(
552 setter.asType(methodType(void.class, MemoryAddress.class, type)),
553 0, MH_BASE_ADDRESS);
554 return collectArguments(returnFilter, returnFilter.type().parameterCount(), setter);
555 }
556 }
557
558 /**
559 * COPY([size], [alignment])
560 * Creates a new MemorySegment with the given [size] and [alignment],
561 * and copies contents from a MemorySegment popped from the top of the operand stack into this new buffer,
562 * and pushes the new buffer onto the operand stack
563 */
564 public static class Copy extends Binding {
565 private final long size;
566 private final long alignment;
567
568 private Copy(long size, long alignment) {
569 super(Tag.COPY_BUFFER);
570 this.size = size;
571 this.alignment = alignment;
572 }
573
574 public long size() {
575 return size;
584 return "Copy{" +
585 "tag=" + tag() +
586 ", size=" + size +
587 ", alignment=" + alignment +
588 '}';
589 }
590
591 @Override
592 public boolean equals(Object o) {
593 if (this == o) return true;
594 if (o == null || getClass() != o.getClass()) return false;
595 Copy copy = (Copy) o;
596 return size == copy.size &&
597 alignment == copy.alignment;
598 }
599
600 @Override
601 public int hashCode() {
602 return Objects.hash(tag(), size, alignment);
603 }
604
605 @Override
606 public void verifyUnbox(Deque<Class<?>> stack) {
607 Class<?> actualType = stack.pop();
608 SharedUtils.checkType(actualType, MemorySegment.class);
609 stack.push(MemorySegment.class);
610 }
611
612 @Override
613 public void verifyBox(Deque<Class<?>> stack) {
614 Class<?> actualType = stack.pop();
615 SharedUtils.checkType(actualType, MemoryAddress.class);
616 stack.push(MemorySegment.class);
617 }
618
619 @Override
620 public void unbox(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc, NativeScope scope) {
621 MemorySegment operand = (MemorySegment) stack.pop();
622 MemorySegment copy = scope.allocate(size, alignment).segment();
623 copy.copyFrom(operand.asSlice(0, size));
624 stack.push(copy);
625 }
626
627 @Override
628 public void box(Deque<Object> stack, BindingInterpreter.LoadFunc loadFunc) {
629 MemoryAddress operand = (MemoryAddress) stack.pop();
630 operand = MemoryAddressImpl.ofLongUnchecked(operand.toRawLongValue(), size);
631 MemorySegment copy = MemorySegment.allocateNative(size, alignment);
632 copy.copyFrom(operand.segment().asSlice(0, size));
633 stack.push(copy); // leaked
634 }
635
636 @Override
637 public MethodHandle specializeUnbox(MethodHandle specializedHandle, int insertPos) {
638 MethodHandle filter = insertArguments(MH_COPY_BUFFER, 1, size, alignment);
639 specializedHandle = collectArguments(specializedHandle, insertPos, filter);
640 return mergeArguments(specializedHandle, 0, insertPos + 1);
641 }
642
643 @Override
644 public MethodHandle specializeBox(MethodHandle returnFilter) {
645 throw new UnsupportedOperationException();
646 }
647 }
648
649 /**
650 * ALLOCATE([size], [alignment])
651 * Creates a new MemorySegment with the give [size] and [alignment], and pushes it onto the operand stack.
652 */
653 public static class Allocate extends Binding {
654 private final long size;
655 private final long alignment;
656
657 private Allocate(long size, long alignment) {
658 super(Tag.ALLOC_BUFFER);
659 this.size = size;
660 this.alignment = alignment;
661 }
662
663 public long size() {
664 return size;
665 }
666
673 return "AllocateBuffer{" +
674 "tag=" + tag() +
675 "size=" + size +
676 ", alignment=" + alignment +
677 '}';
678 }
679
680 @Override
681 public boolean equals(Object o) {
682 if (this == o) return true;
683 if (o == null || getClass() != o.getClass()) return false;
684 Allocate that = (Allocate) o;
685 return size == that.size &&
686 alignment == that.alignment;
687 }
688
689 @Override
690 public int hashCode() {
691 return Objects.hash(tag(), size, alignment);
692 }
693
694 @Override
695 public void verifyUnbox(Deque<Class<?>> stack) {
696 throw new UnsupportedOperationException();
697 }
698
699 @Override
700 public void verifyBox(Deque<Class<?>> stack) {
701 stack.push(MemorySegment.class);
702 }
703
704 @Override
705 public void unbox(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc, NativeScope scope) {
706 throw new UnsupportedOperationException();
707 }
708
709 @Override
710 public void box(Deque<Object> stack, BindingInterpreter.LoadFunc loadFunc) {
711 stack.push(MemorySegment.allocateNative(size, alignment));
712 }
713
714 @Override
715 public MethodHandle specializeUnbox(MethodHandle specializedHandle, int insertPos) {
716 throw new UnsupportedOperationException();
717 }
718
719 @Override
720 public MethodHandle specializeBox(MethodHandle returnFilter) {
721 return collectArguments(returnFilter, 0, insertArguments(MH_ALLOCATE_BUFFER, 0, size, alignment));
722 }
723 }
724
725 /**
726 * CONVERT_ADDRESS()
727 * When unboxing: pops a 'MemoryAddress' from the operand stack, converts it to a 'long',
728 * and pushes that onto the operand stack
729 * When boxing: pops a 'long' from the operand stack, converts it to a 'MemoryAddress',
730 * and pushes that onto the operand stack
731 */
732 public static class ConvertAddress extends Binding {
733 private static final ConvertAddress INSTANCE = new ConvertAddress();
734 private ConvertAddress() {
735 super(Tag.CONVERT_ADDRESS);
736 }
737
738 @Override
739 public String toString() {
740 return "BoxAddress{" +
741 "tag=" + tag() +
742 "}";
743 }
744
745 @Override
746 public int hashCode() {
747 return tag().hashCode();
748 }
749
750 @Override
751 public boolean equals(Object o) {
752 if (this == o) return true;
753 return o != null && getClass() == o.getClass();
754 }
755
756 @Override
757 public void verifyUnbox(Deque<Class<?>> stack) {
758 Class<?> actualType = stack.pop();
759 SharedUtils.checkType(actualType, MemoryAddress.class);
760 stack.push(long.class);
761 }
762
763 @Override
764 public void verifyBox(Deque<Class<?>> stack) {
765 Class<?> actualType = stack.pop();
766 SharedUtils.checkType(actualType, long.class);
767 stack.push(MemoryAddress.class);
768 }
769
770 @Override
771 public void unbox(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc, NativeScope scope) {
772 stack.push(((MemoryAddress) stack.pop()).toRawLongValue());
773 }
774
775 @Override
776 public void box(Deque<Object> stack, BindingInterpreter.LoadFunc loadFunc) {
777 stack.push(MemoryAddress.ofLong((long) stack.pop()));
778 }
779
780 @Override
781 public MethodHandle specializeUnbox(MethodHandle specializedHandle, int insertPos) {
782 return filterArguments(specializedHandle, insertPos, MH_UNBOX_ADDRESS);
783 }
784
785 @Override
786 public MethodHandle specializeBox(MethodHandle returnFilter) {
787 return filterArguments(returnFilter, 0, MH_BOX_ADDRESS);
788 }
789 }
790
791 /**
792 * BASE_ADDRESS()
793 * Pops a MemorySegment from the operand stack, and takes the base address of the segment
794 * (the MemoryAddress that points to the start), and pushes that onto the operand stack
795 */
796 public static class BaseAddress extends Binding {
797 private static final BaseAddress INSTANCE = new BaseAddress();
798 private BaseAddress() {
799 super(Tag.BASE_ADDRESS);
800 }
801
802 @Override
803 public String toString() {
804 return "BaseAddress{" +
805 "tag=" + tag() +
806 "}";
807 }
808
809 @Override
810 public int hashCode() {
811 return tag().hashCode();
812 }
813
814 @Override
815 public boolean equals(Object o) {
816 if (this == o) return true;
817 return o != null && getClass() == o.getClass();
818 }
819
820 @Override
821 public void verifyUnbox(Deque<Class<?>> stack) {
822 Class<?> actualType = stack.pop();
823 SharedUtils.checkType(actualType, MemorySegment.class);
824 stack.push(MemoryAddress.class);
825 }
826
827 @Override
828 public void verifyBox(Deque<Class<?>> stack) {
829 Class<?> actualType = stack.pop();
830 SharedUtils.checkType(actualType, MemorySegment.class);
831 stack.push(MemoryAddress.class);
832 }
833
834 @Override
835 public void unbox(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc, NativeScope scope) {
836 stack.push(((MemorySegment) stack.pop()).baseAddress());
837 }
838
839 @Override
840 public void box(Deque<Object> stack, BindingInterpreter.LoadFunc loadFunc) {
841 stack.push(((MemorySegment) stack.pop()).baseAddress());
842 }
843
844 @Override
845 public MethodHandle specializeUnbox(MethodHandle specializedHandle, int insertPos) {
846 return filterArguments(specializedHandle, insertPos, MH_BASE_ADDRESS);
847 }
848
849 @Override
850 public MethodHandle specializeBox(MethodHandle returnFilter) {
851 throw new UnsupportedOperationException();
852 }
853 }
854
855 /**
856 * DUP()
857 * Duplicates the value on the top of the operand stack (without popping it!),
858 * and pushes the duplicate onto the operand stack
859 */
860 public static class Dup extends Binding {
861 private static final Dup INSTANCE = new Dup();
862 private Dup() {
863 super(Tag.DUP);
864 }
865
866 @Override
867 public String toString() {
868 return "Dup{" +
869 "tag=" + tag() +
870 "}";
871 }
872
873 @Override
874 public int hashCode() {
875 return tag().hashCode();
876 }
877
878 @Override
879 public boolean equals(Object o) {
880 if (this == o) return true;
881 return o != null && getClass() == o.getClass();
882 }
883
884 @Override
885 public void verifyUnbox(Deque<Class<?>> stack) {
886 stack.push(stack.peekLast());
887 }
888
889 @Override
890 public void verifyBox(Deque<Class<?>> stack) {
891 stack.push(stack.peekLast());
892 }
893
894 @Override
895 public void unbox(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc, NativeScope scope) {
896 stack.push(stack.peekLast());
897 }
898
899 @Override
900 public void box(Deque<Object> stack, BindingInterpreter.LoadFunc loadFunc) {
901 stack.push(stack.peekLast());
902 }
903
904 /*
905 * Fixes up Y-shaped data graphs (produced by DEREFERENCE):
906 *
907 * 1. DUP()
908 * 2. DEREFERENCE(0, int.class)
909 * 3. MOVE (ignored)
910 * 4. DEREFERENCE(4, int.class)
911 * 5. MOVE (ignored)
912 *
913 * (specialized in reverse!)
914 *
915 * 5. (int, int) -> void insertPos = 1
916 * 4. (MemorySegment, int) -> void insertPos = 1
917 * 3. (MemorySegment, int) -> void insertPos = 0
918 * 2. (MemorySegment, MemorySegment) -> void insertPos = 0
919 * 1. (MemorySegment) -> void insertPos = 0
920 *
921 */
922 @Override
923 public MethodHandle specializeUnbox(MethodHandle specializedHandle, int insertPos) {
924 return mergeArguments(specializedHandle, insertPos, insertPos + 1);
925 }
926
927 /*
928 * Fixes up Y-shaped data graphs (produced by DEREFERENCE):
929 *
930 * 1. ALLOCATE_BUFFER(4, 4)
931 * 2. DUP
932 * 3. MOVE (ignored)
933 * 4. DEREFERNCE(0, int.class)
934 *
935 * (specialized in reverse!)
936 *
937 * input: (MemorySegment) -> MemorySegment (identity function of high-level return)
938 * 4. (MemorySegment, MemorySegment, int) -> MemorySegment
939 * 3. (MemorySegment, MemorySegment, int) -> MemorySegment
940 * 2. (MemorySegment, int) -> MemorySegment
941 * 1. (int) -> MemorySegment
942 *
943 */
944 @Override
945 public MethodHandle specializeBox(MethodHandle returnFilter) {
946 // assumes shape like: (MS, ..., MS, T) R
947 return mergeArguments(returnFilter, 0, returnFilter.type().parameterCount() - 2);
948 }
949 }
950 }
|