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 ToSegment toSegment(long byteSize) {
 402         return new ToSegment(byteSize);
 403     }
 404 
 405     public static Dup dup() {
 406         return Dup.INSTANCE;
 407     }
 408 
 409 
 410     public static Binding.Builder builder() {
 411         return new Binding.Builder();
 412     }
 413 
 414     @Override
 415     public boolean equals(Object o) {
 416         if (this == o) return true;
 417         if (o == null || getClass() != o.getClass()) return false;
 418         Binding binding = (Binding) o;
 419         return tag == binding.tag;
 420     }
 421 
 422     @Override
 423     public int hashCode() {
 424         return Objects.hash(tag);
 425     }
 426 
 427     /**
 428      * A builder helper class for generating lists of Bindings
 429      */
 430     public static class Builder {
 431         private final List<Binding> bindings = new ArrayList<>();
 432 
 433         public Binding.Builder vmStore(VMStorage storage, Class<?> type) {
 434             bindings.add(Binding.vmStore(storage, type));
 435             return this;
 436         }
 437 
 438         public Binding.Builder vmLoad(VMStorage storage, Class<?> type) {
 439             bindings.add(Binding.vmLoad(storage, type));
 440             return this;
 441         }
 442 
 443         public Binding.Builder bufferStore(long offset, Class<?> type) {
 444             bindings.add(Binding.bufferStore(offset, type));
 445             return this;
 446         }
 447 
 448         public Binding.Builder bufferLoad(long offset, Class<?> type) {
 449             bindings.add(Binding.bufferLoad(offset, type));
 450             return this;
 451         }
 452 
 453         public Binding.Builder copy(MemoryLayout layout) {
 454             bindings.add(Binding.copy(layout));
 455             return this;
 456         }
 457 
 458         public Binding.Builder allocate(MemoryLayout layout) {
 459             bindings.add(Binding.allocate(layout));
 460             return this;
 461         }
 462 
 463         public Binding.Builder boxAddress() {
 464             bindings.add(Binding.boxAddress());
 465             return this;
 466         }
 467 
 468         public Binding.Builder unboxAddress() {
 469             bindings.add(Binding.unboxAddress());
 470             return this;
 471         }
 472 
 473         public Binding.Builder unboxAddress(Class<?> carrier) {
 474             bindings.add(Binding.unboxAddress(carrier));
 475             return this;
 476         }
 477 
 478         public Binding.Builder toSegment(MemoryLayout layout) {
 479             bindings.add(Binding.toSegment(layout));
 480             return this;
 481         }
 482 
 483         public Binding.Builder dup() {
 484             bindings.add(Binding.dup());
 485             return this;
 486         }
 487 
 488         public List<Binding> build() {
 489             return new ArrayList<>(bindings);
 490         }
 491     }
 492 
 493     abstract static class Move extends Binding {
 494         private final VMStorage storage;
 495         private final Class<?> type;
 496 
 497         private Move(Tag tag, VMStorage storage, Class<?> type) {
 498             super(tag);
 499             this.storage = storage;
 500             this.type = type;
 501         }
 502 
 503         public VMStorage storage() {
 504             return storage;
 505         }
 506 
 507         public Class<?> type() {
 508             return type;
 509         }
 510 
 511         @Override
 512         public boolean equals(Object o) {
 513             if (this == o) return true;
 514             if (o == null || getClass() != o.getClass()) return false;
 515             if (!super.equals(o)) return false;
 516             Move move = (Move) o;
 517             return Objects.equals(storage, move.storage) &&
 518                     Objects.equals(type, move.type);
 519         }
 520 
 521         @Override
 522         public int hashCode() {
 523             return Objects.hash(super.hashCode(), storage, type);
 524         }
 525     }
 526 
 527     /**
 528      * VM_STORE([storage location], [type])
 529      * Pops a [type] from the operand stack, and moves it to [storage location]
 530      * The [type] must be one of byte, short, char, int, long, float, or double
 531      */
 532     public static class VMStore extends Move {
 533         private VMStore(VMStorage storage, Class<?> type) {
 534             super(Tag.VM_STORE, storage, type);
 535         }
 536 
 537         @Override
 538         public void verify(Deque<Class<?>> stack) {
 539             Class<?> actualType = stack.pop();
 540             Class<?> expectedType = type();
 541             SharedUtils.checkType(actualType, expectedType);
 542         }
 543 
 544         @Override
 545         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 546                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 547             storeFunc.store(storage(), type(), stack.pop());
 548         }
 549 
 550         @Override
 551         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 552             return specializedHandle; // no-op
 553         }
 554 
 555         @Override
 556         public String toString() {
 557             return "VMStore{" +
 558                     "storage=" + storage() +
 559                     ", type=" + type() +
 560                     '}';
 561         }
 562     }
 563 
 564     /**
 565      * VM_LOAD([storage location], [type])
 566      * Loads a [type] from [storage location], and pushes it onto the operand stack.
 567      * The [type] must be one of byte, short, char, int, long, float, or double
 568      */
 569     public static class VMLoad extends Move {
 570         private VMLoad(VMStorage storage, Class<?> type) {
 571             super(Tag.VM_LOAD, storage, type);
 572         }
 573 
 574         @Override
 575         public void verify(Deque<Class<?>> stack) {
 576             stack.push(type());
 577         }
 578 
 579         @Override
 580         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 581                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 582             stack.push(loadFunc.load(storage(), type()));
 583         }
 584 
 585         @Override
 586         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 587             return specializedHandle; // no-op
 588         }
 589 
 590         @Override
 591         public String toString() {
 592             return "VMLoad{" +
 593                     "storage=" + storage() +
 594                     ", type=" + type() +
 595                     '}';
 596         }
 597     }
 598 
 599     private abstract static class Dereference extends Binding {
 600         private final long offset;
 601         private final Class<?> type;
 602 
 603         private Dereference(Tag tag, long offset, Class<?> type) {
 604             super(tag);
 605             this.offset = offset;
 606             this.type = type;
 607         }
 608 
 609         public long offset() {
 610             return offset;
 611         }
 612 
 613         public Class<?> type() {
 614             return type;
 615         }
 616 
 617         @Override
 618         public boolean equals(Object o) {
 619             if (this == o) return true;
 620             if (o == null || getClass() != o.getClass()) return false;
 621             if (!super.equals(o)) return false;
 622             Dereference that = (Dereference) o;
 623             return offset == that.offset &&
 624                     Objects.equals(type, that.type);
 625         }
 626 
 627         @Override
 628         public int hashCode() {
 629             return Objects.hash(super.hashCode(), offset, type);
 630         }
 631 
 632         public VarHandle varHandle() {
 633             // alignment is set to 1 byte here to avoid exceptions for cases where we do super word
 634             // copies of e.g. 2 int fields of a struct as a single long, while the struct is only
 635             // 4-byte-aligned (since it only contains ints)
 636             ValueLayout layout = MemoryLayout.valueLayout(type(), ByteOrder.nativeOrder()).withBitAlignment(8);
 637             return MemoryHandles.insertCoordinates(MemoryHandles.varHandle(layout), 1, offset);
 638         }
 639     }
 640 
 641     /**
 642      * BUFFER_STORE([offset into memory region], [type])
 643      * Pops a MemorySegment from the operand stack, loads a [type] from
 644      * [offset into memory region] from it, and pushes it onto the operand stack.
 645      * The [type] must be one of byte, short, char, int, long, float, or double
 646      */
 647     public static class BufferStore extends Dereference {
 648         private BufferStore(long offset, Class<?> type) {
 649             super(Tag.BUFFER_STORE, offset, type);
 650         }
 651 
 652         @Override
 653         public void verify(Deque<Class<?>> stack) {
 654             Class<?> storeType = stack.pop();
 655             SharedUtils.checkType(storeType, type());
 656             Class<?> segmentType = stack.pop();
 657             SharedUtils.checkType(segmentType, MemorySegment.class);
 658         }
 659 
 660         @Override
 661         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 662                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 663             Object value = stack.pop();
 664             MemorySegment operand = (MemorySegment) stack.pop();
 665             MemorySegment writeAddress = operand.asSlice(offset());
 666             SharedUtils.write(writeAddress, type(), value);
 667         }
 668 
 669         @Override
 670         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 671             MethodHandle setter = varHandle().toMethodHandle(VarHandle.AccessMode.SET);
 672             setter = setter.asType(methodType(void.class, MemorySegment.class, type()));
 673             return collectArguments(specializedHandle, insertPos + 1, setter);
 674         }
 675 
 676         @Override
 677         public String toString() {
 678             return "BufferStore{" +
 679                     "offset=" + offset() +
 680                     ", type=" + type() +
 681                     '}';
 682         }
 683     }
 684 
 685     /**
 686      * BUFFER_LOAD([offset into memory region], [type])
 687      * Pops a [type], and then a MemorySegment from the operand stack,
 688      * and then stores [type] to [offset into memory region] of the MemorySegment.
 689      * The [type] must be one of byte, short, char, int, long, float, or double
 690      */
 691     public static class BufferLoad extends Dereference {
 692         private BufferLoad(long offset, Class<?> type) {
 693             super(Tag.BUFFER_LOAD, offset, type);
 694         }
 695 
 696         @Override
 697         public void verify(Deque<Class<?>> stack) {
 698             Class<?> actualType = stack.pop();
 699             SharedUtils.checkType(actualType, MemorySegment.class);
 700             Class<?> newType = type();
 701             stack.push(newType);
 702         }
 703 
 704         @Override
 705         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 706                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 707             MemorySegment operand = (MemorySegment) stack.pop();
 708             MemorySegment readAddress = operand.asSlice(offset());
 709             stack.push(SharedUtils.read(readAddress, type()));
 710         }
 711 
 712         @Override
 713         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 714             MethodHandle filter = varHandle()
 715                     .toMethodHandle(VarHandle.AccessMode.GET)
 716                     .asType(methodType(type(), MemorySegment.class));
 717             return filterArguments(specializedHandle, insertPos, filter);
 718         }
 719 
 720         @Override
 721         public String toString() {
 722             return "BufferLoad{" +
 723                     "offset=" + offset() +
 724                     ", type=" + type() +
 725                     '}';
 726         }
 727     }
 728 
 729     /**
 730      * COPY([size], [alignment])
 731      *   Creates a new MemorySegment with the given [size] and [alignment],
 732      *     and copies contents from a MemorySegment popped from the top of the operand stack into this new buffer,
 733      *     and pushes the new buffer onto the operand stack
 734      */
 735     public static class Copy extends Binding {
 736         private final long size;
 737         private final long alignment;
 738 
 739         private Copy(long size, long alignment) {
 740             super(Tag.COPY_BUFFER);
 741             this.size = size;
 742             this.alignment = alignment;
 743         }
 744 
 745         private static MemorySegment copyBuffer(MemorySegment operand, long size, long alignment,
 746                                                     Context context) {
 747             return context.allocator().allocate(size, alignment)
 748                             .copyFrom(operand.asSlice(0, size));
 749         }
 750 
 751         public long size() {
 752             return size;
 753         }
 754 
 755         public long alignment() {
 756             return alignment;
 757         }
 758 
 759         @Override
 760         public String toString() {
 761             return "Copy{" +
 762                     "tag=" + tag() +
 763                     ", size=" + size +
 764                     ", alignment=" + alignment +
 765                     '}';
 766         }
 767 
 768         @Override
 769         public void verify(Deque<Class<?>> stack) {
 770             Class<?> actualType = stack.pop();
 771             SharedUtils.checkType(actualType, MemorySegment.class);
 772             stack.push(MemorySegment.class);
 773         }
 774 
 775         @Override
 776         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 777                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 778             MemorySegment operand = (MemorySegment) stack.pop();
 779             MemorySegment copy = copyBuffer(operand, size, alignment, context);
 780             stack.push(copy);
 781         }
 782 
 783         @Override
 784         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 785             MethodHandle filter = insertArguments(MH_COPY_BUFFER, 1, size, alignment);
 786             specializedHandle = collectArguments(specializedHandle, insertPos, filter);
 787             return SharedUtils.mergeArguments(specializedHandle, allocatorPos, insertPos + 1);
 788         }
 789 
 790         @Override
 791         public boolean equals(Object o) {
 792             if (this == o) return true;
 793             if (o == null || getClass() != o.getClass()) return false;
 794             if (!super.equals(o)) return false;
 795             Copy copy = (Copy) o;
 796             return size == copy.size &&
 797                     alignment == copy.alignment;
 798         }
 799 
 800         @Override
 801         public int hashCode() {
 802             return Objects.hash(super.hashCode(), size, alignment);
 803         }
 804     }
 805 
 806     /**
 807      * ALLOCATE([size], [alignment])
 808      *   Creates a new MemorySegment with the give [size] and [alignment], and pushes it onto the operand stack.
 809      */
 810     public static class Allocate extends Binding {
 811         private final long size;
 812         private final long alignment;
 813 
 814         private Allocate(long size, long alignment) {
 815             super(Tag.ALLOC_BUFFER);
 816             this.size = size;
 817             this.alignment = alignment;
 818         }
 819 
 820         private static MemorySegment allocateBuffer(long size, long allignment, Context context) {
 821             return context.allocator().allocate(size, allignment);
 822         }
 823 
 824         public long size() {
 825             return size;
 826         }
 827 
 828         public long alignment() {
 829             return alignment;
 830         }
 831 
 832         @Override
 833         public String toString() {
 834             return "AllocateBuffer{" +
 835                     "tag=" + tag() +
 836                     "size=" + size +
 837                     ", alignment=" + alignment +
 838                     '}';
 839         }
 840 
 841         @Override
 842         public void verify(Deque<Class<?>> stack) {
 843             stack.push(MemorySegment.class);
 844         }
 845 
 846         @Override
 847         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 848                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 849             stack.push(allocateBuffer(size, alignment, context));
 850         }
 851 
 852         @Override
 853         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 854             MethodHandle allocateBuffer = insertArguments(MH_ALLOCATE_BUFFER, 0, size, alignment);
 855             specializedHandle = collectArguments(specializedHandle, insertPos, allocateBuffer);
 856             return SharedUtils.mergeArguments(specializedHandle, allocatorPos, insertPos);
 857         }
 858 
 859         @Override
 860         public boolean equals(Object o) {
 861             if (this == o) return true;
 862             if (o == null || getClass() != o.getClass()) return false;
 863             if (!super.equals(o)) return false;
 864             Allocate allocate = (Allocate) o;
 865             return size == allocate.size &&
 866                     alignment == allocate.alignment;
 867         }
 868 
 869         @Override
 870         public int hashCode() {
 871             return Objects.hash(super.hashCode(), size, alignment);
 872         }
 873     }
 874 
 875     /**
 876      * UNBOX_ADDRESS()
 877      * Pops a 'MemoryAddress' from the operand stack, converts it to a 'long',
 878      *     and pushes that onto the operand stack.
 879      */
 880     public static class UnboxAddress extends Binding {
 881 
 882         static final ClassValue<UnboxAddress> INSTANCE = new ClassValue<>() {
 883             @Override
 884             protected UnboxAddress computeValue(Class<?> type) {
 885                 return new UnboxAddress(type);
 886             }
 887         };
 888 
 889         final Class<?> carrier;
 890         final MethodHandle toAddress;
 891 
 892         private UnboxAddress(Class<?> carrier) {
 893             super(Tag.UNBOX_ADDRESS);
 894             this.carrier = carrier;
 895             try {
 896                 this.toAddress = MethodHandles.lookup().findVirtual(carrier, "address", MethodType.methodType(MemoryAddress.class));
 897             } catch (Throwable ex) {
 898                 throw new IllegalArgumentException(ex);
 899             }
 900         }
 901 
 902         @Override
 903         public void verify(Deque<Class<?>> stack) {
 904             Class<?> actualType = stack.pop();
 905             SharedUtils.checkType(actualType, carrier);
 906             stack.push(long.class);
 907         }
 908 
 909         @Override
 910         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 911                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 912             stack.push(((Addressable)stack.pop()).address().toRawLongValue());
 913         }
 914 
 915         @Override
 916         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 917             return filterArguments(specializedHandle, insertPos,
 918                     MethodHandles.filterReturnValue(toAddress, MH_UNBOX_ADDRESS));
 919         }
 920 
 921         @Override
 922         public String toString() {
 923             return "UnboxAddress{}";
 924         }
 925     }
 926 
 927     /**
 928      * BOX_ADDRESS()
 929      * Pops a 'long' from the operand stack, converts it to a 'MemoryAddress',
 930      *     and pushes that onto the operand stack.
 931      */
 932     public static class BoxAddress extends Binding {
 933         private static final BoxAddress INSTANCE = new BoxAddress();
 934         private BoxAddress() {
 935             super(Tag.BOX_ADDRESS);
 936         }
 937 
 938         @Override
 939         public void verify(Deque<Class<?>> stack) {
 940             Class<?> actualType = stack.pop();
 941             SharedUtils.checkType(actualType, long.class);
 942             stack.push(MemoryAddress.class);
 943         }
 944 
 945         @Override
 946         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 947                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 948             stack.push(MemoryAddress.ofLong((long) stack.pop()));
 949         }
 950 
 951         @Override
 952         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 953             return filterArguments(specializedHandle, insertPos, MH_BOX_ADDRESS);
 954         }
 955 
 956         @Override
 957         public String toString() {
 958             return "BoxAddress{}";
 959         }
 960     }
 961 
 962     /**
 963      * TO_SEGMENT([size])
 964      *   Pops a MemoryAddress from the operand stack, and converts it to a MemorySegment
 965      *   with the given size, and pushes that onto the operand stack
 966      */
 967     public static class ToSegment extends Binding {
 968         private final long size;
 969         // FIXME alignment?
 970 
 971         public ToSegment(long size) {
 972             super(Tag.TO_SEGMENT);
 973             this.size = size;
 974         }
 975 
 976         private static MemorySegment toSegment(MemoryAddress operand, long size, Context context) {
 977             return MemoryAddressImpl.ofLongUnchecked(operand.toRawLongValue(), size, (ResourceScopeImpl) context.scope);
 978         }
 979 
 980         @Override
 981         public void verify(Deque<Class<?>> stack) {
 982             Class<?> actualType = stack.pop();
 983             SharedUtils.checkType(actualType, MemoryAddress.class);
 984             stack.push(MemorySegment.class);
 985         }
 986 
 987         @Override
 988         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
 989                               BindingInterpreter.LoadFunc loadFunc, Context context) {
 990             MemoryAddress operand = (MemoryAddress) stack.pop();
 991             MemorySegment segment = toSegment(operand, size, context);
 992             stack.push(segment);
 993         }
 994 
 995         @Override
 996         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
 997             MethodHandle toSegmentHandle = insertArguments(MH_TO_SEGMENT, 1, size);
 998             specializedHandle = collectArguments(specializedHandle, insertPos, toSegmentHandle);
 999             return SharedUtils.mergeArguments(specializedHandle, allocatorPos, insertPos + 1);
