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 TestMacOsAArch64CallArranger
 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 import static org.testng.Assert.assertEquals;
 58 import static org.testng.Assert.assertFalse;
 59 import static org.testng.Assert.assertTrue;
 60 
 61 public class TestMacOsAArch64CallArranger extends CallArrangerTestBase {
 62 
 63     private static final VMStorage TARGET_ADDRESS_STORAGE = StubLocations.TARGET_ADDRESS.storage(StorageType.PLACEHOLDER);
 64 
 65     @Test
 66     public void testVarArgsOnStack() {
 67         MethodType mt = MethodType.methodType(void.class, int.class, int.class, float.class);
 68         FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_INT, C_INT, C_FLOAT);
 69         FunctionDescriptor fdExpected = FunctionDescriptor.ofVoid(ADDRESS, C_INT, C_INT, C_FLOAT);
 70         CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false, LinkerOptions.forDowncall(fd, firstVariadicArg(1)));
 71 
 72         assertFalse(bindings.isInMemoryReturn());
 73         CallingSequence callingSequence = bindings.callingSequence();
 74         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
 75         assertEquals(callingSequence.functionDesc(), fdExpected);
 76 
 77         // The two variadic arguments should be allocated on the stack
 78         checkArgumentBindings(callingSequence, new Binding[][]{
 79             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
 80             { vmStore(r0, int.class) },
 81             { vmStore(stackStorage((short) 4, 0), int.class) },
 82             { vmStore(stackStorage((short) 4, 8), float.class) },
 83         });
 84 
 85         checkReturnBindings(callingSequence, new Binding[]{});
 86     }
 87 
 88     @Test
 89     public void testMacArgsOnStack() {
 90         MethodType mt = MethodType.methodType(void.class,
 91                 int.class, int.class, int.class, int.class,
 92                 int.class, int.class, int.class, int.class,
 93                 int.class, int.class, short.class, byte.class);
 94         FunctionDescriptor fd = FunctionDescriptor.ofVoid(
 95                 C_INT, C_INT, C_INT, C_INT,
 96                 C_INT, C_INT, C_INT, C_INT,
 97                 C_INT, C_INT, C_SHORT, C_CHAR);
 98         CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
 99 
100         assertFalse(bindings.isInMemoryReturn());
101         CallingSequence callingSequence = bindings.callingSequence();
102         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
103         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
104 
105         checkArgumentBindings(callingSequence, new Binding[][]{
106             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
107             { vmStore(r0, int.class) },
108             { vmStore(r1, int.class) },
109             { vmStore(r2, int.class) },
110             { vmStore(r3, int.class) },
111             { vmStore(r4, int.class) },
112             { vmStore(r5, int.class) },
113             { vmStore(r6, int.class) },
114             { vmStore(r7, int.class) },
115             { vmStore(stackStorage((short) 4, 0), int.class) },
116             { vmStore(stackStorage((short) 4, 4), int.class) },
117             { cast(short.class, int.class), vmStore(stackStorage((short) 2, 8), int.class) },
118             { cast(byte.class, int.class), vmStore(stackStorage((short) 1, 10), int.class) },
119         });
120 
121         checkReturnBindings(callingSequence, new Binding[]{});
122     }
123 
124     @Test
125     public void testMacArgsOnStack2() {
126         StructLayout struct = MemoryLayout.structLayout(
127             C_FLOAT,
128             C_FLOAT
129         );
130         MethodType mt = MethodType.methodType(void.class,
131                 long.class, long.class, long.class, long.class,
132                 long.class, long.class, long.class, long.class,
133                 double.class, double.class, double.class, double.class,
134                 double.class, double.class, double.class, double.class,
135                 int.class, MemorySegment.class);
136         FunctionDescriptor fd = FunctionDescriptor.ofVoid(
137                 C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
138                 C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
139                 C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
140                 C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
141                 C_INT, struct);
142         CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
143 
144         assertFalse(bindings.isInMemoryReturn());
145         CallingSequence callingSequence = bindings.callingSequence();
146         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
147         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
148 
149         checkArgumentBindings(callingSequence, new Binding[][]{
150             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
151             { vmStore(r0, long.class) },
152             { vmStore(r1, long.class) },
153             { vmStore(r2, long.class) },
154             { vmStore(r3, long.class) },
155             { vmStore(r4, long.class) },
156             { vmStore(r5, long.class) },
157             { vmStore(r6, long.class) },
158             { vmStore(r7, long.class) },
159             { vmStore(v0, double.class) },
160             { vmStore(v1, double.class) },
161             { vmStore(v2, double.class) },
162             { vmStore(v3, double.class) },
163             { vmStore(v4, double.class) },
164             { vmStore(v5, double.class) },
165             { vmStore(v6, double.class) },
166             { vmStore(v7, double.class) },
167             { vmStore(stackStorage((short) 4, 0), int.class) },
168             {
169                 dup(),
170                 bufferLoad(0, int.class),
171                 vmStore(stackStorage((short) 4, 4), int.class),
172                 bufferLoad(4, int.class),
173                 vmStore(stackStorage((short) 4, 8), int.class),
174             }
175         });
176 
177         checkReturnBindings(callingSequence, new Binding[]{});
178     }
179 
180     @Test
181     public void testMacArgsOnStack3() {
182         StructLayout struct = MemoryLayout.structLayout(
183             C_POINTER,
184             C_POINTER
185         );
186         MethodType mt = MethodType.methodType(void.class,
187                 long.class, long.class, long.class, long.class,
188                 long.class, long.class, long.class, long.class,
189                 double.class, double.class, double.class, double.class,
190                 double.class, double.class, double.class, double.class,
191                 MemorySegment.class, float.class);
192         FunctionDescriptor fd = FunctionDescriptor.ofVoid(
193                 C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
194                 C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
195                 C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
196                 C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
197                 struct, C_FLOAT);
198         CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
199 
200         assertFalse(bindings.isInMemoryReturn());
201         CallingSequence callingSequence = bindings.callingSequence();
202         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
203         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
204 
205         checkArgumentBindings(callingSequence, new Binding[][]{
206             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
207             { vmStore(r0, long.class) },
208             { vmStore(r1, long.class) },
209             { vmStore(r2, long.class) },
210             { vmStore(r3, long.class) },
211             { vmStore(r4, long.class) },
212             { vmStore(r5, long.class) },
213             { vmStore(r6, long.class) },
214             { vmStore(r7, long.class) },
215             { vmStore(v0, double.class) },
216             { vmStore(v1, double.class) },
217             { vmStore(v2, double.class) },
218             { vmStore(v3, double.class) },
219             { vmStore(v4, double.class) },
220             { vmStore(v5, double.class) },
221             { vmStore(v6, double.class) },
222             { vmStore(v7, double.class) },
223             { dup(),
224                 bufferLoad(0, long.class), vmStore(stackStorage((short) 8, 0), long.class),
225                 bufferLoad(8, long.class), vmStore(stackStorage((short) 8, 8), long.class) },
226             { vmStore(stackStorage((short) 4, 16), float.class) },
227         });
228 
229         checkReturnBindings(callingSequence, new Binding[]{});
230     }
231 
232     @Test
233     public void testMacArgsOnStack4() {
234         StructLayout struct = MemoryLayout.structLayout(
235             C_INT,
236             C_INT,
237             C_POINTER
238         );
239         MethodType mt = MethodType.methodType(void.class,
240                 long.class, long.class, long.class, long.class,
241                 long.class, long.class, long.class, long.class,
242                 double.class, double.class, double.class, double.class,
243                 double.class, double.class, double.class, double.class,
244                 float.class, MemorySegment.class);
245         FunctionDescriptor fd = FunctionDescriptor.ofVoid(
246                 C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
247                 C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
248                 C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
249                 C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
250                 C_FLOAT, struct);
251         CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
252 
253         assertFalse(bindings.isInMemoryReturn());
254         CallingSequence callingSequence = bindings.callingSequence();
255         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
256         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
257 
258         checkArgumentBindings(callingSequence, new Binding[][]{
259             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
260             { vmStore(r0, long.class) },
261             { vmStore(r1, long.class) },
262             { vmStore(r2, long.class) },
263             { vmStore(r3, long.class) },
264             { vmStore(r4, long.class) },
265             { vmStore(r5, long.class) },
266             { vmStore(r6, long.class) },
267             { vmStore(r7, long.class) },
268             { vmStore(v0, double.class) },
269             { vmStore(v1, double.class) },
270             { vmStore(v2, double.class) },
271             { vmStore(v3, double.class) },
272             { vmStore(v4, double.class) },
273             { vmStore(v5, double.class) },
274             { vmStore(v6, double.class) },
275             { vmStore(v7, double.class) },
276             { vmStore(stackStorage((short) 4, 0), float.class) },
277             { dup(),
278                 bufferLoad(0, long.class), vmStore(stackStorage((short) 8, 8), long.class),
279                 bufferLoad(8, long.class), vmStore(stackStorage((short) 8, 16), long.class) },
280         });
281 
282         checkReturnBindings(callingSequence, new Binding[]{});
283     }
284 
285     // structs that are passed field-wise should not have padding after them
286     @Test
287     public void testMacArgsOnStack5() {
288         StructLayout struct = MemoryLayout.structLayout(
289             C_FLOAT
290         );
291         MethodType mt = MethodType.methodType(void.class,
292                 long.class, long.class, long.class, long.class,
293                 long.class, long.class, long.class, long.class,
294                 double.class, double.class, double.class, double.class,
295                 double.class, double.class, double.class, double.class,
296                 MemorySegment.class, int.class, MemorySegment.class);
297         FunctionDescriptor fd = FunctionDescriptor.ofVoid(
298                 C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
299                 C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
300                 C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
301                 C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
302                 struct, C_INT, C_POINTER);
303         CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
304 
305         assertFalse(bindings.isInMemoryReturn());
306         CallingSequence callingSequence = bindings.callingSequence();
307         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
308         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
309 
310         checkArgumentBindings(callingSequence, new Binding[][]{
311             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
312             { vmStore(r0, long.class) },
313             { vmStore(r1, long.class) },
314             { vmStore(r2, long.class) },
315             { vmStore(r3, long.class) },
316             { vmStore(r4, long.class) },
317             { vmStore(r5, long.class) },
318             { vmStore(r6, long.class) },
319             { vmStore(r7, long.class) },
320             { vmStore(v0, double.class) },
321             { vmStore(v1, double.class) },
322             { vmStore(v2, double.class) },
323             { vmStore(v3, double.class) },
324             { vmStore(v4, double.class) },
325             { vmStore(v5, double.class) },
326             { vmStore(v6, double.class) },
327             { vmStore(v7, double.class) },
328             {
329                 bufferLoad(0, int.class),
330                 vmStore(stackStorage((short) 4, 0), int.class),
331             },
332             { vmStore(stackStorage((short) 4, 4), int.class) },
333             { unboxAddress(), vmStore(stackStorage((short) 8, 8), long.class) },
334         });
335 
336         checkReturnBindings(callingSequence, new Binding[]{});
337     }
338 
339     // structs that are passed chunk-wise should have padding before them, as well as after
340     @Test
341     public void testMacArgsOnStack6() {
342         StructLayout struct = MemoryLayout.structLayout(
343             C_INT
344         );
345         MethodType mt = MethodType.methodType(void.class,
346                 long.class, long.class, long.class, long.class,
347                 long.class, long.class, long.class, long.class,
348                 double.class, double.class, double.class, double.class,
349                 double.class, double.class, double.class, double.class,
350                 int.class, MemorySegment.class, double.class, MemorySegment.class);
351         FunctionDescriptor fd = FunctionDescriptor.ofVoid(
352                 C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
353                 C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
354                 C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
355                 C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
356                 C_INT, struct, C_DOUBLE, C_POINTER);
357         CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
358 
359         assertFalse(bindings.isInMemoryReturn());
360         CallingSequence callingSequence = bindings.callingSequence();
361         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
362         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
363 
364         checkArgumentBindings(callingSequence, new Binding[][]{
365             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
366             { vmStore(r0, long.class) },
367             { vmStore(r1, long.class) },
368             { vmStore(r2, long.class) },
369             { vmStore(r3, long.class) },
370             { vmStore(r4, long.class) },
371             { vmStore(r5, long.class) },
372             { vmStore(r6, long.class) },
373             { vmStore(r7, long.class) },
374             { vmStore(v0, double.class) },
375             { vmStore(v1, double.class) },
376             { vmStore(v2, double.class) },
377             { vmStore(v3, double.class) },
378             { vmStore(v4, double.class) },
379             { vmStore(v5, double.class) },
380             { vmStore(v6, double.class) },
381             { vmStore(v7, double.class) },
382             { vmStore(stackStorage((short) 4, 0), int.class) },
383             {
384                 bufferLoad(0, int.class),
385                 vmStore(stackStorage((short) 4, 8), int.class),
386             },
387             { vmStore(stackStorage((short) 8, 16), double.class) },
388             { unboxAddress(), vmStore(stackStorage((short) 8, 24), long.class) },
389         });
390 
391         checkReturnBindings(callingSequence, new Binding[]{});
392     }
393 }