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