1 /*
  2  * Copyright (c) 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  */
 24 
 25 /*
 26  * @test
 27  * @modules jdk.incubator.foreign/jdk.internal.foreign
 28  *          jdk.incubator.foreign/jdk.internal.foreign.abi
 29  *          jdk.incubator.foreign/jdk.internal.foreign.abi.x64
 30  *          jdk.incubator.foreign/jdk.internal.foreign.abi.x64.sysv
 31  * @build CallArrangerTestBase
 32  * @run testng TestSysVCallArranger
 33  */
 34 
 35 import jdk.incubator.foreign.FunctionDescriptor;
 36 import jdk.incubator.foreign.MemoryAddress;
 37 import jdk.incubator.foreign.MemoryLayout;
 38 import jdk.incubator.foreign.MemorySegment;
 39 import jdk.internal.foreign.abi.Binding;
 40 import jdk.internal.foreign.abi.CallingSequence;
 41 import jdk.internal.foreign.abi.x64.sysv.CallArranger;
 42 import org.testng.annotations.DataProvider;
 43 import org.testng.annotations.Test;
 44 
 45 import java.lang.invoke.MethodType;
 46 
 47 import static jdk.internal.foreign.PlatformLayouts.SysV.*;
 48 import static jdk.internal.foreign.abi.Binding.*;
 49 import static jdk.internal.foreign.abi.x64.X86_64Architecture.*;
 50 import static org.testng.Assert.assertEquals;
 51 import static org.testng.Assert.assertFalse;
 52 import static org.testng.Assert.assertTrue;
 53 
 54 public class TestSysVCallArranger extends CallArrangerTestBase {
 55 
 56     @Test
 57     public void testEmpty() {
 58         MethodType mt = MethodType.methodType(void.class);
 59         FunctionDescriptor fd = FunctionDescriptor.ofVoid();
 60         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
 61 
 62         assertFalse(bindings.isInMemoryReturn);
 63         CallingSequence callingSequence = bindings.callingSequence;
 64         assertEquals(callingSequence.methodType(), mt.appendParameterTypes(long.class));
 65         assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG));
 66 
 67         checkArgumentBindings(callingSequence, new Binding[][]{
 68             { vmStore(rax, long.class) }
 69         });
 70 
 71         checkReturnBindings(callingSequence, new Binding[]{});
 72 
 73         assertEquals(bindings.nVectorArgs, 0);
 74     }
 75 
 76     @Test
 77     public void testNestedStructs() {
 78         MemoryLayout POINT = MemoryLayout.structLayout(
 79                 C_INT,
 80                 MemoryLayout.structLayout(
 81                         C_INT,
 82                         C_INT
 83                 )
 84         );
 85         MethodType mt = MethodType.methodType(void.class, MemorySegment.class);
 86         FunctionDescriptor fd = FunctionDescriptor.ofVoid(POINT);
 87         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
 88 
 89         assertFalse(bindings.isInMemoryReturn);
 90         CallingSequence callingSequence = bindings.callingSequence;
 91         assertEquals(callingSequence.methodType(), mt.appendParameterTypes(long.class));
 92         assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG));
 93 
 94         checkArgumentBindings(callingSequence, new Binding[][]{
 95                 { dup(), bufferLoad(0, long.class), vmStore(rdi, long.class),
 96                   bufferLoad(8, int.class), vmStore(rsi, int.class)},
 97                 { vmStore(rax, long.class) },
 98         });
 99 