1000         }
1001 
1002         @Override
1003         public String toString() {
1004             return "ToSegemnt{" +
1005                     "size=" + size +
1006                     '}';
1007         }
1008 
1009         @Override
1010         public boolean equals(Object o) {
1011             if (this == o) return true;
1012             if (o == null || getClass() != o.getClass()) return false;
1013             if (!super.equals(o)) return false;
1014             ToSegment toSegemnt = (ToSegment) o;
1015             return size == toSegemnt.size;
1016         }
1017 
1018         @Override
1019         public int hashCode() {
1020             return Objects.hash(super.hashCode(), size);
1021         }
1022     }
1023 
1024     /**
1025      * DUP()
1026      *   Duplicates the value on the top of the operand stack (without popping it!),
1027      *   and pushes the duplicate onto the operand stack
1028      */
1029     public static class Dup extends Binding {
1030         private static final Dup INSTANCE = new Dup();
1031         private Dup() {
1032             super(Tag.DUP);
1033         }
1034 
1035         @Override
1036         public void verify(Deque<Class<?>> stack) {
1037             stack.push(stack.peekLast());
1038         }
1039 
1040         @Override
1041         public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
1042                               BindingInterpreter.LoadFunc loadFunc, Context context) {
1043             stack.push(stack.peekLast());
1044         }
1045 
1046         /*
1047          * Fixes up Y-shaped data graphs (produced by DEREFERENCE):
1048          *
1049          * 1. DUP()
1050          * 2. BUFFER_LOAD(0, int.class)
1051          * 3. VM_STORE  (ignored)
1052          * 4. BUFFER_LOAD(4, int.class)
1053          * 5. VM_STORE  (ignored)
1054          *
1055          * (specialized in reverse!)
1056          *
1057          * 5. (int, int) -> void                       insertPos = 1
1058          * 4. (MemorySegment, int) -> void             insertPos = 1
1059          * 3. (MemorySegment, int) -> void             insertPos = 0
1060          * 2. (MemorySegment, MemorySegment) -> void   insertPos = 0
1061          * 1. (MemorySegment) -> void                  insertPos = 0
1062          *
1063          */
1064         @Override
1065         public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
1066             return SharedUtils.mergeArguments(specializedHandle, insertPos, insertPos + 1);
1067         }
1068 
1069         @Override
1070         public boolean equals(Object o) {
1071             if (this == o) return true;
1072             return o != null && getClass() == o.getClass();
1073         }
1074 
1075         @Override
1076         public String toString() {
1077             return "Dup{}";
1078         }
1079     }
1080 }