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.internal.foreign.abi.Binding;
 39 import jdk.internal.foreign.abi.CallingSequence;
 40 import jdk.internal.foreign.abi.aarch64.CallArranger;
 41 import org.testng.annotations.DataProvider;
 42 import org.testng.annotations.Test;
 43 
 44 import java.lang.invoke.MethodType;
 45 

 46 import static jdk.internal.foreign.PlatformLayouts.AArch64.*;
 47 import static jdk.internal.foreign.abi.Binding.*;
 48 import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.*;
 49 import static org.testng.Assert.assertEquals;
 50 import static org.testng.Assert.assertFalse;
 51 import static org.testng.Assert.assertTrue;
 52 
 53 public class TestAarch64CallArranger extends CallArrangerTestBase {
 54 
 55     @Test
 56     public void testEmpty() {
 57         MethodType mt = MethodType.methodType(void.class);
 58         FunctionDescriptor fd = FunctionDescriptor.ofVoid();
 59         CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false);
 60 
 61         assertFalse(bindings.isInMemoryReturn);
 62         CallingSequence callingSequence = bindings.callingSequence;
 63         assertEquals(callingSequence.methodType(), mt);
 64         assertEquals(callingSequence.functionDesc(), fd);
 65 
 66         checkArgumentBindings(callingSequence, new Binding[][]{});


 67 
 68         checkReturnBindings(callingSequence, new Binding[]{});
 69     }
 70 
 71     @Test
 72     public void testInteger() {
 73         MethodType mt = MethodType.methodType(void.class,
 74                 int.class, int.class, int.class, int.class,
 75                 int.class, int.class, int.class, int.class,
 76                 int.class, int.class);
 77         FunctionDescriptor fd = FunctionDescriptor.ofVoid(
 78                 C_INT, C_INT, C_INT, C_INT,
 79                 C_INT, C_INT, C_INT, C_INT,
 80                 C_INT, C_INT);
 81         CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false);
 82 
 83         assertFalse(bindings.isInMemoryReturn);
 84         CallingSequence callingSequence = bindings.callingSequence;
 85         assertEquals(callingSequence.methodType(), mt);
 86         assertEquals(callingSequence.functionDesc(), fd);
 87 
 88         checkArgumentBindings(callingSequence, new Binding[][]{

 89             { vmStore(r0, int.class) },
 90             { vmStore(r1, int.class) },
 91             { vmStore(r2, int.class) },
 92             { vmStore(r3, int.class) },
 93             { vmStore(r4, int.class) },
 94             { vmStore(r5, int.class) },
 95             { vmStore(r6, int.class) },
 96             { vmStore(r7, int.class) },
 97             { vmStore(stackStorage(0), int.class) },
 98             { vmStore(stackStorage(1), int.class) },
 99         });
