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