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