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 }