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.MemoryAddress;
  28 import jdk.incubator.foreign.MemoryHandles;
  29 import jdk.incubator.foreign.MemoryLayout;
  30 import jdk.incubator.foreign.MemorySegment;
  31 import jdk.incubator.foreign.ResourceScope;
  32 import jdk.incubator.foreign.SegmentAllocator;
  33 import jdk.internal.foreign.MemoryAddressImpl;
  34 import jdk.internal.foreign.ResourceScopeImpl;
  35 
  36 import java.lang.invoke.MethodHandle;
  37 import java.lang.invoke.MethodHandles;
  38 import java.util.ArrayList;
  39 import java.util.Deque;
  40 import java.util.List;
  41 import java.util.Objects;
  42 
  43 import java.lang.invoke.VarHandle;
  44 import java.nio.ByteOrder;
  45 
  46 import static java.lang.invoke.MethodHandles.collectArguments;
  47 import static java.lang.invoke.MethodHandles.filterArguments;
  48 import static java.lang.invoke.MethodHandles.insertArguments;
  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: VM_STORE(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: UNBOX_ADDRESS // the 'MemoryAddress' is converted into a 'long'
  86  *    VM_STORE(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: VM_LOAD(rax, long) // load a 'long' from the RAX register
 100  *    BOX_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: BUFFER_LOAD(0, long.class) // From the struct's memory region, load a 'long' from offset '0'
 113  *    VM_STORE(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  *    UNBOX_ADDRESS // converts the base address to a 'long'
 133  *    VM_STORE(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  *    BUFFER_LOAD(0, long.class) // loads a 'long' from offset '0'
 143  *    VM_STORE(rdx, long.class) // moves the long into the RDX register
 144  *    BUFFER_LOAD(8, long.class) // loads a 'long' from offset '8'
 145  *    VM_STORE(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  *    VM_LOAD(rax, long.class) // loads a 'long' from rax
 166  *    BUFFER_STORE(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: UNBOX_ADDRESS // unbox the MemoryAddress synthetic argument
 181  *    VM_STORE(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: VM_STORE(rcx, int.class) // moves the 'int dummy' into the RCX register
 194  *
 195  * 1: DUP // duplicates the '10f' argument
 196  *    VM_STORE(rdx, float.class) // move one copy into the RDX register
 197  *    VM_STORE(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     private static final MethodHandle MH_TO_SEGMENT;
 211 
 212     static {
 213         try {
 214             MethodHandles.Lookup lookup = MethodHandles.lookup();
 215             MH_UNBOX_ADDRESS = lookup.findVirtual(MemoryAddress.class, "toRawLongValue",
 216                     methodType(long.class));
 217             MH_BOX_ADDRESS = lookup.findStatic(MemoryAddress.class, "ofLong",
 218                     methodType(MemoryAddress.class, long.class));
 219             MH_BASE_ADDRESS = lookup.findVirtual(MemorySegment.class, "address",
 220                     methodType(MemoryAddress.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.arenaAllocator(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         BASE_ADDRESS,
 325         TO_SEGMENT,
 326         DUP
 327     }
 328 
 329     private final Tag tag;
 330 
 331     private Binding(Tag tag) {
 332         this.tag = tag;
 333     }
 334 
 335     public Tag tag() {
 336         return tag;
 337     }
 338 
 339     public abstract void verify(Deque<Class<?>> stack);
 340 
 341     public abstract void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 342                                    BindingInterpreter.LoadFunc loadFunc, Context context);
 343 
 344     public abstract MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos);
 345 
 346     private static void checkType(Class<?> type) {
 347         if (!type.isPrimitive() || type == void.class || type == boolean.class)
 348             throw new IllegalArgumentException("Illegal type: " + type);
 349     }
 350 
 351     private static void checkOffset(long offset) {
 352         if (offset < 0)
 353             throw new IllegalArgumentException("Negative offset: " + offset);
 354     }
 355 
 356     public static VMStore vmStore(VMStorage storage, Class<?> type) {
 357         checkType(type);
 358         return new VMStore(storage, type);
 359     }
 360 
 361     public static VMLoad vmLoad(VMStorage storage, Class<?> type) {
 362         checkType(type);
 363         return new VMLoad(storage, type);
 364     }
 365 
 366     public static BufferStore bufferStore(long offset, Class<?> type) {
 367         checkType(type);
 368         checkOffset(offset);
 369         return new BufferStore(offset, type);
 370     }
 371 
 372     public static BufferLoad bufferLoad(long offset, Class<?> type) {
 373         checkType(type);
 374         checkOffset(offset);
 375         return new BufferLoad(offset, type);
 376     }
 377 
 378     public static Copy copy(MemoryLayout layout) {
 379         return new Copy(layout.byteSize(), layout.byteAlignment());
 380     }
 381 
 382     public static Allocate allocate(MemoryLayout layout) {
 383         return new Allocate(layout.byteSize(), layout.byteAlignment());
 384     }
 385 
 386     public static BoxAddress boxAddress() {
 387         return BoxAddress.INSTANCE;
 388     }
 389 
 390     public static UnboxAddress unboxAddress() {
 391         return UnboxAddress.INSTANCE;
 392     }
 393 
 394     public static BaseAddress baseAddress() {
 395         return BaseAddress.INSTANCE;
 396     }
 397 
 398     public static ToSegment toSegment(MemoryLayout layout) {
 399         return new ToSegment(layout.byteSize());
 400     }
 401 
 402     public static Dup dup() {
 403         return Dup.INSTANCE;
 404     }
 405 
 406 
 407     public static Binding.Builder builder() {
 408         return new Binding.Builder();
 409     }
 410 
 411     @Override
 412     public boolean equals(Object o) {
 413         if (this == o) return true;
 414         if (o == null || getClass() != o.getClass()) return false;
 415         Binding binding = (Binding) o;
 416         return tag == binding.tag;
 417     }
 418 
 419     @Override
 420     public int hashCode() {
 421         return Objects.hash(tag);
 422     }
 423 
 424     /**
 425      * A builder helper class for generating lists of Bindings
 426      */
 427     public static class Builder {
 428         private final List<Binding> bindings = new ArrayList<>();
 429 
 430         public Binding.Builder vmStore(VMStorage storage, Class<?> type) {
 431             bindings.add(Binding.vmStore(storage, type));
 432             return this;
 433         }
 434 
 435         public Binding.Builder vmLoad(VMStorage storage, Class<?> type) {
 436             bindings.add(Binding.vmLoad(storage, type));
 437             return this;
 438         }
 439 
 440         public Binding.Builder bufferStore(long offset, Class<?> type) {
 441             bindings.add(Binding.bufferStore(offset, type));
 442             return this;
 443         }
 444 
 445         public Binding.Builder bufferLoad(long offset, Class<?> type) {
 446             bindings.add(Binding.bufferLoad(offset, type));
 447             return this;
 448         }
 449 
 450         public Binding.Builder copy(MemoryLayout layout) {
 451             bindings.add(Binding.copy(layout));
 452             return this;
 453         }
 454 
 455         public Binding.Builder allocate(MemoryLayout layout) {
 456             bindings.add(Binding.allocate(layout));
 457             return this;
 458         }
 459 
 460         public Binding.Builder boxAddress() {
 461             bindings.add(Binding.boxAddress());
 462             return this;
 463         }
 464 
 465         public Binding.Builder unboxAddress() {
 466             bindings.add(Binding.unboxAddress());
 467             return this;
 468         }
 469 
 470         public Binding.Builder baseAddress() {
 471             bindings.add(Binding.baseAddress());
 472             return this;
 473         }
 474 
 475         public Binding.Builder toSegment(MemoryLayout layout) {
 476             bindings.add(Binding.toSegment(layout));
 477             return this;
 478         }
 479 
 480         public Binding.Builder dup() {
 481             bindings.add(Binding.dup());
 482             return this;
 483         }
 484 
 485         public List<Binding> build() {
 486             return new ArrayList<>(bindings);
 487         }
 488     }
 489 
 490     static abstract class Move extends Binding {
 491         private final VMStorage storage;
 492         private final Class<?> type;
 493 
 494         private Move(Tag tag, VMStorage storage, Class<?> type) {
 495             super(tag);
 496             this.storage = storage;
 497             this.type = type;
 498         }
 499 
 500         public VMStorage storage() {
 501             return storage;
 502         }
 503 
 504         public Class<?> type() {
 505             return type;
 506         }
 507 
 508         @Override
 509         public boolean equals(Object o) {
 510             if (this == o) return true;
 511             if (o == null || getClass() != o.getClass()) return false;
 512             if (!super.equals(o)) return false;
 513             Move move = (Move) o;
 514             return Objects.equals(storage, move.storage) &&
 515                     Objects.equals(type, move.type);
 516         }
 517 
 518         @Override
 519         public int hashCode() {
 520             return Objects.hash(super.hashCode(), storage, type);
 521         }
 522     }
 523 
 524     /**
 525      * VM_STORE([storage location], [type])
 526      * Pops a [type] from the operand stack, and moves it to [storage location]
 527      * The [type] must be one of byte, short, char, int, long, float, or double
 528      */
 529     public static class VMStore extends Move {
 530         private VMStore(VMStorage storage, Class<?> type) {
 531             super(Tag.VM_STORE, storage, type);
 532         }
 533 
 534         @Override
 535         public void verify(Deque<Class<?>> stack) {
 536             Class<?> actualType = stack.pop();
 537             Class<?> expectedType = type();
 538             SharedUtils.checkType(actualType, expectedType);
 539         }
 540 
 541         @Override
 542         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 543                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 544             storeFunc.store(storage(), type(), stack.pop());
 545         }
 546 
 547         @Override
 548         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 549             return specializedHandle; // no-op
 550         }
 551 
 552         @Override
 553         public String toString() {
 554             return "VMStore{" +
 555                     "storage=" + storage() +
 556                     ", type=" + type() +
 557                     '}';
 558         }
 559     }
 560 
 561     /**
 562      * VM_LOAD([storage location], [type])
 563      * Loads a [type] from [storage location], and pushes it onto the operand stack.
 564      * The [type] must be one of byte, short, char, int, long, float, or double
 565      */
 566     public static class VMLoad extends Move {
 567         private VMLoad(VMStorage storage, Class<?> type) {
 568             super(Tag.VM_LOAD, storage, type);
 569         }
 570 
 571         @Override
 572         public void verify(Deque<Class<?>> stack) {
 573             stack.push(type());
 574         }
 575 
 576         @Override
 577         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 578                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 579             stack.push(loadFunc.load(storage(), type()));
 580         }
 581 
 582         @Override
 583         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 584             return specializedHandle; // no-op
 585         }
 586 
 587         @Override
 588         public String toString() {
 589             return "VMLoad{" +
 590                     "storage=" + storage() +
 591                     ", type=" + type() +
 592                     '}';
 593         }
 594     }
 595 
 596     private static abstract class Dereference extends Binding {
 597         private final long offset;
 598         private final Class<?> type;
 599 
 600         private Dereference(Tag tag, long offset, Class<?> type) {
 601             super(tag);
 602             this.offset = offset;
 603             this.type = type;
 604         }
 605 
 606         public long offset() {
 607             return offset;
 608         }
 609 
 610         public Class<?> type() {
 611             return type;
 612         }
 613 
 614         @Override
 615         public boolean equals(Object o) {
 616             if (this == o) return true;
 617             if (o == null || getClass() != o.getClass()) return false;
 618             if (!super.equals(o)) return false;
 619             Dereference that = (Dereference) o;
 620             return offset == that.offset &&
 621                     Objects.equals(type, that.type);
 622         }
 623 
 624         @Override
 625         public int hashCode() {
 626             return Objects.hash(super.hashCode(), offset, type);
 627         }
 628 
 629         public VarHandle varHandle() {
 630             // alignment is set to 1 byte here to avoid exceptions for cases where we do super word
 631             // copies of e.g. 2 int fields of a struct as a single long, while the struct is only
 632             // 4-byte-aligned (since it only contains ints)
 633             return MemoryHandles.insertCoordinates(MemoryHandles.varHandle(type, 1, ByteOrder.nativeOrder()), 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             MemorySegment copy = context.allocator().allocate(size, alignment);
 744             copy.copyFrom(operand.asSlice(0, size));
 745             return copy;
 746         }
 747 
 748         public long size() {
 749             return size;
 750         }
 751 
 752         public long alignment() {
 753             return alignment;
 754         }
 755 
 756         @Override
 757         public String toString() {
 758             return "Copy{" +
 759                     "tag=" + tag() +
 760                     ", size=" + size +
 761                     ", alignment=" + alignment +
 762                     '}';
 763         }
 764 
 765         @Override
 766         public void verify(Deque<Class<?>> stack) {
 767             Class<?> actualType = stack.pop();
 768             SharedUtils.checkType(actualType, MemorySegment.class);
 769             stack.push(MemorySegment.class);
 770         }
 771 
 772         @Override
 773         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 774                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 775             MemorySegment operand = (MemorySegment) stack.pop();
 776             MemorySegment copy = copyBuffer(operand, size, alignment, context);
 777             stack.push(copy);
 778         }
 779 
 780         @Override
 781         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 782             MethodHandle filter = insertArguments(MH_COPY_BUFFER, 1, size, alignment);
 783             specializedHandle = collectArguments(specializedHandle, insertPos, filter);
 784             return SharedUtils.mergeArguments(specializedHandle, allocatorPos, insertPos + 1);
 785         }
 786 
 787         @Override
 788         public boolean equals(Object o) {
 789             if (this == o) return true;
 790             if (o == null || getClass() != o.getClass()) return false;
 791             if (!super.equals(o)) return false;
 792             Copy copy = (Copy) o;
 793             return size == copy.size &&
 794                     alignment == copy.alignment;
 795         }
 796 
 797         @Override
 798         public int hashCode() {
 799             return Objects.hash(super.hashCode(), size, alignment);
 800         }
 801     }
 802 
 803     /**
 804      * ALLOCATE([size], [alignment])
 805      *   Creates a new MemorySegment with the give [size] and [alignment], and pushes it onto the operand stack.
 806      */
 807     public static class Allocate extends Binding {
 808         private final long size;
 809         private final long alignment;
 810 
 811         private Allocate(long size, long alignment) {
 812             super(Tag.ALLOC_BUFFER);
 813             this.size = size;
 814             this.alignment = alignment;
 815         }
 816 
 817         private static MemorySegment allocateBuffer(long size, long allignment, Context context) {
 818             return context.allocator().allocate(size, allignment);
 819         }
 820 
 821         public long size() {
 822             return size;
 823         }
 824 
 825         public long alignment() {
 826             return alignment;
 827         }
 828 
 829         @Override
 830         public String toString() {
 831             return "AllocateBuffer{" +
 832                     "tag=" + tag() +
 833                     "size=" + size +
 834                     ", alignment=" + alignment +
 835                     '}';
 836         }
 837 
 838         @Override
 839         public void verify(Deque<Class<?>> stack) {
 840             stack.push(MemorySegment.class);
 841         }
 842 
 843         @Override
 844         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 845                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 846             stack.push(allocateBuffer(size, alignment, context));
 847         }
 848 
 849         @Override
 850         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 851             MethodHandle allocateBuffer = insertArguments(MH_ALLOCATE_BUFFER, 0, size, alignment);
 852             specializedHandle = collectArguments(specializedHandle, insertPos, allocateBuffer);
 853             return SharedUtils.mergeArguments(specializedHandle, allocatorPos, insertPos);
 854         }
 855 
 856         @Override
 857         public boolean equals(Object o) {
 858             if (this == o) return true;
 859             if (o == null || getClass() != o.getClass()) return false;
 860             if (!super.equals(o)) return false;
 861             Allocate allocate = (Allocate) o;
 862             return size == allocate.size &&
 863                     alignment == allocate.alignment;
 864         }
 865 
 866         @Override
 867         public int hashCode() {
 868             return Objects.hash(super.hashCode(), size, alignment);
 869         }
 870     }
 871 
 872     /**
 873      * UNBOX_ADDRESS()
 874      * Pops a 'MemoryAddress' from the operand stack, converts it to a 'long',
 875      *     and pushes that onto the operand stack.
 876      */
 877     public static class UnboxAddress extends Binding {
 878         private static final UnboxAddress INSTANCE = new UnboxAddress();
 879         private UnboxAddress() {
 880             super(Tag.UNBOX_ADDRESS);
 881         }
 882 
 883         @Override
 884         public void verify(Deque<Class<?>> stack) {
 885             Class<?> actualType = stack.pop();
 886             SharedUtils.checkType(actualType, MemoryAddress.class);
 887             stack.push(long.class);
 888         }
 889 
 890         @Override
 891         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 892                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 893             stack.push(((MemoryAddress)stack.pop()).toRawLongValue());
 894         }
 895 
 896         @Override
 897         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 898             return filterArguments(specializedHandle, insertPos, MH_UNBOX_ADDRESS);
 899         }
 900 
 901         @Override
 902         public String toString() {
 903             return "UnboxAddress{}";
 904         }
 905     }
 906 
 907     /**
 908      * BOX_ADDRESS()
 909      * Pops a 'long' from the operand stack, converts it to a 'MemoryAddress',
 910      *     and pushes that onto the operand stack.
 911      */
 912     public static class BoxAddress extends Binding {
 913         private static final BoxAddress INSTANCE = new BoxAddress();
 914         private BoxAddress() {
 915             super(Tag.BOX_ADDRESS);
 916         }
 917 
 918         @Override
 919         public void verify(Deque<Class<?>> stack) {
 920             Class<?> actualType = stack.pop();
 921             SharedUtils.checkType(actualType, long.class);
 922             stack.push(MemoryAddress.class);
 923         }
 924 
 925         @Override
 926         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 927                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 928             stack.push(MemoryAddress.ofLong((long) stack.pop()));
 929         }
 930 
 931         @Override
 932         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 933             return filterArguments(specializedHandle, insertPos, MH_BOX_ADDRESS);
 934         }
 935 
 936         @Override
 937         public String toString() {
 938             return "BoxAddress{}";
 939         }
 940     }
 941 
 942     /**
 943      * BASE_ADDRESS()
 944      *   Pops a MemorySegment from the operand stack, and takes the base address of the segment
 945      *   (the MemoryAddress that points to the start), and pushes that onto the operand stack
 946      */
 947     public static class BaseAddress extends Binding {
 948         private static final BaseAddress INSTANCE = new BaseAddress();
 949         private BaseAddress() {
 950             super(Tag.BASE_ADDRESS);
 951         }
 952 
 953         @Override
 954         public void verify(Deque<Class<?>> stack) {
 955             Class<?> actualType = stack.pop();
 956             SharedUtils.checkType(actualType, MemorySegment.class);
 957             stack.push(MemoryAddress.class);
 958         }
 959 
 960         @Override
 961         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 962                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 963             stack.push(((MemorySegment) stack.pop()).address());
 964         }
 965 
 966         @Override
 967         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 968             return filterArguments(specializedHandle, insertPos, MH_BASE_ADDRESS);
 969         }
 970 
 971         @Override
 972         public String toString() {
 973             return "BaseAddress{}";
 974         }
 975     }
 976 
 977     /**
 978      * TO_SEGMENT([size])
 979      *   Pops a MemoryAddress from the operand stack, and converts it to a MemorySegment
 980      *   with the given size, and pushes that onto the operand stack
 981      */
 982     public static class ToSegment extends Binding {
 983         private final long size;
 984         // FIXME alignment?
 985 
 986         public ToSegment(long size) {
 987             super(Tag.TO_SEGMENT);
 988             this.size = size;
 989         }
 990 
 991         private static MemorySegment toSegment(MemoryAddress operand, long size, Context context) {
 992             return MemoryAddressImpl.ofLongUnchecked(operand.toRawLongValue(), size, (ResourceScopeImpl) context.scope);
 993         }
 994 
 995         @Override
 996         public void verify(Deque<Class<?>> stack) {
 997             Class<?> actualType = stack.pop();
 998             SharedUtils.checkType(actualType, MemoryAddress.class);
 999             stack.push(MemorySegment.class);
1000         }
1001 
1002         @Override
1003         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
1004                               BindingInterpreter.LoadFunc loadFunc, Context context) {
1005             MemoryAddress operand = (MemoryAddress) stack.pop();
1006             MemorySegment segment = toSegment(operand, size, context);
1007             stack.push(segment);
1008         }
1009 
1010         @Override
1011         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
1012             MethodHandle toSegmentHandle = insertArguments(MH_TO_SEGMENT, 1, size);
1013             specializedHandle = collectArguments(specializedHandle, insertPos, toSegmentHandle);
1014             return SharedUtils.mergeArguments(specializedHandle, allocatorPos, insertPos + 1);
1015         }
1016 
1017         @Override
1018         public String toString() {
1019             return "ToSegemnt{" +
1020                     "size=" + size +
1021                     '}';
1022         }
1023 
1024         @Override
1025         public boolean equals(Object o) {
1026             if (this == o) return true;
1027             if (o == null || getClass() != o.getClass()) return false;
1028             if (!super.equals(o)) return false;
1029             ToSegment toSegemnt = (ToSegment) o;
1030             return size == toSegemnt.size;
1031         }
1032 
1033         @Override
1034         public int hashCode() {
1035             return Objects.hash(super.hashCode(), size);
1036         }
1037     }
1038 
1039     /**
1040      * DUP()
1041      *   Duplicates the value on the top of the operand stack (without popping it!),
1042      *   and pushes the duplicate onto the operand stack
1043      */
1044     public static class Dup extends Binding {
1045         private static final Dup INSTANCE = new Dup();
1046         private Dup() {
1047             super(Tag.DUP);
1048         }
1049 
1050         @Override
1051         public void verify(Deque<Class<?>> stack) {
1052             stack.push(stack.peekLast());
1053         }
1054 
1055         @Override
1056         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
1057                               BindingInterpreter.LoadFunc loadFunc, Context context) {
1058             stack.push(stack.peekLast());
1059         }
1060 
1061         /*
1062          * Fixes up Y-shaped data graphs (produced by DEREFERENCE):
1063          *
1064          * 1. DUP()
1065          * 2. BUFFER_LOAD(0, int.class)
1066          * 3. VM_STORE  (ignored)
1067          * 4. BUFFER_LOAD(4, int.class)
1068          * 5. VM_STORE  (ignored)
1069          *
1070          * (specialized in reverse!)
1071          *
1072          * 5. (int, int) -> void                       insertPos = 1
1073          * 4. (MemorySegment, int) -> void             insertPos = 1
1074          * 3. (MemorySegment, int) -> void             insertPos = 0
1075          * 2. (MemorySegment, MemorySegment) -> void   insertPos = 0
1076          * 1. (MemorySegment) -> void                  insertPos = 0
1077          *
1078          */
1079         @Override
1080         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
1081             return SharedUtils.mergeArguments(specializedHandle, insertPos, insertPos + 1);
1082         }
1083 
1084         @Override
1085         public boolean equals(Object o) {
1086             if (this == o) return true;
1087             return o != null && getClass() == o.getClass();
1088         }
1089 
1090         @Override
1091         public String toString() {
1092             return "Dup{}";
1093         }
1094     }
1095 }