100 
101         checkReturnBindings(callingSequence, new Binding[]{});
102     }
103 
104     @Test
105     public void testTwoIntTwoFloat() {
106         MethodType mt = MethodType.methodType(void.class,
107                 int.class, int.class, float.class, float.class);
108         FunctionDescriptor fd = FunctionDescriptor.ofVoid(
109                 C_INT, C_INT, C_FLOAT, C_FLOAT);
110         CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false);
111 
112         assertFalse(bindings.isInMemoryReturn);
113         CallingSequence callingSequence = bindings.callingSequence;
114         assertEquals(callingSequence.methodType(), mt);
115         assertEquals(callingSequence.functionDesc(), fd);
116 
117         checkArgumentBindings(callingSequence, new Binding[][]{

118             { vmStore(r0, int.class) },
119             { vmStore(r1, int.class) },
120             { vmStore(v0, float.class) },
121             { vmStore(v1, float.class) },
122         });
123 
124         checkReturnBindings(callingSequence, new Binding[]{});
125     }
126 
127     @Test(dataProvider = "structs")
128     public void testStruct(MemoryLayout struct, Binding[] expectedBindings) {
129         MethodType mt = MethodType.methodType(void.class, MemorySegment.class);
130         FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct);
131         CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false);
132 
133         assertFalse(bindings.isInMemoryReturn);
134         CallingSequence callingSequence = bindings.callingSequence;
135         assertEquals(callingSequence.methodType(), mt);
136         assertEquals(callingSequence.functionDesc(), fd);
137 
138         checkArgumentBindings(callingSequence, new Binding[][]{

139             expectedBindings
140         });
141 
142         checkReturnBindings(callingSequence, new Binding[]{});
143     }
144 
145     @DataProvider
146     public static Object[][] structs() {
147         MemoryLayout struct2 = MemoryLayout.structLayout(C_INT, C_INT, C_DOUBLE, C_INT);
148         return new Object[][]{
149             // struct s { int32_t a, b; double c; };
150             { MemoryLayout.structLayout(C_INT, C_INT, C_DOUBLE), new Binding[] {
151                 dup(),
152                     // s.a & s.b
153                     bufferLoad(0, long.class), vmStore(r0, long.class),
154                     // s.c --> note AArch64 passes this in an *integer* register
155                     bufferLoad(8, long.class), vmStore(r1, long.class),
156             }},
157             // struct s { int32_t a, b; double c; int32_t d };
158             { struct2, new Binding[] {
159                 copy(struct2),
160                 unboxAddress(MemorySegment.class),
161                 vmStore(r0, long.class)
162             }},
163             // struct s { int32_t a[2]; float b[2] };
164             { MemoryLayout.structLayout(C_INT, C_INT, C_FLOAT, C_FLOAT), new Binding[] {
165                 dup(),
166                     // s.a[0] & s.a[1]
167                     bufferLoad(0, long.class), vmStore(r0, long.class),
168                     // s.b[0] & s.b[1]
169                     bufferLoad(8, long.class), vmStore(r1, long.class),
170             }},
171             // struct s { float a; /* padding */ double b };
172             { MemoryLayout.structLayout(C_FLOAT, MemoryLayout.paddingLayout(32), C_DOUBLE),
173               new Binding[] {
174                 dup(),
175                 // s.a
176                 bufferLoad(0, long.class), vmStore(r0, long.class),
177                 // s.b
178                 bufferLoad(8, long.class), vmStore(r1, long.class),
179             }},
180         };
181     }
182 
183     @Test
184     public void testMultipleStructs() {
185         MemoryLayout struct1 = MemoryLayout.structLayout(C_INT, C_INT, C_DOUBLE, C_INT);
186         MemoryLayout struct2 = MemoryLayout.structLayout(C_LONG, C_LONG, C_LONG);
187 
188         MethodType mt = MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class, int.class);
189         FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct1, struct2, C_INT);
190         CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false);
191 
192         assertFalse(bindings.isInMemoryReturn);
193         CallingSequence callingSequence = bindings.callingSequence;
194         assertEquals(callingSequence.methodType(), mt);
195         assertEquals(callingSequence.functionDesc(), fd);
196 
197         checkArgumentBindings(callingSequence, new Binding[][]{

198             {
199                 copy(struct1),
200                 unboxAddress(MemorySegment.class),
201                 vmStore(r0, long.class)
202             },
203             {
204                 copy(struct2),
205                 unboxAddress(MemorySegment.class),
206                 vmStore(r1, long.class)
207             },
208             { vmStore(r2, int.class) }
209         });
210 
211         checkReturnBindings(callingSequence, new Binding[]{});
212     }
213 
214     @Test
215     public void testReturnStruct1() {
216         MemoryLayout struct = MemoryLayout.structLayout(C_LONG, C_LONG, C_FLOAT);
217 
218         MethodType mt = MethodType.methodType(MemorySegment.class);
219         FunctionDescriptor fd = FunctionDescriptor.of(struct);
220         CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false);
221 
222         assertTrue(bindings.isInMemoryReturn);
223         CallingSequence callingSequence = bindings.callingSequence;
224         assertEquals(callingSequence.methodType(), MethodType.methodType(void.class, MemoryAddress.class));
225         assertEquals(callingSequence.functionDesc(), FunctionDescriptor.ofVoid(C_POINTER));
226 
227         checkArgumentBindings(callingSequence, new Binding[][]{

228             {
229                 unboxAddress(),
230                 vmStore(r8, long.class)
231             }
232         });
233 
234         checkReturnBindings(callingSequence, new Binding[]{});
235     }
236 
237     @Test
238     public void testReturnStruct2() {
239         MemoryLayout struct = MemoryLayout.structLayout(C_LONG, C_LONG);
240 
241         MethodType mt = MethodType.methodType(MemorySegment.class);
242         FunctionDescriptor fd = FunctionDescriptor.of(struct);
243         CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false);
244 
245         assertFalse(bindings.isInMemoryReturn);
246         CallingSequence callingSequence = bindings.callingSequence;
247         assertEquals(callingSequence.methodType(), mt);
248         assertEquals(callingSequence.functionDesc(), fd);
249 
250         checkArgumentBindings(callingSequence, new Binding[][]{});



