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