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