251 
252         checkReturnBindings(callingSequence, new Binding[]{
253             allocate(struct),
254             dup(),
255             vmLoad(r0, long.class),
256             bufferStore(0, long.class),
257             dup(),
258             vmLoad(r1, long.class),
259             bufferStore(8, long.class),
260         });
261     }
262 
263     @Test
264     public void testStructHFA1() {
265         MemoryLayout hfa = MemoryLayout.structLayout(C_FLOAT, C_FLOAT);
266 
267         MethodType mt = MethodType.methodType(MemorySegment.class, float.class, int.class, MemorySegment.class);
268         FunctionDescriptor fd = FunctionDescriptor.of(hfa, C_FLOAT, C_INT, hfa);
269         CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false);
270 
271         assertFalse(bindings.isInMemoryReturn);
272         CallingSequence callingSequence = bindings.callingSequence;
273         assertEquals(callingSequence.methodType(), mt);
274         assertEquals(callingSequence.functionDesc(), fd);
275 
276         checkArgumentBindings(callingSequence, new Binding[][]{


277             { vmStore(v0, float.class) },
278             { vmStore(r0, int.class) },
279             {
280                 dup(),
281                 bufferLoad(0, float.class),
282                 vmStore(v1, float.class),
283                 bufferLoad(4, float.class),
284                 vmStore(v2, float.class)
285             }
286         });
287 
288         checkReturnBindings(callingSequence, new Binding[]{
289             allocate(hfa),
290             dup(),
291             vmLoad(v0, float.class),
292             bufferStore(0, float.class),
293             dup(),
294             vmLoad(v1, float.class),
295             bufferStore(4, float.class),
296         });
297     }
298 
299     @Test
300     public void testStructHFA3() {
301         MemoryLayout struct = MemoryLayout.structLayout(C_FLOAT, C_FLOAT, C_FLOAT);
302 
303         MethodType mt = MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class, MemorySegment.class);
304         FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct, struct, struct);
305         CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false);
306 
307         assertFalse(bindings.isInMemoryReturn);
308         CallingSequence callingSequence = bindings.callingSequence;
309         assertEquals(callingSequence.methodType(), mt);
310         assertEquals(callingSequence.functionDesc(), fd);
311 
312         checkArgumentBindings(callingSequence, new Binding[][]{

313             {
314                 dup(),
315                 bufferLoad(0, float.class),
316                 vmStore(v0, float.class),
317                 dup(),
318                 bufferLoad(4, float.class),
319                 vmStore(v1, float.class),
320                 bufferLoad(8, float.class),
321                 vmStore(v2, float.class)
322             },
323             {
324                 dup(),
325                 bufferLoad(0, float.class),
326                 vmStore(v3, float.class),
327                 dup(),
328                 bufferLoad(4, float.class),
329                 vmStore(v4, float.class),
330                 bufferLoad(8, float.class),
331                 vmStore(v5, float.class)
332             },
333             {
334                 dup(),
335                 bufferLoad(0, long.class),
336                 vmStore(stackStorage(0), long.class),
337                 bufferLoad(8, int.class),
338                 vmStore(stackStorage(1), int.class),
339             }
340         });
341 
342         checkReturnBindings(callingSequence, new Binding[]{});
343     }
344 
345     @Test
346     public void testStructStackSpill() {
347         // A large (> 16 byte) struct argument that is spilled to the
348         // stack should be passed as a pointer to a copy and occupy one
349         // stack slot.
350 
351         MemoryLayout struct = MemoryLayout.structLayout(C_INT, C_INT, C_DOUBLE, C_INT);
352 
353         MethodType mt = MethodType.methodType(
354             void.class, MemorySegment.class, MemorySegment.class, int.class, int.class,
355             int.class, int.class, int.class, int.class, MemorySegment.class, int.class);
356         FunctionDescriptor fd = FunctionDescriptor.ofVoid(
357             struct, struct, C_INT, C_INT, C_INT, C_INT, C_INT, C_INT, struct, C_INT);
358         CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false);
359 
360         assertFalse(bindings.isInMemoryReturn);
361         CallingSequence callingSequence = bindings.callingSequence;
362         assertEquals(callingSequence.methodType(), mt);
363         assertEquals(callingSequence.functionDesc(), fd);
364 
365         checkArgumentBindings(callingSequence, new Binding[][]{

366             { copy(struct), unboxAddress(MemorySegment.class), vmStore(r0, long.class) },
367             { copy(struct), unboxAddress(MemorySegment.class), vmStore(r1, long.class) },
368             { vmStore(r2, int.class) },
369             { vmStore(r3, int.class) },
370             { vmStore(r4, int.class) },
371             { vmStore(r5, int.class) },
372             { vmStore(r6, int.class) },
373             { vmStore(r7, int.class) },
374             { copy(struct), unboxAddress(MemorySegment.class), vmStore(stackStorage(0), long.class) },
375             { vmStore(stackStorage(1), int.class) },
376         });
377 
378         checkReturnBindings(callingSequence, new Binding[]{});
379     }
380 
381     @Test
382     public void testVarArgsInRegs() {
383         MethodType mt = MethodType.methodType(void.class, int.class, int.class, float.class);
384         FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_INT).asVariadic(C_INT, C_FLOAT);

