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.layout.Address; 26 import java.foreign.layout.Group; 27 import java.foreign.layout.Group.Kind; 28 import java.foreign.layout.Layout; 29 import java.foreign.layout.Padding; 30 import java.foreign.layout.Sequence; 31 import java.foreign.layout.Value; 32 import java.util.ArrayList; 33 import java.util.List; 34 import java.util.function.BiConsumer; 35 36 import jdk.internal.foreign.Util; 37 import jdk.internal.foreign.abi.Argument; 38 import jdk.internal.foreign.abi.ArgumentBinding; 39 import jdk.internal.foreign.abi.CallingSequenceBuilder; 40 import jdk.internal.foreign.abi.Storage; 41 import jdk.internal.foreign.abi.StorageClass; 42 import jdk.internal.foreign.abi.x64.ArgumentClass; 43 import jdk.internal.foreign.abi.x64.SharedUtils; 44 45 import static sun.security.action.GetBooleanAction.privilegedGetProperty; 46 47 public class CallingSequenceBuilderImpl extends CallingSequenceBuilder { 48 49 private static final SharedUtils.StorageDebugHelper storageDbgHelper = new SharedUtils.StorageDebugHelper( 50 new String[] { "rdi", "rsi", "rdx", "rcx", "r8", "r9" }, 51 new String[] { "rax", "rdx" }, 52 new String[] { "st0", "st1" }, 53 SysVx64ABI.MAX_VECTOR_ARGUMENT_REGISTERS, 54 SysVx64ABI.MAX_VECTOR_RETURN_REGISTERS 55 ); 56 57 private static final boolean DEBUG = 58 privilegedGetProperty("jdk.internal.foreign.abi.x64.sysv.DEBUG"); 59 60 // The AVX 512 enlightened ABI says "eight eightbytes" 61 // Although AMD64 0.99.6 states 4 eightbytes 62 private static final int MAX_AGGREGATE_REGS_SIZE = 8; 63 64 private static final ArrayList<ArgumentClass> COMPLEX_X87_CLASSES; 65 66 static { 67 COMPLEX_X87_CLASSES = new ArrayList<>(); 68 COMPLEX_X87_CLASSES.add(ArgumentClass.X87); 69 COMPLEX_X87_CLASSES.add(ArgumentClass.X87UP); 70 COMPLEX_X87_CLASSES.add(ArgumentClass.X87); 71 COMPLEX_X87_CLASSES.add(ArgumentClass.X87UP); 72 } 73 74 public CallingSequenceBuilderImpl(Layout layout) { 75 this(layout, new StorageCalculator(false), new StorageCalculator(true)); 76 } 77 78 private CallingSequenceBuilderImpl(Layout layout, StorageCalculator retCalculator, StorageCalculator argCalculator) { 79 super(layout, retCalculator::addBindings, argCalculator::addBindings, argCalculator::addBindings); 80 } 81 82 @Override 83 protected ArgumentInfo makeArgument(Layout layout, int pos, String name) { 84 return new ArgumentInfo(layout, pos, name); 85 } 86 87 static class ArgumentInfo extends Argument { 88 private final List<ArgumentClass> classes; 89 90 public ArgumentInfo(Layout layout, int argumentIndex, String debugName) { 91 super(layout, argumentIndex, debugName); 92 this.classes = classifyType(layout); 93 } 94 95 public int getIntegerRegs() { 96 return (int)classes.stream() 97 .filter(cl -> cl == ArgumentClass.INTEGER) 98 .count(); 99 } 100 101 public int getVectorRegs() { 102 return (int)classes.stream() 103 .filter(cl -> cl == ArgumentClass.SSE) 104 .count(); 105 } 106 107 @Override 108 public boolean inMemory() { 109 return classes.stream().allMatch(this::isMemoryClass); 110 } 111 112 private boolean isMemoryClass(ArgumentClass cl) { 113 return cl == ArgumentClass.MEMORY || 114 (argumentIndex() != -1 && 115 (cl == ArgumentClass.X87 || cl == ArgumentClass.X87UP)); 116 } 117 118 public List<ArgumentClass> getClasses() { 119 return classes; 120 } 121 } 122 123 private static List<ArgumentClass> classifyValueType(Value type) { 124 ArrayList<ArgumentClass> classes = new ArrayList<>(); 125 126 switch (type.kind()) { 127 case INTEGRAL_SIGNED: case INTEGRAL_UNSIGNED: 128 classes.add(ArgumentClass.INTEGER); 129 // int128 130 long left = (type.bitsSize() / 8) - 8; 131 while (left > 0) { 132 classes.add(ArgumentClass.INTEGER); 133 left -= 8; 134 } 135 return classes; 136 case FLOATING_POINT: 137 if ((type.bitsSize() / 8) > 8) { 138 classes.add(ArgumentClass.X87); 139 classes.add(ArgumentClass.X87UP); 140 return classes; 141 } else { 142 classes.add(ArgumentClass.SSE); 143 return classes; 144 } 145 default: 146 throw new IllegalArgumentException("Type " + type + " is not yet supported"); 147 } 148 } 149 150 private static List<ArgumentClass> classifyArrayType(Sequence type) { 151 long nWords = Util.alignUp((type.bitsSize() / 8), 8) / 8; 152 if (nWords > MAX_AGGREGATE_REGS_SIZE) { 153 return createMemoryClassArray(nWords); 154 } 155 156 ArrayList<ArgumentClass> classes = new ArrayList<>(); 157 158 for (long i = 0; i < nWords; i++) { 159 classes.add(ArgumentClass.NO_CLASS); 160 } 161 162 long offset = 0; 163 final long count = type.elementsSize(); 164 for (long idx = 0; idx < count; idx++) { 165 Layout t = type.element(); 166 offset = SharedUtils.align(t, false, offset); 167 List<ArgumentClass> subclasses = classifyType(t); 168 if (subclasses.isEmpty()) { 169 return classes; 170 } 171 172 for (int i = 0; i < subclasses.size(); i++) { 173 int pos = (int)(offset / 8); 174 ArgumentClass newClass = classes.get(i + pos).merge(subclasses.get(i)); 175 classes.set(i + pos, newClass); 176 } 177 178 offset += t.bitsSize() / 8; 179 } 180 181 for (int i = 0; i < classes.size(); i++) { 182 ArgumentClass c = classes.get(i); 183 184 if (c == ArgumentClass.MEMORY) { 185 return createMemoryClassArray(classes.size()); 186 } 187 188 if (c == ArgumentClass.X87UP) { 189 if (i == 0) { 190 throw new IllegalArgumentException("Unexpected leading X87UP class"); 191 } 192 193 if (classes.get(i - 1) != ArgumentClass.X87) { 194 return createMemoryClassArray(classes.size()); 195 } 196 } 197 } 198 199 if (classes.size() > 2) { 200 if (classes.get(0) != ArgumentClass.SSE) { 201 return createMemoryClassArray(classes.size()); 202 } 203 204 for (int i = 1; i < classes.size(); i++) { 205 if (classes.get(i) != ArgumentClass.SSEUP) { 206 return createMemoryClassArray(classes.size()); 207 } 208 } 209 } 210 211 return classes; 212 } 213 214 // TODO: handle zero length arrays 215 // TODO: Handle nested structs (and primitives) 216 private static List<ArgumentClass> classifyStructType(Group type) { 217 long nWords = Util.alignUp((type.bitsSize() / 8), 8) / 8; 218 if (nWords > MAX_AGGREGATE_REGS_SIZE) { 219 return createMemoryClassArray(nWords); 220 } 221 222 ArrayList<ArgumentClass> classes = new ArrayList<>(); 223 224 for (long i = 0; i < nWords; i++) { 225 classes.add(ArgumentClass.NO_CLASS); 226 } 227 228 long offset = 0; 229 final int count = type.elements().size(); 230 for (int idx = 0; idx < count; idx++) { 231 Layout t = type.elements().get(idx); 232 if (t instanceof Padding) { 233 continue; 234 } 235 // ignore zero-length array for now 236 // TODO: handle zero length arrays here 237 if (t instanceof Sequence) { 238 if (((Sequence) t).elementsSize() == 0) { 239 continue; 240 } 241 } 242 offset = SharedUtils.align(t, false, offset); 243 List<ArgumentClass> subclasses = classifyType(t); 244 if (subclasses.isEmpty()) { 245 return classes; 246 } 247 248 for (int i = 0; i < subclasses.size(); i++) { 249 int pos = (int)(offset / 8); 250 ArgumentClass newClass = classes.get(i + pos).merge(subclasses.get(i)); 251 classes.set(i + pos, newClass); 252 } 253 254 // TODO: validate union strategy is sound 255 if (type.kind() != Kind.UNION) { 256 offset += t.bitsSize() / 8; 257 } 258 } 259 260 for (int i = 0; i < classes.size(); i++) { 261 ArgumentClass c = classes.get(i); 262 263 if (c == ArgumentClass.MEMORY) { 264 return createMemoryClassArray(classes.size()); 265 } 266 267 if (c == ArgumentClass.X87UP) { 268 if (i == 0) { 269 throw new IllegalArgumentException("Unexpected leading X87UP class"); 270 } 271 272 if (classes.get(i - 1) != ArgumentClass.X87) { 273 return createMemoryClassArray(classes.size()); 274 } 275 } 276 } 277 278 if (classes.size() > 2) { 279 if (classes.get(0) != ArgumentClass.SSE) { 280 return createMemoryClassArray(classes.size()); 281 } 282 283 for (int i = 1; i < classes.size(); i++) { 284 if (classes.get(i) != ArgumentClass.SSEUP) { 285 return createMemoryClassArray(classes.size()); 286 } 287 } 288 } 289 290 return classes; 291 } 292 293 private static List<ArgumentClass> classifyType(Layout type) { 294 try { 295 if (type instanceof Value) { 296 return classifyValueType((Value) type); 297 } else if (type instanceof Address) { 298 ArrayList<ArgumentClass> classes = new ArrayList<>(); 299 classes.add(ArgumentClass.INTEGER); 300 return classes; 301 } else if (type instanceof Sequence) { 302 return classifyArrayType((Sequence) type); 303 } else if (type instanceof Group) { 304 return type.name().isPresent() && type.name().get().equals("LongDoubleComplex") ? 305 COMPLEX_X87_CLASSES : 306 classifyStructType((Group) type); 307 } else { 308 throw new IllegalArgumentException("Unhandled type " + type); 309 } 310 } catch (UnsupportedOperationException e) { 311 System.err.println("Failed to classify layout: " + type); 312 throw e; 313 } 314 } 315 316 private static List<ArgumentClass> createMemoryClassArray(long n) { 317 ArrayList<ArgumentClass> classes = new ArrayList<>(); 318 for (int i = 0; i < n; i++) { 319 classes.add(ArgumentClass.MEMORY); 320 } 321 322 return classes; 323 } 324 325 static class StorageCalculator { 326 private final boolean forArguments; 327 328 private int nIntegerRegs = 0; 329 private int nVectorRegs = 0; 330 private int nX87Regs = 0; 331 private long stackOffset = 0; 332 333 StorageCalculator(boolean forArguments) { 334 this.forArguments = forArguments; 335 } 336 337 public void addBindings(Argument arg, BiConsumer<StorageClass, ArgumentBinding> bindingConsumer) { 338 ArgumentInfo info = (ArgumentInfo)arg; 339 if (info.inMemory() || 340 nIntegerRegs + info.getIntegerRegs() > (forArguments ? SysVx64ABI.MAX_INTEGER_ARGUMENT_REGISTERS : SysVx64ABI.MAX_INTEGER_RETURN_REGISTERS) || 341 nVectorRegs + info.getVectorRegs() > (forArguments ? SysVx64ABI.MAX_VECTOR_ARGUMENT_REGISTERS : SysVx64ABI.MAX_VECTOR_RETURN_REGISTERS)) { 342 // stack 343 344 long alignment = Math.max(SharedUtils.alignment(info.layout(), true), 8); 345 346 long newStackOffset = Util.alignUp(stackOffset, alignment); 347 stackOffset = newStackOffset; 348 349 long tmpStackOffset = stackOffset; 350 for (int i = 0; i < info.getClasses().size(); i++) { 351 Storage storage = new Storage(StorageClass.STACK_ARGUMENT_SLOT, tmpStackOffset / 8, 8); 352 bindingConsumer.accept(StorageClass.STACK_ARGUMENT_SLOT, new ArgumentBinding(storage, info, i * 8)); 353 354 if (DEBUG) { 355 System.out.println("Argument " + info.name() + " will be passed on stack at offset " + tmpStackOffset); 356 } 357 358 tmpStackOffset += 8; 359 } 360 361 stackOffset += info.layout().bitsSize() / 8; 362 } else { 363 // regs 364 for (int i = 0; i < info.getClasses().size(); i++) { 365 Storage storage; 366 367 ArgumentClass c = info.getClasses().get(i); 368 369 switch (c) { 370 case INTEGER: 371 storage = new Storage(forArguments ? StorageClass.INTEGER_ARGUMENT_REGISTER : StorageClass.INTEGER_RETURN_REGISTER, nIntegerRegs++, SharedUtils.INTEGER_REGISTER_SIZE); 372 bindingConsumer.accept(storage.getStorageClass(), new ArgumentBinding(storage, info, i * 8)); 373 374 if (DEBUG) { 375 System.out.println("Argument " + info.name() + " will be passed in register " + 376 storageDbgHelper.getStorageName(storage)); 377 } 378 break; 379 380 case SSE: { 381 int width = 8; 382 383 for (int j = i + 1; j < info.getClasses().size(); j++) { 384 if (info.getClasses().get(j) == ArgumentClass.SSEUP) { 385 width += 8; 386 } 387 } 388 389 if (width > 64) { 390 throw new IllegalArgumentException((width * 8) + "-bit vector arguments not supported"); 391 } 392 393 storage = new Storage(forArguments ? StorageClass.VECTOR_ARGUMENT_REGISTER : StorageClass.VECTOR_RETURN_REGISTER, 394 nVectorRegs++, width, SharedUtils.VECTOR_REGISTER_SIZE); 395 396 bindingConsumer.accept(storage.getStorageClass(), new ArgumentBinding(storage, info, i * 8)); 397 398 if (DEBUG) { 399 System.out.println("Argument " + info.name() + " will be passed in register " + 400 storageDbgHelper.getStorageName(storage)); 401 } 402 break; 403 } 404 405 case SSEUP: 406 break; 407 408 case X87: { 409 int width = 8; 410 411 if (i < info.getClasses().size() && info.getClasses().get(i + 1) == ArgumentClass.X87UP) { 412 width += 8; 413 } 414 415 assert !forArguments; 416 417 storage = new Storage(StorageClass.X87_RETURN_REGISTER, nX87Regs++, width, SharedUtils.X87_REGISTER_SIZE); 418 bindingConsumer.accept(storage.getStorageClass(), new ArgumentBinding(storage, info, i * 8)); 419 420 if (DEBUG) { 421 System.out.println("Argument " + info.name() + " will be passed in register " + 422 storageDbgHelper.getStorageName(storage)); 423 } 424 break; 425 } 426 427 case X87UP: 428 break; 429 430 default: 431 throw new UnsupportedOperationException("Unhandled class " + c); 432 } 433 } 434 } 435 } 436 } 437 } --- EOF ---