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 }