File: StructLayout.h

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (492 lines) | stat: -rw-r--r-- 17,148 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
//===--- StructLayout.h - Structure layout ----------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines some routines that are useful for performing
// structure layout.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_IRGEN_STRUCTLAYOUT_H
#define SWIFT_IRGEN_STRUCTLAYOUT_H

#include "llvm/ADT/ArrayRef.h"
#include "swift/Basic/ClusteredBitVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Twine.h"
#include "IRGen.h"

namespace llvm {
  class Constant;
  class StructType;
  class Type;
  class Value;
}

namespace swift {
namespace irgen {
  class Address;
  class IRGenFunction;
  class IRGenModule;
  class TypeInfo;

/// An algorithm for laying out a structure.
enum class LayoutStrategy {
  /// Compute an optimal layout;  there are no constraints at all.
  Optimal,

  /// The 'universal' strategy: all modules must agree on the layout.
  Universal
};

/// The kind of object being laid out.
enum class LayoutKind {
  /// A non-heap object does not require a heap header.
  NonHeapObject,

  /// A heap object is destined to be allocated on the heap and must
  /// be emitted with the standard heap header.
  HeapObject,
};

class NonFixedOffsetsImpl;

/// The type to pass around for non-fixed offsets.
using NonFixedOffsets = std::optional<NonFixedOffsetsImpl *>;

/// An abstract class for determining non-fixed offsets.
class NonFixedOffsetsImpl {
protected:
  virtual ~NonFixedOffsetsImpl() = default;
public:
  /// Return the offset (in bytes, as a size_t) of the element with
  /// the given index.
  virtual llvm::Value *getOffsetForIndex(IRGenFunction &IGF,
                                         unsigned index) = 0;

  operator NonFixedOffsets() { return NonFixedOffsets(this); }
};

/// An element layout is the layout for a single element of some sort
/// of aggregate structure.
class ElementLayout {
public:
  enum class Kind {
    /// The element is known to require no storage in the aggregate.
    /// Its offset in the aggregate is always statically zero.
    Empty,

    /// The element is known to require no storage in the aggregate.
    /// But it has an offset in the aggregate. This is to support getting the
    /// offset of tail allocated storage using MemoryLayout<>.offset(of:).
    EmptyTailAllocatedCType,

    /// The element can be positioned at a fixed offset within the
    /// aggregate.
    Fixed,

    /// The element cannot be positioned at a fixed offset within the
    /// aggregate.
    NonFixed,

    /// The element is an object lacking a fixed size but located at
    /// offset zero.  This is necessary because LLVM forbids even a
    /// 'gep 0' on an unsized type.
    InitialNonFixedSize

    // IncompleteKind comes here
  };

private:
  enum : unsigned { IncompleteKind  = unsigned(Kind::InitialNonFixedSize) + 1 };

  /// The swift type information for this element's layout.
  const TypeInfo *Type;

  /// The offset in bytes from the start of the struct.
  unsigned ByteOffset;

  /// The offset in bytes from the start of the struct, except EmptyFields are
  /// placed at the current byte offset instead of 0. For the purpose of the
  /// final layout empty fields are placed at offset 0, that however creates a
  /// whole slew of special cases to deal with. Instead of dealing with these
  /// special cases during layout, we pretend that empty fields are placed
  /// just like any other field at the current offset.
  unsigned ByteOffsetForLayout;

  /// The index of this element, either in the LLVM struct (if fixed)
  /// or in the non-fixed elements array (if non-fixed).
  unsigned Index : 28;

  /// Whether this element is known to be trivially destructible in the local
  /// resilience domain.
  unsigned IsTriviallyDestroyable : 1;

  /// The kind of layout performed for this element.
  unsigned TheKind : 3;

  explicit ElementLayout(const TypeInfo &type)
    : Type(&type), TheKind(IncompleteKind) {}

  bool isCompleted() const {
    return (TheKind != IncompleteKind);
  }

public:
  static ElementLayout getIncomplete(const TypeInfo &type) {
    return ElementLayout(type);
  }

  void completeFrom(const ElementLayout &other) {
    assert(!isCompleted());
    TheKind = other.TheKind;
    IsTriviallyDestroyable = other.IsTriviallyDestroyable;
    ByteOffset = other.ByteOffset;
    ByteOffsetForLayout = other.ByteOffsetForLayout;
    Index = other.Index;
  }

