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 }