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