1 /*
  2  * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 package jdk.internal.foreign.abi;
 24 
 25 import jdk.incubator.foreign.MemoryHandles;
 26 import jdk.incubator.foreign.MemoryLayout;
 27 
 28 import java.util.ArrayList;
 29 import java.util.List;
 30 import java.util.Objects;
 31 
 32 import java.lang.invoke.VarHandle;
 33 import java.nio.ByteOrder;
 34 
 35 /**
 36  * The binding operators defined in the Binding class can be combined into argument and return value processing 'recipes'.
 37  *
 38  * The binding operators are interpreted using a stack-base interpreter. Operators can either consume operands from the
 39  * stack, or push them onto the stack.
 40  *
 41  * In the description of each binding we talk about 'boxing' and 'unboxing'.
 42  *  - Unboxing is the process of taking a Java value and decomposing it, and storing components into machine
 43  *    storage locations. As such, the binding interpreter stack starts with the Java value on it, and should end empty.
 44  *  - Boxing is the process of re-composing a Java value by pulling components from machine storage locations.
 45  *    If a MemorySegment is needed to store the result, one should be allocated using the ALLOCATE_BUFFER operator.
 46  *    The binding interpreter stack starts off empty, and ends with the value to be returned as the only value on it.
 47  * A binding operator can be interpreted differently based on whether we are boxing or unboxing a value. For example,
 48  * the CONVERT_ADDRESS operator 'unboxes' a MemoryAddress to a long, but 'boxes' a long to a MemoryAddress.
 49  *
 50  * Here are some examples of binding recipes derived from C declarations, and according to the Windows ABI (recipes are
 51  * ABI-specific). Note that each argument has it's own recipe, which is indicated by '[number]:' (though, the only
 52  * example that has multiple arguments is the one using varargs).
 53  *
 54  * --------------------
 55  *
 56  * void f(int i);
 57  *
 58  * Argument bindings:
 59  * 0: MOVE(rcx, int.class) // move an 'int' into the RCX register
 60  *
 61  * Return bindings:
 62  * none
 63  *
 64  * --------------------
 65  *
 66  * void f(int* i);
 67  *
 68  * Argument bindings:
 69  * 0: CONVERT_ADDRESS // the 'MemoryAddress' is converted into a 'long'
 70  *    MOVE(rcx, long.class) // the 'long' is moved into the RCX register
 71  *
 72  * Return bindings:
 73  * none
 74  *
 75  * --------------------
 76  *
 77  * int* f();
 78  *
 79  * Argument bindings:
 80  * none
 81  *
 82  * Return bindings:
 83  * 0: MOVE(rax, long) // load a 'long' from the RAX register
 84  *    CONVERT_ADDRESS // convert the 'long' into a 'MemoryAddress'
 85  *
 86  * --------------------
 87  *
 88  * typedef struct { // fits into single register
 89  *   int x;
 90  *   int y;
 91  * } MyStruct;
 92  *
 93  * void f(MyStruct ms);
 94  *
 95  * Argument bindings:
 96  * 0: DEREFERENCE(0, long.class) // From the struct's memory region, load a 'long' from offset '0'
 97  *    MOVE(rcx, long.class) // and copy that into the RCX register
 98  *
 99  * Return bindings:
100  * none
101  *
102  * --------------------
103  *
104  * typedef struct { // does not fit into single register
105  *   long long x;
106  *   long long y;
107  * } MyStruct;
108  *
109  * void f(MyStruct ms);
110  *
111  * For the Windows ABI:
112  *
113  * Argument bindings:
114  * 0: COPY(16, 8) // copy the memory region containing the struct
115  *    BASE_ADDRESS // take the base address of the copy
116  *    CONVERT_ADDRESS // converts the base address to a 'long'
117  *    MOVE(rcx, long.class) // moves the 'long' into the RCX register
118  *
119  * Return bindings:
120  * none
121  *
122  * For the SysV ABI:
123  *
124  * Argument bindings:
125  * 0: DUP // duplicates the MemoryRegion operand
126  *    DEREFERENCE(0, long.class) // loads a 'long' from offset '0'
127  *    MOVE(rdx, long.class) // moves the long into the RDX register
128  *    DEREFERENCE(8, long.class) // loads a 'long' from offset '8'
129  *    MOVE(rcx, long.class) // moves the long into the RCX register
130  *
131  * Return bindings:
132  * none
133  *
134  * --------------------
135  *
136  * typedef struct { // fits into single register
137  *   int x;
138  *   int y;
139  * } MyStruct;
140  *
141  * MyStruct f();
142  *
143  * Argument bindings:
144  * none
145  *
146  * Return bindings:
147  * 0: ALLOCATE(GroupLayout(C_INT, C_INT)) // allocate a buffer with the memory layout of the struct
148  *    DUP // duplicate the allocated buffer
149  *    MOVE(rax, long.class) // loads a 'long' from rax
150  *    DEREFERENCE(0, long.class) // stores a 'long' at offset 0
151  *
152  * --------------------
153  *
154  * typedef struct { // does not fit into single register
155  *   long long x;
156  *   long long y;
157  * } MyStruct;
158  *
159  * MyStruct f();
160  *
161  * !! uses synthetic argument, which is a pointer to a pre-allocated buffer
162  *
163  * Argument bindings:
164  * 0: CONVERT_ADDRESS // unbox the MemoryAddress synthetic argument
165  *    MOVE(rcx, long.class) // moves the 'long' into the RCX register
166  *
167  * Return bindings:
168  * none
169  *
170  * --------------------
171  *
172  * void f(int dummy, ...); // varargs
173  *
174  * f(0, 10f); // passing a float
175  *
176  * Argument bindings:
177  * 0: MOVE(rcx, int.class) // moves the 'int dummy' into the RCX register
178  *
179  * 1: DUP // duplicates the '10f' argument
180  *    MOVE(rdx, float.class) // move one copy into the RDX register
181  *    MOVE(xmm1, float.class) // moves the other copy into the xmm2 register
182  *
183  * Return bindings:
184  * none
185  *
186  * --------------------
187  */
188 public abstract class Binding {
189     enum Tag {
190         MOVE,
191         DEREFERENCE,
192         COPY_BUFFER,
193         ALLOC_BUFFER,
194         CONVERT_ADDRESS,
195         BASE_ADDRESS,
196         DUP
197     }
198 
199     private final Tag tag;
200 
201     private Binding(Tag tag) {
202         this.tag = tag;
203     }
204 
205     public Tag tag() {
206         return tag;
207     }
208 
209     private static void checkType(Class<?> type) {
210         if (!type.isPrimitive() || type == void.class || type == boolean.class)
211             throw new IllegalArgumentException("Illegal type: " + type);
212     }
213 
214     public static Move move(VMStorage storage, Class<?> type) {
215         checkType(type);
216         return new Move(storage, type);
217     }
218 
219     public static Dereference dereference(long offset, Class<?> type) {
220         checkType(type);
221         if (offset < 0)
222             throw new IllegalArgumentException("Negative offset: " + offset);
223         return new Dereference(offset, type);
224     }
225 
226     public static Copy copy(MemoryLayout layout) {
227         return new Copy(layout.byteSize(), layout.byteAlignment());
228     }
229 
230     public static Allocate allocate(MemoryLayout layout) {
231         return new Allocate(layout.byteSize(), layout.byteAlignment());
232     }
233 
234     public static ConvertAddress convertAddress() {
235         return ConvertAddress.INSTANCE;
236     }
237 
238     public static BaseAddress baseAddress() {
239         return BaseAddress.INSTANCE;
240     }
241 
242     public static Dup dup() {
243         return Dup.INSTANCE;
244     }
245 
246 
247     public static Binding.Builder builder() {
248         return new Binding.Builder();
249     }
250 
251     /**
252      * A builder helper class for generating lists of Bindings
253      */
254     public static class Builder {
255         private final List<Binding> bindings = new ArrayList<>();
256 
257         public Binding.Builder move(VMStorage storage, Class<?> type) {
258             bindings.add(Binding.move(storage, type));
259             return this;
260         }
261 
262         public Binding.Builder dereference(long offset, Class<?> type) {
263             bindings.add(Binding.dereference(offset, type));
264             return this;
265         }
266 
267         public Binding.Builder copy(MemoryLayout layout) {
268             bindings.add(Binding.copy(layout));
269             return this;
270         }
271 
272         public Binding.Builder allocate(MemoryLayout layout) {
273             bindings.add(Binding.allocate(layout));
274             return this;
275         }
276 
277         public Binding.Builder convertAddress() {
278             bindings.add(Binding.convertAddress());
279             return this;
280         }
281 
282         public Binding.Builder baseAddress() {
283             bindings.add(Binding.baseAddress());
284             return this;
285         }
286 
287         public Binding.Builder dup() {
288             bindings.add(Binding.dup());
289             return this;
290         }
291 
292         public List<Binding> build() {
293             return new ArrayList<>(bindings);
294         }
295     }
296 
297     /**
298      * MOVE([storage location], [type])
299      *   When unboxing: pops a [type] from the operand stack, and moves it to [storage location]
300      *   When boxing: loads a [type] from [storage location], and pushes it onto the operand stack
301      * The [type] must be one of byte, short, char, int, long, float, or double
302      */
303     public static class Move extends Binding {
304         private final VMStorage storage;
305         private final Class<?> type;
306 
307         private Move(VMStorage storage, Class<?> type) {
308             super(Tag.MOVE);
309             this.storage = storage;
310             this.type = type;
311         }
312 
313         public VMStorage storage() {
314             return storage;
315         }
316 
317         public Class<?> type() {
318             return type;
319         }
320 
321         @Override
322         public String toString() {
323             return "Move{" +
324                     "tag=" + tag() +
325                     ", storage=" + storage +
326                     ", type=" + type +
327                     '}';
328         }
329 
330         @Override
331         public boolean equals(Object o) {
332             if (this == o) return true;
333             if (o == null || getClass() != o.getClass()) return false;
334             Move move = (Move) o;
335             return storage.equals(move.storage) &&
336                     type.equals(move.type);
337         }
338 
339         @Override
340         public int hashCode() {
341             return Objects.hash(tag(), storage, type);
342         }
343     }
344 
345     /**
346      * DEREFERENCE([offset into memory region], [type])
347      *   When unboxing: pops a MemorySegment from the operand stack,
348      *     loads a [type] from [offset into memory region] from it, and pushes it onto the operand stack
349      *   When boxing: pops a [type], and then a MemorySegment from the operand stack,
350      *     and then stores [type] to [offset into memory region] of the MemorySegment
351      * The [type] must be one of byte, short, char, int, long, float, or double
352      */
353     public static class Dereference extends Binding {
354         private final long offset;
355         private final Class<?> type;
356 
357         private Dereference(long offset, Class<?> type) {
358             super(Tag.DEREFERENCE);
359             this.offset = offset;
360             this.type = type;
361         }
362 
363         public long offset() {
364             return offset;
365         }
366 
367         public Class<?> type() {
368             return type;
369         }
370 
371         public VarHandle varHandle() {
372             return MemoryHandles.withOffset(MemoryHandles.varHandle(type, ByteOrder.nativeOrder()), offset);
373         }
374 
375         @Override
376         public String toString() {
377             return "Dereference{" +
378                     "tag=" + tag() +
379                     ", offset=" + offset +
380                     ", type=" + type +
381                     '}';
382         }
383 
384         @Override
385         public boolean equals(Object o) {
386             if (this == o) return true;
387             if (o == null || getClass() != o.getClass()) return false;
388             Dereference that = (Dereference) o;
389             return offset == that.offset &&
390                     type.equals(that.type);
391         }
392 
393         @Override
394         public int hashCode() {
395             return Objects.hash(tag(), offset, type);
396         }
397     }
398 
399     /**
400      * COPY([size], [alignment])
401      *   Creates a new MemorySegment with the given [size] and [alignment],
402      *     and copies contents from a MemorySegment popped from the top of the operand stack into this new buffer,
403      *     and pushes the new buffer onto the operand stack
404      */
405     public static class Copy extends Binding {
406         private final long size;
407         private final long alignment;
408 
409         private Copy(long size, long alignment) {
410             super(Tag.COPY_BUFFER);
411             this.size = size;
412             this.alignment = alignment;
413         }
414 
415         public long size() {
416             return size;
417         }
418 
419         public long alignment() {
420             return alignment;
421         }
422 
423         @Override
424         public String toString() {
425             return "Copy{" +
426                     "tag=" + tag() +
427                     ", size=" + size +
428                     ", alignment=" + alignment +
429                     '}';
430         }
431 
432         @Override
433         public boolean equals(Object o) {
434             if (this == o) return true;
435             if (o == null || getClass() != o.getClass()) return false;
436             Copy copy = (Copy) o;
437             return size == copy.size &&
438                     alignment == copy.alignment;
439         }
440 
441         @Override
442         public int hashCode() {
443             return Objects.hash(tag(), size, alignment);
444         }
445     }
446 
447     /**
448      * ALLOCATE([size], [alignment])
449      *   Creates a new MemorySegment with the give [size] and [alignment], and pushes it onto the operand stack.
450      */
451     public static class Allocate extends Binding {
452         private final long size;
453         private final long alignment;
454 
455         private Allocate(long size, long alignment) {
456             super(Tag.ALLOC_BUFFER);
457             this.size = size;
458             this.alignment = alignment;
459         }
460 
461         public long size() {
462             return size;
463         }
464 
465         public long alignment() {
466             return alignment;
467         }
468 
469         @Override
470         public String toString() {
471             return "AllocateBuffer{" +
472                     "tag=" + tag() +
473                     "size=" + size +
474                     ", alignment=" + alignment +
475                     '}';
476         }
477 
478         @Override
479         public boolean equals(Object o) {
480             if (this == o) return true;
481             if (o == null || getClass() != o.getClass()) return false;
482             Allocate that = (Allocate) o;
483             return size == that.size &&
484                     alignment == that.alignment;
485         }
486 
487         @Override
488         public int hashCode() {
489             return Objects.hash(tag(), size, alignment);
490         }
491     }
492 
493     /**
494      * CONVERT_ADDRESS()
495      *   When unboxing: pops a 'MemoryAddress' from the operand stack, converts it to a 'long',
496      *     and pushes that onto the operand stack
497      *   When boxing: pops a 'long' from the operand stack, converts it to a 'MemoryAddress',
498      *     and pushes that onto the operand stack
499      */
500     public static class ConvertAddress extends Binding {
501         private static final ConvertAddress INSTANCE = new ConvertAddress();
502         private ConvertAddress() {
503             super(Tag.CONVERT_ADDRESS);
504         }
505 
506         @Override
507         public String toString() {
508             return "BoxAddress{" +
509                     "tag=" + tag() +
510                     "}";
511         }
512 
513         @Override
514         public int hashCode() {
515             return tag().hashCode();
516         }
517 
518         @Override
519         public boolean equals(Object o) {
520             if (this == o) return true;
521             return o != null && getClass() == o.getClass();
522         }
523     }
524 
525     /**
526      * BASE_ADDRESS()
527      *   Pops a MemorySegment from the operand stack, and takes the base address of the segment
528      *   (the MemoryAddress that points to the start), and pushes that onto the operand stack
529      */
530     public static class BaseAddress extends Binding {
531         private static final BaseAddress INSTANCE = new BaseAddress();
532         private BaseAddress() {
533             super(Tag.BASE_ADDRESS);
534         }
535 
536         @Override
537         public String toString() {
538             return "BaseAddress{" +
539                     "tag=" + tag() +
540                     "}";
541         }
542 
543         @Override
544         public int hashCode() {
545             return tag().hashCode();
546         }
547 
548         @Override
549         public boolean equals(Object o) {
550             if (this == o) return true;
551             return o != null && getClass() == o.getClass();
552         }
553     }
554 
555     /**
556      * DUP()
557      *   Duplicates the value on the top of the operand stack (without popping it!),
558      *   and pushes the duplicate onto the operand stack
559      */
560     public static class Dup extends Binding {
561         private static final Dup INSTANCE = new Dup();
562         private Dup() {
563             super(Tag.DUP);
564         }
565 
566         @Override
567         public String toString() {
568             return "Dup{" +
569                     "tag=" + tag() +
570                     "}";
571         }
572 
573         @Override
574         public int hashCode() {
575             return tag().hashCode();
576         }
577 
578         @Override
579         public boolean equals(Object o) {
580             if (this == o) return true;
581             return o != null && getClass() == o.getClass();
582         }
583     }
584 }