  void completeEmpty(IsTriviallyDestroyable_t isTriviallyDestroyable, Size byteOffset) {
    TheKind = unsigned(Kind::Empty);
    IsTriviallyDestroyable = unsigned(isTriviallyDestroyable);
    ByteOffset = 0;
    ByteOffsetForLayout = byteOffset.getValue();
    Index = 0; // make a complete write of the bitfield
  }

  void completeInitialNonFixedSize(IsTriviallyDestroyable_t isTriviallyDestroyable) {
    TheKind = unsigned(Kind::InitialNonFixedSize);
    IsTriviallyDestroyable = unsigned(isTriviallyDestroyable);
    ByteOffset = 0;
    ByteOffsetForLayout = ByteOffset;
    Index = 0; // make a complete write of the bitfield
  }

  void completeFixed(IsTriviallyDestroyable_t isTriviallyDestroyable, Size byteOffset, unsigned structIndex) {
    TheKind = unsigned(Kind::Fixed);
    IsTriviallyDestroyable = unsigned(isTriviallyDestroyable);
    ByteOffset = byteOffset.getValue();
    ByteOffsetForLayout = ByteOffset;
    Index = structIndex;

    assert(getByteOffset() == byteOffset);
  }

  void completeEmptyTailAllocatedCType(IsTriviallyDestroyable_t isTriviallyDestroyable, Size byteOffset) {
    TheKind = unsigned(Kind::EmptyTailAllocatedCType);
    IsTriviallyDestroyable = unsigned(isTriviallyDestroyable);
    ByteOffset = byteOffset.getValue();
    ByteOffsetForLayout = ByteOffset;
    Index = 0;

    assert(getByteOffset() == byteOffset);
  }

  /// Complete this element layout with a non-fixed offset.
  ///
  /// \param nonFixedElementIndex - the index into the elements array
  void completeNonFixed(IsTriviallyDestroyable_t isTriviallyDestroyable, unsigned nonFixedElementIndex) {
    TheKind = unsigned(Kind::NonFixed);
    IsTriviallyDestroyable = unsigned(isTriviallyDestroyable);
    Index = nonFixedElementIndex;
  }

  const TypeInfo &getType() const { return *Type; }

  Kind getKind() const {
    assert(isCompleted());
    return Kind(TheKind);
  }

  /// Is this element known to be empty?
  bool isEmpty() const {
    return getKind() == Kind::Empty ||
           getKind() == Kind::EmptyTailAllocatedCType;
  }

  /// Is this element known to be POD?
  IsTriviallyDestroyable_t isTriviallyDestroyable() const {
    assert(isCompleted());
    return IsTriviallyDestroyable_t(IsTriviallyDestroyable);
  }

  /// Can we access this element at a static offset?
  bool hasByteOffset() const {
    switch (getKind()) {
    case Kind::Empty:
    case Kind::EmptyTailAllocatedCType:
    case Kind::Fixed:
      return true;

    // FIXME: InitialNonFixedSize should go in the above, but I'm being
    // paranoid about changing behavior.
    case Kind::InitialNonFixedSize:
    case Kind::NonFixed:
      return false;
    }
    llvm_unreachable("bad kind");
  }

  /// Given that this element has a fixed offset, return that offset in bytes.
  Size getByteOffset() const {
    assert(isCompleted() && hasByteOffset());
    return Size(ByteOffset);
  }

  /// The offset in bytes from the start of the struct, except EmptyFields are
  /// placed at the current byte offset instead of 0. For the purpose of the
  /// final layout empty fields are placed at offset 0, that however creates a
  /// whole slew of special cases to deal with. Instead of dealing with these
  /// special cases during layout, we pretend that empty fields are placed
  /// just like any other field at the current offset.
  Size getByteOffsetDuringLayout() const {
    assert(isCompleted() && hasByteOffset());
    return Size(ByteOffsetForLayout);
  }

  /// Given that this element has a fixed offset, return the index in
  /// the LLVM struct.
  unsigned getStructIndex() const {
    assert(isCompleted() && getKind() == Kind::Fixed);
    return Index;
  }

  /// Given that this element does not have a fixed offset, return its
  /// index in the nonfixed-elements array.
  unsigned getNonFixedElementIndex() const {
    assert(isCompleted() && getKind() == Kind::NonFixed);
    return Index;
  }

