1 /*
   2  * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package jdk.internal.foreign.abi;
  26 
  27 import jdk.incubator.foreign.Addressable;
  28 import jdk.incubator.foreign.MemoryAddress;
  29 import jdk.incubator.foreign.MemoryHandles;
  30 import jdk.incubator.foreign.MemoryLayout;
  31 import jdk.incubator.foreign.MemorySegment;
  32 import jdk.incubator.foreign.ResourceScope;
  33 import jdk.incubator.foreign.SegmentAllocator;
  34 import jdk.incubator.foreign.ValueLayout;
  35 import jdk.internal.foreign.MemoryAddressImpl;
  36 import jdk.internal.foreign.ResourceScopeImpl;
  37 
  38 import java.lang.invoke.MethodHandle;
  39 import java.lang.invoke.MethodHandles;
  40 import java.lang.invoke.MethodType;
  41 import java.util.ArrayList;
  42 import java.util.Deque;
  43 import java.util.List;
  44 import java.util.Objects;
  45 
  46 import java.lang.invoke.VarHandle;
  47 import java.nio.ByteOrder;
  48 
  49 import static java.lang.invoke.MethodHandles.collectArguments;
  50 import static java.lang.invoke.MethodHandles.filterArguments;
  51 import static java.lang.invoke.MethodHandles.insertArguments;
  52 import static java.lang.invoke.MethodType.methodType;
  53 
  54 /**
  55  * The binding operators defined in the Binding class can be combined into argument and return value processing 'recipes'.
  56  *
  57  * The binding operators are interpreted using a stack-base interpreter. Operators can either consume operands from the
  58  * stack, or push them onto the stack.
  59  *
  60  * In the description of each binding we talk about 'boxing' and 'unboxing'.
  61  *  - Unboxing is the process of taking a Java value and decomposing it, and storing components into machine
  62  *    storage locations. As such, the binding interpreter stack starts with the Java value on it, and should end empty.
  63  *  - Boxing is the process of re-composing a Java value by pulling components from machine storage locations.
  64  *    If a MemorySegment is needed to store the result, one should be allocated using the ALLOCATE_BUFFER operator.
  65  *    The binding interpreter stack starts off empty, and ends with the value to be returned as the only value on it.
  66  * A binding operator can be interpreted differently based on whether we are boxing or unboxing a value. For example,
  67  * the CONVERT_ADDRESS operator 'unboxes' a MemoryAddress to a long, but 'boxes' a long to a MemoryAddress.
  68  *
  69  * Here are some examples of binding recipes derived from C declarations, and according to the Windows ABI (recipes are
  70  * ABI-specific). Note that each argument has it's own recipe, which is indicated by '[number]:' (though, the only
  71  * example that has multiple arguments is the one using varargs).
  72  *
  73  * --------------------
  74  *
  75  * void f(int i);
  76  *
  77  * Argument bindings:
  78  * 0: VM_STORE(rcx, int.class) // move an 'int' into the RCX register
  79  *
  80  * Return bindings:
  81  * none
  82  *
  83  * --------------------
  84  *
  85  * void f(int* i);
  86  *
  87  * Argument bindings:
  88  * 0: UNBOX_ADDRESS // the 'MemoryAddress' is converted into a 'long'
  89  *    VM_STORE(rcx, long.class) // the 'long' is moved into the RCX register
  90  *
  91  * Return bindings:
  92  * none
  93  *
  94  * --------------------
  95  *
  96  * int* f();
  97  *
  98  * Argument bindings:
  99  * none
 100  *
 101  * Return bindings:
 102  * 0: VM_LOAD(rax, long) // load a 'long' from the RAX register
 103  *    BOX_ADDRESS // convert the 'long' into a 'MemoryAddress'
 104  *
 105  * --------------------
 106  *
 107  * typedef struct { // fits into single register
 108  *   int x;
 109  *   int y;
 110  * } MyStruct;
 111  *
 112  * void f(MyStruct ms);
 113  *
 114  * Argument bindings:
 115  * 0: BUFFER_LOAD(0, long.class) // From the struct's memory region, load a 'long' from offset '0'
 116  *    VM_STORE(rcx, long.class) // and copy that into the RCX register
 117  *
 118  * Return bindings:
 119  * none
 120  *
 121  * --------------------
 122  *
 123  * typedef struct { // does not fit into single register
 124  *   long long x;
 125  *   long long y;
 126  * } MyStruct;
 127  *
 128  * void f(MyStruct ms);
 129  *
 130  * For the Windows ABI:
 131  *
 132  * Argument bindings:
 133  * 0: COPY(16, 8) // copy the memory region containing the struct
 134  *    BASE_ADDRESS // take the base address of the copy
 135  *    UNBOX_ADDRESS // converts the base address to a 'long'
 136  *    VM_STORE(rcx, long.class) // moves the 'long' into the RCX register
 137  *
 138  * Return bindings:
 139  * none
 140  *
 141  * For the SysV ABI:
 142  *
 143  * Argument bindings:
 144  * 0: DUP // duplicates the MemoryRegion operand
 145  *    BUFFER_LOAD(0, long.class) // loads a 'long' from offset '0'
 146  *    VM_STORE(rdx, long.class) // moves the long into the RDX register
 147  *    BUFFER_LOAD(8, long.class) // loads a 'long' from offset '8'
 148  *    VM_STORE(rcx, long.class) // moves the long into the RCX register
 149  *
 150  * Return bindings:
 151  * none
 152  *
 153  * --------------------
 154  *
 155  * typedef struct { // fits into single register
 156  *   int x;
 157  *   int y;
 158  * } MyStruct;
 159  *
 160  * MyStruct f();
 161  *
 162  * Argument bindings:
 163  * none
 164  *
 165  * Return bindings:
 166  * 0: ALLOCATE(GroupLayout(C_INT, C_INT)) // allocate a buffer with the memory layout of the struct
 167  *    DUP // duplicate the allocated buffer
 168  *    VM_LOAD(rax, long.class) // loads a 'long' from rax
 169  *    BUFFER_STORE(0, long.class) // stores a 'long' at offset 0
 170  *
 171  * --------------------
 172  *
 173  * typedef struct { // does not fit into single register
 174  *   long long x;
 175  *   long long y;
 176  * } MyStruct;
 177  *
 178  * MyStruct f();
 179  *
 180  * !! uses synthetic argument, which is a pointer to a pre-allocated buffer
 181  *
 182  * Argument bindings:
 183  * 0: UNBOX_ADDRESS // unbox the MemoryAddress synthetic argument
 184  *    VM_STORE(rcx, long.class) // moves the 'long' into the RCX register
 185  *
 186  * Return bindings:
 187  * none
 188  *
 189  * --------------------
 190  *
 191  * void f(int dummy, ...); // varargs
 192  *
 193  * f(0, 10f); // passing a float
 194  *
 195  * Argument bindings:
 196  * 0: VM_STORE(rcx, int.class) // moves the 'int dummy' into the RCX register
 197  *
 198  * 1: DUP // duplicates the '10f' argument
 199  *    VM_STORE(rdx, float.class) // move one copy into the RDX register
 200  *    VM_STORE(xmm1, float.class) // moves the other copy into the xmm2 register
 201  *
 202  * Return bindings:
 203  * none
 204  *
 205  * --------------------
 206  */
 207 public abstract class Binding {
 208     private static final MethodHandle MH_UNBOX_ADDRESS;
 209     private static final MethodHandle MH_BOX_ADDRESS;
 210     private static final MethodHandle MH_COPY_BUFFER;
 211     private static final MethodHandle MH_ALLOCATE_BUFFER;
 212     private static final MethodHandle MH_TO_SEGMENT;
 213 
 214     static {
 215         try {
 216             MethodHandles.Lookup lookup = MethodHandles.lookup();
 217             MH_UNBOX_ADDRESS = lookup.findVirtual(MemoryAddress.class, "toRawLongValue",
 218                     methodType(long.class));
 219             MH_BOX_ADDRESS = lookup.findStatic(MemoryAddress.class, "ofLong",
 220                     methodType(MemoryAddress.class, long.class));
 221             MH_COPY_BUFFER = lookup.findStatic(Binding.Copy.class, "copyBuffer",
 222                     methodType(MemorySegment.class, MemorySegment.class, long.class, long.class, Context.class));
 223             MH_ALLOCATE_BUFFER = lookup.findStatic(Binding.Allocate.class, "allocateBuffer",
 224                     methodType(MemorySegment.class, long.class, long.class, Context.class));
 225             MH_TO_SEGMENT = lookup.findStatic(Binding.ToSegment.class, "toSegment",
 226                     methodType(MemorySegment.class, MemoryAddress.class, long.class, Context.class));
 227         } catch (ReflectiveOperationException e) {
 228             throw new RuntimeException(e);
 229         }
 230     }
 231 
 232     /**
 233      * A binding context is used as an helper to carry out evaluation of certain bindings; for instance,
 234      * it helps {@link Allocate} bindings, by providing the {@link SegmentAllocator} that should be used for
 235      * the allocation operation, or {@link ToSegment} bindings, by providing the {@link ResourceScope} that
 236      * should be used to create an unsafe struct from a memory address.
 237      */
 238     public static class Context implements AutoCloseable {
 239         private final SegmentAllocator allocator;
 240         private final ResourceScope scope;
 241 
 242         private Context(SegmentAllocator allocator, ResourceScope scope) {
 243             this.allocator = allocator;
 244             this.scope = scope;
 245         }
 246 
 247         public SegmentAllocator allocator() {
 248             return allocator;
 249         }
 250 
 251         public ResourceScope scope() {
 252             return scope;
 253         }
 254 
 255         @Override
 256         public void close() {
 257             scope().close();
 258         }
 259 
 260         /**
 261          * Create a binding context from given native scope.
 262          */
 263         public static Context ofBoundedAllocator(long size) {
 264             ResourceScope scope = ResourceScope.newConfinedScope();
 265             return new Context(SegmentAllocator.newNativeArena(size, scope), scope);
 266         }
 267 
 268         /**
 269          * Create a binding context from given segment allocator. The resulting context will throw when
 270          * the context's scope is accessed.
 271          */
 272         public static Context ofAllocator(SegmentAllocator allocator) {
 273             return new Context(allocator, null) {
 274                 @Override
 275                 public ResourceScope scope() {
 276                     throw new UnsupportedOperationException();
 277                 }
 278             };
 279         }
 280 
 281         /**
 282          * Create a binding context from given scope. The resulting context will throw when
 283          * the context's allocator is accessed.
 284          */
 285         public static Context ofScope() {
 286             ResourceScope scope = ResourceScope.newConfinedScope();
 287             return new Context(null, scope) {
 288                 @Override
 289                 public SegmentAllocator allocator() { throw new UnsupportedOperationException(); }
 290             };
 291         }
 292 
 293         /**
 294          * Dummy binding context. Throws exceptions when attempting to access scope, return a throwing allocator, and has
 295          * an idempotent {@link #close()}.
 296          */
 297         public static final Context DUMMY = new Context(null, null) {
 298             @Override
 299             public SegmentAllocator allocator() {
 300                 return SharedUtils.THROWING_ALLOCATOR;
 301             }
 302 
 303             @Override
 304             public ResourceScope scope() {
 305                 throw new UnsupportedOperationException();
 306             }
 307 
 308             @Override
 309             public void close() {
 310                 // do nothing
 311             }
 312         };
 313     }
 314 
 315     enum Tag {
 316         VM_STORE,
 317         VM_LOAD,
 318         BUFFER_STORE,
 319         BUFFER_LOAD,
 320         COPY_BUFFER,
 321         ALLOC_BUFFER,
 322         BOX_ADDRESS,
 323         UNBOX_ADDRESS,
 324         TO_SEGMENT,
 325         DUP
 326     }
 327 
 328     private final Tag tag;
 329 
 330     private Binding(Tag tag) {
 331         this.tag = tag;
 332     }
 333 
 334     public Tag tag() {
 335         return tag;
 336     }
 337 
 338     public abstract void verify(Deque<Class<?>> stack);
 339 
 340     public abstract void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 341                                    BindingInterpreter.LoadFunc loadFunc, Context context);
 342 
 343     public abstract MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos);
 344 
 345     private static void checkType(Class<?> type) {
 346         if (!type.isPrimitive() || type == void.class)
 347             throw new IllegalArgumentException("Illegal type: " + type);
 348     }
 349 
 350     private static void checkOffset(long offset) {
 351         if (offset < 0)
 352             throw new IllegalArgumentException("Negative offset: " + offset);
 353     }
 354 
 355     public static VMStore vmStore(VMStorage storage, Class<?> type) {
 356         checkType(type);
 357         return new VMStore(storage, type);
 358     }
 359 
 360     public static VMLoad vmLoad(VMStorage storage, Class<?> type) {
 361         checkType(type);
 362         return new VMLoad(storage, type);
 363     }
 364 
 365     public static BufferStore bufferStore(long offset, Class<?> type) {
 366         checkType(type);
 367         checkOffset(offset);
 368         return new BufferStore(offset, type);
 369     }
 370 
 371     public static BufferLoad bufferLoad(long offset, Class<?> type) {
 372         checkType(type);
 373         checkOffset(offset);
 374         return new BufferLoad(offset, type);
 375     }
 376 
 377     public static Copy copy(MemoryLayout layout) {
 378         return new Copy(layout.byteSize(), layout.byteAlignment());
 379     }
 380 
 381     public static Allocate allocate(MemoryLayout layout) {
 382         return new Allocate(layout.byteSize(), layout.byteAlignment());
 383     }
 384 
 385     public static BoxAddress boxAddress() {
 386         return BoxAddress.INSTANCE;
 387     }
 388 
 389     public static UnboxAddress unboxAddress() {
 390         return UnboxAddress.INSTANCE.get(MemoryAddress.class);
 391     }
 392 
 393     public static UnboxAddress unboxAddress(Class<?> carrier) {
 394         return UnboxAddress.INSTANCE.get(carrier);
 395     }
 396 
 397     public static ToSegment toSegment(MemoryLayout layout) {
 398         return new ToSegment(layout.byteSize());
 399     }
 400 
 401     public static Dup dup() {
 402         return Dup.INSTANCE;
 403     }
 404 
 405 
 406     public static Binding.Builder builder() {
 407         return new Binding.Builder();
 408     }
 409 
 410     @Override
 411     public boolean equals(Object o) {
 412         if (this == o) return true;
 413         if (o == null || getClass() != o.getClass()) return false;
 414         Binding binding = (Binding) o;
 415         return tag == binding.tag;
 416     }
 417 
 418     @Override
 419     public int hashCode() {
 420         return Objects.hash(tag);
 421     }
 422 
 423     /**
 424      * A builder helper class for generating lists of Bindings
 425      */
 426     public static class Builder {
 427         private final List<Binding> bindings = new ArrayList<>();
 428 
 429         public Binding.Builder vmStore(VMStorage storage, Class<?> type) {
 430             bindings.add(Binding.vmStore(storage, type));
 431             return this;
 432         }
 433 
 434         public Binding.Builder vmLoad(VMStorage storage, Class<?> type) {
 435             bindings.add(Binding.vmLoad(storage, type));
 436             return this;
 437         }
 438 
 439         public Binding.Builder bufferStore(long offset, Class<?> type) {
 440             bindings.add(Binding.bufferStore(offset, type));
 441             return this;
 442         }
 443 
 444         public Binding.Builder bufferLoad(long offset, Class<?> type) {
 445             bindings.add(Binding.bufferLoad(offset, type));
 446             return this;
 447         }
 448 
 449         public Binding.Builder copy(MemoryLayout layout) {
 450             bindings.add(Binding.copy(layout));
 451             return this;
 452         }
 453 
 454         public Binding.Builder allocate(MemoryLayout layout) {
 455             bindings.add(Binding.allocate(layout));
 456             return this;
 457         }
 458 
 459         public Binding.Builder boxAddress() {
 460             bindings.add(Binding.boxAddress());
 461             return this;
 462         }
 463 
 464         public Binding.Builder unboxAddress() {
 465             bindings.add(Binding.unboxAddress());
 466             return this;
 467         }
 468 
 469         public Binding.Builder unboxAddress(Class<?> carrier) {
 470             bindings.add(Binding.unboxAddress(carrier));
 471             return this;
 472         }
 473 
 474         public Binding.Builder toSegment(MemoryLayout layout) {
 475             bindings.add(Binding.toSegment(layout));
 476             return this;
 477         }
 478 
 479         public Binding.Builder dup() {
 480             bindings.add(Binding.dup());
 481             return this;
 482         }
 483 
 484         public List<Binding> build() {
 485             return new ArrayList<>(bindings);
 486         }
 487     }
 488 
 489     static abstract class Move extends Binding {
 490         private final VMStorage storage;
 491         private final Class<?> type;
 492 
 493         private Move(Tag tag, VMStorage storage, Class<?> type) {
 494             super(tag);
 495             this.storage = storage;
 496             this.type = type;
 497         }
 498 
 499         public VMStorage storage() {
 500             return storage;
 501         }
 502 
 503         public Class<?> type() {
 504             return type;
 505         }
 506 
 507         @Override
 508         public boolean equals(Object o) {
 509             if (this == o) return true;
 510             if (o == null || getClass() != o.getClass()) return false;
 511             if (!super.equals(o)) return false;
 512             Move move = (Move) o;
 513             return Objects.equals(storage, move.storage) &&
 514                     Objects.equals(type, move.type);
 515         }
 516 
 517         @Override
 518         public int hashCode() {
 519             return Objects.hash(super.hashCode(), storage, type);
 520         }
 521     }
 522 
 523     /**
 524      * VM_STORE([storage location], [type])
 525      * Pops a [type] from the operand stack, and moves it to [storage location]
 526      * The [type] must be one of byte, short, char, int, long, float, or double
 527      */
 528     public static class VMStore extends Move {
 529         private VMStore(VMStorage storage, Class<?> type) {
 530             super(Tag.VM_STORE, storage, type);
 531         }
 532 
 533         @Override
 534         public void verify(Deque<Class<?>> stack) {
 535             Class<?> actualType = stack.pop();
 536             Class<?> expectedType = type();
 537             SharedUtils.checkType(actualType, expectedType);
 538         }
 539 
 540         @Override
 541         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 542                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 543             storeFunc.store(storage(), type(), stack.pop());
 544         }
 545 
 546         @Override
 547         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 548             return specializedHandle; // no-op
 549         }
 550 
 551         @Override
 552         public String toString() {
 553             return "VMStore{" +
 554                     "storage=" + storage() +
 555                     ", type=" + type() +
 556                     '}';
 557         }
 558     }
 559 
 560     /**
 561      * VM_LOAD([storage location], [type])
 562      * Loads a [type] from [storage location], and pushes it onto the operand stack.
 563      * The [type] must be one of byte, short, char, int, long, float, or double
 564      */
 565     public static class VMLoad extends Move {
 566         private VMLoad(VMStorage storage, Class<?> type) {
 567             super(Tag.VM_LOAD, storage, type);
 568         }
 569 
 570         @Override
 571         public void verify(Deque<Class<?>> stack) {
 572             stack.push(type());
 573         }
 574 
 575         @Override
 576         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 577                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 578             stack.push(loadFunc.load(storage(), type()));
 579         }
 580 
 581         @Override
 582         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 583             return specializedHandle; // no-op
 584         }
 585 
 586         @Override
 587         public String toString() {
 588             return "VMLoad{" +
 589                     "storage=" + storage() +
 590                     ", type=" + type() +
 591                     '}';
 592         }
 593     }
 594 
 595     private static abstract class Dereference extends Binding {
 596         private final long offset;
 597         private final Class<?> type;
 598 
 599         private Dereference(Tag tag, long offset, Class<?> type) {
 600             super(tag);
 601             this.offset = offset;
 602             this.type = type;
 603         }
 604 
 605         public long offset() {
 606             return offset;
 607         }
 608 
 609         public Class<?> type() {
 610             return type;
 611         }
 612 
 613         @Override
 614         public boolean equals(Object o) {
 615             if (this == o) return true;
 616             if (o == null || getClass() != o.getClass()) return false;
 617             if (!super.equals(o)) return false;
 618             Dereference that = (Dereference) o;
 619             return offset == that.offset &&
 620                     Objects.equals(type, that.type);
 621         }
 622 
 623         @Override
 624         public int hashCode() {
 625             return Objects.hash(super.hashCode(), offset, type);
 626         }
 627 
 628         public VarHandle varHandle() {
 629             // alignment is set to 1 byte here to avoid exceptions for cases where we do super word
 630             // copies of e.g. 2 int fields of a struct as a single long, while the struct is only
 631             // 4-byte-aligned (since it only contains ints)
 632             ValueLayout layout = MemoryLayout.valueLayout(type(), ByteOrder.nativeOrder()).withBitAlignment(8);
 633             return MemoryHandles.insertCoordinates(MemoryHandles.varHandle(layout), 1, offset);
 634         }
 635     }
 636 
 637     /**
 638      * BUFFER_STORE([offset into memory region], [type])
 639      * Pops a MemorySegment from the operand stack, loads a [type] from
 640      * [offset into memory region] from it, and pushes it onto the operand stack.
 641      * The [type] must be one of byte, short, char, int, long, float, or double
 642      */
 643     public static class BufferStore extends Dereference {
 644         private BufferStore(long offset, Class<?> type) {
 645             super(Tag.BUFFER_STORE, offset, type);
 646         }
 647 
 648         @Override
 649         public void verify(Deque<Class<?>> stack) {
 650             Class<?> storeType = stack.pop();
 651             SharedUtils.checkType(storeType, type());
 652             Class<?> segmentType = stack.pop();
 653             SharedUtils.checkType(segmentType, MemorySegment.class);
 654         }
 655 
 656         @Override
 657         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 658                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 659             Object value = stack.pop();
 660             MemorySegment operand = (MemorySegment) stack.pop();
 661             MemorySegment writeAddress = operand.asSlice(offset());
 662             SharedUtils.write(writeAddress, type(), value);
 663         }
 664 
 665         @Override
 666         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 667             MethodHandle setter = varHandle().toMethodHandle(VarHandle.AccessMode.SET);
 668             setter = setter.asType(methodType(void.class, MemorySegment.class, type()));
 669             return collectArguments(specializedHandle, insertPos + 1, setter);
 670         }
 671 
 672         @Override
 673         public String toString() {
 674             return "BufferStore{" +
 675                     "offset=" + offset() +
 676                     ", type=" + type() +
 677                     '}';
 678         }
 679     }
 680 
 681     /**
 682      * BUFFER_LOAD([offset into memory region], [type])
 683      * Pops a [type], and then a MemorySegment from the operand stack,
 684      * and then stores [type] to [offset into memory region] of the MemorySegment.
 685      * The [type] must be one of byte, short, char, int, long, float, or double
 686      */
 687     public static class BufferLoad extends Dereference {
 688         private BufferLoad(long offset, Class<?> type) {
 689             super(Tag.BUFFER_LOAD, offset, type);
 690         }
 691 
 692         @Override
 693         public void verify(Deque<Class<?>> stack) {
 694             Class<?> actualType = stack.pop();
 695             SharedUtils.checkType(actualType, MemorySegment.class);
 696             Class<?> newType = type();
 697             stack.push(newType);
 698         }
 699 
 700         @Override
 701         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 702                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 703             MemorySegment operand = (MemorySegment) stack.pop();
 704             MemorySegment readAddress = operand.asSlice(offset());
 705             stack.push(SharedUtils.read(readAddress, type()));
 706         }
 707 
 708         @Override
 709         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 710             MethodHandle filter = varHandle()
 711                     .toMethodHandle(VarHandle.AccessMode.GET)
 712                     .asType(methodType(type(), MemorySegment.class));
 713             return filterArguments(specializedHandle, insertPos, filter);
 714         }
 715 
 716         @Override
 717         public String toString() {
 718             return "BufferLoad{" +
 719                     "offset=" + offset() +
 720                     ", type=" + type() +
 721                     '}';
 722         }
 723     }
 724 
 725     /**
 726      * COPY([size], [alignment])
 727      *   Creates a new MemorySegment with the given [size] and [alignment],
 728      *     and copies contents from a MemorySegment popped from the top of the operand stack into this new buffer,
 729      *     and pushes the new buffer onto the operand stack
 730      */
 731     public static class Copy extends Binding {
 732         private final long size;
 733         private final long alignment;
 734 
 735         private Copy(long size, long alignment) {
 736             super(Tag.COPY_BUFFER);
 737             this.size = size;
 738             this.alignment = alignment;
 739         }
 740 
 741         private static MemorySegment copyBuffer(MemorySegment operand, long size, long alignment,
 742                                                     Context context) {
 743             return context.allocator().allocate(size, alignment)
 744                             .copyFrom(operand.asSlice(0, size));
 745         }
 746 
 747         public long size() {
 748             return size;
 749         }
 750 
 751         public long alignment() {
 752             return alignment;
 753         }
 754 
 755         @Override
 756         public String toString() {
 757             return "Copy{" +
 758                     "tag=" + tag() +
 759                     ", size=" + size +
 760                     ", alignment=" + alignment +
 761                     '}';
 762         }
 763 
 764         @Override
 765         public void verify(Deque<Class<?>> stack) {
 766             Class<?> actualType = stack.pop();
 767             SharedUtils.checkType(actualType, MemorySegment.class);
 768             stack.push(MemorySegment.class);
 769         }
 770 
 771         @Override
 772         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 773                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 774             MemorySegment operand = (MemorySegment) stack.pop();
 775             MemorySegment copy = copyBuffer(operand, size, alignment, context);
 776             stack.push(copy);
 777         }
 778 
 779         @Override
 780         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 781             MethodHandle filter = insertArguments(MH_COPY_BUFFER, 1, size, alignment);
 782             specializedHandle = collectArguments(specializedHandle, insertPos, filter);
 783             return SharedUtils.mergeArguments(specializedHandle, allocatorPos, insertPos + 1);
 784         }
 785 
 786         @Override
 787         public boolean equals(Object o) {
 788             if (this == o) return true;
 789             if (o == null || getClass() != o.getClass()) return false;
 790             if (!super.equals(o)) return false;
 791             Copy copy = (Copy) o;
 792             return size == copy.size &&
 793                     alignment == copy.alignment;
 794         }
 795 
 796         @Override
 797         public int hashCode() {
 798             return Objects.hash(super.hashCode(), size, alignment);
 799         }
 800     }
 801 
 802     /**
 803      * ALLOCATE([size], [alignment])
 804      *   Creates a new MemorySegment with the give [size] and [alignment], and pushes it onto the operand stack.
 805      */
 806     public static class Allocate extends Binding {
 807         private final long size;
 808         private final long alignment;
 809 
 810         private Allocate(long size, long alignment) {
 811             super(Tag.ALLOC_BUFFER);
 812             this.size = size;
 813             this.alignment = alignment;
 814         }
 815 
 816         private static MemorySegment allocateBuffer(long size, long allignment, Context context) {
 817             return context.allocator().allocate(size, allignment);
 818         }
 819 
 820         public long size() {
 821             return size;
 822         }
 823 
 824         public long alignment() {
 825             return alignment;
 826         }
 827 
 828         @Override
 829         public String toString() {
 830             return "AllocateBuffer{" +
 831                     "tag=" + tag() +
 832                     "size=" + size +
 833                     ", alignment=" + alignment +
 834                     '}';
 835         }
 836 
 837         @Override
 838         public void verify(Deque<Class<?>> stack) {
 839             stack.push(MemorySegment.class);
 840         }
 841 
 842         @Override
 843         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 844                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 845             stack.push(allocateBuffer(size, alignment, context));
 846         }
 847 
 848         @Override
 849         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 850             MethodHandle allocateBuffer = insertArguments(MH_ALLOCATE_BUFFER, 0, size, alignment);
 851             specializedHandle = collectArguments(specializedHandle, insertPos, allocateBuffer);
 852             return SharedUtils.mergeArguments(specializedHandle, allocatorPos, insertPos);
 853         }
 854 
 855         @Override
 856         public boolean equals(Object o) {
 857             if (this == o) return true;
 858             if (o == null || getClass() != o.getClass()) return false;
 859             if (!super.equals(o)) return false;
 860             Allocate allocate = (Allocate) o;
 861             return size == allocate.size &&
 862                     alignment == allocate.alignment;
 863         }
 864 
 865         @Override
 866         public int hashCode() {
 867             return Objects.hash(super.hashCode(), size, alignment);
 868         }
 869     }
 870 
 871     /**
 872      * UNBOX_ADDRESS()
 873      * Pops a 'MemoryAddress' from the operand stack, converts it to a 'long',
 874      *     and pushes that onto the operand stack.
 875      */
 876     public static class UnboxAddress extends Binding {
 877 
 878         static final ClassValue<UnboxAddress> INSTANCE = new ClassValue<>() {
 879             @Override
 880             protected UnboxAddress computeValue(Class<?> type) {
 881                 return new UnboxAddress(type);
 882             }
 883         };
 884 
 885         final Class<?> carrier;
 886         final MethodHandle toAddress;
 887 
 888         private UnboxAddress(Class<?> carrier) {
 889             super(Tag.UNBOX_ADDRESS);
 890             this.carrier = carrier;
 891             try {
 892                 this.toAddress = MethodHandles.lookup().findVirtual(carrier, "address", MethodType.methodType(MemoryAddress.class));
 893             } catch (Throwable ex) {
 894                 throw new IllegalArgumentException(ex);
 895             }
 896         }
 897 
 898         @Override
 899         public void verify(Deque<Class<?>> stack) {
 900             Class<?> actualType = stack.pop();
 901             SharedUtils.checkType(actualType, carrier);
 902             stack.push(long.class);
 903         }
 904 
 905         @Override
 906         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 907                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 908             stack.push(((Addressable)stack.pop()).address().toRawLongValue());
 909         }
 910 
 911         @Override
 912         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 913             return filterArguments(specializedHandle, insertPos,
 914                     MethodHandles.filterReturnValue(toAddress, MH_UNBOX_ADDRESS));
 915         }
 916 
 917         @Override
 918         public String toString() {
 919             return "UnboxAddress{}";
 920         }
 921     }
 922 
 923     /**
 924      * BOX_ADDRESS()
 925      * Pops a 'long' from the operand stack, converts it to a 'MemoryAddress',
 926      *     and pushes that onto the operand stack.
 927      */
 928     public static class BoxAddress extends Binding {
 929         private static final BoxAddress INSTANCE = new BoxAddress();
 930         private BoxAddress() {
 931             super(Tag.BOX_ADDRESS);
 932         }
 933 
 934         @Override
 935         public void verify(Deque<Class<?>> stack) {
 936             Class<?> actualType = stack.pop();
 937             SharedUtils.checkType(actualType, long.class);
 938             stack.push(MemoryAddress.class);
 939         }
 940 
 941         @Override
 942         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 943                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 944             stack.push(MemoryAddress.ofLong((long) stack.pop()));
 945         }
 946 
 947         @Override
 948         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 949             return filterArguments(specializedHandle, insertPos, MH_BOX_ADDRESS);
 950         }
 951 
 952         @Override
 953         public String toString() {
 954             return "BoxAddress{}";
 955         }
 956     }
 957 
 958     /**
 959      * TO_SEGMENT([size])
 960      *   Pops a MemoryAddress from the operand stack, and converts it to a MemorySegment
 961      *   with the given size, and pushes that onto the operand stack
 962      */
 963     public static class ToSegment extends Binding {
 964         private final long size;
 965         // FIXME alignment?
 966 
 967         public ToSegment(long size) {
 968             super(Tag.TO_SEGMENT);
 969             this.size = size;
 970         }
 971 
 972         private static MemorySegment toSegment(MemoryAddress operand, long size, Context context) {
 973             return MemoryAddressImpl.ofLongUnchecked(operand.toRawLongValue(), size, (ResourceScopeImpl) context.scope);
 974         }
 975 
 976         @Override
 977         public void verify(Deque<Class<?>> stack) {
 978             Class<?> actualType = stack.pop();
 979             SharedUtils.checkType(actualType, MemoryAddress.class);
 980             stack.push(MemorySegment.class);
 981         }
 982 
 983         @Override
 984         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 985                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 986             MemoryAddress operand = (MemoryAddress) stack.pop();
 987             MemorySegment segment = toSegment(operand, size, context);
 988             stack.push(segment);
 989         }
 990 
 991         @Override
 992         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 993             MethodHandle toSegmentHandle = insertArguments(MH_TO_SEGMENT, 1, size);
 994             specializedHandle = collectArguments(specializedHandle, insertPos, toSegmentHandle);
 995             return SharedUtils.mergeArguments(specializedHandle, allocatorPos, insertPos + 1);
 996         }
 997 
 998         @Override
 999         public String toString() {
1000             return "ToSegemnt{" +
1001                     "size=" + size +
1002                     '}';
1003         }
1004 
1005         @Override
1006         public boolean equals(Object o) {
1007             if (this == o) return true;
1008             if (o == null || getClass() != o.getClass()) return false;
1009             if (!super.equals(o)) return false;
1010             ToSegment toSegemnt = (ToSegment) o;
1011             return size == toSegemnt.size;
1012         }
1013 
1014         @Override
1015         public int hashCode() {
1016             return Objects.hash(super.hashCode(), size);
1017         }
1018     }
1019 
1020     /**
1021      * DUP()
1022      *   Duplicates the value on the top of the operand stack (without popping it!),
1023      *   and pushes the duplicate onto the operand stack
1024      */
1025     public static class Dup extends Binding {
1026         private static final Dup INSTANCE = new Dup();
1027         private Dup() {
1028             super(Tag.DUP);
1029         }
1030 
1031         @Override
1032         public void verify(Deque<Class<?>> stack) {
1033             stack.push(stack.peekLast());
1034         }
1035 
1036         @Override
1037         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
1038                               BindingInterpreter.LoadFunc loadFunc, Context context) {
1039             stack.push(stack.peekLast());
1040         }
1041 
1042         /*
1043          * Fixes up Y-shaped data graphs (produced by DEREFERENCE):
1044          *
1045          * 1. DUP()
1046          * 2. BUFFER_LOAD(0, int.class)
1047          * 3. VM_STORE  (ignored)
1048          * 4. BUFFER_LOAD(4, int.class)
1049          * 5. VM_STORE  (ignored)
1050          *
1051          * (specialized in reverse!)
1052          *
1053          * 5. (int, int) -> void                       insertPos = 1
1054          * 4. (MemorySegment, int) -> void             insertPos = 1
1055          * 3. (MemorySegment, int) -> void             insertPos = 0
1056          * 2. (MemorySegment, MemorySegment) -> void   insertPos = 0
1057          * 1. (MemorySegment) -> void                  insertPos = 0
1058          *
1059          */
1060         @Override
1061         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
1062             return SharedUtils.mergeArguments(specializedHandle, insertPos, insertPos + 1);
1063         }
1064 
1065         @Override
1066         public boolean equals(Object o) {
1067             if (this == o) return true;
1068             return o != null && getClass() == o.getClass();
1069         }
1070 
1071         @Override
1072         public String toString() {
1073             return "Dup{}";
1074         }
1075     }
1076 }