1 /* 2 * Copyright (c) 2015, 2018, 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.x64.sysv; 24 25 import java.foreign.NativeMethodType; 26 import java.foreign.NativeTypes; 27 import java.foreign.layout.Address; 28 import java.foreign.layout.Group; 29 import java.foreign.layout.Group.Kind; 30 import java.foreign.layout.Layout; 31 import java.foreign.layout.Padding; 32 import java.foreign.layout.Sequence; 33 import java.foreign.layout.Value; 34 import java.foreign.memory.LayoutType; 35 import java.util.ArrayList; 36 import java.util.stream.Stream; 37 import jdk.internal.foreign.Util; 38 import jdk.internal.foreign.abi.Argument; 39 import jdk.internal.foreign.abi.ArgumentBinding; 40 import jdk.internal.foreign.abi.CallingSequence; 41 import jdk.internal.foreign.abi.Storage; 42 import jdk.internal.foreign.abi.StorageClass; 43 import jdk.internal.foreign.abi.x64.CallingSequenceBuilder; 44 import jdk.internal.foreign.abi.x64.ArgumentClass; 45 import jdk.internal.foreign.abi.x64.SharedConstants; 46 47 import static sun.security.action.GetBooleanAction.privilegedGetProperty; 48 49 public class StandardCall extends CallingSequenceBuilder { 50 private static final String[] INTEGER_ARGUMENT_REGISTER_NAMES = { "rdi", "rsi", "rdx", "rcx", "r8", "r9" }; 51 private static final String[] INTEGER_RETURN_REGISTERS_NAMES = { "rax", "rdx" }; 52 private static final String[] X87_RETURN_REGISTERS_NAMES = { "st0", "st1" }; 53 54 private static final boolean DEBUG = 55 privilegedGetProperty("jdk.internal.foreign.abi.x64.sysv.DEBUG"); 56 57 // The AVX 512 enlightened ABI says "eight eightbytes" 58 // Although AMD64 0.99.6 states 4 eightbytes 59 private static final int MAX_AGGREGATE_REGS_SIZE = 8; 60 61 private static final ArrayList<ArgumentClass> COMPLEX_X87_CLASSES; 62 private static final SysVx64ABI abi = SysVx64ABI.getInstance(); 63 64 static { 65 COMPLEX_X87_CLASSES = new ArrayList<>(); 66 COMPLEX_X87_CLASSES.add(ArgumentClass.X87); 67 COMPLEX_X87_CLASSES.add(ArgumentClass.X87UP); 68 COMPLEX_X87_CLASSES.add(ArgumentClass.X87); 69 COMPLEX_X87_CLASSES.add(ArgumentClass.X87UP); 70 } 71 72 public StandardCall() { 73 super(INTEGER_ARGUMENT_REGISTER_NAMES, INTEGER_RETURN_REGISTERS_NAMES, X87_RETURN_REGISTERS_NAMES, 74 Constants.MAX_VECTOR_ARGUMENT_REGISTERS, Constants.MAX_VECTOR_RETURN_REGISTERS); 75 } 76 77 static class ArgumentInfo { 78 private final ArrayList<ArgumentClass> classes; 79 private final int nIntegerRegs, nVectorRegs, nX87Regs; 80 private final boolean inMemory; 81 82 public ArgumentInfo(ArrayList<ArgumentClass> classes, int nIntegerRegs, int nVectorRegs, int nX87Regs) { 83 this.classes = classes; 84 85 this.nIntegerRegs = nIntegerRegs; 86 this.nVectorRegs = nVectorRegs; 87 this.nX87Regs = nX87Regs; 88 89 this.inMemory = false; 90 } 91 92 public ArgumentInfo(int n) { 93 this.classes = new ArrayList<>(); 94 for (int i = 0; i < n; i++) { 95 classes.add(ArgumentClass.MEMORY); 96 } 97 98 this.inMemory = true; 99 this.nIntegerRegs = 0; 100 this.nVectorRegs = 0; 101 this.nX87Regs = 0; 102 } 103 104 public int getIntegerRegs() { 105 return nIntegerRegs; 106 } 107 108 public int getVectorRegs() { 109 return nVectorRegs; 110 } 111 112 public int getX87Regs() { 113 return nX87Regs; 114 } 115 116 public boolean inMemory() { 117 return inMemory; 118 } 119 120 public ArrayList<ArgumentClass> getClasses() { 121 return classes; 122 } 123 124 static ArgumentInfo EMPTY = new ArgumentInfo(new ArrayList<>(), 0, 0, 0); 125 } 126 127 private ArrayList<ArgumentClass> classifyValueType(Value type) { 128 ArrayList<ArgumentClass> classes = new ArrayList<>(); 129 130 switch (type.kind()) { 131 case INTEGRAL_SIGNED: case INTEGRAL_UNSIGNED: 132 classes.add(ArgumentClass.INTEGER); 133 // int128 134 long left = (type.bitsSize() / 8) - 8; 135 while (left > 0) { 136 classes.add(ArgumentClass.INTEGER); 137 left -= 8; 138 } 139 return classes; 140 case FLOATING_POINT: 141 if ((type.bitsSize() / 8) > 8) { 142 classes.add(ArgumentClass.X87); 143 classes.add(ArgumentClass.X87UP); 144 return classes; 145 } else { 146 classes.add(ArgumentClass.SSE); 147 return classes; 148 } 149 default: 150 throw new IllegalArgumentException("Type " + type + " is not yet supported"); 151 } 152 } 153 154 private ArrayList<ArgumentClass> classifyArrayType(Sequence type) { 155 long nWords = Util.alignUp((type.bitsSize() / 8), 8) / 8; 156 if (nWords > MAX_AGGREGATE_REGS_SIZE) { 157 return createMemoryClassArray(nWords); 158 } 159 160 ArrayList<ArgumentClass> classes = new ArrayList<>(); 161 162 for (long i = 0; i < nWords; i++) { 163 classes.add(ArgumentClass.NO_CLASS); 164 } 165 166 long offset = 0; 167 final long count = type.elementsSize(); 168 for (long idx = 0; idx < count; idx++) { 169 Layout t = type.element(); 170 offset = align(t, false, offset); 171 ArrayList<ArgumentClass> subclasses = classifyType(t); 172 if (subclasses.isEmpty()) { 173 return classes; 174 } 175 176 for (int i = 0; i < subclasses.size(); i++) { 177 int pos = (int)(offset / 8); 178 ArgumentClass newClass = classes.get(i + pos).merge(subclasses.get(i)); 179 classes.set(i + pos, newClass); 180 } 181 182 offset += t.bitsSize() / 8; 183 } 184 185 for (int i = 0; i < classes.size(); i++) { 186 ArgumentClass c = classes.get(i); 187 188 if (c == ArgumentClass.MEMORY) { 189 return createMemoryClassArray(classes.size()); 190 } 191 192 if (c == ArgumentClass.X87UP) { 193 if (i == 0) { 194 throw new IllegalArgumentException("Unexpected leading X87UP class"); 195 } 196 197 if (classes.get(i - 1) != ArgumentClass.X87) { 198 return createMemoryClassArray(classes.size()); 199 } 200 } 201 } 202 203 if (classes.size() > 2) { 204 if (classes.get(0) != ArgumentClass.SSE) { 205 return createMemoryClassArray(classes.size()); 206 } 207 208 for (int i = 1; i < classes.size(); i++) { 209 if (classes.get(i) != ArgumentClass.SSEUP) { 210 return createMemoryClassArray(classes.size()); 211 } 212 } 213 } 214 215 return classes; 216 } 217 218 // TODO: handle zero length arrays 219 // TODO: Handle nested structs (and primitives) 220 private ArrayList<ArgumentClass> classifyStructType(Group type) { 221 long nWords = Util.alignUp((type.bitsSize() / 8), 8) / 8; 222 if (nWords > MAX_AGGREGATE_REGS_SIZE) { 223 return createMemoryClassArray(nWords); 224 } 225 226 ArrayList<ArgumentClass> classes = new ArrayList<>(); 227 228 for (long i = 0; i < nWords; i++) { 229 classes.add(ArgumentClass.NO_CLASS); 230 } 231 232 long offset = 0; 233 final int count = type.elements().size(); 234 for (int idx = 0; idx < count; idx++) { 235 Layout t = type.elements().get(idx); 236 if (t instanceof Padding) { 237 continue; 238 } 239 // ignore zero-length array for now 240 // TODO: handle zero length arrays here 241 if (t instanceof Sequence) { 242 if (((Sequence) t).elementsSize() == 0) { 243 continue; 244 } 245 } 246 offset = align(t, false, offset); 247 ArrayList<ArgumentClass> subclasses = classifyType(t); 248 if (subclasses.isEmpty()) { 249 return classes; 250 } 251 252 for (int i = 0; i < subclasses.size(); i++) { 253 int pos = (int)(offset / 8); 254 ArgumentClass newClass = classes.get(i + pos).merge(subclasses.get(i)); 255 classes.set(i + pos, newClass); 256 } 257 258 // TODO: validate union strategy is sound 259 if (type.kind() != Kind.UNION) { 260 offset += t.bitsSize() / 8; 261 } 262 } 263 264 for (int i = 0; i < classes.size(); i++) { 265 ArgumentClass c = classes.get(i); 266 267 if (c == ArgumentClass.MEMORY) { 268 return createMemoryClassArray(classes.size()); 269 } 270 271 if (c == ArgumentClass.X87UP) { 272 if (i == 0) { 273 throw new IllegalArgumentException("Unexpected leading X87UP class"); 274 } 275 276 if (classes.get(i - 1) != ArgumentClass.X87) { 277 return createMemoryClassArray(classes.size()); 278 } 279 } 280 } 281 282 if (classes.size() > 2) { 283 if (classes.get(0) != ArgumentClass.SSE) { 284 return createMemoryClassArray(classes.size()); 285 } 286 287 for (int i = 1; i < classes.size(); i++) { 288 if (classes.get(i) != ArgumentClass.SSEUP) { 289 return createMemoryClassArray(classes.size()); 290 } 291 } 292 } 293 294 return classes; 295 } 296 297 private ArrayList<ArgumentClass> classifyType(Layout type) { 298 try { 299 if (type instanceof Value) { 300 return classifyValueType((Value) type); 301 } else if (type instanceof Address) { 302 ArrayList<ArgumentClass> classes = new ArrayList<>(); 303 classes.add(ArgumentClass.INTEGER); 304 return classes; 305 } else if (type instanceof Sequence) { 306 return classifyArrayType((Sequence) type); 307 } else if (type instanceof Group) { 308 return type.name().isPresent() && type.name().get().equals("LongDoubleComplex") ? 309 COMPLEX_X87_CLASSES : 310 classifyStructType((Group) type); 311 } else { 312 throw new IllegalArgumentException("Unhandled type " + type); 313 } 314 } catch (UnsupportedOperationException e) { 315 System.err.println("Failed to classify layout: " + type); 316 throw e; 317 } 318 } 319 320 private ArrayList<ArgumentClass> createMemoryClassArray(long n) { 321 ArrayList<ArgumentClass> classes = new ArrayList<>(); 322 for (int i = 0; i < n; i++) { 323 classes.add(ArgumentClass.MEMORY); 324 } 325 326 return classes; 327 } 328 329 private ArgumentInfo examineArgument(boolean forArguments, Layout type) { 330 ArrayList<ArgumentClass> classes = classifyType(type); 331 if (classes.isEmpty()) { 332 return ArgumentInfo.EMPTY; 333 } 334 335 int nIntegerRegs = 0; 336 int nVectorRegs = 0; 337 int nX87Regs = 0; 338 339 for (ArgumentClass c : classes) { 340 switch (c) { 341 case INTEGER: 342 nIntegerRegs++; 343 break; 344 case SSE: 345 nVectorRegs++; 346 break; 347 case X87: 348 case X87UP: 349 if (forArguments) { 350 return new ArgumentInfo(classes.size()); 351 } else { 352 nX87Regs++; 353 break; 354 } 355 default: 356 break; 357 } 358 } 359 360 if (nIntegerRegs != 0 || nVectorRegs != 0 || nX87Regs != 0) { 361 return new ArgumentInfo(classes, nIntegerRegs, nVectorRegs, nX87Regs); 362 } else { 363 return new ArgumentInfo(classes.size()); 364 } 365 } 366 367 class StorageCalculator { 368 private final ArrayList<ArgumentBinding>[] bindings; 369 private final boolean forArguments; 370 371 private int nIntegerRegs = 0; 372 private int nVectorRegs = 0; 373 private int nX87Regs = 0; 374 private long stackOffset = 0; 375 376 StorageCalculator(ArrayList<ArgumentBinding>[] bindings, boolean forArguments) { 377 this.bindings = bindings; 378 this.forArguments = forArguments; 379 } 380 381 void addBindings(Argument arg, ArgumentInfo info) { 382 if (info.inMemory() || 383 nIntegerRegs + info.getIntegerRegs() > (forArguments ? Constants.MAX_INTEGER_ARGUMENT_REGISTERS : Constants.MAX_INTEGER_RETURN_REGISTERS) || 384 nVectorRegs + info.getVectorRegs() > (forArguments ? Constants.MAX_VECTOR_ARGUMENT_REGISTERS : Constants.MAX_VECTOR_RETURN_REGISTERS)) { 385 // stack 386 387 long alignment = Math.max(alignment(arg.getType(), true), 8); 388 389 long newStackOffset = Util.alignUp(stackOffset, alignment); 390 391 // fill holes on stack with nulls 392 for (int i = 0; i < (newStackOffset - stackOffset) / 8; i++) { 393 bindings[StorageClass.STACK_ARGUMENT_SLOT.ordinal()].add(null); 394 } 395 stackOffset = newStackOffset; 396 397 long tmpStackOffset = stackOffset; 398 for (int i = 0; i < info.getClasses().size(); i++) { 399 Storage storage = new Storage(StorageClass.STACK_ARGUMENT_SLOT, tmpStackOffset / 8, 8); 400 bindings[StorageClass.STACK_ARGUMENT_SLOT.ordinal()].add(new ArgumentBinding(storage, arg, i * 8)); 401 402 if (DEBUG) { 403 System.out.println("Argument " + arg.getName() + " will be passed on stack at offset " + tmpStackOffset); 404 } 405 406 tmpStackOffset += 8; 407 } 408 409 stackOffset += arg.getType().bitsSize() / 8; 410 } else { 411 // regs 412 for (int i = 0; i < info.getClasses().size(); i++) { 413 Storage storage; 414 415 ArgumentClass c = info.getClasses().get(i); 416 417 switch (c) { 418 case INTEGER: 419 storage = new Storage(forArguments ? StorageClass.INTEGER_ARGUMENT_REGISTER : StorageClass.INTEGER_RETURN_REGISTER, nIntegerRegs++, SharedConstants.INTEGER_REGISTER_SIZE); 420 bindings[storage.getStorageClass().ordinal()].add(new ArgumentBinding(storage, arg, i * 8)); 421 422 if (DEBUG) { 423 System.out.println("Argument " + arg.getName() + " will be passed in register " + getStorageName(storage)); 424 } 425 break; 426 427 case SSE: { 428 int width = 8; 429 430 for (int j = i + 1; j < info.getClasses().size(); j++) { 431 if (info.getClasses().get(j) == ArgumentClass.SSEUP) { 432 width += 8; 433 } 434 } 435 436 if (width > 64) { 437 throw new IllegalArgumentException((width * 8) + "-bit vector arguments not supported"); 438 } 439 440 storage = new Storage(forArguments ? StorageClass.VECTOR_ARGUMENT_REGISTER : StorageClass.VECTOR_RETURN_REGISTER, nVectorRegs++, width); 441 bindings[storage.getStorageClass().ordinal()].add(new ArgumentBinding(storage, arg, i * 8)); 442 443 if (DEBUG) { 444 System.out.println("Argument " + arg.getName() + " will be passed in register " + getStorageName(storage)); 445 } 446 break; 447 } 448 449 case SSEUP: 450 break; 451 452 case X87: { 453 int width = 8; 454 455 if (i < info.getClasses().size() && info.getClasses().get(i + 1) == ArgumentClass.X87UP) { 456 width += 8; 457 } 458 459 assert !forArguments; 460 461 storage = new Storage(StorageClass.X87_RETURN_REGISTER, nX87Regs++, width); 462 bindings[storage.getStorageClass().ordinal()].add(new ArgumentBinding(storage, arg, i * 8)); 463 464 if (DEBUG) { 465 System.out.println("Argument " + arg.getName() + " will be passed in register " + getStorageName(storage)); 466 } 467 break; 468 } 469 470 case X87UP: 471 break; 472 473 default: 474 throw new UnsupportedOperationException("Unhandled class " + c); 475 } 476 } 477 } 478 } 479 } 480 481 private void addBindings(ArrayList<Argument> members, StorageCalculator calculator) { 482 members.stream().forEach(arg -> calculator.addBindings(arg, examineArgument(calculator.forArguments, arg.getType()))); 483 } 484 485 public CallingSequence arrangeCall(NativeMethodType nmt) { 486 return arrangeCall(nmt.returnType() == NativeTypes.VOID ? null : nmt.returnType().layout(), 487 Stream.of(nmt.parameterArray()).map(LayoutType::layout).toArray(Layout[]::new)); 488 } 489 490 public CallingSequence arrangeCall(Layout ret, Layout... params) { 491 ArrayList<Argument> returns = new ArrayList<>(); 492 ArrayList<Argument> args = new ArrayList<>(); 493 boolean returnsInMemory = false; 494 495 if (ret != null) { 496 returnsInMemory = examineArgument(false, ret).inMemory(); 497 498 // In some cases the return is passed in as first implicit pointer argument, and a corresponding pointer type is returned 499 if (returnsInMemory) { 500 args = new ArrayList<>(); 501 502 Argument returnPointer = new Argument(-1, Address.ofLayout(64, ret), "__retval"); 503 args.add(returnPointer); 504 returns.add(returnPointer); 505 } else { 506 returns.add(new Argument(-1, ret, "__retval")); 507 } 508 } 509 510 for (int i = 0; i < params.length; i++) { 511 args.add(new Argument(i, params[i], "arg" + i)); 512 } 513 514 @SuppressWarnings("unchecked") 515 ArrayList<ArgumentBinding>[] bindings = (ArrayList<ArgumentBinding>[]) new ArrayList<?>[StorageClass.values().length]; 516 517 for (int i = 0; i < StorageClass.values().length; i++) { 518 bindings[i] = new ArrayList<>(); 519 } 520 521 addBindings(args, new StorageCalculator(bindings, true)); 522 addBindings(returns, new StorageCalculator(bindings, false)); 523 524 return new CallingSequence(params.length, bindings, returnsInMemory); 525 } 526 } --- EOF ---