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.x64
 33  *          java.base/jdk.internal.foreign.abi.x64.windows
 34  * @build CallArrangerTestBase
 35  * @run testng TestWindowsCallArranger
 36  */
 37 
 38 import java.lang.foreign.FunctionDescriptor;
 39 import java.lang.foreign.MemoryLayout;
 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.x64.windows.CallArranger;
 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.Binding.copy;
 55 import static jdk.internal.foreign.abi.x64.X86_64Architecture.*;
 56 import static jdk.internal.foreign.abi.x64.X86_64Architecture.Regs.*;
 57 import static platform.PlatformLayouts.Win64.*;
 58 
 59 import static org.testng.Assert.*;
 60 
 61 public class TestWindowsCallArranger extends CallArrangerTestBase {
 62 
 63     private static final short STACK_SLOT_SIZE = 8;
 64     private static final VMStorage TARGET_ADDRESS_STORAGE = StubLocations.TARGET_ADDRESS.storage(StorageType.PLACEHOLDER);
 65 
 66     @Test
 67     public void testEmpty() {
 68         MethodType mt = MethodType.methodType(void.class);
 69         FunctionDescriptor fd = FunctionDescriptor.ofVoid();
 70         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
 71 
 72         assertFalse(bindings.isInMemoryReturn());
 73         CallingSequence callingSequence = bindings.callingSequence();
 74         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
 75         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
 76 
 77         checkArgumentBindings(callingSequence, new Binding[][]{
 78             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }
 79         });
 80         checkReturnBindings(callingSequence, new Binding[]{});
 81     }
 82 
 83     @Test
 84     public void testIntegerRegs() {
 85         MethodType mt = MethodType.methodType(void.class, int.class, int.class, int.class, int.class);
 86         FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_INT, C_INT, C_INT, C_INT);
 87         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
 88 
 89         assertFalse(bindings.isInMemoryReturn());
 90         CallingSequence callingSequence = bindings.callingSequence();
 91         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
 92         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
 93 
 94         checkArgumentBindings(callingSequence, new Binding[][]{
 95             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
 96             { vmStore(rcx, int.class) },
 97             { vmStore(rdx, int.class) },
 98             { vmStore(r8, int.class) },
 99             { vmStore(r9, int.class) }
100         });
101 
102         checkReturnBindings(callingSequence, new Binding[]{});
103     }
104 
105     @Test
106     public void testDoubleRegs() {
107         MethodType mt = MethodType.methodType(void.class, double.class, double.class, double.class, double.class);
108         FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE);
109         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
110 
111         assertFalse(bindings.isInMemoryReturn());
112         CallingSequence callingSequence = bindings.callingSequence();
113         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
114         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
115 
116         checkArgumentBindings(callingSequence, new Binding[][]{
117             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
118             { vmStore(xmm0, double.class) },
119             { vmStore(xmm1, double.class) },
120             { vmStore(xmm2, double.class) },
121             { vmStore(xmm3, double.class) }
122         });
123 
124         checkReturnBindings(callingSequence, new Binding[]{});
125     }
126 
127     @Test
128     public void testMixed() {
129         MethodType mt = MethodType.methodType(void.class,
130                 long.class, long.class, float.class, float.class, long.class, long.class, float.class, float.class);
131         FunctionDescriptor fd = FunctionDescriptor.ofVoid(
132                 C_LONG_LONG, C_LONG_LONG, C_FLOAT, C_FLOAT, C_LONG_LONG, C_LONG_LONG, C_FLOAT, C_FLOAT);
133         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
134 
135         assertFalse(bindings.isInMemoryReturn());
136         CallingSequence callingSequence = bindings.callingSequence();
137         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
138         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
139 
140         checkArgumentBindings(callingSequence, new Binding[][]{
141             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
142             { vmStore(rcx, long.class) },
143             { vmStore(rdx, long.class) },
144             { vmStore(xmm2, float.class) },
145             { vmStore(xmm3, float.class) },
146             { vmStore(stackStorage(STACK_SLOT_SIZE, 0), long.class) },
147             { vmStore(stackStorage(STACK_SLOT_SIZE, 8), long.class) },
148             { vmStore(stackStorage(STACK_SLOT_SIZE, 16), float.class) },
149             { vmStore(stackStorage(STACK_SLOT_SIZE, 24), float.class) }
150         });
151 
152         checkReturnBindings(callingSequence, new Binding[]{});
153     }
154 
155     @Test
156     public void testAbiExample() {
157         MemoryLayout structLayout = MemoryLayout.structLayout(C_INT, C_INT, C_DOUBLE);
158         MethodType mt = MethodType.methodType(void.class,
159                 int.class, int.class, MemorySegment.class, int.class, int.class,
160                 double.class, double.class, double.class, int.class, int.class, int.class);
161         FunctionDescriptor fd = FunctionDescriptor.ofVoid(
162                 C_INT, C_INT, structLayout, C_INT, C_INT,
163                 C_DOUBLE, C_DOUBLE, C_DOUBLE, C_INT, C_INT, C_INT);
164         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
165 
166         assertFalse(bindings.isInMemoryReturn());
167         CallingSequence callingSequence = bindings.callingSequence();
168         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
169         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
170 
171         checkArgumentBindings(callingSequence, new Binding[][]{
172             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
173             { vmStore(rcx, int.class) },
174             { vmStore(rdx, int.class) },
175             {
176                 copy(structLayout),
177                 unboxAddress(),
178                 vmStore(r8, long.class)
179             },
180             { vmStore(r9, int.class) },
181             { vmStore(stackStorage(STACK_SLOT_SIZE, 0), int.class) },
182             { vmStore(stackStorage(STACK_SLOT_SIZE, 8), double.class) },
183             { vmStore(stackStorage(STACK_SLOT_SIZE, 16), double.class) },
184             { vmStore(stackStorage(STACK_SLOT_SIZE, 24), double.class) },
185             { vmStore(stackStorage(STACK_SLOT_SIZE, 32), int.class) },
186             { vmStore(stackStorage(STACK_SLOT_SIZE, 40), int.class) },
187             { vmStore(stackStorage(STACK_SLOT_SIZE, 48), int.class) }
188         });
189 
190         checkReturnBindings(callingSequence, new Binding[]{});
191     }
192 
193     @Test
194     public void testAbiExampleVarargs() {
195         MethodType mt = MethodType.methodType(void.class,
196                 int.class, double.class, int.class, double.class, double.class);
197         FunctionDescriptor fd = FunctionDescriptor.ofVoid(
198                 C_INT, C_DOUBLE, C_INT, C_DOUBLE, C_DOUBLE);
199         FunctionDescriptor fdExpected = FunctionDescriptor.ofVoid(
200                 ADDRESS, C_INT, C_DOUBLE, C_INT, C_DOUBLE, C_DOUBLE);
201         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false, LinkerOptions.forDowncall(fd, firstVariadicArg(2)));
202 
203         assertFalse(bindings.isInMemoryReturn());
204         CallingSequence callingSequence = bindings.callingSequence();
205         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
206         assertEquals(callingSequence.functionDesc(), fdExpected);
207 
208         checkArgumentBindings(callingSequence, new Binding[][]{
209             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
210             { vmStore(rcx, int.class) },
211             { vmStore(xmm1, double.class) },
212             { vmStore(r8, int.class) },
213             { dup(), vmStore(r9, double.class), vmStore(xmm3, double.class) },
214             { vmStore(stackStorage(STACK_SLOT_SIZE, 0), double.class) },
215         });
216 
217         checkReturnBindings(callingSequence, new Binding[]{});
218     }
219 
220     /**
221      * struct s {
222      *   uint64_t u0;
223      * } s;
224      *
225      * void m(struct s s);
226      *
227      * m(s);
228      */
229     @Test
230     public void testStructRegister() {
231         MemoryLayout struct = MemoryLayout.structLayout(C_LONG_LONG);
232 
233         MethodType mt = MethodType.methodType(void.class, MemorySegment.class);
234         FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct);
235         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
236 
237         assertFalse(bindings.isInMemoryReturn());
238         CallingSequence callingSequence = bindings.callingSequence();
239         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
240         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
241 
242         checkArgumentBindings(callingSequence, new Binding[][]{
243             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
244             { bufferLoad(0, long.class), vmStore(rcx, long.class) }
245         });
246 
247         checkReturnBindings(callingSequence, new Binding[]{});
248     }
249 
250     /**
251      * struct s {
252      *   uint64_t u0, u1;
253      * } s;
254      *
255      * void m(struct s s);
256      *
257      * m(s);
258      */
259     @Test
260     public void testStructReference() {
261         MemoryLayout struct = MemoryLayout.structLayout(C_LONG_LONG, C_LONG_LONG);
262 
263         MethodType mt = MethodType.methodType(void.class, MemorySegment.class);
264         FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct);
265         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
266 
267         assertFalse(bindings.isInMemoryReturn());
268         CallingSequence callingSequence = bindings.callingSequence();
269         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
270         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
271 
272         checkArgumentBindings(callingSequence, new Binding[][]{
273             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
274             {
275                 copy(struct),
276                 unboxAddress(),
277                 vmStore(rcx, long.class)
278             }
279         });
280 
281         checkReturnBindings(callingSequence, new Binding[]{});
282     }
283 
284     /**
285      * typedef void (*f)(void);
286      *
287      * void m(f f);
288      * void f_impl(void);
289      *
290      * m(f_impl);
291      */
292     @Test
293     public void testMemoryAddress() {
294         MethodType mt = MethodType.methodType(void.class, MemorySegment.class);
295         FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_POINTER);
296         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
297 
298         assertFalse(bindings.isInMemoryReturn());
299         CallingSequence callingSequence = bindings.callingSequence();
300         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
301         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
302 
303         checkArgumentBindings(callingSequence, new Binding[][]{
304             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
305             { unboxAddress(), vmStore(rcx, long.class) }
306         });
307 
308         checkReturnBindings(callingSequence, new Binding[]{});
309     }
310 
311     @Test
312     public void testReturnRegisterStruct() {
313         MemoryLayout struct = MemoryLayout.structLayout(C_LONG_LONG);
314 
315         MethodType mt = MethodType.methodType(MemorySegment.class);
316         FunctionDescriptor fd = FunctionDescriptor.of(struct);
317         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
318 
319         assertFalse(bindings.isInMemoryReturn());
320         CallingSequence callingSequence = bindings.callingSequence();
321         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
322         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
323 
324         checkArgumentBindings(callingSequence, new Binding[][]{
325             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
326         });
327 
328         checkReturnBindings(callingSequence,
329             new Binding[]{ allocate(struct),
330                 dup(),
331                 vmLoad(rax, long.class),
332                 bufferStore(0, long.class) });
333     }
334 
335     @Test
336     public void testIMR() {
337         MemoryLayout struct = MemoryLayout.structLayout(C_LONG_LONG, C_LONG_LONG);
338 
339         MethodType mt = MethodType.methodType(MemorySegment.class);
340         FunctionDescriptor fd = FunctionDescriptor.of(struct);
341         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
342 
343         assertTrue(bindings.isInMemoryReturn());
344         CallingSequence callingSequence = bindings.callingSequence();
345         assertEquals(callingSequence.callerMethodType(), MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class));
346         assertEquals(callingSequence.functionDesc(), FunctionDescriptor.ofVoid(ADDRESS, C_POINTER));
347 
348         checkArgumentBindings(callingSequence, new Binding[][]{
349             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
350             { unboxAddress(), vmStore(rcx, long.class) }
351         });
352 
353         checkReturnBindings(callingSequence, new Binding[]{});
354     }
355 
356     @Test
357     public void testStackStruct() {
358         MemoryLayout struct = MemoryLayout.structLayout(C_POINTER, C_DOUBLE, C_INT);
359 
360         MethodType mt = MethodType.methodType(void.class,
361             MemorySegment.class, int.class, double.class, MemorySegment.class,
362             MemorySegment.class, int.class, double.class, MemorySegment.class,
363             MemorySegment.class, int.class, double.class, MemorySegment.class,
364             MemorySegment.class, int.class, double.class, MemorySegment.class);
365         FunctionDescriptor fd = FunctionDescriptor.ofVoid(
366             struct, C_INT, C_DOUBLE, C_POINTER,
367             struct, C_INT, C_DOUBLE, C_POINTER,
368             struct, C_INT, C_DOUBLE, C_POINTER,
369             struct, C_INT, C_DOUBLE, C_POINTER);
370         CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);
371 
372         assertFalse(bindings.isInMemoryReturn());
373         CallingSequence callingSequence = bindings.callingSequence();
374         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
375         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
376 
377         checkArgumentBindings(callingSequence, new Binding[][]{
378             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
379             { copy(struct), unboxAddress(), vmStore(rcx, long.class) },
380             { vmStore(rdx, int.class) },
381             { vmStore(xmm2, double.class) },
382             { unboxAddress(), vmStore(r9, long.class) },
383             { copy(struct), unboxAddress(), vmStore(stackStorage(STACK_SLOT_SIZE, 0), long.class) },
384             { vmStore(stackStorage(STACK_SLOT_SIZE, 8), int.class) },
385             { vmStore(stackStorage(STACK_SLOT_SIZE, 16), double.class) },
386             { unboxAddress(), vmStore(stackStorage(STACK_SLOT_SIZE, 24), long.class) },
387             { copy(struct), unboxAddress(), vmStore(stackStorage(STACK_SLOT_SIZE, 32), long.class) },
388             { vmStore(stackStorage(STACK_SLOT_SIZE, 40), int.class) },
389             { vmStore(stackStorage(STACK_SLOT_SIZE, 48), double.class) },
390             { unboxAddress(), vmStore(stackStorage(STACK_SLOT_SIZE, 56), long.class) },
391             { copy(struct), unboxAddress(), vmStore(stackStorage(STACK_SLOT_SIZE, 64), long.class) },
392             { vmStore(stackStorage(STACK_SLOT_SIZE, 72), int.class) },
393             { vmStore(stackStorage(STACK_SLOT_SIZE, 80), double.class) },
394             { unboxAddress(), vmStore(stackStorage(STACK_SLOT_SIZE, 88), long.class) },
395         });
396 
397         checkReturnBindings(callingSequence, new Binding[]{});
398     }
399 }