100         checkReturnBindings(callingSequence, new Binding[]{});
101 
102         assertEquals(bindings.nVectorArgs, 0);
103     }
104 
105     @Test
106     public void testNestedUnion() {
107         MemoryLayout POINT = MemoryLayout.structLayout(
108                 C_INT,
109                 MemoryLayout.paddingLayout(32),
110                 MemoryLayout.unionLayout(
111                         MemoryLayout.structLayout(C_INT, C_INT),
112                         C_LONG
113                 )
114         );
115         MethodType mt = MethodType.methodType(void.class, MemorySegment.class);
116         FunctionDescriptor fd = FunctionDescriptor.ofVoid(POINT);
117         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
118 
119         assertFalse(bindings.isInMemoryReturn);
120         CallingSequence callingSequence = bindings.callingSequence;
121         assertEquals(callingSequence.methodType(), mt.appendParameterTypes(long.class));
122         assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG));
123 
124         checkArgumentBindings(callingSequence, new Binding[][]{
125                 { dup(), bufferLoad(0, long.class), vmStore(rdi, long.class),
126                         bufferLoad(8, long.class), vmStore(rsi, long.class)},
127                 { vmStore(rax, long.class) },
128         });
129 
130         checkReturnBindings(callingSequence, new Binding[]{});
131 
132         assertEquals(bindings.nVectorArgs, 0);
133     }
134 
135     @Test
136     public void testNestedStructsUnaligned() {
137         MemoryLayout POINT = MemoryLayout.structLayout(
138                 C_INT,
139                 MemoryLayout.structLayout(
140                         C_LONG,
141                         C_INT
142                 )
143         );
144         MethodType mt = MethodType.methodType(void.class, MemorySegment.class);
145         FunctionDescriptor fd = FunctionDescriptor.ofVoid(POINT);
146         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
147 
148         assertFalse(bindings.isInMemoryReturn);
149         CallingSequence callingSequence = bindings.callingSequence;
150         assertEquals(callingSequence.methodType(), mt.appendParameterTypes(long.class));
151         assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG));
152 
153         checkArgumentBindings(callingSequence, new Binding[][]{
154                 { dup(), bufferLoad(0, long.class), vmStore(stackStorage(0), long.class),
155                         bufferLoad(8, long.class), vmStore(stackStorage(1), long.class)},
156                 { vmStore(rax, long.class) },
157         });
158 
159         checkReturnBindings(callingSequence, new Binding[]{});
160 
161         assertEquals(bindings.nVectorArgs, 0);
162     }
163 
164     @Test
165     public void testNestedUnionUnaligned() {
166         MemoryLayout POINT = MemoryLayout.structLayout(
167                 C_INT,
168                 MemoryLayout.unionLayout(
169                         MemoryLayout.structLayout(C_INT, C_INT),
170                         C_LONG
171                 )
172         );
173         MethodType mt = MethodType.methodType(void.class, MemorySegment.class);
174         FunctionDescriptor fd = FunctionDescriptor.ofVoid(POINT);
175         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
176 
177         assertFalse(bindings.isInMemoryReturn);
178         CallingSequence callingSequence = bindings.callingSequence;
179         assertEquals(callingSequence.methodType(), mt.appendParameterTypes(long.class));
180         assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG));
181 
182         checkArgumentBindings(callingSequence, new Binding[][]{
183                 { dup(), bufferLoad(0, long.class), vmStore(stackStorage(0), long.class),
184                         bufferLoad(8, int.class), vmStore(stackStorage(1), int.class)},
185                 { vmStore(rax, long.class) },
186         });
187 
188         checkReturnBindings(callingSequence, new Binding[]{});
189 
190         assertEquals(bindings.nVectorArgs, 0);
191     }
192 
193     @Test
194     public void testIntegerRegs() {
195         MethodType mt = MethodType.methodType(void.class,
196                 int.class, int.class, int.class, int.class, int.class, int.class);
197         FunctionDescriptor fd = FunctionDescriptor.ofVoid(
198                 C_INT, C_INT, C_INT, C_INT, C_INT, C_INT);
199         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
200 
201         assertFalse(bindings.isInMemoryReturn);
202         CallingSequence callingSequence = bindings.callingSequence;
203         assertEquals(callingSequence.methodType(), mt.appendParameterTypes(long.class));
204         assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG));
205 
206         checkArgumentBindings(callingSequence, new Binding[][]{
207             { vmStore(rdi, int.class) },
208             { vmStore(rsi, int.class) },
209             { vmStore(rdx, int.class) },
210             { vmStore(rcx, int.class) },
211             { vmStore(r8, int.class) },
212             { vmStore(r9, int.class) },
213             { vmStore(rax, long.class) },
214         });
215 
216         checkReturnBindings(callingSequence, new Binding[]{});
217 
218         assertEquals(bindings.nVectorArgs, 0);
219     }
220 
221     @Test
222     public void testDoubleRegs() {
223         MethodType mt = MethodType.methodType(void.class,
224                 double.class, double.class, double.class, double.class,
225                 double.class, double.class, double.class, double.class);
226         FunctionDescriptor fd = FunctionDescriptor.ofVoid(
227                 C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
228                 C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE);
229         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
230 
231         assertFalse(bindings.isInMemoryReturn);
232         CallingSequence callingSequence = bindings.callingSequence;
233         assertEquals(callingSequence.methodType(), mt.appendParameterTypes(long.class));
234         assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG));
235 
236         checkArgumentBindings(callingSequence, new Binding[][]{
237             { vmStore(xmm0, double.class) },
238             { vmStore(xmm1, double.class) },
239             { vmStore(xmm2, double.class) },
240             { vmStore(xmm3, double.class) },
241             { vmStore(xmm4, double.class) },
242             { vmStore(xmm5, double.class) },
243             { vmStore(xmm6, double.class) },
244             { vmStore(xmm7, double.class) },
245             { vmStore(rax, long.class) },
246         });
247 
248         checkReturnBindings(callingSequence, new Binding[]{});
249 
250         assertEquals(bindings.nVectorArgs, 8);
251     }
252 
253     @Test
254     public void testMixed() {
255         MethodType mt = MethodType.methodType(void.class,
256                 long.class, long.class, long.class, long.class, long.class, long.class, long.class, long.class,
257                 float.class, float.class, float.class, float.class,
258                 float.class, float.class, float.class, float.class, float.class, float.class);
259         FunctionDescriptor fd = FunctionDescriptor.ofVoid(
260                 C_LONG, C_LONG, C_LONG, C_LONG, C_LONG, C_LONG, C_LONG, C_LONG,
261                 C_FLOAT, C_FLOAT, C_FLOAT, C_FLOAT,
262                 C_FLOAT, C_FLOAT, C_FLOAT, C_FLOAT, C_FLOAT, C_FLOAT);
263         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
264 
265         assertFalse(bindings.isInMemoryReturn);
266         CallingSequence callingSequence = bindings.callingSequence;
267         assertEquals(callingSequence.methodType(), mt.appendParameterTypes(long.class));
268         assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG));
269 
270         checkArgumentBindings(callingSequence, new Binding[][]{
271             { vmStore(rdi, long.class) },
272             { vmStore(rsi, long.class) },
273             { vmStore(rdx, long.class) },
274             { vmStore(rcx, long.class) },
275             { vmStore(r8, long.class) },
276             { vmStore(r9, long.class) },
277             { vmStore(stackStorage(0), long.class) },
278             { vmStore(stackStorage(1), long.class) },
279             { vmStore(xmm0, float.class) },
280             { vmStore(xmm1, float.class) },
281             { vmStore(xmm2, float.class) },
282             { vmStore(xmm3, float.class) },
283             { vmStore(xmm4, float.class) },
284             { vmStore(xmm5, float.class) },
285             { vmStore(xmm6, float.class) },
286             { vmStore(xmm7, float.class) },
287             { vmStore(stackStorage(2), float.class) },
288             { vmStore(stackStorage(3), float.class) },
289             { vmStore(rax, long.class) },
290         });
291 
292         checkReturnBindings(callingSequence, new Binding[]{});
293 
294         assertEquals(bindings.nVectorArgs, 8);
295     }
296 
297     /**
298      * This is the example from the System V ABI AMD64 document
299      *
300      * struct structparm {
301      *   int32_t a, int32_t b, double d;
302      * } s;
303      * int32_t e, f, g, h, i, j, k;
304      * double m, n;
305      *
306      * void m(e, f, s, g, h, m, n, i, j, k);
307      *
308      * m(s);
309      */
310     @Test
311     public void testAbiExample() {
312         MemoryLayout struct = MemoryLayout.structLayout(C_INT, C_INT, C_DOUBLE);
313 
314         MethodType mt = MethodType.methodType(void.class,
315                 int.class, int.class, MemorySegment.class, int.class, int.class,
316                 double.class, double.class, int.class, int.class, int.class);
317         FunctionDescriptor fd = FunctionDescriptor.ofVoid(
318                 C_INT, C_INT, struct, C_INT, C_INT, C_DOUBLE, C_DOUBLE, C_INT, C_INT, C_INT);
319         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
320 
321         assertFalse(bindings.isInMemoryReturn);
322         CallingSequence callingSequence = bindings.callingSequence;
323         assertEquals(callingSequence.methodType(), mt.appendParameterTypes(long.class));
324         assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG));
325 
326         checkArgumentBindings(callingSequence, new Binding[][]{
327             { vmStore(rdi, int.class) },
328             { vmStore(rsi, int.class) },
329             {
330                 dup(),
331                 bufferLoad(0, long.class), vmStore(rdx, long.class),
332                 bufferLoad(8, double.class), vmStore(xmm0, double.class)
333             },
334             { vmStore(rcx, int.class) },
335             { vmStore(r8, int.class) },
336             { vmStore(xmm1, double.class) },
337             { vmStore(xmm2, double.class) },
338             { vmStore(r9, int.class) },
339             { vmStore(stackStorage(0), int.class) },
340             { vmStore(stackStorage(1), int.class) },
341             { vmStore(rax, long.class) },
342         });
343 
344         checkReturnBindings(callingSequence, new Binding[]{});
345 
346         assertEquals(bindings.nVectorArgs, 3);
347     }
348 
349     /**
350      * typedef void (*f)(void);
351      *
352      * void m(f f);
353      * void f_impl(void);
354      *
355      * m(f_impl);
356      */
357     @Test
358     public void testMemoryAddress() {
359         MethodType mt = MethodType.methodType(void.class, MemoryAddress.class);
360         FunctionDescriptor fd = FunctionDescriptor.ofVoid( C_POINTER);
361         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
362 
363         assertFalse(bindings.isInMemoryReturn);
364         CallingSequence callingSequence = bindings.callingSequence;
365         assertEquals(callingSequence.methodType(), mt.appendParameterTypes(long.class));
366         assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG));
367 
368         checkArgumentBindings(callingSequence, new Binding[][]{
369             { unboxAddress(), vmStore(rdi, long.class) },
370             { vmStore(rax, long.class) },
371         });
372 
373         checkReturnBindings(callingSequence, new Binding[]{});
374 
375         assertEquals(bindings.nVectorArgs, 0);
376     }
377 
378     @Test(dataProvider = "structs")
379     public void testStruct(MemoryLayout struct, Binding[] expectedBindings) {
380         MethodType mt = MethodType.methodType(void.class, MemorySegment.class);
381         FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct);
382         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
383 
384         assertFalse(bindings.isInMemoryReturn);
385         CallingSequence callingSequence = bindings.callingSequence;
386         assertEquals(callingSequence.methodType(), mt.appendParameterTypes(long.class));
387         assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG));
388 
389         checkArgumentBindings(callingSequence, new Binding[][]{
390             expectedBindings,
391             { vmStore(rax, long.class) },
392         });
393 
394         checkReturnBindings(callingSequence, new Binding[]{});
395 
396         assertEquals(bindings.nVectorArgs, 0);
397     }
398 
399 
400     @DataProvider
401     public static Object[][] structs() {
402         return new Object[][]{
403             { MemoryLayout.structLayout(C_LONG), new Binding[]{
404                     bufferLoad(0, long.class), vmStore(rdi, long.class)
405                 }
406             },
407             { MemoryLayout.structLayout(C_LONG, C_LONG), new Binding[]{
408                     dup(),
409                     bufferLoad(0, long.class), vmStore(rdi, long.class),
410                     bufferLoad(8, long.class), vmStore(rsi, long.class)
411                 }
412             },
413             { MemoryLayout.structLayout(C_LONG, C_LONG, C_LONG), new Binding[]{
414                     dup(),
415                     bufferLoad(0, long.class), vmStore(stackStorage(0), long.class),
416                     dup(),
417                     bufferLoad(8, long.class), vmStore(stackStorage(1), long.class),
418                     bufferLoad(16, long.class), vmStore(stackStorage(2), long.class)
419                 }
420             },
421             { MemoryLayout.structLayout(C_LONG, C_LONG, C_LONG, C_LONG), new Binding[]{
422                     dup(),
423                     bufferLoad(0, long.class), vmStore(stackStorage(0), long.class),
424                     dup(),
425                     bufferLoad(8, long.class), vmStore(stackStorage(1), long.class),
426                     dup(),
427                     bufferLoad(16, long.class), vmStore(stackStorage(2), long.class),
428                     bufferLoad(24, long.class), vmStore(stackStorage(3), long.class)
429                 }
430             },
431         };
432     }
433 
434     @Test
435     public void testReturnRegisterStruct() {
436         MemoryLayout struct = MemoryLayout.structLayout(C_LONG, C_LONG);
437 
438         MethodType mt = MethodType.methodType(MemorySegment.class);
439         FunctionDescriptor fd = FunctionDescriptor.of(struct);
440         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
441 
442         assertFalse(bindings.isInMemoryReturn);
443         CallingSequence callingSequence = bindings.callingSequence;
444         assertEquals(callingSequence.methodType(), mt.appendParameterTypes(long.class));
445         assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG));
446 
447         checkArgumentBindings(callingSequence, new Binding[][]{
448             { vmStore(rax, long.class) }
449         });
450 
451         checkReturnBindings(callingSequence, new Binding[] {
452             allocate(struct),
453             dup(),
454             vmLoad(rax, long.class),
455             bufferStore(0, long.class),
456             dup(),
457             vmLoad(rdx, long.class),
458             bufferStore(8, long.class)
459         });
460 
461         assertEquals(bindings.nVectorArgs, 0);
462     }
463 
464     @Test
465     public void testIMR() {
466         MemoryLayout struct = MemoryLayout.structLayout(C_LONG, C_LONG, C_LONG);
467 
468         MethodType mt = MethodType.methodType(MemorySegment.class);
469         FunctionDescriptor fd = FunctionDescriptor.of(struct);
470         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
471 
472         assertTrue(bindings.isInMemoryReturn);
473         CallingSequence callingSequence = bindings.callingSequence;
474         assertEquals(callingSequence.methodType(), MethodType.methodType(void.class, MemoryAddress.class, long.class));
475         assertEquals(callingSequence.functionDesc(), FunctionDescriptor.ofVoid(C_POINTER, C_LONG));
476 
477         checkArgumentBindings(callingSequence, new Binding[][]{
478             { unboxAddress(), vmStore(rdi, long.class) },
479             { vmStore(rax, long.class) }
480         });
481 
482         checkReturnBindings(callingSequence, new Binding[] {});
483 
484         assertEquals(bindings.nVectorArgs, 0);
485     }
486 
487     @Test
488     public void testFloatStructsUpcall() {
489         MemoryLayout struct = MemoryLayout.structLayout(C_FLOAT); // should be passed in float regs
490 
491         MethodType mt = MethodType.methodType(MemorySegment.class, MemorySegment.class);
492         FunctionDescriptor fd = FunctionDescriptor.of(struct, struct);
493         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, true);
494 
495         assertFalse(bindings.isInMemoryReturn);
496         CallingSequence callingSequence = bindings.callingSequence;
497         assertEquals(callingSequence.methodType(), mt);
498         assertEquals(callingSequence.functionDesc(), fd);
499 
500         checkArgumentBindings(callingSequence, new Binding[][]{
501             { allocate(struct), dup(), vmLoad(xmm0, float.class), bufferStore(0, float.class) },
502         });
503 
504         checkReturnBindings(callingSequence, new Binding[] {
505             bufferLoad(0, float.class), vmStore(xmm0, float.class)
506         });
507 
508         assertEquals(bindings.nVectorArgs, 1);
509     }
510 
511 }