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.MemoryHandles;
26 import jdk.incubator.foreign.MemoryLayout;
27
28 import java.util.ArrayList;
29 import java.util.List;
30 import java.util.Objects;
31
32 import java.lang.invoke.VarHandle;
33 import java.nio.ByteOrder;
34
35 /**
36 * The binding operators defined in the Binding class can be combined into argument and return value processing 'recipes'.
37 *
38 * The binding operators are interpreted using a stack-base interpreter. Operators can either consume operands from the
39 * stack, or push them onto the stack.
40 *
41 * In the description of each binding we talk about 'boxing' and 'unboxing'.
42 * - Unboxing is the process of taking a Java value and decomposing it, and storing components into machine
43 * storage locations. As such, the binding interpreter stack starts with the Java value on it, and should end empty.
44 * - Boxing is the process of re-composing a Java value by pulling components from machine storage locations.
45 * If a MemorySegment is needed to store the result, one should be allocated using the ALLOCATE_BUFFER operator.
46 * The binding interpreter stack starts off empty, and ends with the value to be returned as the only value on it.
47 * A binding operator can be interpreted differently based on whether we are boxing or unboxing a value. For example,
48 * the CONVERT_ADDRESS operator 'unboxes' a MemoryAddress to a long, but 'boxes' a long to a MemoryAddress.
49 *
50 * Here are some examples of binding recipes derived from C declarations, and according to the Windows ABI (recipes are
51 * ABI-specific). Note that each argument has it's own recipe, which is indicated by '[number]:' (though, the only
52 * example that has multiple arguments is the one using varargs).
53 *
169 *
170 * --------------------
171 *
172 * void f(int dummy, ...); // varargs
173 *
174 * f(0, 10f); // passing a float
175 *
176 * Argument bindings:
177 * 0: MOVE(rcx, int.class) // moves the 'int dummy' into the RCX register
178 *
179 * 1: DUP // duplicates the '10f' argument
180 * MOVE(rdx, float.class) // move one copy into the RDX register
181 * MOVE(xmm1, float.class) // moves the other copy into the xmm2 register
182 *
183 * Return bindings:
184 * none
185 *
186 * --------------------
187 */
188 public abstract class Binding {
189 enum Tag {
190 MOVE,
191 DEREFERENCE,
192 COPY_BUFFER,
193 ALLOC_BUFFER,
194 CONVERT_ADDRESS,
195 BASE_ADDRESS,
196 DUP
197 }
198
199 private final Tag tag;
200
201 private Binding(Tag tag) {
202 this.tag = tag;
203 }
204
205 public Tag tag() {
206 return tag;
207 }
208
209 private static void checkType(Class<?> type) {
210 if (!type.isPrimitive() || type == void.class || type == boolean.class)
211 throw new IllegalArgumentException("Illegal type: " + type);
212 }
213
214 public static Move move(VMStorage storage, Class<?> type) {
215 checkType(type);
216 return new Move(storage, type);
217 }
218
219 public static Dereference dereference(long offset, Class<?> type) {
220 checkType(type);
221 if (offset < 0)
222 throw new IllegalArgumentException("Negative offset: " + offset);
223 return new Dereference(offset, type);
224 }
225
226 public static Copy copy(MemoryLayout layout) {
227 return new Copy(layout.byteSize(), layout.byteAlignment());
228 }
229
230 public static Allocate allocate(MemoryLayout layout) {
231 return new Allocate(layout.byteSize(), layout.byteAlignment());
232 }
233
323 return "Move{" +
324 "tag=" + tag() +
325 ", storage=" + storage +
326 ", type=" + type +
327 '}';
328 }
329
330 @Override
331 public boolean equals(Object o) {
332 if (this == o) return true;
333 if (o == null || getClass() != o.getClass()) return false;
334 Move move = (Move) o;
335 return storage.equals(move.storage) &&
336 type.equals(move.type);
337 }
338
339 @Override
340 public int hashCode() {
341 return Objects.hash(tag(), storage, type);
342 }
343 }
344
345 /**
346 * DEREFERENCE([offset into memory region], [type])
347 * When unboxing: pops a MemorySegment from the operand stack,
348 * loads a [type] from [offset into memory region] from it, and pushes it onto the operand stack
349 * When boxing: pops a [type], and then a MemorySegment from the operand stack,
350 * and then stores [type] to [offset into memory region] of the MemorySegment
351 * The [type] must be one of byte, short, char, int, long, float, or double
352 */
353 public static class Dereference extends Binding {
354 private final long offset;
355 private final Class<?> type;
356
357 private Dereference(long offset, Class<?> type) {
358 super(Tag.DEREFERENCE);
359 this.offset = offset;
360 this.type = type;
361 }
362
363 public long offset() {
364 return offset;
365 }
366
367 public Class<?> type() {
368 return type;
369 }
370
371 public VarHandle varHandle() {
372 return MemoryHandles.withOffset(MemoryHandles.varHandle(type, ByteOrder.nativeOrder()), offset);
373 }
374
375 @Override
376 public String toString() {
377 return "Dereference{" +
378 "tag=" + tag() +
379 ", offset=" + offset +
380 ", type=" + type +
381 '}';
382 }
383
384 @Override
385 public boolean equals(Object o) {
386 if (this == o) return true;
387 if (o == null || getClass() != o.getClass()) return false;
388 Dereference that = (Dereference) o;
389 return offset == that.offset &&
390 type.equals(that.type);
391 }
392
393 @Override
394 public int hashCode() {
395 return Objects.hash(tag(), offset, type);
396 }
397 }
398
399 /**
400 * COPY([size], [alignment])
401 * Creates a new MemorySegment with the given [size] and [alignment],
402 * and copies contents from a MemorySegment popped from the top of the operand stack into this new buffer,
403 * and pushes the new buffer onto the operand stack
404 */
405 public static class Copy extends Binding {
406 private final long size;
407 private final long alignment;
408
409 private Copy(long size, long alignment) {
410 super(Tag.COPY_BUFFER);
411 this.size = size;
412 this.alignment = alignment;
413 }
414
415 public long size() {
416 return size;
425 return "Copy{" +
426 "tag=" + tag() +
427 ", size=" + size +
428 ", alignment=" + alignment +
429 '}';
430 }
431
432 @Override
433 public boolean equals(Object o) {
434 if (this == o) return true;
435 if (o == null || getClass() != o.getClass()) return false;
436 Copy copy = (Copy) o;
437 return size == copy.size &&
438 alignment == copy.alignment;
439 }
440
441 @Override
442 public int hashCode() {
443 return Objects.hash(tag(), size, alignment);
444 }
445 }
446
447 /**
448 * ALLOCATE([size], [alignment])
449 * Creates a new MemorySegment with the give [size] and [alignment], and pushes it onto the operand stack.
450 */
451 public static class Allocate extends Binding {
452 private final long size;
453 private final long alignment;
454
455 private Allocate(long size, long alignment) {
456 super(Tag.ALLOC_BUFFER);
457 this.size = size;
458 this.alignment = alignment;
459 }
460
461 public long size() {
462 return size;
463 }
464
471 return "AllocateBuffer{" +
472 "tag=" + tag() +
473 "size=" + size +
474 ", alignment=" + alignment +
475 '}';
476 }
477
478 @Override
479 public boolean equals(Object o) {
480 if (this == o) return true;
481 if (o == null || getClass() != o.getClass()) return false;
482 Allocate that = (Allocate) o;
483 return size == that.size &&
484 alignment == that.alignment;
485 }
486
487 @Override
488 public int hashCode() {
489 return Objects.hash(tag(), size, alignment);
490 }
491 }
492
493 /**
494 * CONVERT_ADDRESS()
495 * When unboxing: pops a 'MemoryAddress' from the operand stack, converts it to a 'long',
496 * and pushes that onto the operand stack
497 * When boxing: pops a 'long' from the operand stack, converts it to a 'MemoryAddress',
498 * and pushes that onto the operand stack
499 */
500 public static class ConvertAddress extends Binding {
501 private static final ConvertAddress INSTANCE = new ConvertAddress();
502 private ConvertAddress() {
503 super(Tag.CONVERT_ADDRESS);
504 }
505
506 @Override
507 public String toString() {
508 return "BoxAddress{" +
509 "tag=" + tag() +
510 "}";
511 }
512
513 @Override
514 public int hashCode() {
515 return tag().hashCode();
516 }
517
518 @Override
519 public boolean equals(Object o) {
520 if (this == o) return true;
521 return o != null && getClass() == o.getClass();
522 }
523 }
524
525 /**
526 * BASE_ADDRESS()
527 * Pops a MemorySegment from the operand stack, and takes the base address of the segment
528 * (the MemoryAddress that points to the start), and pushes that onto the operand stack
529 */
530 public static class BaseAddress extends Binding {
531 private static final BaseAddress INSTANCE = new BaseAddress();
532 private BaseAddress() {
533 super(Tag.BASE_ADDRESS);
534 }
535
536 @Override
537 public String toString() {
538 return "BaseAddress{" +
539 "tag=" + tag() +
540 "}";
541 }
542
543 @Override
544 public int hashCode() {
545 return tag().hashCode();
546 }
547
548 @Override
549 public boolean equals(Object o) {
550 if (this == o) return true;
551 return o != null && getClass() == o.getClass();
552 }
553 }
554
555 /**
556 * DUP()
557 * Duplicates the value on the top of the operand stack (without popping it!),
558 * and pushes the duplicate onto the operand stack
559 */
560 public static class Dup extends Binding {
561 private static final Dup INSTANCE = new Dup();
562 private Dup() {
563 super(Tag.DUP);
564 }
565
566 @Override
567 public String toString() {
568 return "Dup{" +
569 "tag=" + tag() +
570 "}";
571 }
572
573 @Override
574 public int hashCode() {
575 return tag().hashCode();
576 }
577
578 @Override
579 public boolean equals(Object o) {
580 if (this == o) return true;
581 return o != null && getClass() == o.getClass();
582 }
583 }
584 }
|
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 *
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
471 public long offset() {
472 return offset;
473 }
474
475 public Class<?> type() {
476 return type;
477 }
478
479 @Override
480 public String toString() {
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 }
|