1 /* 2 * Copyright (c) 2019, 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 package jdk.internal.foreign.abi; 24 25 import jdk.incubator.foreign.MemoryHandles; 26 import jdk.incubator.foreign.MemoryLayout; 27 28 import java.util.ArrayList; 29 import java.util.List; 30 import java.util.Objects; 31 32 import java.lang.invoke.VarHandle; 33 import java.nio.ByteOrder; 34 35 /** 36 * The binding operators defined in the Binding class can be combined into argument and return value processing 'recipes'. 37 * 38 * The binding operators are interpreted using a stack-base interpreter. Operators can either consume operands from the 39 * stack, or push them onto the stack. 40 * 41 * In the description of each binding we talk about 'boxing' and 'unboxing'. 42 * - Unboxing is the process of taking a Java value and decomposing it, and storing components into machine 43 * storage locations. As such, the binding interpreter stack starts with the Java value on it, and should end empty. 44 * - Boxing is the process of re-composing a Java value by pulling components from machine storage locations. 45 * If a MemorySegment is needed to store the result, one should be allocated using the ALLOCATE_BUFFER operator. 46 * The binding interpreter stack starts off empty, and ends with the value to be returned as the only value on it. 47 * A binding operator can be interpreted differently based on whether we are boxing or unboxing a value. For example, 48 * the CONVERT_ADDRESS operator 'unboxes' a MemoryAddress to a long, but 'boxes' a long to a MemoryAddress. 49 * 50 * Here are some examples of binding recipes derived from C declarations, and according to the Windows ABI (recipes are 51 * ABI-specific). Note that each argument has it's own recipe, which is indicated by '[number]:' (though, the only 52 * example that has multiple arguments is the one using varargs). 53 * 54 * -------------------- 55 * 56 * void f(int i); 57 * 58 * Argument bindings: 59 * 0: MOVE(rcx, int.class) // move an 'int' into the RCX register 60 * 61 * Return bindings: 62 * none 63 * 64 * -------------------- 65 * 66 * void f(int* i); 67 * 68 * Argument bindings: 69 * 0: CONVERT_ADDRESS // the 'MemoryAddress' is converted into a 'long' 70 * MOVE(rcx, long.class) // the 'long' is moved into the RCX register 71 * 72 * Return bindings: 73 * none 74 * 75 * -------------------- 76 * 77 * int* f(); 78 * 79 * Argument bindings: 80 * none 81 * 82 * Return bindings: 83 * 0: MOVE(rax, long) // load a 'long' from the RAX register 84 * CONVERT_ADDRESS // convert the 'long' into a 'MemoryAddress' 85 * 86 * -------------------- 87 * 88 * typedef struct { // fits into single register 89 * int x; 90 * int y; 91 * } MyStruct; 92 * 93 * void f(MyStruct ms); 94 * 95 * Argument bindings: 96 * 0: DEREFERENCE(0, long.class) // From the struct's memory region, load a 'long' from offset '0' 97 * MOVE(rcx, long.class) // and copy that into the RCX register 98 * 99 * Return bindings: 100 * none 101 * 102 * -------------------- 103 * 104 * typedef struct { // does not fit into single register 105 * long long x; 106 * long long y; 107 * } MyStruct; 108 * 109 * void f(MyStruct ms); 110 * 111 * For the Windows ABI: 112 * 113 * Argument bindings: 114 * 0: COPY(16, 8) // copy the memory region containing the struct 115 * BASE_ADDRESS // take the base address of the copy 116 * CONVERT_ADDRESS // converts the base address to a 'long' 117 * MOVE(rcx, long.class) // moves the 'long' into the RCX register 118 * 119 * Return bindings: 120 * none 121 * 122 * For the SysV ABI: 123 * 124 * Argument bindings: 125 * 0: DUP // duplicates the MemoryRegion operand 126 * DEREFERENCE(0, long.class) // loads a 'long' from offset '0' 127 * MOVE(rdx, long.class) // moves the long into the RDX register 128 * DEREFERENCE(8, long.class) // loads a 'long' from offset '8' 129 * MOVE(rcx, long.class) // moves the long into the RCX register 130 * 131 * Return bindings: 132 * none 133 * 134 * -------------------- 135 * 136 * typedef struct { // fits into single register 137 * int x; 138 * int y; 139 * } MyStruct; 140 * 141 * MyStruct f(); 142 * 143 * Argument bindings: 144 * none 145 * 146 * Return bindings: 147 * 0: ALLOCATE(GroupLayout(C_INT, C_INT)) // allocate a buffer with the memory layout of the struct 148 * DUP // duplicate the allocated buffer 149 * MOVE(rax, long.class) // loads a 'long' from rax 150 * DEREFERENCE(0, long.class) // stores a 'long' at offset 0 151 * 152 * -------------------- 153 * 154 * typedef struct { // does not fit into single register 155 * long long x; 156 * long long y; 157 * } MyStruct; 158 * 159 * MyStruct f(); 160 * 161 * !! uses synthetic argument, which is a pointer to a pre-allocated buffer 162 * 163 * Argument bindings: 164 * 0: CONVERT_ADDRESS // unbox the MemoryAddress synthetic argument 165 * MOVE(rcx, long.class) // moves the 'long' into the RCX register 166 * 167 * Return bindings: 168 * none 169 * 170 * -------------------- 171 * 172 * void f(int dummy, ...); // varargs 173 * 174 * f(0, 10f); // passing a float 175 * 176 * Argument bindings: 177 * 0: MOVE(rcx, int.class) // moves the 'int dummy' into the RCX register 178 * 179 * 1: DUP // duplicates the '10f' argument 180 * MOVE(rdx, float.class) // move one copy into the RDX register 181 * MOVE(xmm1, float.class) // moves the other copy into the xmm2 register 182 * 183 * Return bindings: 184 * none 185 * 186 * -------------------- 187 */ 188 public abstract class Binding { 189 enum Tag { 190 MOVE, 191 DEREFERENCE, 192 COPY_BUFFER, 193 ALLOC_BUFFER, 194 CONVERT_ADDRESS, 195 BASE_ADDRESS, 196 DUP 197 } 198 199 private final Tag tag; 200 201 private Binding(Tag tag) { 202 this.tag = tag; 203 } 204 205 public Tag tag() { 206 return tag; 207 } 208 209 private static void checkType(Class<?> type) { 210 if (!type.isPrimitive() || type == void.class || type == boolean.class) 211 throw new IllegalArgumentException("Illegal type: " + type); 212 } 213 214 public static Move move(VMStorage storage, Class<?> type) { 215 checkType(type); 216 return new Move(storage, type); 217 } 218 219 public static Dereference dereference(long offset, Class<?> type) { 220 checkType(type); 221 if (offset < 0) 222 throw new IllegalArgumentException("Negative offset: " + offset); 223 return new Dereference(offset, type); 224 } 225 226 public static Copy copy(MemoryLayout layout) { 227 return new Copy(layout.byteSize(), layout.byteAlignment()); 228 } 229 230 public static Allocate allocate(MemoryLayout layout) { 231 return new Allocate(layout.byteSize(), layout.byteAlignment()); 232 } 233 234 public static ConvertAddress convertAddress() { 235 return ConvertAddress.INSTANCE; 236 } 237 238 public static BaseAddress baseAddress() { 239 return BaseAddress.INSTANCE; 240 } 241 242 public static Dup dup() { 243 return Dup.INSTANCE; 244 } 245 246 247 public static Binding.Builder builder() { 248 return new Binding.Builder(); 249 } 250 251 /** 252 * A builder helper class for generating lists of Bindings 253 */ 254 public static class Builder { 255 private final List<Binding> bindings = new ArrayList<>(); 256 257 public Binding.Builder move(VMStorage storage, Class<?> type) { 258 bindings.add(Binding.move(storage, type)); 259 return this; 260 } 261 262 public Binding.Builder dereference(long offset, Class<?> type) { 263 bindings.add(Binding.dereference(offset, type)); 264 return this; 265 } 266 267 public Binding.Builder copy(MemoryLayout layout) { 268 bindings.add(Binding.copy(layout)); 269 return this; 270 } 271 272 public Binding.Builder allocate(MemoryLayout layout) { 273 bindings.add(Binding.allocate(layout)); 274 return this; 275 } 276 277 public Binding.Builder convertAddress() { 278 bindings.add(Binding.convertAddress()); 279 return this; 280 } 281 282 public Binding.Builder baseAddress() { 283 bindings.add(Binding.baseAddress()); 284 return this; 285 } 286 287 public Binding.Builder dup() { 288 bindings.add(Binding.dup()); 289 return this; 290 } 291 292 public List<Binding> build() { 293 return new ArrayList<>(bindings); 294 } 295 } 296 297 /** 298 * MOVE([storage location], [type]) 299 * When unboxing: pops a [type] from the operand stack, and moves it to [storage location] 300 * When boxing: loads a [type] from [storage location], and pushes it onto the operand stack 301 * The [type] must be one of byte, short, char, int, long, float, or double 302 */ 303 public static class Move extends Binding { 304 private final VMStorage storage; 305 private final Class<?> type; 306 307 private Move(VMStorage storage, Class<?> type) { 308 super(Tag.MOVE); 309 this.storage = storage; 310 this.type = type; 311 } 312 313 public VMStorage storage() { 314 return storage; 315 } 316 317 public Class<?> type() { 318 return type; 319 } 320 321 @Override 322 public String toString() { 323 return "Move{" + 324 "tag=" + tag() + 325 ", storage=" + storage + 326 ", type=" + type + 327 '}'; 328 } 329 330 @Override 331 public boolean equals(Object o) { 332 if (this == o) return true; 333 if (o == null || getClass() != o.getClass()) return false; 334 Move move = (Move) o; 335 return storage.equals(move.storage) && 336 type.equals(move.type); 337 } 338 339 @Override 340 public int hashCode() { 341 return Objects.hash(tag(), storage, type); 342 } 343 } 344 345 /** 346 * DEREFERENCE([offset into memory region], [type]) 347 * When unboxing: pops a MemorySegment from the operand stack, 348 * loads a [type] from [offset into memory region] from it, and pushes it onto the operand stack 349 * When boxing: pops a [type], and then a MemorySegment from the operand stack, 350 * and then stores [type] to [offset into memory region] of the MemorySegment 351 * The [type] must be one of byte, short, char, int, long, float, or double 352 */ 353 public static class Dereference extends Binding { 354 private final long offset; 355 private final Class<?> type; 356 357 private Dereference(long offset, Class<?> type) { 358 super(Tag.DEREFERENCE); 359 this.offset = offset; 360 this.type = type; 361 } 362 363 public long offset() { 364 return offset; 365 } 366 367 public Class<?> type() { 368 return type; 369 } 370 371 public VarHandle varHandle() { 372 return MemoryHandles.withOffset(MemoryHandles.varHandle(type, ByteOrder.nativeOrder()), offset); 373 } 374 375 @Override 376 public String toString() { 377 return "Dereference{" + 378 "tag=" + tag() + 379 ", offset=" + offset + 380 ", type=" + type + 381 '}'; 382 } 383 384 @Override 385 public boolean equals(Object o) { 386 if (this == o) return true; 387 if (o == null || getClass() != o.getClass()) return false; 388 Dereference that = (Dereference) o; 389 return offset == that.offset && 390 type.equals(that.type); 391 } 392 393 @Override 394 public int hashCode() { 395 return Objects.hash(tag(), offset, type); 396 } 397 } 398 399 /** 400 * COPY([size], [alignment]) 401 * Creates a new MemorySegment with the given [size] and [alignment], 402 * and copies contents from a MemorySegment popped from the top of the operand stack into this new buffer, 403 * and pushes the new buffer onto the operand stack 404 */ 405 public static class Copy extends Binding { 406 private final long size; 407 private final long alignment; 408 409 private Copy(long size, long alignment) { 410 super(Tag.COPY_BUFFER); 411 this.size = size; 412 this.alignment = alignment; 413 } 414 415 public long size() { 416 return size; 417 } 418 419 public long alignment() { 420 return alignment; 421 } 422 423 @Override 424 public String toString() { 425 return "Copy{" + 426 "tag=" + tag() + 427 ", size=" + size + 428 ", alignment=" + alignment + 429 '}'; 430 } 431 432 @Override 433 public boolean equals(Object o) { 434 if (this == o) return true; 435 if (o == null || getClass() != o.getClass()) return false; 436 Copy copy = (Copy) o; 437 return size == copy.size && 438 alignment == copy.alignment; 439 } 440 441 @Override 442 public int hashCode() { 443 return Objects.hash(tag(), size, alignment); 444 } 445 } 446 447 /** 448 * ALLOCATE([size], [alignment]) 449 * Creates a new MemorySegment with the give [size] and [alignment], and pushes it onto the operand stack. 450 */ 451 public static class Allocate extends Binding { 452 private final long size; 453 private final long alignment; 454 455 private Allocate(long size, long alignment) { 456 super(Tag.ALLOC_BUFFER); 457 this.size = size; 458 this.alignment = alignment; 459 } 460 461 public long size() { 462 return size; 463 } 464 465 public long alignment() { 466 return alignment; 467 } 468 469 @Override 470 public String toString() { 471 return "AllocateBuffer{" + 472 "tag=" + tag() + 473 "size=" + size + 474 ", alignment=" + alignment + 475 '}'; 476 } 477 478 @Override 479 public boolean equals(Object o) { 480 if (this == o) return true; 481 if (o == null || getClass() != o.getClass()) return false; 482 Allocate that = (Allocate) o; 483 return size == that.size && 484 alignment == that.alignment; 485 } 486 487 @Override 488 public int hashCode() { 489 return Objects.hash(tag(), size, alignment); 490 } 491 } 492 493 /** 494 * CONVERT_ADDRESS() 495 * When unboxing: pops a 'MemoryAddress' from the operand stack, converts it to a 'long', 496 * and pushes that onto the operand stack 497 * When boxing: pops a 'long' from the operand stack, converts it to a 'MemoryAddress', 498 * and pushes that onto the operand stack 499 */ 500 public static class ConvertAddress extends Binding { 501 private static final ConvertAddress INSTANCE = new ConvertAddress(); 502 private ConvertAddress() { 503 super(Tag.CONVERT_ADDRESS); 504 } 505 506 @Override 507 public String toString() { 508 return "BoxAddress{" + 509 "tag=" + tag() + 510 "}"; 511 } 512 513 @Override 514 public int hashCode() { 515 return tag().hashCode(); 516 } 517 518 @Override 519 public boolean equals(Object o) { 520 if (this == o) return true; 521 return o != null && getClass() == o.getClass(); 522 } 523 } 524 525 /** 526 * BASE_ADDRESS() 527 * Pops a MemorySegment from the operand stack, and takes the base address of the segment 528 * (the MemoryAddress that points to the start), and pushes that onto the operand stack 529 */ 530 public static class BaseAddress extends Binding { 531 private static final BaseAddress INSTANCE = new BaseAddress(); 532 private BaseAddress() { 533 super(Tag.BASE_ADDRESS); 534 } 535 536 @Override 537 public String toString() { 538 return "BaseAddress{" + 539 "tag=" + tag() + 540 "}"; 541 } 542 543 @Override 544 public int hashCode() { 545 return tag().hashCode(); 546 } 547 548 @Override 549 public boolean equals(Object o) { 550 if (this == o) return true; 551 return o != null && getClass() == o.getClass(); 552 } 553 } 554 555 /** 556 * DUP() 557 * Duplicates the value on the top of the operand stack (without popping it!), 558 * and pushes the duplicate onto the operand stack 559 */ 560 public static class Dup extends Binding { 561 private static final Dup INSTANCE = new Dup(); 562 private Dup() { 563 super(Tag.DUP); 564 } 565 566 @Override 567 public String toString() { 568 return "Dup{" + 569 "tag=" + tag() + 570 "}"; 571 } 572 573 @Override 574 public int hashCode() { 575 return tag().hashCode(); 576 } 577 578 @Override 579 public boolean equals(Object o) { 580 if (this == o) return true; 581 return o != null && getClass() == o.getClass(); 582 } 583 } 584 }