Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 1 | //===-- OptimizedStructLayout.h - Struct layout algorithm ---------*- C++ -*-=// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | /// |
| 9 | /// This file provides an interface for laying out a sequence of fields |
| 10 | /// as a struct in a way that attempts to minimizes the total space |
| 11 | /// requirements of the struct while still satisfying the layout |
| 12 | /// requirements of the individual fields. The resulting layout may be |
| 13 | /// substantially more compact than simply laying out the fields in their |
| 14 | /// original order. |
| 15 | /// |
| 16 | /// Fields may be pre-assigned fixed offsets. They may also be given sizes |
| 17 | /// that are not multiples of their alignments. There is no currently no |
| 18 | /// way to describe that a field has interior padding that other fields may |
| 19 | /// be allocated into. |
| 20 | /// |
| 21 | /// This algorithm does not claim to be "optimal" for several reasons: |
| 22 | /// |
| 23 | /// - First, it does not guarantee that the result is minimal in size. |
| 24 | /// There is no known efficient algoorithm to achieve minimality for |
| 25 | /// unrestricted inputs. Nonetheless, this algorithm |
| 26 | /// |
| 27 | /// - Second, there are other ways that a struct layout could be optimized |
| 28 | /// besides space usage, such as locality. This layout may have a mixed |
| 29 | /// impact on locality: less overall memory may be used, but adjacent |
| 30 | /// fields in the original array may be moved further from one another. |
| 31 | /// |
| 32 | //===----------------------------------------------------------------------===// |
| 33 | |
| 34 | #ifndef LLVM_SUPPORT_OPTIMIZEDSTRUCTLAYOUT_H |
| 35 | #define LLVM_SUPPORT_OPTIMIZEDSTRUCTLAYOUT_H |
| 36 | |
| 37 | #include "llvm/Support/Alignment.h" |
| 38 | #include "llvm/ADT/ArrayRef.h" |
| 39 | #include <utility> |
| 40 | |
| 41 | namespace llvm { |
| 42 | |
| 43 | /// A field in a structure. |
| 44 | struct OptimizedStructLayoutField { |
| 45 | /// A special value for Offset indicating that the field can be moved |
| 46 | /// anywhere. |
| 47 | static constexpr uint64_t FlexibleOffset = ~(uint64_t)0; |
| 48 | |
| 49 | OptimizedStructLayoutField(const void *Id, uint64_t Size, Align Alignment, |
| 50 | uint64_t FixedOffset = FlexibleOffset) |
| 51 | : Offset(FixedOffset), Size(Size), Id(Id), Alignment(Alignment) { |
| 52 | assert(Size > 0 && "adding an empty field to the layout"); |
| 53 | } |
| 54 | |
| 55 | /// The offset of this field in the final layout. If this is |
| 56 | /// initialized to FlexibleOffset, layout will overwrite it with |
| 57 | /// the assigned offset of the field. |
| 58 | uint64_t Offset; |
| 59 | |
| 60 | /// The required size of this field in bytes. Does not have to be |
| 61 | /// a multiple of Alignment. Must be non-zero. |
| 62 | uint64_t Size; |
| 63 | |
| 64 | /// A opaque value which uniquely identifies this field. |
| 65 | const void *Id; |
| 66 | |
| 67 | /// Private scratch space for the algorithm. The implementation |
| 68 | /// must treat this as uninitialized memory on entry. |
| 69 | void *Scratch; |
| 70 | |
| 71 | /// The required alignment of this field. |
| 72 | Align Alignment; |
| 73 | |
| 74 | /// Return true if this field has been assigned a fixed offset. |
| 75 | /// After layout, this will be true of all the fields. |
| 76 | bool hasFixedOffset() const { |
| 77 | return (Offset != FlexibleOffset); |
| 78 | } |
| 79 | |
| 80 | /// Given that this field has a fixed offset, return the offset |
| 81 | /// of the first byte following it. |
| 82 | uint64_t getEndOffset() const { |
| 83 | assert(hasFixedOffset()); |
| 84 | return Offset + Size; |
| 85 | } |
| 86 | }; |
| 87 | |
| 88 | /// Compute a layout for a struct containing the given fields, making a |
| 89 | /// best-effort attempt to minimize the amount of space required. |
| 90 | /// |
| 91 | /// Two features are supported which require a more careful solution |
| 92 | /// than the well-known "sort by decreasing alignment" solution: |
| 93 | /// |
| 94 | /// - Fields may be assigned a fixed offset in the layout. If there are |
| 95 | /// gaps among the fixed-offset fields, the algorithm may attempt |
| 96 | /// to allocate flexible-offset fields into those gaps. If that's |
| 97 | /// undesirable, the caller should "block out" those gaps by e.g. |
| 98 | /// just creating a single fixed-offset field that represents the |
| 99 | /// entire "header". |
| 100 | /// |
| 101 | /// - The size of a field is not required to be a multiple of, or even |
| 102 | /// greater than, the field's required alignment. The only constraint |
| 103 | /// on fields is that they must not be zero-sized. |
| 104 | /// |
| 105 | /// To simplify the implementation, any fixed-offset fields in the |
| 106 | /// layout must appear at the start of the field array, and they must |
| 107 | /// be ordered by increasing offset. |
| 108 | /// |
| 109 | /// The algorithm will produce a guaranteed-minimal layout with no |
| 110 | /// interior padding in the following "C-style" case: |
| 111 | /// |
| 112 | /// - every field's size is a multiple of its required alignment and |
| 113 | /// - either no fields have initially fixed offsets, or the fixed-offset |
| 114 | /// fields have no interior padding and end at an offset that is at |
| 115 | /// least as aligned as all the flexible-offset fields. |
| 116 | /// |
| 117 | /// Otherwise, while the algorithm will make a best-effort attempt to |
| 118 | /// avoid padding, it cannot guarantee a minimal layout, as there is |
| 119 | /// no known efficient algorithm for doing so. |
| 120 | /// |
| 121 | /// The layout produced by this algorithm may not be stable across LLVM |
| 122 | /// releases. Do not use this anywhere where ABI stability is required. |
| 123 | /// |
| 124 | /// Flexible-offset fields with the same size and alignment will be ordered |
| 125 | /// the same way they were in the initial array. Otherwise the current |
| 126 | /// algorithm makes no effort to preserve the initial order of |
| 127 | /// flexible-offset fields. |
| 128 | /// |
| 129 | /// On return, all fields will have been assigned a fixed offset, and the |
| 130 | /// array will be sorted in order of ascending offsets. Note that this |
| 131 | /// means that the fixed-offset fields may no longer form a strict prefix |
| 132 | /// if there's any padding before they end. |
| 133 | /// |
| 134 | /// The return value is the total size of the struct and its required |
| 135 | /// alignment. Note that the total size is not rounded up to a multiple |
| 136 | /// of the required alignment; clients which require this can do so easily. |
| 137 | std::pair<uint64_t, Align> performOptimizedStructLayout( |
| 138 | MutableArrayRef<OptimizedStructLayoutField> Fields); |
| 139 | |
| 140 | } // namespace llvm |
| 141 | |
| 142 | #endif |