1 /*
  2  * Copyright (c) 2019, 2020, 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.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  * --------------------
 71  *
 72  * void f(int i);
 73  *
 74  * Argument bindings:
 75  * 0: MOVE(rcx, int.class) // move an 'int' into the RCX register
 76  *
 77  * Return bindings:
 78  * none
 79  *
 80  * --------------------
 81  *
 82  * void f(int* i);
 83  *
 84  * Argument bindings:
 85  * 0: CONVERT_ADDRESS // the 'MemoryAddress' is converted into a 'long'
 86  *    MOVE(rcx, long.class) // the 'long' is moved into the RCX register
 87  *
 88  * Return bindings:
 89  * none
 90  *
 91  * --------------------
 92  *
 93  * int* f();
 94  *
 95  * Argument bindings:
 96  * none
 97  *
 98  * Return bindings:
 99  * 0: MOVE(rax, long) // load a 'long' from the RAX register
100  *    CONVERT_ADDRESS // convert the 'long' into a 'MemoryAddress'
101  *
102  * --------------------
103  *
104  * typedef struct { // fits into single register
105  *   int x;
106  *   int y;
107  * } MyStruct;
108  *
109  * void f(MyStruct ms);
110  *
111  * Argument bindings:
112  * 0: DEREFERENCE(0, long.class) // From the struct's memory region, load a 'long' from offset '0'
113  *    MOVE(rcx, long.class) // and copy that into the RCX register
114  *
115  * Return bindings:
116  * none
117  *
118  * --------------------
119  *
120  * typedef struct { // does not fit into single register
121  *   long long x;
122  *   long long y;
123  * } MyStruct;
124  *
125  * void f(MyStruct ms);
126  *
127  * For the Windows ABI:
128  *
129  * Argument bindings:
130  * 0: COPY(16, 8) // copy the memory region containing the struct
131  *    BASE_ADDRESS // take the base address of the copy
132  *    CONVERT_ADDRESS // converts the base address to a 'long'
133  *    MOVE(rcx, long.class) // moves the 'long' into the RCX register
134  *
135  * Return bindings:
136  * none
137  *
138  * For the SysV ABI:
139  *
140  * Argument bindings:
141  * 0: DUP // duplicates the MemoryRegion operand
142  *    DEREFERENCE(0, long.class) // loads a 'long' from offset '0'
143  *    MOVE(rdx, long.class) // moves the long into the RDX register
144  *    DEREFERENCE(8, long.class) // loads a 'long' from offset '8'
145  *    MOVE(rcx, long.class) // moves the long into the RCX register
146  *
147  * Return bindings:
148  * none
149  *
150  * --------------------
151  *
152  * typedef struct { // fits into single register
153  *   int x;
154  *   int y;
155  * } MyStruct;
156  *
157  * MyStruct f();
158  *
159  * Argument bindings:
160  * none
161  *
162  * Return bindings:
163  * 0: ALLOCATE(GroupLayout(C_INT, C_INT)) // allocate a buffer with the memory layout of the struct
164  *    DUP // duplicate the allocated buffer
165  *    MOVE(rax, long.class) // loads a 'long' from rax
166  *    DEREFERENCE(0, long.class) // stores a 'long' at offset 0
167  *
168  * --------------------
169  *
170  * typedef struct { // does not fit into single register
171  *   long long x;
172  *   long long y;
173  * } MyStruct;
174  *
175  * MyStruct f();
176  *
177  * !! uses synthetic argument, which is a pointer to a pre-allocated buffer
178  *
179  * Argument bindings:
180  * 0: CONVERT_ADDRESS // unbox the MemoryAddress synthetic argument
181  *    MOVE(rcx, long.class) // moves the 'long' into the RCX register
182  *
183  * Return bindings:
184  * none
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 
310     public static ConvertAddress convertAddress() {
311         return ConvertAddress.INSTANCE;
312     }
313 
314     public static BaseAddress baseAddress() {
315         return BaseAddress.INSTANCE;
316     }
317 
318     public static Dup dup() {
319         return Dup.INSTANCE;
320     }
321 
322 
323     public static Binding.Builder builder() {
324         return new Binding.Builder();
325     }
326 
327     /**
328      * A builder helper class for generating lists of Bindings
329      */
330     public static class Builder {
331         private final List<Binding> bindings = new ArrayList<>();
332 
333         public Binding.Builder move(VMStorage storage, Class<?> type) {
334             bindings.add(Binding.move(storage, type));
335             return this;
336         }
337 
338         public Binding.Builder dereference(long offset, Class<?> type) {
339             bindings.add(Binding.dereference(offset, type));
340             return this;
341         }
342 
343         public Binding.Builder copy(MemoryLayout layout) {
344             bindings.add(Binding.copy(layout));
345             return this;
346         }
347 
348         public Binding.Builder allocate(MemoryLayout layout) {
349             bindings.add(Binding.allocate(layout));
350             return this;
351         }
352 
353         public Binding.Builder convertAddress() {
354             bindings.add(Binding.convertAddress());
355             return this;
356         }
357 
358         public Binding.Builder baseAddress() {
359             bindings.add(Binding.baseAddress());
360             return this;
361         }
362 
363         public Binding.Builder dup() {
364             bindings.add(Binding.dup());
365             return this;
366         }
367 
368         public List<Binding> build() {
369             return new ArrayList<>(bindings);
370         }
371     }
372 
373     /**
374      * MOVE([storage location], [type])
375      *   When unboxing: pops a [type] from the operand stack, and moves it to [storage location]
376      *   When boxing: loads a [type] from [storage location], and pushes it onto the operand stack
377      * The [type] must be one of byte, short, char, int, long, float, or double
378      */
379     public static class Move extends Binding {
380         private final VMStorage storage;
381         private final Class<?> type;
382 
383         private Move(VMStorage storage, Class<?> type) {
384             super(Tag.MOVE);
385             this.storage = storage;
386             this.type = type;
387         }
388 
389         public VMStorage storage() {
390             return storage;
391         }
392 
393         public Class<?> type() {
394             return type;
395         }
396 
397         @Override
398         public String toString() {
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;
576         }
577 
578         public long alignment() {
579             return alignment;
580         }
581 
582         @Override
583         public String toString() {
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 
667         public long alignment() {
668             return alignment;
669         }
670 
671         @Override
672         public String toString() {
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 }