1 /*
  2  * Copyright (c) 2020, 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.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.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.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.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                 baseAddress(),
161                 unboxAddress(),
162                 vmStore(r0, long.class)
163             }},
164             // struct s { int32_t a[2]; float b[2] };
165             { MemoryLayout.structLayout(C_INT, C_INT, C_FLOAT, C_FLOAT), new Binding[] {
166                 dup(),
167                     // s.a[0] & s.a[1]
168                     bufferLoad(0, long.class), vmStore(r0, long.class),
169                     // s.b[0] & s.b[1]
170                     bufferLoad(8, long.class), vmStore(r1, long.class),
171             }},
172             // struct s { float a; /* padding */ double b };
173             { MemoryLayout.structLayout(C_FLOAT, MemoryLayout.paddingLayout(32), C_DOUBLE),
174               new Binding[] {
175                 dup(),
176                 // s.a
177                 bufferLoad(0, long.class), vmStore(r0, long.class),
178                 // s.b
179                 bufferLoad(8, long.class), vmStore(r1, long.class),
180             }},
181         };
182     }
183 
184     @Test
185     public void testMultipleStructs() {
186         MemoryLayout struct1 = MemoryLayout.structLayout(C_INT, C_INT, C_DOUBLE, C_INT);
187         MemoryLayout struct2 = MemoryLayout.structLayout(C_LONG, C_LONG, C_LONG);
188 
189         MethodType mt = MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class, int.class);
190         FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct1, struct2, C_INT);
191         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
192 
193         assertFalse(bindings.isInMemoryReturn);
194         CallingSequence callingSequence = bindings.callingSequence;
195         assertEquals(callingSequence.methodType(), mt);
196         assertEquals(callingSequence.functionDesc(), fd);
197 
198         checkArgumentBindings(callingSequence, new Binding[][]{
199             {
200                 copy(struct1),
201                 baseAddress(),
202                 unboxAddress(),
203                 vmStore(r0, long.class)
204             },
205             {
206                 copy(struct2),
207                 baseAddress(),
208                 unboxAddress(),
209                 vmStore(r1, long.class)
210             },
211             { vmStore(r2, int.class) }
212         });
213 
214         checkReturnBindings(callingSequence, new Binding[]{});
215     }
216 
217     @Test
218     public void testReturnStruct1() {
219         MemoryLayout struct = MemoryLayout.structLayout(C_LONG, C_LONG, C_FLOAT);
220 
221         MethodType mt = MethodType.methodType(MemorySegment.class);
222         FunctionDescriptor fd = FunctionDescriptor.of(struct);
223         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
224 
225         assertTrue(bindings.isInMemoryReturn);
226         CallingSequence callingSequence = bindings.callingSequence;
227         assertEquals(callingSequence.methodType(), MethodType.methodType(void.class, MemoryAddress.class));
228         assertEquals(callingSequence.functionDesc(), FunctionDescriptor.ofVoid(C_POINTER));
229 
230         checkArgumentBindings(callingSequence, new Binding[][]{
231             {
232                 unboxAddress(),
233                 vmStore(r8, long.class)
234             }
235         });
236 
237         checkReturnBindings(callingSequence, new Binding[]{});
238     }
239 
240     @Test
241     public void testReturnStruct2() {
242         MemoryLayout struct = MemoryLayout.structLayout(C_LONG, C_LONG);
243 
244         MethodType mt = MethodType.methodType(MemorySegment.class);
245         FunctionDescriptor fd = FunctionDescriptor.of(struct);
246         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
247 
248         assertFalse(bindings.isInMemoryReturn);
249         CallingSequence callingSequence = bindings.callingSequence;
250         assertEquals(callingSequence.methodType(), mt);
251         assertEquals(callingSequence.functionDesc(), fd);
252 
253         checkArgumentBindings(callingSequence, new Binding[][]{});
254 
255         checkReturnBindings(callingSequence, new Binding[]{
256             allocate(struct),
257             dup(),
258             vmLoad(r0, long.class),
259             bufferStore(0, long.class),
260             dup(),
261             vmLoad(r1, long.class),
262             bufferStore(8, long.class),
263         });
264     }
265 
266     @Test
267     public void testStructHFA1() {
268         MemoryLayout hfa = MemoryLayout.structLayout(C_FLOAT, C_FLOAT);
269 
270         MethodType mt = MethodType.methodType(MemorySegment.class, float.class, int.class, MemorySegment.class);
271         FunctionDescriptor fd = FunctionDescriptor.of(hfa, C_FLOAT, C_INT, hfa);
272         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
273 
274         assertFalse(bindings.isInMemoryReturn);
275         CallingSequence callingSequence = bindings.callingSequence;
276         assertEquals(callingSequence.methodType(), mt);
277         assertEquals(callingSequence.functionDesc(), fd);
278 
279         checkArgumentBindings(callingSequence, new Binding[][]{
280             { vmStore(v0, float.class) },
281             { vmStore(r0, int.class) },
282             {
283                 dup(),
284                 bufferLoad(0, float.class),
285                 vmStore(v1, float.class),
286                 bufferLoad(4, float.class),
287                 vmStore(v2, float.class)
288             }
289         });
290 
291         checkReturnBindings(callingSequence, new Binding[]{
292             allocate(hfa),
293             dup(),
294             vmLoad(v0, float.class),
295             bufferStore(0, float.class),
296             dup(),
297             vmLoad(v1, float.class),
298             bufferStore(4, float.class),
299         });
300     }
301 
302     @Test
303     public void testStructHFA3() {
304         MemoryLayout struct = MemoryLayout.structLayout(C_FLOAT, C_FLOAT, C_FLOAT);
305 
306         MethodType mt = MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class, MemorySegment.class);
307         FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct, struct, struct);
308         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
309 
310         assertFalse(bindings.isInMemoryReturn);
311         CallingSequence callingSequence = bindings.callingSequence;
312         assertEquals(callingSequence.methodType(), mt);
313         assertEquals(callingSequence.functionDesc(), fd);
314 
315         checkArgumentBindings(callingSequence, new Binding[][]{
316             {
317                 dup(),
318                 bufferLoad(0, float.class),
319                 vmStore(v0, float.class),
320                 dup(),
321                 bufferLoad(4, float.class),
322                 vmStore(v1, float.class),
323                 bufferLoad(8, float.class),
324                 vmStore(v2, float.class)
325             },
326             {
327                 dup(),
328                 bufferLoad(0, float.class),
329                 vmStore(v3, float.class),
330                 dup(),
331                 bufferLoad(4, float.class),
332                 vmStore(v4, float.class),
333                 bufferLoad(8, float.class),
334                 vmStore(v5, float.class)
335             },
336             {
337                 dup(),
338                 bufferLoad(0, long.class),
339                 vmStore(stackStorage(0), long.class),
340                 bufferLoad(8, int.class),
341                 vmStore(stackStorage(1), int.class),
342             }
343         });
344 
345         checkReturnBindings(callingSequence, new Binding[]{});
346     }
347 
348     @Test
349     public void testStructStackSpill() {
350         // A large (> 16 byte) struct argument that is spilled to the
351         // stack should be passed as a pointer to a copy and occupy one
352         // stack slot.
353 
354         MemoryLayout struct = MemoryLayout.structLayout(C_INT, C_INT, C_DOUBLE, C_INT);
355 
356         MethodType mt = MethodType.methodType(
357             void.class, MemorySegment.class, MemorySegment.class, int.class, int.class,
358             int.class, int.class, int.class, int.class, MemorySegment.class, int.class);
359         FunctionDescriptor fd = FunctionDescriptor.ofVoid(
360             struct, struct, C_INT, C_INT, C_INT, C_INT, C_INT, C_INT, struct, C_INT);
361         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
362 
363         assertFalse(bindings.isInMemoryReturn);
364         CallingSequence callingSequence = bindings.callingSequence;
365         assertEquals(callingSequence.methodType(), mt);
366         assertEquals(callingSequence.functionDesc(), fd);
367 
368         checkArgumentBindings(callingSequence, new Binding[][]{
369             { copy(struct), baseAddress(), unboxAddress(), vmStore(r0, long.class) },
370             { copy(struct), baseAddress(), unboxAddress(), vmStore(r1, long.class) },
371             { vmStore(r2, int.class) },
372             { vmStore(r3, int.class) },
373             { vmStore(r4, int.class) },
374             { vmStore(r5, int.class) },
375             { vmStore(r6, int.class) },
376             { vmStore(r7, int.class) },
377             { copy(struct), baseAddress(), unboxAddress(), vmStore(stackStorage(0), long.class) },
378             { vmStore(stackStorage(1), int.class) },
379         });
380 
381         checkReturnBindings(callingSequence, new Binding[]{});
382     }
383 }