  Address project(IRGenFunction &IGF, Address addr,
                  NonFixedOffsets offsets,
                  const llvm::Twine &suffix = "") const;
};

/// A class for building a structure layout.
class StructLayoutBuilder {
protected:
  IRGenModule &IGM;
  SmallVector<llvm::Type*, 8> StructFields;
  Size CurSize = Size(0);
  Size headerSize = Size(0);
private:
  Alignment CurAlignment = Alignment(1);
  SmallVector<SpareBitVector, 8> CurSpareBits;
  unsigned NextNonFixedOffsetIndex = 0;
  bool IsFixedLayout = true;
  bool IsLoadable = true;
  IsTriviallyDestroyable_t IsKnownTriviallyDestroyable = IsTriviallyDestroyable;
  IsBitwiseTakable_t IsKnownBitwiseTakable = IsBitwiseTakableAndBorrowable;
  IsCopyable_t IsKnownCopyable = IsCopyable;
  IsFixedSize_t IsKnownAlwaysFixedSize = IsFixedSize;
public:
  StructLayoutBuilder(IRGenModule &IGM) : IGM(IGM) {}

  /// Add a swift heap header to the layout.  This must be the first
  /// thing added to the layout.
  void addHeapHeader();
  /// Add the NSObject object header to the layout. This must be the first
  /// thing added to the layout.
  void addNSObjectHeader();
  /// Add the default-actor header to the layout.  This must be the second
  /// thing added to the layout, following the Swift heap header.
  void addDefaultActorHeader(ElementLayout &elt);
  /// Add the non-default distributed actor header to the layout.
  /// This must be the second thing added to the layout, following the Swift heap header.
  void addNonDefaultDistributedActorHeader(ElementLayout &elt);

  /// Add a number of fields to the layout.  The field layouts need
  /// only have the TypeInfo set; the rest will be filled out.
  ///
  /// Returns true if the fields may have increased the storage
  /// requirements of the layout.
  bool addFields(llvm::MutableArrayRef<ElementLayout> fields,
                 LayoutStrategy strategy);

  /// Add a field to the layout.  The field layout needs
  /// only have the TypeInfo set; the rest will be filled out.
  ///
  /// Returns true if the field may have increased the storage
  /// requirements of the layout.
  bool addField(ElementLayout &elt, LayoutStrategy strategy);

  /// Return whether the layout is known to be empty.
  bool empty() const { return IsFixedLayout && CurSize == Size(0); }

  /// Return the current set of fields.
  ArrayRef<llvm::Type *> getStructFields() const { return StructFields; }

  /// Return whether the structure has a fixed-size layout.
  bool isFixedLayout() const { return IsFixedLayout; }

  /// Return whether the structure has a loadable layout.
  bool isLoadable() const { return IsLoadable; }

  /// Return whether the structure is known to be POD in the local
  /// resilience scope.
  IsTriviallyDestroyable_t isTriviallyDestroyable() const { return IsKnownTriviallyDestroyable; }

  /// Return whether the structure is known to be bitwise-takable in the local
  /// resilience scope.
  IsBitwiseTakable_t isBitwiseTakable() const {
    return IsKnownBitwiseTakable;
  }
  
  /// Return whether the structure is known to be copyable in the local
  /// resilience scope.
  IsCopyable_t isCopyable() const {
    return IsKnownCopyable;
  }

  /// Return whether the structure is known to be fixed-size in all
  /// resilience scopes.
  IsFixedSize_t isAlwaysFixedSize() const {
    return IsKnownAlwaysFixedSize;
  }

  /// Return the size of the structure built so far.
  Size getSize() const { return CurSize; }

  // Return the size of the header.
  Size getHeaderSize() const { return headerSize; }

  /// Return the alignment of the structure built so far.
  Alignment getAlignment() const { return CurAlignment; }

  /// Return the spare bit mask of the structure built so far.
  SpareBitVector getSpareBits() const;

  /// Build the current elements as a new anonymous struct type.
  llvm::StructType *getAsAnonStruct() const;

  /// Build the current elements as a new anonymous struct type.
  void setAsBodyOfStruct(llvm::StructType *type) const;

private:
  void addFixedSizeElement(ElementLayout &elt);
  void addNonFixedSizeElement(ElementLayout &elt);
  void addEmptyElement(ElementLayout &elt);

