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