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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package jdk.internal.foreign.abi.x64.sysv; 26 27 import java.lang.foreign.GroupLayout; 28 import java.lang.foreign.MemoryLayout; 29 import java.lang.foreign.MemorySegment; 30 import java.lang.foreign.PaddingLayout; 31 import java.lang.foreign.SequenceLayout; 32 import java.lang.foreign.StructLayout; 33 import java.lang.foreign.ValueLayout; 34 import jdk.internal.foreign.Utils; 35 36 import java.util.ArrayList; 37 import java.util.List; 38 import java.util.stream.Collectors; 39 import java.util.stream.IntStream; 40 41 class TypeClass { 42 enum Kind { 43 STRUCT, 44 POINTER, 45 INTEGER, 46 FLOAT 47 } 48 49 private final Kind kind; 50 final List<ArgumentClassImpl> classes; 51 52 private TypeClass(Kind kind, List<ArgumentClassImpl> classes) { 53 this.kind = kind; 54 this.classes = classes; 55 } 56 57 public static TypeClass ofValue(ValueLayout layout) { 58 final Kind kind; 59 ArgumentClassImpl argClass = argumentClassFor(layout); 60 kind = switch (argClass) { 61 case POINTER -> Kind.POINTER; 62 case INTEGER -> Kind.INTEGER; 63 case SSE -> Kind.FLOAT; 64 default -> throw new IllegalStateException("Unexpected argument class: " + argClass); 65 }; 66 return new TypeClass(kind, List.of(argClass)); 67 } 68 69 public static TypeClass ofStruct(GroupLayout layout) { 70 return new TypeClass(Kind.STRUCT, classifyStructType(layout)); 71 } 72 73 boolean inMemory() { 74 return classes.stream().anyMatch(c -> c == ArgumentClassImpl.MEMORY); 75 } 76 77 private long numClasses(ArgumentClassImpl clazz) { 78 return classes.stream().filter(c -> c == clazz).count(); 79 } 80 81 public long nIntegerRegs() { 82 return numClasses(ArgumentClassImpl.INTEGER) + numClasses(ArgumentClassImpl.POINTER); 83 } 84 85 public long nVectorRegs() { 86 return numClasses(ArgumentClassImpl.SSE); 87 } 88 89 public Kind kind() { 90 return kind; 91 } 92 93 // layout classification 94 95 // The AVX 512 enlightened ABI says "eight eightbytes" 96 // Although AMD64 0.99.6 states 4 eightbytes 97 private static final int MAX_AGGREGATE_REGS_SIZE = 8; 98 static final List<ArgumentClassImpl> COMPLEX_X87_CLASSES = List.of( 99 ArgumentClassImpl.X87, 100 ArgumentClassImpl.X87UP, 101 ArgumentClassImpl.X87, 102 ArgumentClassImpl.X87UP 103 ); 104 105 private static List<ArgumentClassImpl> createMemoryClassArray(long size) { 106 return IntStream.range(0, (int)size) 107 .mapToObj(i -> ArgumentClassImpl.MEMORY) 108 .collect(Collectors.toCollection(ArrayList::new)); 109 } 110 111 private static ArgumentClassImpl argumentClassFor(ValueLayout layout) { 112 Class<?> carrier = layout.carrier(); 113 if (carrier == boolean.class || carrier == byte.class || carrier == char.class || 114 carrier == short.class || carrier == int.class || carrier == long.class) { 115 return ArgumentClassImpl.INTEGER; 116 } else if (carrier == float.class || carrier == double.class) { 117 return ArgumentClassImpl.SSE; 118 } else if (carrier == MemorySegment.class) { 119 return ArgumentClassImpl.POINTER; 120 } else { 121 throw new IllegalStateException("Cannot get here: " + carrier.getName()); 122 } 123 } 124 125 // TODO: handle zero length arrays 126 private static List<ArgumentClassImpl> classifyStructType(GroupLayout type) { 127 List<ArgumentClassImpl>[] eightbytes = groupByEightBytes(type); 128 long nWords = eightbytes.length; 129 if (nWords > MAX_AGGREGATE_REGS_SIZE) { 130 return createMemoryClassArray(nWords); 131 } 132 133 ArrayList<ArgumentClassImpl> classes = new ArrayList<>(); 134 135 for (int idx = 0; idx < nWords; idx++) { 136 List<ArgumentClassImpl> subclasses = eightbytes[idx]; 137 ArgumentClassImpl result = subclasses.stream() 138 .reduce(ArgumentClassImpl.NO_CLASS, ArgumentClassImpl::merge); 139 classes.add(result); 140 } 141 142 for (int i = 0; i < classes.size(); i++) { 143 ArgumentClassImpl c = classes.get(i); 144 145 if (c == ArgumentClassImpl.MEMORY) { 146 // if any of the eightbytes was passed in memory, pass the whole thing in memory 147 return createMemoryClassArray(classes.size()); 148 } 149 150 if (c == ArgumentClassImpl.X87UP) { 151 if (i == 0) { 152 throw new IllegalArgumentException("Unexpected leading X87UP class"); 153 } 154 155 if (classes.get(i - 1) != ArgumentClassImpl.X87) { 156 return createMemoryClassArray(classes.size()); 157 } 158 } 159 } 160 161 if (classes.size() > 2) { 162 if (classes.get(0) != ArgumentClassImpl.SSE) { 163 return createMemoryClassArray(classes.size()); 164 } 165 166 for (int i = 1; i < classes.size(); i++) { 167 if (classes.get(i) != ArgumentClassImpl.SSEUP) { 168 return createMemoryClassArray(classes.size()); 169 } 170 } 171 } 172 173 return classes; 174 } 175 176 static TypeClass classifyLayout(MemoryLayout type) { 177 try { 178 if (type instanceof ValueLayout valueLayout) { 179 return ofValue(valueLayout); 180 } else if (type instanceof GroupLayout groupLayout) { 181 return ofStruct(groupLayout); 182 } else { 183 throw new IllegalArgumentException("Unsupported layout: " + type); 184 } 185 } catch (UnsupportedOperationException e) { 186 System.err.println("Failed to classify layout: " + type); 187 throw e; 188 } 189 } 190 191 private static List<ArgumentClassImpl>[] groupByEightBytes(GroupLayout group) { 192 long offset = 0L; 193 int nEightbytes; 194 try { 195 // alignUp can overflow the value, but it's okay since toIntExact still catches it 196 nEightbytes = Math.toIntExact(Utils.alignUp(group.byteSize(), 8) / 8); 197 } catch (ArithmeticException e) { 198 throw new IllegalArgumentException("GroupLayout is too large: " + group, e); 199 } 200 @SuppressWarnings({"unchecked", "rawtypes"}) 201 List<ArgumentClassImpl>[] groups = new List[nEightbytes]; 202 for (MemoryLayout l : group.memberLayouts()) { 203 groupByEightBytes(l, offset, groups); 204 if (group instanceof StructLayout) { 205 offset += l.byteSize(); 206 } 207 } 208 return groups; 209 } 210 211 private static void groupByEightBytes(MemoryLayout layout, 212 long offset, 213 List<ArgumentClassImpl>[] groups) { 214 switch (layout) { 215 case GroupLayout group -> { 216 for (MemoryLayout m : group.memberLayouts()) { 217 groupByEightBytes(m, offset, groups); 218 if (group instanceof StructLayout) { 219 offset += m.byteSize(); 220 } 221 } 222 } 223 case PaddingLayout _ -> { 224 } 225 case SequenceLayout seq -> { 226 MemoryLayout elem = seq.elementLayout(); 227 for (long i = 0; i < seq.elementCount(); i++) { 228 groupByEightBytes(elem, offset, groups); 229 offset += elem.byteSize(); 230 } 231 } 232 case ValueLayout vl -> { 233 List<ArgumentClassImpl> layouts = groups[(int) offset / 8]; 234 if (layouts == null) { 235 layouts = new ArrayList<>(); 236 groups[(int) offset / 8] = layouts; 237 } 238 // if the aggregate contains unaligned fields, it has class MEMORY 239 ArgumentClassImpl argumentClass = (offset % vl.byteAlignment()) == 0 ? 240 argumentClassFor(vl) : 241 ArgumentClassImpl.MEMORY; 242 layouts.add(argumentClass); 243 } 244 case null, default -> throw new IllegalStateException("Unexpected layout: " + layout); 245 } 246 } 247 }