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