  void addElementAtFixedOffset(ElementLayout &elt);
  void addElementAtNonFixedOffset(ElementLayout &elt);
  void addNonFixedSizeElementAtOffsetZero(ElementLayout &elt);
};

/// Apply layout attributes such as @_alignment to the layout properties of a
/// type, diagnosing any problems with them.
void applyLayoutAttributes(IRGenModule &IGM,
                           NominalTypeDecl *decl,
                           bool isFixedLayout,
                           /*inout*/ Alignment &alignment);

/// A struct layout is the result of laying out a complete structure.
class StructLayout {
  /// The statically-known minimum bound on the alignment.
  Alignment MinimumAlign;

  /// The statically-known minimum bound on the size.
  Size MinimumSize;

  /// The size of a header if present.
  Size headerSize;
  
  /// The statically-known spare bit mask.
  SpareBitVector SpareBits;

  /// Whether this layout is fixed in size.  If so, the size and
  /// alignment are exact.
  bool IsFixedLayout;

  /// Whether this layout 
  bool IsLoadable;

  IsTriviallyDestroyable_t IsKnownTriviallyDestroyable;
  IsBitwiseTakable_t IsKnownBitwiseTakable;
  IsCopyable_t IsKnownCopyable;
  IsFixedSize_t IsKnownAlwaysFixedSize = IsFixedSize;
  
  llvm::Type *Ty;
  SmallVector<ElementLayout, 8> Elements;

public:
  /// Create a structure layout.
  ///
  /// \param strategy - how much leeway the algorithm has to rearrange
  ///   and combine the storage of fields
  /// \param kind - the kind of layout to perform, including whether the
  ///   layout must include the reference-counting header
  /// \param typeToFill - if present, must be an opaque type whose body
  ///   will be filled with this layout
  StructLayout(IRGenModule &IGM, std::optional<CanType> type, LayoutKind kind,
               LayoutStrategy strategy, ArrayRef<const TypeInfo *> fields,
               llvm::StructType *typeToFill = 0);

  /// Create a structure layout from a builder.
  StructLayout(const StructLayoutBuilder &builder,
               NominalTypeDecl *decl,
               llvm::Type *type,
               ArrayRef<ElementLayout> elements)
    : MinimumAlign(builder.getAlignment()),
      MinimumSize(builder.getSize()),
      headerSize(builder.getHeaderSize()),
      SpareBits(builder.getSpareBits()),
      IsFixedLayout(builder.isFixedLayout()),
      IsLoadable(builder.isLoadable()),
      IsKnownTriviallyDestroyable(builder.isTriviallyDestroyable()),
      IsKnownBitwiseTakable(builder.isBitwiseTakable()),
      IsKnownCopyable(builder.isCopyable()),
      IsKnownAlwaysFixedSize(builder.isAlwaysFixedSize()),
      Ty(type),
      Elements(elements.begin(), elements.end()) {}

  /// Return the element layouts.  This is parallel to the fields
  /// passed in the constructor.
  ArrayRef<ElementLayout> getElements() const { return Elements; }
  const ElementLayout &getElement(unsigned i) const { return Elements[i]; }
  
  llvm::Type *getType() const { return Ty; }
  Size getSize() const { return MinimumSize; }
  Size getHeaderSize() const { return headerSize; }
  Alignment getAlignment() const { return MinimumAlign; }
  const SpareBitVector &getSpareBits() const { return SpareBits; }
  SpareBitVector &getSpareBits() { return SpareBits; }
  bool isKnownEmpty() const { return isFixedLayout() && MinimumSize.isZero(); }
  IsTriviallyDestroyable_t isTriviallyDestroyable() const {
    return IsKnownTriviallyDestroyable;
  }
  IsBitwiseTakable_t isBitwiseTakable() const {
    return IsKnownBitwiseTakable;
  }
  IsCopyable_t isCopyable() const {
    return IsKnownCopyable;
  }
  IsFixedSize_t isAlwaysFixedSize() const {
    return IsKnownAlwaysFixedSize;
  }

  bool isFixedLayout() const { return IsFixedLayout; }
  bool isLoadable() const { return IsLoadable; }
  llvm::Constant *emitSize(IRGenModule &IGM) const;
  llvm::Constant *emitAlignMask(IRGenModule &IGM) const;

  /// Bitcast the given pointer to this type.
  Address emitCastTo(IRGenFunction &IGF, llvm::Value *ptr,
                     const llvm::Twine &name = "") const;
};

Size getDefaultActorStorageFieldOffset(IRGenModule &IGM);
Size getNonDefaultDistributedActorStorageFieldOffset(IRGenModule &IGM);

} // end namespace irgen
} // end namespace swift

#endif