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 * @compile platform/PlatformLayouts.java 28 * @modules java.base/jdk.internal.foreign 29 * java.base/jdk.internal.foreign.abi 30 * java.base/jdk.internal.foreign.abi.x64 31 * java.base/jdk.internal.foreign.abi.x64.sysv 32 * @build CallArrangerTestBase 33 * @run testng TestSysVCallArranger 34 */ 35 36 import java.lang.foreign.FunctionDescriptor; 37 import java.lang.foreign.MemoryLayout; 38 import java.lang.foreign.MemorySegment; 39 import jdk.internal.foreign.abi.Binding; 40 import jdk.internal.foreign.abi.CallingSequence; 41 import jdk.internal.foreign.abi.StubLocations; 42 import jdk.internal.foreign.abi.VMStorage; 43 import jdk.internal.foreign.abi.x64.sysv.CallArranger; 44 import org.testng.annotations.DataProvider; 45 import org.testng.annotations.Test; 46 47 import java.lang.invoke.MethodType; 48 49 import static java.lang.foreign.ValueLayout.ADDRESS; 50 import static jdk.internal.foreign.abi.Binding.*; 51 import static jdk.internal.foreign.abi.x64.X86_64Architecture.*; 52 import static jdk.internal.foreign.abi.x64.X86_64Architecture.Regs.*; 53 import static platform.PlatformLayouts.SysV.*; 54 55 import static org.testng.Assert.assertEquals; 56 import static org.testng.Assert.assertFalse; 57 import static org.testng.Assert.assertTrue; 58 59 public class TestSysVCallArranger extends CallArrangerTestBase { 60 61 private static final short STACK_SLOT_SIZE = 8; 62 private static final VMStorage TARGET_ADDRESS_STORAGE = StubLocations.TARGET_ADDRESS.storage(StorageType.PLACEHOLDER); 63 private static final VMStorage RETURN_BUFFER_STORAGE = StubLocations.RETURN_BUFFER.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 80 checkReturnBindings(callingSequence, new Binding[]{}); 81 82 assertEquals(bindings.nVectorArgs(), 0); 83 } 84 85 @Test 86 public void testNestedStructs() { 87 MemoryLayout POINT = MemoryLayout.structLayout( 88 C_INT, 89 MemoryLayout.structLayout( 90 C_INT, 91 C_INT 92 ) 93 ); 94 MethodType mt = MethodType.methodType(void.class, MemorySegment.class); 95 FunctionDescriptor fd = FunctionDescriptor.ofVoid(POINT); 96 CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); 97 98 assertFalse(bindings.isInMemoryReturn()); 99 CallingSequence callingSequence = bindings.callingSequence(); 100 assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); 101 assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); 102 103 checkArgumentBindings(callingSequence, new Binding[][]{ 104 { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }, 105 { dup(), bufferLoad(0, long.class), vmStore(rdi, long.class), 106 bufferLoad(8, int.class), vmStore(rsi, int.class)}, 107 }); 108 109 checkReturnBindings(callingSequence, new Binding[]{}); 110 111 assertEquals(bindings.nVectorArgs(), 0); 112 } 113 114 @Test 115 public void testNestedUnion() { 116 MemoryLayout POINT = MemoryLayout.structLayout( 117 C_INT, 118 MemoryLayout.paddingLayout(4), 119 MemoryLayout.unionLayout( 120 MemoryLayout.structLayout(C_INT, C_INT), 121 C_LONG 122 ) 123 ); 124 MethodType mt = MethodType.methodType(void.class, MemorySegment.class); 125 FunctionDescriptor fd = FunctionDescriptor.ofVoid(POINT); 126 CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); 127 128 assertFalse(bindings.isInMemoryReturn()); 129 CallingSequence callingSequence = bindings.callingSequence(); 130 assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); 131 assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); 132 133 checkArgumentBindings(callingSequence, new Binding[][]{ 134 { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }, 135 { dup(), bufferLoad(0, long.class), vmStore(rdi, long.class), 136 bufferLoad(8, long.class), vmStore(rsi, long.class)}, 137 }); 138 139 checkReturnBindings(callingSequence, new Binding[]{}); 140 141 assertEquals(bindings.nVectorArgs(), 0); 142 } 143 144 @Test 145 public void testIntegerRegs() { 146 MethodType mt = MethodType.methodType(void.class, 147 int.class, int.class, int.class, int.class, int.class, int.class); 148 FunctionDescriptor fd = FunctionDescriptor.ofVoid( 149 C_INT, C_INT, C_INT, C_INT, C_INT, C_INT); 150 CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); 151 152 assertFalse(bindings.isInMemoryReturn()); 153 CallingSequence callingSequence = bindings.callingSequence(); 154 assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); 155 assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); 156 157 checkArgumentBindings(callingSequence, new Binding[][]{ 158 { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }, 159 { vmStore(rdi, int.class) }, 160 { vmStore(rsi, int.class) }, 161 { vmStore(rdx, int.class) }, 162 { vmStore(rcx, int.class) }, 163 { vmStore(r8, int.class) }, 164 { vmStore(r9, int.class) }, 165 }); 166 167 checkReturnBindings(callingSequence, new Binding[]{}); 168 169 assertEquals(bindings.nVectorArgs(), 0); 170 } 171 172 @Test 173 public void testDoubleRegs() { 174 MethodType mt = MethodType.methodType(void.class, 175 double.class, double.class, double.class, double.class, 176 double.class, double.class, double.class, double.class); 177 FunctionDescriptor fd = FunctionDescriptor.ofVoid( 178 C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE, 179 C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE); 180 CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); 181 182 assertFalse(bindings.isInMemoryReturn()); 183 CallingSequence callingSequence = bindings.callingSequence(); 184 assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); 185 assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); 186 187 checkArgumentBindings(callingSequence, new Binding[][]{ 188 { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }, 189 { vmStore(xmm0, double.class) }, 190 { vmStore(xmm1, double.class) }, 191 { vmStore(xmm2, double.class) }, 192 { vmStore(xmm3, double.class) }, 193 { vmStore(xmm4, double.class) }, 194 { vmStore(xmm5, double.class) }, 195 { vmStore(xmm6, double.class) }, 196 { vmStore(xmm7, double.class) }, 197 }); 198 199 checkReturnBindings(callingSequence, new Binding[]{}); 200 201 assertEquals(bindings.nVectorArgs(), 8); 202 } 203 204 @Test 205 public void testMixed() { 206 MethodType mt = MethodType.methodType(void.class, 207 long.class, long.class, long.class, long.class, long.class, long.class, long.class, long.class, 208 float.class, float.class, float.class, float.class, 209 float.class, float.class, float.class, float.class, float.class, float.class); 210 FunctionDescriptor fd = FunctionDescriptor.ofVoid( 211 C_LONG, C_LONG, C_LONG, C_LONG, C_LONG, C_LONG, C_LONG, C_LONG, 212 C_FLOAT, C_FLOAT, C_FLOAT, C_FLOAT, 213 C_FLOAT, C_FLOAT, C_FLOAT, C_FLOAT, C_FLOAT, C_FLOAT); 214 CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); 215 216 assertFalse(bindings.isInMemoryReturn()); 217 CallingSequence callingSequence = bindings.callingSequence(); 218 assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); 219 assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); 220 221 checkArgumentBindings(callingSequence, new Binding[][]{ 222 { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }, 223 { vmStore(rdi, long.class) }, 224 { vmStore(rsi, long.class) }, 225 { vmStore(rdx, long.class) }, 226 { vmStore(rcx, long.class) }, 227 { vmStore(r8, long.class) }, 228 { vmStore(r9, long.class) }, 229 { vmStore(stackStorage(STACK_SLOT_SIZE, 0), long.class) }, 230 { vmStore(stackStorage(STACK_SLOT_SIZE, 8), long.class) }, 231 { vmStore(xmm0, float.class) }, 232 { vmStore(xmm1, float.class) }, 233 { vmStore(xmm2, float.class) }, 234 { vmStore(xmm3, float.class) }, 235 { vmStore(xmm4, float.class) }, 236 { vmStore(xmm5, float.class) }, 237 { vmStore(xmm6, float.class) }, 238 { vmStore(xmm7, float.class) }, 239 { vmStore(stackStorage(STACK_SLOT_SIZE, 16), float.class) }, 240 { vmStore(stackStorage(STACK_SLOT_SIZE, 24), float.class) }, 241 }); 242 243 checkReturnBindings(callingSequence, new Binding[]{}); 244 245 assertEquals(bindings.nVectorArgs(), 8); 246 } 247 248 /** 249 * This is the example from the System V ABI AMD64 document 250 * 251 * struct structparm { 252 * int32_t a, int32_t b, double d; 253 * } s; 254 * int32_t e, f, g, h, i, j, k; 255 * double m, n; 256 * 257 * void m(e, f, s, g, h, m, n, i, j, k); 258 * 259 * m(s); 260 */ 261 @Test 262 public void testAbiExample() { 263 MemoryLayout struct = MemoryLayout.structLayout(C_INT, C_INT, C_DOUBLE); 264 265 MethodType mt = MethodType.methodType(void.class, 266 int.class, int.class, MemorySegment.class, int.class, int.class, 267 double.class, double.class, int.class, int.class, int.class); 268 FunctionDescriptor fd = FunctionDescriptor.ofVoid( 269 C_INT, C_INT, struct, C_INT, C_INT, C_DOUBLE, C_DOUBLE, C_INT, C_INT, C_INT); 270 CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); 271 272 assertFalse(bindings.isInMemoryReturn()); 273 CallingSequence callingSequence = bindings.callingSequence(); 274 assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); 275 assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); 276 277 checkArgumentBindings(callingSequence, new Binding[][]{ 278 { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }, 279 { vmStore(rdi, int.class) }, 280 { vmStore(rsi, int.class) }, 281 { 282 dup(), 283 bufferLoad(0, long.class), vmStore(rdx, long.class), 284 bufferLoad(8, double.class), vmStore(xmm0, double.class) 285 }, 286 { vmStore(rcx, int.class) }, 287 { vmStore(r8, int.class) }, 288 { vmStore(xmm1, double.class) }, 289 { vmStore(xmm2, double.class) }, 290 { vmStore(r9, int.class) }, 291 { vmStore(stackStorage(STACK_SLOT_SIZE, 0), int.class) }, 292 { vmStore(stackStorage(STACK_SLOT_SIZE, 8), int.class) }, 293 }); 294 295 checkReturnBindings(callingSequence, new Binding[]{}); 296 297 assertEquals(bindings.nVectorArgs(), 3); 298 } 299 300 /** 301 * typedef void (*f)(void); 302 * 303 * void m(f f); 304 * void f_impl(void); 305 * 306 * m(f_impl); 307 */ 308 @Test 309 public void testMemoryAddress() { 310 MethodType mt = MethodType.methodType(void.class, MemorySegment.class); 311 FunctionDescriptor fd = FunctionDescriptor.ofVoid( C_POINTER); 312 CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); 313 314 assertFalse(bindings.isInMemoryReturn()); 315 CallingSequence callingSequence = bindings.callingSequence(); 316 assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); 317 assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); 318 319 checkArgumentBindings(callingSequence, new Binding[][]{ 320 { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }, 321 { unboxAddress(), vmStore(rdi, long.class) }, 322 }); 323 324 checkReturnBindings(callingSequence, new Binding[]{}); 325 326 assertEquals(bindings.nVectorArgs(), 0); 327 } 328 329 @Test(dataProvider = "structs") 330 public void testStruct(MemoryLayout struct, Binding[] expectedBindings) { 331 MethodType mt = MethodType.methodType(void.class, MemorySegment.class); 332 FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct); 333 CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); 334 335 assertFalse(bindings.isInMemoryReturn()); 336 CallingSequence callingSequence = bindings.callingSequence(); 337 assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); 338 assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); 339 340 checkArgumentBindings(callingSequence, new Binding[][]{ 341 { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }, 342 expectedBindings, 343 }); 344 345 checkReturnBindings(callingSequence, new Binding[]{}); 346 347 assertEquals(bindings.nVectorArgs(), 0); 348 } 349 350 351 @DataProvider 352 public static Object[][] structs() { 353 return new Object[][]{ 354 { MemoryLayout.structLayout(C_LONG), new Binding[]{ 355 bufferLoad(0, long.class), vmStore(rdi, long.class) 356 } 357 }, 358 { MemoryLayout.structLayout(C_LONG, C_LONG), new Binding[]{ 359 dup(), 360 bufferLoad(0, long.class), vmStore(rdi, long.class), 361 bufferLoad(8, long.class), vmStore(rsi, long.class) 362 } 363 }, 364 { MemoryLayout.structLayout(C_LONG, C_LONG, C_LONG), new Binding[]{ 365 dup(), 366 bufferLoad(0, long.class), vmStore(stackStorage(STACK_SLOT_SIZE, 0), long.class), 367 dup(), 368 bufferLoad(8, long.class), vmStore(stackStorage(STACK_SLOT_SIZE, 8), long.class), 369 bufferLoad(16, long.class), vmStore(stackStorage(STACK_SLOT_SIZE, 16), long.class) 370 } 371 }, 372 { MemoryLayout.structLayout(C_LONG, C_LONG, C_LONG, C_LONG), new Binding[]{ 373 dup(), 374 bufferLoad(0, long.class), vmStore(stackStorage(STACK_SLOT_SIZE, 0), long.class), 375 dup(), 376 bufferLoad(8, long.class), vmStore(stackStorage(STACK_SLOT_SIZE, 8), long.class), 377 dup(), 378 bufferLoad(16, long.class), vmStore(stackStorage(STACK_SLOT_SIZE, 16), long.class), 379 bufferLoad(24, long.class), vmStore(stackStorage(STACK_SLOT_SIZE, 24), long.class) 380 } 381 }, 382 }; 383 } 384 385 @Test 386 public void testReturnRegisterStruct() { 387 MemoryLayout struct = MemoryLayout.structLayout(C_LONG, C_LONG); 388 389 MethodType mt = MethodType.methodType(MemorySegment.class); 390 FunctionDescriptor fd = FunctionDescriptor.of(struct); 391 CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); 392 393 assertFalse(bindings.isInMemoryReturn()); 394 CallingSequence callingSequence = bindings.callingSequence(); 395 assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class, MemorySegment.class)); 396 assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS, ADDRESS)); 397 398 checkArgumentBindings(callingSequence, new Binding[][]{ 399 { unboxAddress(), vmStore(RETURN_BUFFER_STORAGE, long.class) }, 400 { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }, 401 }); 402 403 checkReturnBindings(callingSequence, new Binding[] { 404 allocate(struct), 405 dup(), 406 vmLoad(rax, long.class), 407 bufferStore(0, long.class), 408 dup(), 409 vmLoad(rdx, long.class), 410 bufferStore(8, long.class) 411 }); 412 413 assertEquals(bindings.nVectorArgs(), 0); 414 } 415 416 @Test 417 public void testIMR() { 418 MemoryLayout struct = MemoryLayout.structLayout(C_LONG, C_LONG, C_LONG); 419 420 MethodType mt = MethodType.methodType(MemorySegment.class); 421 FunctionDescriptor fd = FunctionDescriptor.of(struct); 422 CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); 423 424 assertTrue(bindings.isInMemoryReturn()); 425 CallingSequence callingSequence = bindings.callingSequence(); 426 assertEquals(callingSequence.callerMethodType(), MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class)); 427 assertEquals(callingSequence.functionDesc(), FunctionDescriptor.ofVoid(ADDRESS, C_POINTER)); 428 429 checkArgumentBindings(callingSequence, new Binding[][]{ 430 { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }, 431 { unboxAddress(), vmStore(rdi, long.class) } 432 }); 433 434 checkReturnBindings(callingSequence, new Binding[] {}); 435 436 assertEquals(bindings.nVectorArgs(), 0); 437 } 438 439 @Test 440 public void testFloatStructsUpcall() { 441 MemoryLayout struct = MemoryLayout.structLayout(C_FLOAT); // should be passed in float regs 442 443 MethodType mt = MethodType.methodType(MemorySegment.class, MemorySegment.class); 444 FunctionDescriptor fd = FunctionDescriptor.of(struct, struct); 445 CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, true); 446 447 assertFalse(bindings.isInMemoryReturn()); 448 CallingSequence callingSequence = bindings.callingSequence(); 449 assertEquals(callingSequence.calleeMethodType(), mt); 450 assertEquals(callingSequence.functionDesc(), fd); 451 452 checkArgumentBindings(callingSequence, new Binding[][]{ 453 { allocate(struct), dup(), vmLoad(xmm0, float.class), bufferStore(0, float.class) }, 454 }); 455 456 checkReturnBindings(callingSequence, new Binding[] { 457 bufferLoad(0, float.class), vmStore(xmm0, float.class) 458 }); 459 460 assertEquals(bindings.nVectorArgs(), 1); 461 } 462 463 }