385         CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false);
386 
387         assertFalse(bindings.isInMemoryReturn);
388         CallingSequence callingSequence = bindings.callingSequence;
389         assertEquals(callingSequence.methodType(), mt);
390         assertEquals(callingSequence.functionDesc(), fd);
391 
392         // This is identical to the non-variadic calling sequence
393         checkArgumentBindings(callingSequence, new Binding[][]{

394             { vmStore(r0, int.class) },
395             { vmStore(r1, int.class) },
396             { vmStore(v0, float.class) },
397         });
398 
399         checkReturnBindings(callingSequence, new Binding[]{});
400     }
401 
402     @Test
403     public void testVarArgsOnStack() {
404         MethodType mt = MethodType.methodType(void.class, int.class, int.class, float.class);
405         FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_INT).asVariadic(C_INT, C_FLOAT);

406         CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
407 
408         assertFalse(bindings.isInMemoryReturn);
409         CallingSequence callingSequence = bindings.callingSequence;
410         assertEquals(callingSequence.methodType(), mt);
411         assertEquals(callingSequence.functionDesc(), fd);
412 
413         // The two variadic arguments should be allocated on the stack
414         checkArgumentBindings(callingSequence, new Binding[][]{

415             { vmStore(r0, int.class) },
416             { vmStore(stackStorage(0), int.class) },
417             { vmStore(stackStorage(1), float.class) },
418         });
419 
420         checkReturnBindings(callingSequence, new Binding[]{});
421     }
422 }
--- EOF ---