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