1 /* 2 * Copyright (c) 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 24 package com.sun.tools.jextract.tree; 25 26 import java.foreign.layout.Address; 27 import java.foreign.layout.Function; 28 import java.foreign.layout.Group; 29 import java.foreign.layout.Layout; 30 import java.foreign.layout.Padding; 31 import java.foreign.layout.Sequence; 32 import java.foreign.layout.Unresolved; 33 import java.foreign.layout.Value; 34 import java.util.ArrayList; 35 import java.util.List; 36 import java.util.function.BiFunction; 37 import java.util.stream.Collectors; 38 import java.util.stream.Stream; 39 import jdk.internal.clang.Cursor; 40 import jdk.internal.clang.CursorKind; 41 import jdk.internal.clang.SourceLocation; 42 import jdk.internal.clang.Type; 43 import jdk.internal.clang.TypeKind; 44 import jdk.internal.foreign.memory.Types; 45 46 /** 47 * General Layout utility functions 48 */ 49 public final class LayoutUtils { 50 private LayoutUtils() {} 51 52 public static String getName(Type type) { 53 Cursor c = type.getDeclarationCursor(); 54 if (c.isInvalid()) { 55 return type.spelling(); 56 } 57 return getName(c); 58 } 59 60 public static String getName(Tree tree) { 61 String name = tree.name(); 62 return name.isEmpty()? getName(tree.cursor()) : name; 63 } 64 65 private static String getName(Cursor cursor) { 66 // Use cursor name instead of type name, this way we don't have struct 67 // or enum prefix 68 String nativeName = cursor.spelling(); 69 if (nativeName.isEmpty()) { 70 Type t = cursor.type(); 71 nativeName = t.spelling(); 72 if (nativeName.contains("::") || nativeName.contains(" ")) { 73 SourceLocation.Location loc = cursor.getSourceLocation().getFileLocation(); 74 return "anon$" 75 + loc.path().getFileName().toString().replaceAll("\\.", "_") 76 + "$" + loc.offset(); 77 } 78 } 79 80 return nativeName; 81 } 82 83 private static boolean isFunction(Type clang_type) { 84 switch (clang_type.kind()) { 85 case Unexposed: 86 case Typedef: 87 case Elaborated: 88 return isFunction(clang_type.canonicalType()); 89 case FunctionProto: 90 case FunctionNoProto: 91 return true; 92 default: 93 return false; 94 } 95 } 96 97 public static Function getFunction(Type t) { 98 assert isFunction(t) : "not a function type"; 99 switch (t.kind()) { 100 case Unexposed: 101 case Typedef: 102 case Elaborated: 103 return parseFunctionInternal(t.canonicalType()); 104 case FunctionProto: 105 case FunctionNoProto: 106 return parseFunctionInternal(t); 107 default: 108 throw new IllegalArgumentException( 109 "Unsupported type kind: " + t.kind()); 110 } 111 } 112 113 private static Function parseFunctionInternal(Type t) { 114 final int argSize = t.numberOfArgs(); 115 Layout[] args = new Layout[argSize]; 116 for (int i = 0; i < argSize; i++) { 117 Layout l = getLayout(t.argType(i)); 118 args[i] = l instanceof Sequence? Address.ofLayout(64, ((Sequence)l).element()) : l; 119 } 120 if (t.resultType().kind() == TypeKind.Void) { 121 return Function.ofVoid(t.isVariadic(), args); 122 } else { 123 return Function.of(getLayout(t.resultType()), t.isVariadic(), args); 124 } 125 } 126 127 public static Layout getLayout(Type t) { 128 switch(t.kind()) { 129 case Bool: 130 return Types.BOOLEAN; 131 case Int: 132 return Types.INT; 133 case UInt: 134 return Types.UNSIGNED.INT; 135 case Int128: 136 return Types.INT128; 137 case UInt128: 138 return Types.UNSIGNED.INT128; 139 case Short: 140 return Types.SHORT; 141 case UShort: 142 return Types.UNSIGNED.SHORT; 143 case Long: 144 return Types.LONG; 145 case ULong: 146 return Types.UNSIGNED.LONG; 147 case LongLong: 148 return Types.LONG_LONG; 149 case ULongLong: 150 return Types.UNSIGNED.LONG_LONG; 151 case SChar: 152 return Types.BYTE; 153 case Char_S: 154 case Char_U: 155 case UChar: 156 return Types.UNSIGNED.BYTE; 157 case Float: 158 return Types.FLOAT; 159 case Double: 160 return Types.DOUBLE; 161 case LongDouble: 162 return Types.LONG_DOUBLE; 163 case Record: 164 return getRecordReferenceLayout(t); 165 case Enum: 166 return Types.INT; 167 case ConstantArray: 168 return Sequence.of(t.getNumberOfElements(), getLayout(t.getElementType())); 169 case IncompleteArray: 170 return Sequence.of(0L, getLayout(t.getElementType())); 171 case Unexposed: 172 case Typedef: 173 case Elaborated: 174 return getLayout(t.canonicalType()); 175 case Pointer: 176 case BlockPointer: 177 return parsePointerInternal(t.getPointeeType()); 178 default: 179 throw new IllegalArgumentException( 180 "Unsupported type kind: " + t.kind()); 181 } 182 } 183 184 private static Address parsePointerInternal(Type pointeeType) { 185 switch (pointeeType.kind()) { 186 case Unexposed: 187 case Typedef: 188 case Elaborated: 189 return parsePointerInternal(pointeeType.canonicalType()); 190 case FunctionProto: 191 case FunctionNoProto: 192 return Address.ofFunction(64, parseFunctionInternal(pointeeType)); 193 case Void: 194 return Address.ofVoid(64); 195 default: 196 return Address.ofLayout(64, getLayout(pointeeType)); 197 } 198 } 199 200 private static Layout getRecordReferenceLayout(Type t) { 201 //symbolic reference 202 return Unresolved.of() 203 .withAnnotation(Layout.NAME, getName(t.canonicalType())); 204 } 205 206 static Layout getRecordLayout(Type t, BiFunction<Cursor, Layout, Layout> fieldMapper) { 207 return getRecordLayoutInternal(0, t, t, fieldMapper); 208 } 209 210 private static Layout getRecordLayoutInternal(long offset, Type parent, Type t, BiFunction<Cursor, Layout, Layout> fieldMapper) { 211 Cursor cu = t.getDeclarationCursor().getDefinition(); 212 if (cu.isInvalid()) { 213 return getRecordReferenceLayout(t); 214 } 215 final boolean isUnion = cu.kind() == CursorKind.UnionDecl; 216 Stream<Cursor> fieldTypes = cu.children() 217 .filter(cx -> cx.isAnonymousStruct() || cx.kind() == CursorKind.FieldDecl); 218 List<Layout> fieldLayouts = new ArrayList<>(); 219 int pendingBitfieldStart = -1; 220 long actualSize = 0L; 221 for (Cursor c : fieldTypes.collect(Collectors.toList())) { 222 boolean isBitfield = c.isBitField(); 223 if (isBitfield && c.getBitFieldWidth() == 0) continue; 224 long expectedOffset = offsetOf(parent, c); 225 if (expectedOffset > offset) { 226 if (isUnion) { 227 throw new IllegalStateException("No padding in union elements!"); 228 } 229 fieldLayouts.add(Padding.of(expectedOffset - offset)); 230 actualSize += (expectedOffset - offset); 231 offset = expectedOffset; 232 } 233 if (isBitfield && !isUnion && pendingBitfieldStart == -1) { 234 pendingBitfieldStart = fieldLayouts.size(); 235 } 236 if (!isBitfield && pendingBitfieldStart >= 0) { 237 //emit/replace bitfields 238 replaceBitfields(fieldLayouts, pendingBitfieldStart); 239 pendingBitfieldStart = -1; 240 } 241 Layout fieldLayout = (c.isAnonymousStruct()) ? 242 getRecordLayoutInternal(offset, parent, c.type(), fieldMapper) : 243 fieldLayout(isUnion, c, fieldMapper); 244 fieldLayouts.add(fieldLayout); 245 long size = fieldSize(isUnion, c); 246 if (isUnion) { 247 actualSize = Math.max(actualSize, size); 248 } else { 249 offset += size; 250 actualSize += size; 251 } 252 } 253 long expectedSize = t.size() * 8; 254 if (actualSize < expectedSize) { 255 fieldLayouts.add(Padding.of(expectedSize - actualSize)); 256 } 257 if (pendingBitfieldStart >= 0) { 258 //emit/replace bitfields 259 replaceBitfields(fieldLayouts, pendingBitfieldStart); 260 } 261 Layout[] fields = fieldLayouts.toArray(new Layout[0]); 262 Group g = isUnion ? 263 Group.union(fields) : Group.struct(fields); 264 return g.withAnnotation(Layout.NAME, getName(cu)); 265 } 266 267 private static Layout fieldLayout(boolean isUnion, Cursor c, BiFunction<Cursor, Layout, Layout> fieldMapper) { 268 Layout layout = getLayout(c.type()); 269 if (c.isBitField()) { 270 boolean isSigned = ((Value)layout).kind() == Value.Kind.INTEGRAL_SIGNED; 271 Layout sublayout = isSigned ? 272 Value.ofSignedInt(c.getBitFieldWidth()) : 273 Value.ofUnsignedInt(c.getBitFieldWidth()); 274 sublayout = fieldMapper.apply(c, sublayout); 275 return isUnion ? 276 bitfield((Value)layout, List.of(sublayout)) : 277 sublayout; 278 } else { 279 return fieldMapper.apply(c, layout); 280 } 281 } 282 283 private static long fieldSize(boolean isUnion, Cursor c) { 284 if (!c.isBitField() || isUnion) { 285 return c.type().size() * 8; 286 } else { 287 return c.getBitFieldWidth(); 288 } 289 } 290 291 private static void replaceBitfields(List<Layout> layouts, int pendingBitfieldsStart) { 292 long storageSize = storageSize(layouts); 293 long offset = 0L; 294 List<Layout> newFields = new ArrayList<>(); 295 List<Layout> pendingFields = new ArrayList<>(); 296 while (layouts.size() > pendingBitfieldsStart) { 297 Layout l = layouts.remove(pendingBitfieldsStart); 298 offset += l.bitsSize(); 299 pendingFields.add(l); 300 if (!pendingFields.isEmpty() && 301 offset == storageSize) { 302 //emit new 303 newFields.add(bitfield(Value.ofUnsignedInt(storageSize), pendingFields)); 304 pendingFields.clear(); 305 offset = 0L; 306 } else if (offset > storageSize) { 307 throw new IllegalStateException("Crossing storage unit boundaries"); 308 } 309 } 310 if (!pendingFields.isEmpty()) { 311 throw new IllegalStateException("Partially used storage unit"); 312 } 313 //add back new fields 314 newFields.forEach(layouts::add); 315 } 316 317 private static long storageSize(List<Layout> layouts) { 318 long size = layouts.stream().mapToLong(Layout::bitsSize).sum(); 319 int[] sizes = { 64, 32, 16, 8 }; 320 for (int s : sizes) { 321 if (size % s == 0) { 322 return s; 323 } 324 } 325 throw new IllegalStateException("Cannot infer storage size"); 326 } 327 328 private static Value bitfield(Value v, List<Layout> sublayouts) { 329 return v.withContents(Group.struct(sublayouts.toArray(new Layout[0]))); 330 } 331 332 private static long offsetOf(Type parent, Cursor c) { 333 if (c.kind() == CursorKind.FieldDecl) { 334 return parent.getOffsetOf(c.spelling()); 335 } else { 336 return c.children() 337 .mapToLong(child -> offsetOf(parent, child)) 338 .findFirst().orElseThrow(IllegalStateException::new); 339 } 340 } 341 }