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  * @requires sun.arch.data.model == "64"
 28  * @compile platform/PlatformLayouts.java
 29  * @modules java.base/jdk.internal.foreign
 30  *          java.base/jdk.internal.foreign.abi
 31  *          java.base/jdk.internal.foreign.abi.aarch64
 32  * @build CallArrangerTestBase
 33  * @run testng TestLinuxAArch64CallArranger
 34  */
 35 
 36 import java.lang.foreign.FunctionDescriptor;
 37 import java.lang.foreign.MemoryLayout;
 38 import java.lang.foreign.StructLayout;
 39 import java.lang.foreign.MemorySegment;
 40 import jdk.internal.foreign.abi.Binding;
 41 import jdk.internal.foreign.abi.CallingSequence;
 42 import jdk.internal.foreign.abi.LinkerOptions;
 43 import jdk.internal.foreign.abi.StubLocations;
 44 import jdk.internal.foreign.abi.VMStorage;
 45 import jdk.internal.foreign.abi.aarch64.CallArranger;
 46 import org.testng.annotations.DataProvider;
 47 import org.testng.annotations.Test;
 48 
 49 import java.lang.invoke.MethodType;
 50 
 51 import static java.lang.foreign.Linker.Option.firstVariadicArg;
 52 import static java.lang.foreign.ValueLayout.ADDRESS;
 53 import static jdk.internal.foreign.abi.Binding.*;
 54 import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.*;
 55 import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.Regs.*;
 56 import static platform.PlatformLayouts.AArch64.*;
 57 
 58 import static org.testng.Assert.assertEquals;
 59 import static org.testng.Assert.assertFalse;
 60 import static org.testng.Assert.assertTrue;
 61 
 62 public class TestLinuxAArch64CallArranger extends CallArrangerTestBase {
 63 
 64     private static final VMStorage TARGET_ADDRESS_STORAGE = StubLocations.TARGET_ADDRESS.storage(StorageType.PLACEHOLDER);
 65     private static final VMStorage RETURN_BUFFER_STORAGE = StubLocations.RETURN_BUFFER.storage(StorageType.PLACEHOLDER);
 66 
 67     @Test
 68     public void testEmpty() {
 69         MethodType mt = MethodType.methodType(void.class);
 70         FunctionDescriptor fd = FunctionDescriptor.ofVoid();
 71         CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false);
 72 
 73         assertFalse(bindings.isInMemoryReturn());
 74         CallingSequence callingSequence = bindings.callingSequence();
 75         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
 76         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
 77 
 78         checkArgumentBindings(callingSequence, new Binding[][]{
 79             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }
 80         });
 81 
 82         checkReturnBindings(callingSequence, new Binding[]{});
 83     }
 84 
 85     @Test
 86     public void testInteger() {
 87         MethodType mt = MethodType.methodType(void.class,
 88                 int.class, int.class, int.class, int.class,
 89                 int.class, int.class, int.class, int.class,
 90                 int.class, int.class);
 91         FunctionDescriptor fd = FunctionDescriptor.ofVoid(
 92                 C_INT, C_INT, C_INT, C_INT,
 93                 C_INT, C_INT, C_INT, C_INT,
 94                 C_INT, C_INT);
 95         CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false);
 96 
 97         assertFalse(bindings.isInMemoryReturn());
 98         CallingSequence callingSequence = bindings.callingSequence();
 99         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
