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