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 }
--- EOF ---