100         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
101 
102         checkArgumentBindings(callingSequence, new Binding[][]{
103             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
104             { vmStore(r0, int.class) },
105             { vmStore(r1, int.class) },
106             { vmStore(r2, int.class) },
107             { vmStore(r3, int.class) },
108             { vmStore(r4, int.class) },
109             { vmStore(r5, int.class) },
110             { vmStore(r6, int.class) },
111             { vmStore(r7, int.class) },
112             { vmStore(stackStorage((short) 4, 0), int.class) },
113             { vmStore(stackStorage((short) 4, 8), int.class) },
114         });
115 
116         checkReturnBindings(callingSequence, new Binding[]{});
117     }
118 
119     @Test
120     public void testTwoIntTwoFloat() {
121         MethodType mt = MethodType.methodType(void.class,
122                 int.class, int.class, float.class, float.class);
123         FunctionDescriptor fd = FunctionDescriptor.ofVoid(
124                 C_INT, C_INT, C_FLOAT, C_FLOAT);
125         CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false);
126 
127         assertFalse(bindings.isInMemoryReturn());
128         CallingSequence callingSequence = bindings.callingSequence();
129         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
130         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
131 
132         checkArgumentBindings(callingSequence, new Binding[][]{
133             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
134             { vmStore(r0, int.class) },
135             { vmStore(r1, int.class) },
136             { vmStore(v0, float.class) },
137             { vmStore(v1, float.class) },
138         });
139 
140         checkReturnBindings(callingSequence, new Binding[]{});
141     }
142 
143     @Test(dataProvider = "structs")
144     public void testStruct(MemoryLayout struct, Binding[] expectedBindings) {
145         MethodType mt = MethodType.methodType(void.class, MemorySegment.class);
146         FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct);
147         CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false);
148 
149         assertFalse(bindings.isInMemoryReturn());
150         CallingSequence callingSequence = bindings.callingSequence();
151         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
152         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
153 
154         checkArgumentBindings(callingSequence, new Binding[][]{
155             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
156             expectedBindings
157         });
158 
159         checkReturnBindings(callingSequence, new Binding[]{});
160     }
161 
162     @DataProvider
163     public static Object[][] structs() {
164         MemoryLayout struct2 = MemoryLayout.structLayout(C_INT, C_INT, C_DOUBLE, C_INT);
165         return new Object[][]{
166             // struct s { int32_t a, b; double c; };
167             { MemoryLayout.structLayout(C_INT, C_INT, C_DOUBLE), new Binding[] {
168                 dup(),
169                     // s.a & s.b
170                     bufferLoad(0, long.class), vmStore(r0, long.class),
171                     // s.c --> note AArch64 passes this in an *integer* register
172                     bufferLoad(8, long.class), vmStore(r1, long.class),
173             }},
174             // struct s { int32_t a, b; double c; int32_t d };
175             { struct2, new Binding[] {
176                 copy(struct2),
177                 unboxAddress(),
178                 vmStore(r0, long.class)
179             }},
180             // struct s { int32_t a[2]; float b[2] };
181             { MemoryLayout.structLayout(C_INT, C_INT, C_FLOAT, C_FLOAT), new Binding[] {
182                 dup(),
183                     // s.a[0] & s.a[1]
184                     bufferLoad(0, long.class), vmStore(r0, long.class),
185                     // s.b[0] & s.b[1]
186                     bufferLoad(8, long.class), vmStore(r1, long.class),
187             }},
188             // struct s { float a; /* padding */ double b };
189             { MemoryLayout.structLayout(C_FLOAT, MemoryLayout.paddingLayout(4), C_DOUBLE),
190               new Binding[] {
191                 dup(),
192                 // s.a
193                 bufferLoad(0, long.class), vmStore(r0, long.class),
194                 // s.b
195                 bufferLoad(8, long.class), vmStore(r1, long.class),
196             }},
197         };
198     }
199 
200     @Test
201     public void testMultipleStructs() {
202         MemoryLayout struct1 = MemoryLayout.structLayout(C_INT, C_INT, C_DOUBLE, C_INT);
203         MemoryLayout struct2 = MemoryLayout.structLayout(C_LONG, C_LONG, C_LONG);
204 
205         MethodType mt = MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class, int.class);
206         FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct1, struct2, C_INT);
207         CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false);
208 
209         assertFalse(bindings.isInMemoryReturn());
210         CallingSequence callingSequence = bindings.callingSequence();
211         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
212         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
213 
214         checkArgumentBindings(callingSequence, new Binding[][]{
215             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
216             {
217                 copy(struct1),
218                 unboxAddress(),
219                 vmStore(r0, long.class)
220             },
221             {
222                 copy(struct2),
223                 unboxAddress(),
224                 vmStore(r1, long.class)
225             },
226             { vmStore(r2, int.class) }
227         });
228 
229         checkReturnBindings(callingSequence, new Binding[]{});
230     }
231 
232     @Test
233     public void testReturnStruct1() {
234         MemoryLayout struct = MemoryLayout.structLayout(C_LONG, C_LONG, C_FLOAT);
235 
236         MethodType mt = MethodType.methodType(MemorySegment.class);
237         FunctionDescriptor fd = FunctionDescriptor.of(struct);
238         CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false);
239 
240         assertTrue(bindings.isInMemoryReturn());
241         CallingSequence callingSequence = bindings.callingSequence();
242         assertEquals(callingSequence.callerMethodType(), MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class));
243         assertEquals(callingSequence.functionDesc(), FunctionDescriptor.ofVoid(ADDRESS, C_POINTER));
244 
245         checkArgumentBindings(callingSequence, new Binding[][]{
246             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
247             {
248                 unboxAddress(),
249                 vmStore(r8, long.class)
250             }
251         });
252 
253         checkReturnBindings(callingSequence, new Binding[]{});
254     }
255 
256     @Test
257     public void testReturnStruct2() {
258         MemoryLayout struct = MemoryLayout.structLayout(C_LONG, C_LONG);
259 
260         MethodType mt = MethodType.methodType(MemorySegment.class);
261         FunctionDescriptor fd = FunctionDescriptor.of(struct);
262         CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false);
263 
264         assertFalse(bindings.isInMemoryReturn());
265         CallingSequence callingSequence = bindings.callingSequence();
266         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class, MemorySegment.class));
267         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS, ADDRESS));
268 
269         checkArgumentBindings(callingSequence, new Binding[][]{
270             { unboxAddress(), vmStore(RETURN_BUFFER_STORAGE, long.class) },
271             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }
272         });
273 
274         checkReturnBindings(callingSequence, new Binding[]{
275             allocate(struct),
276             dup(),
277             vmLoad(r0, long.class),
278             bufferStore(0, long.class),
279             dup(),
280             vmLoad(r1, long.class),
281             bufferStore(8, long.class),
282         });
283     }
284 
285     @Test
286     public void testStructHFA1() {
287         MemoryLayout hfa = MemoryLayout.structLayout(C_FLOAT, C_FLOAT);
288 
289         MethodType mt = MethodType.methodType(MemorySegment.class, float.class, int.class, MemorySegment.class);
290         FunctionDescriptor fd = FunctionDescriptor.of(hfa, C_FLOAT, C_INT, hfa);
291         CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false);
292 
293         assertFalse(bindings.isInMemoryReturn());
294         CallingSequence callingSequence = bindings.callingSequence();
295         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class, MemorySegment.class));
296         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS, ADDRESS));
297 
298         checkArgumentBindings(callingSequence, new Binding[][]{
299             { unboxAddress(), vmStore(RETURN_BUFFER_STORAGE, long.class) },
300             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
301             { vmStore(v0, float.class) },
302             { vmStore(r0, int.class) },
303             {
304                 dup(),
305                 bufferLoad(0, float.class),
306                 vmStore(v1, float.class),
307                 bufferLoad(4, float.class),
308                 vmStore(v2, float.class)
309             }
310         });
311 
312         checkReturnBindings(callingSequence, new Binding[]{
313             allocate(hfa),
314             dup(),
315             vmLoad(v0, float.class),
316             bufferStore(0, float.class),
317             dup(),
318             vmLoad(v1, float.class),
319             bufferStore(4, float.class),
320         });
321     }
322 
323     @Test
324     public void testStructHFA3() {
325         MemoryLayout struct = MemoryLayout.structLayout(C_FLOAT, C_FLOAT, C_FLOAT);
326 
327         MethodType mt = MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class, MemorySegment.class);
328         FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct, struct, struct);
329         CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false);
330 
331         assertFalse(bindings.isInMemoryReturn());
332         CallingSequence callingSequence = bindings.callingSequence();
333         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
334         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
335 
336         checkArgumentBindings(callingSequence, new Binding[][]{
337             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
338             {
339                 dup(),
340                 bufferLoad(0, float.class),
341                 vmStore(v0, float.class),
342                 dup(),
343                 bufferLoad(4, float.class),
344                 vmStore(v1, float.class),
345                 bufferLoad(8, float.class),
346                 vmStore(v2, float.class)
347             },
348             {
349                 dup(),
350                 bufferLoad(0, float.class),
351                 vmStore(v3, float.class),
352                 dup(),
353                 bufferLoad(4, float.class),
354                 vmStore(v4, float.class),
355                 bufferLoad(8, float.class),
356                 vmStore(v5, float.class)
357             },
358             {
359                 dup(),
360                 bufferLoad(0, long.class),
361                 vmStore(stackStorage((short) 8, 0), long.class),
362                 bufferLoad(8, int.class),
363                 vmStore(stackStorage((short) 4, 8), int.class),
364             }
365         });
366 
367         checkReturnBindings(callingSequence, new Binding[]{});
368     }
369 
370     @Test
371     public void testStructStackSpill() {
372         // A large (> 16 byte) struct argument that is spilled to the
373         // stack should be passed as a pointer to a copy and occupy one
374         // stack slot.
375 
376         MemoryLayout struct = MemoryLayout.structLayout(C_INT, C_INT, C_DOUBLE, C_INT);
377 
378         MethodType mt = MethodType.methodType(
379             void.class, MemorySegment.class, MemorySegment.class, int.class, int.class,
380             int.class, int.class, int.class, int.class, MemorySegment.class, int.class);
381         FunctionDescriptor fd = FunctionDescriptor.ofVoid(
382             struct, struct, C_INT, C_INT, C_INT, C_INT, C_INT, C_INT, struct, C_INT);
383         CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false);
384 
385         assertFalse(bindings.isInMemoryReturn());
386         CallingSequence callingSequence = bindings.callingSequence();
387         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
388         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
389 
390         checkArgumentBindings(callingSequence, new Binding[][]{
391             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
392             { copy(struct), unboxAddress(), vmStore(r0, long.class) },
393             { copy(struct), unboxAddress(), vmStore(r1, long.class) },
394             { vmStore(r2, int.class) },
395             { vmStore(r3, int.class) },
396             { vmStore(r4, int.class) },
397             { vmStore(r5, int.class) },
398             { vmStore(r6, int.class) },
399             { vmStore(r7, int.class) },
400             { copy(struct), unboxAddress(), vmStore(stackStorage((short) 8, 0), long.class) },
401             { vmStore(stackStorage((short) 4, 8), int.class) },
402         });
403 
404         checkReturnBindings(callingSequence, new Binding[]{});
405     }
406 
407     @Test
408     public void testVarArgsInRegs() {
409         MethodType mt = MethodType.methodType(void.class, int.class, int.class, float.class);
410         FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_INT, C_INT, C_FLOAT);
411         FunctionDescriptor fdExpected = FunctionDescriptor.ofVoid(ADDRESS, C_INT, C_INT, C_FLOAT);
412         CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false, LinkerOptions.forDowncall(fd, firstVariadicArg(1)));
413 
414         assertFalse(bindings.isInMemoryReturn());
415         CallingSequence callingSequence = bindings.callingSequence();
416         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
417         assertEquals(callingSequence.functionDesc(), fdExpected);
418 
419         // This is identical to the non-variadic calling sequence
420         checkArgumentBindings(callingSequence, new Binding[][]{
421             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
422             { vmStore(r0, int.class) },
423             { vmStore(r1, int.class) },
424             { vmStore(v0, float.class) },
425         });
426 
427         checkReturnBindings(callingSequence, new Binding[]{});
428     }
429 
430     @Test
431     public void testFloatArrayStruct() {
432         // should be classified as HFA
433         StructLayout S10 = MemoryLayout.structLayout(
434                 MemoryLayout.sequenceLayout(4, C_DOUBLE)
435         );
436         MethodType mt = MethodType.methodType(MemorySegment.class, MemorySegment.class);
437         FunctionDescriptor fd = FunctionDescriptor.of(S10, S10);
438         FunctionDescriptor fdExpected = FunctionDescriptor.of(S10, ADDRESS, ADDRESS, S10); // uses return buffer
439         CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false);
440 
441         assertFalse(bindings.isInMemoryReturn());
442         CallingSequence callingSequence = bindings.callingSequence();
443         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class, MemorySegment.class));
444         assertEquals(callingSequence.functionDesc(), fdExpected);
445 
446         // This is identical to the non-variadic calling sequence
447         checkArgumentBindings(callingSequence, new Binding[][]{
448             { unboxAddress(), vmStore(RETURN_BUFFER_STORAGE, long.class) },
449             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
450             { dup(),
451                 bufferLoad(0, double.class),
452                 vmStore(v0, double.class),
453               dup(),
454                 bufferLoad(8, double.class),
455                 vmStore(v1, double.class),
456               dup(),
457                 bufferLoad(16, double.class),
458                 vmStore(v2, double.class),
459                 bufferLoad(24, double.class),
460                 vmStore(v3, double.class) },
461         });
462 
463         checkReturnBindings(callingSequence, new Binding[]{
464             allocate(S10),
465               dup(),
466                  vmLoad(v0, double.class),
467                  bufferStore(0, double.class),
468               dup(),
469                  vmLoad(v1, double.class),
470                  bufferStore(8, double.class),
471               dup(),
472                  vmLoad(v2, double.class),
473                  bufferStore(16, double.class),
474               dup(),
475                  vmLoad(v3, double.class),
476                  bufferStore(24, double.class),
477         });
478     }
479 }