|
| 1 | +//===-- OptimalLayout.h - Optimal data 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. |
| 12 | +/// |
| 13 | +/// The word "optimal" is a misnomer in several ways. First, minimizing |
| 14 | +/// space usage doesn't necessarily yield optimal performance because it |
| 15 | +/// may decrease locality. Second, there is no known efficient algorithm |
| 16 | +/// that guarantees a minimal layout for arbitrary inputs. Nonetheless, |
| 17 | +/// this algorithm is likely to produce much more compact layouts than |
| 18 | +/// would be produced by just allocating space in a buffer. |
| 19 | +/// |
| 20 | +//===----------------------------------------------------------------------===// |
| 21 | + |
| 22 | +#ifndef LLVM_SUPPORT_OPTIMALLAYOUT_H |
| 23 | +#define LLVM_SUPPORT_OPTIMALLAYOUT_H |
| 24 | + |
| 25 | +#include "llvm/Support/Alignment.h" |
| 26 | +#include "llvm/ADT/ArrayRef.h" |
| 27 | +#include <utility> |
| 28 | + |
| 29 | +namespace llvm { |
| 30 | + |
| 31 | +/// A field in a structure. |
| 32 | +struct OptimalLayoutField { |
| 33 | + /// A special value for Offset indicating that the field can be moved |
| 34 | + /// anywhere. |
| 35 | + static constexpr uint64_t FlexibleOffset = ~(uint64_t)0; |
| 36 | + |
| 37 | + OptimalLayoutField(const void *Id, uint64_t Size, Align Alignment, |
| 38 | + uint64_t FixedOffset = FlexibleOffset) |
| 39 | + : Offset(FixedOffset), Size(Size), Id(Id), Alignment(Alignment) { |
| 40 | + assert(Size > 0 && "adding an empty field to the layout"); |
| 41 | + } |
| 42 | + |
| 43 | + /// The offset of this field in the final layout. If this is |
| 44 | + /// initialized to FlexibleOffset, layout will overwrite it with |
| 45 | + /// the assigned offset of the field. |
| 46 | + uint64_t Offset; |
| 47 | + |
| 48 | + /// The required size of this field in bytes. Does not have to be |
| 49 | + /// a multiple of Alignment. Must be non-zero. |
| 50 | + uint64_t Size; |
| 51 | + |
| 52 | + /// A opaque value which uniquely identifies this field. |
| 53 | + const void *Id; |
| 54 | + |
| 55 | + /// Private scratch space for the algorithm. The implementation |
| 56 | + /// must treat this as uninitialized memory on entry. |
| 57 | + void *Scratch; |
| 58 | + |
| 59 | + /// The required alignment of this field. |
| 60 | + Align Alignment; |
| 61 | + |
| 62 | + /// Return true if this field has been assigned a fixed offset. |
| 63 | + /// After layout, this will be true of all the fields. |
| 64 | + bool hasFixedOffset() const { |
| 65 | + return (Offset != FlexibleOffset); |
| 66 | + } |
| 67 | + |
| 68 | + /// Given that this field has a fixed offset, return the offset |
| 69 | + /// of the first byte following it. |
| 70 | + uint64_t getEndOffset() const { |
| 71 | + assert(hasFixedOffset()); |
| 72 | + return Offset + Size; |
| 73 | + } |
| 74 | +}; |
| 75 | + |
| 76 | +/// Compute a layout for a struct containing the given fields, making a |
| 77 | +/// best-effort attempt to minimize the amount of space required. |
| 78 | +/// |
| 79 | +/// Two features are supported which require a more careful solution |
| 80 | +/// than the well-known "sort by decreasing alignment" solution: |
| 81 | +/// |
| 82 | +/// - Fields may be assigned a fixed offset in the layout. If there are |
| 83 | +/// gaps among the fixed-offset fields, the algorithm may attempt |
| 84 | +/// to allocate flexible-offset fields into those gaps. If that's |
| 85 | +/// undesirable, the caller should "block out" those gaps by e.g. |
| 86 | +/// just creating a single fixed-offset field that represents the |
| 87 | +/// entire "header". |
| 88 | +/// |
| 89 | +/// - The size of a field is not required to be a multiple of, or even |
| 90 | +/// greater than, the field's required alignment. The only constraint |
| 91 | +/// on fields is that they must not be zero-sized. |
| 92 | +/// |
| 93 | +/// To simplify the implementation, any fixed-offset fields in the |
| 94 | +/// layout must appear at the start of the field array, and they must |
| 95 | +/// be ordered by increasing offset. |
| 96 | +/// |
| 97 | +/// The algorithm will produce a guaranteed-minimal layout with no |
| 98 | +/// interior padding in the following "C-style" case: |
| 99 | +/// |
| 100 | +/// - every field's size is a multiple of its required alignment and |
| 101 | +/// - either no fields have initially fixed offsets, or the fixed-offset |
| 102 | +/// fields have no interior padding and end at an offset that is at |
| 103 | +/// least as aligned as all the flexible-offset fields. |
| 104 | +/// |
| 105 | +/// Otherwise, while the algorithm will make a best-effort attempt to |
| 106 | +/// avoid padding, it cannot guarantee a minimal layout, as there is |
| 107 | +/// no known efficient algorithm for doing so. |
| 108 | +/// |
| 109 | +/// The layout produced by this algorithm may not be stable across LLVM |
| 110 | +/// releases. Do not use this anywhere where ABI stability is required. |
| 111 | +/// |
| 112 | +/// Flexible-offset fields with the same size and alignment will be ordered |
| 113 | +/// the same way they were in the initial array. Otherwise the current |
| 114 | +/// algorithm makes no effort to preserve the initial order of |
| 115 | +/// flexible-offset fields. |
| 116 | +/// |
| 117 | +/// On return, all fields will have been assigned a fixed offset, and the |
| 118 | +/// array will be sorted in order of ascending offsets. Note that this |
| 119 | +/// means that the fixed-offset fields may no longer form a strict prefix |
| 120 | +/// if there's any padding before they end. |
| 121 | +/// |
| 122 | +/// The return value is the total size of the struct and its required |
| 123 | +/// alignment. Note that the total size is not rounded up to a multiple |
| 124 | +/// of the required alignment; clients which require this can do so easily. |
| 125 | +std::pair<uint64_t, Align> |
| 126 | +performOptimalLayout(MutableArrayRef<OptimalLayoutField> Fields); |
| 127 | + |
| 128 | +} // namespace llvm |
| 129 | + |
| 130 | +#endif |
0 commit comments