15ffd83dbSDimitry Andric //===--- OptimizedStructLayout.cpp - Optimal data layout algorithm ----------------===//
25ffd83dbSDimitry Andric //
35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65ffd83dbSDimitry Andric //
75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
85ffd83dbSDimitry Andric //
95ffd83dbSDimitry Andric // This file implements the performOptimizedStructLayout interface.
105ffd83dbSDimitry Andric //
115ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
125ffd83dbSDimitry Andric
135ffd83dbSDimitry Andric #include "llvm/Support/OptimizedStructLayout.h"
14*bdd1243dSDimitry Andric #include <optional>
155ffd83dbSDimitry Andric
165ffd83dbSDimitry Andric using namespace llvm;
175ffd83dbSDimitry Andric
185ffd83dbSDimitry Andric using Field = OptimizedStructLayoutField;
195ffd83dbSDimitry Andric
205ffd83dbSDimitry Andric #ifndef NDEBUG
checkValidLayout(ArrayRef<Field> Fields,uint64_t Size,Align MaxAlign)215ffd83dbSDimitry Andric static void checkValidLayout(ArrayRef<Field> Fields, uint64_t Size,
225ffd83dbSDimitry Andric Align MaxAlign) {
235ffd83dbSDimitry Andric uint64_t LastEnd = 0;
245ffd83dbSDimitry Andric Align ComputedMaxAlign;
255ffd83dbSDimitry Andric for (auto &Field : Fields) {
265ffd83dbSDimitry Andric assert(Field.hasFixedOffset() &&
275ffd83dbSDimitry Andric "didn't assign a fixed offset to field");
285ffd83dbSDimitry Andric assert(isAligned(Field.Alignment, Field.Offset) &&
295ffd83dbSDimitry Andric "didn't assign a correctly-aligned offset to field");
305ffd83dbSDimitry Andric assert(Field.Offset >= LastEnd &&
315ffd83dbSDimitry Andric "didn't assign offsets in ascending order");
325ffd83dbSDimitry Andric LastEnd = Field.getEndOffset();
335ffd83dbSDimitry Andric assert(Field.Alignment <= MaxAlign &&
345ffd83dbSDimitry Andric "didn't compute MaxAlign correctly");
355ffd83dbSDimitry Andric ComputedMaxAlign = std::max(Field.Alignment, MaxAlign);
365ffd83dbSDimitry Andric }
375ffd83dbSDimitry Andric assert(LastEnd == Size && "didn't compute LastEnd correctly");
385ffd83dbSDimitry Andric assert(ComputedMaxAlign == MaxAlign && "didn't compute MaxAlign correctly");
395ffd83dbSDimitry Andric }
405ffd83dbSDimitry Andric #endif
415ffd83dbSDimitry Andric
425ffd83dbSDimitry Andric std::pair<uint64_t, Align>
performOptimizedStructLayout(MutableArrayRef<Field> Fields)435ffd83dbSDimitry Andric llvm::performOptimizedStructLayout(MutableArrayRef<Field> Fields) {
445ffd83dbSDimitry Andric #ifndef NDEBUG
455ffd83dbSDimitry Andric // Do some simple precondition checks.
465ffd83dbSDimitry Andric {
475ffd83dbSDimitry Andric bool InFixedPrefix = true;
485ffd83dbSDimitry Andric size_t LastEnd = 0;
495ffd83dbSDimitry Andric for (auto &Field : Fields) {
505ffd83dbSDimitry Andric assert(Field.Size > 0 && "field of zero size");
515ffd83dbSDimitry Andric if (Field.hasFixedOffset()) {
525ffd83dbSDimitry Andric assert(InFixedPrefix &&
535ffd83dbSDimitry Andric "fixed-offset fields are not a strict prefix of array");
545ffd83dbSDimitry Andric assert(LastEnd <= Field.Offset &&
555ffd83dbSDimitry Andric "fixed-offset fields overlap or are not in order");
565ffd83dbSDimitry Andric LastEnd = Field.getEndOffset();
575ffd83dbSDimitry Andric assert(LastEnd > Field.Offset &&
585ffd83dbSDimitry Andric "overflow in fixed-offset end offset");
595ffd83dbSDimitry Andric } else {
605ffd83dbSDimitry Andric InFixedPrefix = false;
615ffd83dbSDimitry Andric }
625ffd83dbSDimitry Andric }
635ffd83dbSDimitry Andric }
645ffd83dbSDimitry Andric #endif
655ffd83dbSDimitry Andric
665ffd83dbSDimitry Andric // Do an initial pass over the fields.
675ffd83dbSDimitry Andric Align MaxAlign;
685ffd83dbSDimitry Andric
695ffd83dbSDimitry Andric // Find the first flexible-offset field, tracking MaxAlign.
705ffd83dbSDimitry Andric auto FirstFlexible = Fields.begin(), E = Fields.end();
715ffd83dbSDimitry Andric while (FirstFlexible != E && FirstFlexible->hasFixedOffset()) {
725ffd83dbSDimitry Andric MaxAlign = std::max(MaxAlign, FirstFlexible->Alignment);
735ffd83dbSDimitry Andric ++FirstFlexible;
745ffd83dbSDimitry Andric }
755ffd83dbSDimitry Andric
765ffd83dbSDimitry Andric // If there are no flexible fields, we're done.
775ffd83dbSDimitry Andric if (FirstFlexible == E) {
785ffd83dbSDimitry Andric uint64_t Size = 0;
795ffd83dbSDimitry Andric if (!Fields.empty())
805ffd83dbSDimitry Andric Size = Fields.back().getEndOffset();
815ffd83dbSDimitry Andric
825ffd83dbSDimitry Andric #ifndef NDEBUG
835ffd83dbSDimitry Andric checkValidLayout(Fields, Size, MaxAlign);
845ffd83dbSDimitry Andric #endif
855ffd83dbSDimitry Andric return std::make_pair(Size, MaxAlign);
865ffd83dbSDimitry Andric }
875ffd83dbSDimitry Andric
885ffd83dbSDimitry Andric // Walk over the flexible-offset fields, tracking MaxAlign and
895ffd83dbSDimitry Andric // assigning them a unique number in order of their appearance.
905ffd83dbSDimitry Andric // We'll use this unique number in the comparison below so that
915ffd83dbSDimitry Andric // we can use array_pod_sort, which isn't stable. We won't use it
925ffd83dbSDimitry Andric // past that point.
935ffd83dbSDimitry Andric {
945ffd83dbSDimitry Andric uintptr_t UniqueNumber = 0;
955ffd83dbSDimitry Andric for (auto I = FirstFlexible; I != E; ++I) {
965ffd83dbSDimitry Andric I->Scratch = reinterpret_cast<void*>(UniqueNumber++);
975ffd83dbSDimitry Andric MaxAlign = std::max(MaxAlign, I->Alignment);
985ffd83dbSDimitry Andric }
995ffd83dbSDimitry Andric }
1005ffd83dbSDimitry Andric
1015ffd83dbSDimitry Andric // Sort the flexible elements in order of decreasing alignment,
1025ffd83dbSDimitry Andric // then decreasing size, and then the original order as recorded
1035ffd83dbSDimitry Andric // in Scratch. The decreasing-size aspect of this is only really
1045ffd83dbSDimitry Andric // important if we get into the gap-filling stage below, but it
1055ffd83dbSDimitry Andric // doesn't hurt here.
1065ffd83dbSDimitry Andric array_pod_sort(FirstFlexible, E,
1075ffd83dbSDimitry Andric [](const Field *lhs, const Field *rhs) -> int {
1085ffd83dbSDimitry Andric // Decreasing alignment.
1095ffd83dbSDimitry Andric if (lhs->Alignment != rhs->Alignment)
1105ffd83dbSDimitry Andric return (lhs->Alignment < rhs->Alignment ? 1 : -1);
1115ffd83dbSDimitry Andric
1125ffd83dbSDimitry Andric // Decreasing size.
1135ffd83dbSDimitry Andric if (lhs->Size != rhs->Size)
1145ffd83dbSDimitry Andric return (lhs->Size < rhs->Size ? 1 : -1);
1155ffd83dbSDimitry Andric
1165ffd83dbSDimitry Andric // Original order.
1175ffd83dbSDimitry Andric auto lhsNumber = reinterpret_cast<uintptr_t>(lhs->Scratch);
1185ffd83dbSDimitry Andric auto rhsNumber = reinterpret_cast<uintptr_t>(rhs->Scratch);
1195ffd83dbSDimitry Andric if (lhsNumber != rhsNumber)
1205ffd83dbSDimitry Andric return (lhsNumber < rhsNumber ? -1 : 1);
1215ffd83dbSDimitry Andric
1225ffd83dbSDimitry Andric return 0;
1235ffd83dbSDimitry Andric });
1245ffd83dbSDimitry Andric
1255ffd83dbSDimitry Andric // Do a quick check for whether that sort alone has given us a perfect
1265ffd83dbSDimitry Andric // layout with no interior padding. This is very common: if the
1275ffd83dbSDimitry Andric // fixed-layout fields have no interior padding, and they end at a
1285ffd83dbSDimitry Andric // sufficiently-aligned offset for all the flexible-layout fields,
1295ffd83dbSDimitry Andric // and the flexible-layout fields all have sizes that are multiples
1305ffd83dbSDimitry Andric // of their alignment, then this will reliably trigger.
1315ffd83dbSDimitry Andric {
1325ffd83dbSDimitry Andric bool HasPadding = false;
1335ffd83dbSDimitry Andric uint64_t LastEnd = 0;
1345ffd83dbSDimitry Andric
1355ffd83dbSDimitry Andric // Walk the fixed-offset fields.
1365ffd83dbSDimitry Andric for (auto I = Fields.begin(); I != FirstFlexible; ++I) {
1375ffd83dbSDimitry Andric assert(I->hasFixedOffset());
1385ffd83dbSDimitry Andric if (LastEnd != I->Offset) {
1395ffd83dbSDimitry Andric HasPadding = true;
1405ffd83dbSDimitry Andric break;
1415ffd83dbSDimitry Andric }
1425ffd83dbSDimitry Andric LastEnd = I->getEndOffset();
1435ffd83dbSDimitry Andric }
1445ffd83dbSDimitry Andric
1455ffd83dbSDimitry Andric // Walk the flexible-offset fields, optimistically assigning fixed
1465ffd83dbSDimitry Andric // offsets. Note that we maintain a strict division between the
1475ffd83dbSDimitry Andric // fixed-offset and flexible-offset fields, so if we end up
1485ffd83dbSDimitry Andric // discovering padding later in this loop, we can just abandon this
1495ffd83dbSDimitry Andric // work and we'll ignore the offsets we already assigned.
1505ffd83dbSDimitry Andric if (!HasPadding) {
1515ffd83dbSDimitry Andric for (auto I = FirstFlexible; I != E; ++I) {
1525ffd83dbSDimitry Andric auto Offset = alignTo(LastEnd, I->Alignment);
1535ffd83dbSDimitry Andric if (LastEnd != Offset) {
1545ffd83dbSDimitry Andric HasPadding = true;
1555ffd83dbSDimitry Andric break;
1565ffd83dbSDimitry Andric }
1575ffd83dbSDimitry Andric I->Offset = Offset;
1585ffd83dbSDimitry Andric LastEnd = I->getEndOffset();
1595ffd83dbSDimitry Andric }
1605ffd83dbSDimitry Andric }
1615ffd83dbSDimitry Andric
1625ffd83dbSDimitry Andric // If we already have a perfect layout, we're done.
1635ffd83dbSDimitry Andric if (!HasPadding) {
1645ffd83dbSDimitry Andric #ifndef NDEBUG
1655ffd83dbSDimitry Andric checkValidLayout(Fields, LastEnd, MaxAlign);
1665ffd83dbSDimitry Andric #endif
1675ffd83dbSDimitry Andric return std::make_pair(LastEnd, MaxAlign);
1685ffd83dbSDimitry Andric }
1695ffd83dbSDimitry Andric }
1705ffd83dbSDimitry Andric
1715ffd83dbSDimitry Andric // The algorithm sketch at this point is as follows.
1725ffd83dbSDimitry Andric //
1735ffd83dbSDimitry Andric // Consider the padding gaps between fixed-offset fields in ascending
1745ffd83dbSDimitry Andric // order. Let LastEnd be the offset of the first byte following the
1755ffd83dbSDimitry Andric // field before the gap, or 0 if the gap is at the beginning of the
1765ffd83dbSDimitry Andric // structure. Find the "best" flexible-offset field according to the
1775ffd83dbSDimitry Andric // criteria below. If no such field exists, proceed to the next gap.
1785ffd83dbSDimitry Andric // Otherwise, add the field at the first properly-aligned offset for
1795ffd83dbSDimitry Andric // that field that is >= LastEnd, then update LastEnd and repeat in
1805ffd83dbSDimitry Andric // order to fill any remaining gap following that field.
1815ffd83dbSDimitry Andric //
1825ffd83dbSDimitry Andric // Next, let LastEnd to be the offset of the first byte following the
1835ffd83dbSDimitry Andric // last fixed-offset field, or 0 if there are no fixed-offset fields.
1845ffd83dbSDimitry Andric // While there are flexible-offset fields remaining, find the "best"
1855ffd83dbSDimitry Andric // flexible-offset field according to the criteria below, add it at
1865ffd83dbSDimitry Andric // the first properly-aligned offset for that field that is >= LastEnd,
1875ffd83dbSDimitry Andric // and update LastEnd to the first byte following the field.
1885ffd83dbSDimitry Andric //
1895ffd83dbSDimitry Andric // The "best" field is chosen by the following criteria, considered
1905ffd83dbSDimitry Andric // strictly in order:
1915ffd83dbSDimitry Andric //
1925ffd83dbSDimitry Andric // - When filling a gap betweeen fields, the field must fit.
1935ffd83dbSDimitry Andric // - A field is preferred if it requires less padding following LastEnd.
1945ffd83dbSDimitry Andric // - A field is preferred if it is more aligned.
1955ffd83dbSDimitry Andric // - A field is preferred if it is larger.
1965ffd83dbSDimitry Andric // - A field is preferred if it appeared earlier in the initial order.
1975ffd83dbSDimitry Andric //
1985ffd83dbSDimitry Andric // Minimizing leading padding is a greedy attempt to avoid padding
1995ffd83dbSDimitry Andric // entirely. Preferring more-aligned fields is an attempt to eliminate
2005ffd83dbSDimitry Andric // stricter constraints earlier, with the idea that weaker alignment
2015ffd83dbSDimitry Andric // constraints may be resolvable with less padding elsewhere. These
2025ffd83dbSDimitry Andric // These two rules are sufficient to ensure that we get the optimal
2035ffd83dbSDimitry Andric // layout in the "C-style" case. Preferring larger fields tends to take
2045ffd83dbSDimitry Andric // better advantage of large gaps and may be more likely to have a size
2055ffd83dbSDimitry Andric // that's a multiple of a useful alignment. Preferring the initial
2065ffd83dbSDimitry Andric // order may help somewhat with locality but is mostly just a way of
2075ffd83dbSDimitry Andric // ensuring deterministic output.
2085ffd83dbSDimitry Andric //
2095ffd83dbSDimitry Andric // Note that this algorithm does not guarantee a minimal layout. Picking
2105ffd83dbSDimitry Andric // a larger object greedily may leave a gap that cannot be filled as
2115ffd83dbSDimitry Andric // efficiently. Unfortunately, solving this perfectly is an NP-complete
2125ffd83dbSDimitry Andric // problem (by reduction from bin-packing: let B_i be the bin sizes and
2135ffd83dbSDimitry Andric // O_j be the object sizes; add fixed-offset fields such that the gaps
2145ffd83dbSDimitry Andric // between them have size B_i, and add flexible-offset fields with
2155ffd83dbSDimitry Andric // alignment 1 and size O_j; if the layout size is equal to the end of
2165ffd83dbSDimitry Andric // the last fixed-layout field, the objects fit in the bins; note that
2175ffd83dbSDimitry Andric // this doesn't even require the complexity of alignment).
2185ffd83dbSDimitry Andric
2195ffd83dbSDimitry Andric // The implementation below is essentially just an optimized version of
2205ffd83dbSDimitry Andric // scanning the list of remaining fields looking for the best, which
2215ffd83dbSDimitry Andric // would be O(n^2). In the worst case, it doesn't improve on that.
2225ffd83dbSDimitry Andric // However, in practice it'll just scan the array of alignment bins
2235ffd83dbSDimitry Andric // and consider the first few elements from one or two bins. The
2245ffd83dbSDimitry Andric // number of bins is bounded by a small constant: alignments are powers
2255ffd83dbSDimitry Andric // of two that are vanishingly unlikely to be over 64 and fairly unlikely
2265ffd83dbSDimitry Andric // to be over 8. And multiple elements only need to be considered when
2275ffd83dbSDimitry Andric // filling a gap between fixed-offset fields, which doesn't happen very
2285ffd83dbSDimitry Andric // often. We could use a data structure within bins that optimizes for
2295ffd83dbSDimitry Andric // finding the best-sized match, but it would require allocating memory
2305ffd83dbSDimitry Andric // and copying data, so it's unlikely to be worthwhile.
2315ffd83dbSDimitry Andric
2325ffd83dbSDimitry Andric
2335ffd83dbSDimitry Andric // Start by organizing the flexible-offset fields into bins according to
2345ffd83dbSDimitry Andric // their alignment. We expect a small enough number of bins that we
2355ffd83dbSDimitry Andric // don't care about the asymptotic costs of walking this.
2365ffd83dbSDimitry Andric struct AlignmentQueue {
2375ffd83dbSDimitry Andric /// The minimum size of anything currently in this queue.
2385ffd83dbSDimitry Andric uint64_t MinSize;
2395ffd83dbSDimitry Andric
2405ffd83dbSDimitry Andric /// The head of the queue. A singly-linked list. The order here should
2415ffd83dbSDimitry Andric /// be consistent with the earlier sort, i.e. the elements should be
2425ffd83dbSDimitry Andric /// monotonically descending in size and otherwise in the original order.
2435ffd83dbSDimitry Andric ///
2445ffd83dbSDimitry Andric /// We remove the queue from the array as soon as this is empty.
2455ffd83dbSDimitry Andric OptimizedStructLayoutField *Head;
2465ffd83dbSDimitry Andric
2475ffd83dbSDimitry Andric /// The alignment requirement of the queue.
2485ffd83dbSDimitry Andric Align Alignment;
2495ffd83dbSDimitry Andric
2505ffd83dbSDimitry Andric static Field *getNext(Field *Cur) {
2515ffd83dbSDimitry Andric return static_cast<Field *>(Cur->Scratch);
2525ffd83dbSDimitry Andric }
2535ffd83dbSDimitry Andric };
2545ffd83dbSDimitry Andric SmallVector<AlignmentQueue, 8> FlexibleFieldsByAlignment;
2555ffd83dbSDimitry Andric for (auto I = FirstFlexible; I != E; ) {
2565ffd83dbSDimitry Andric auto Head = I;
2575ffd83dbSDimitry Andric auto Alignment = I->Alignment;
2585ffd83dbSDimitry Andric
2595ffd83dbSDimitry Andric uint64_t MinSize = I->Size;
2605ffd83dbSDimitry Andric auto LastInQueue = I;
2615ffd83dbSDimitry Andric for (++I; I != E && I->Alignment == Alignment; ++I) {
2625ffd83dbSDimitry Andric LastInQueue->Scratch = I;
2635ffd83dbSDimitry Andric LastInQueue = I;
2645ffd83dbSDimitry Andric MinSize = std::min(MinSize, I->Size);
2655ffd83dbSDimitry Andric }
2665ffd83dbSDimitry Andric LastInQueue->Scratch = nullptr;
2675ffd83dbSDimitry Andric
2685ffd83dbSDimitry Andric FlexibleFieldsByAlignment.push_back({MinSize, Head, Alignment});
2695ffd83dbSDimitry Andric }
2705ffd83dbSDimitry Andric
2715ffd83dbSDimitry Andric #ifndef NDEBUG
2725ffd83dbSDimitry Andric // Verify that we set the queues up correctly.
2735ffd83dbSDimitry Andric auto checkQueues = [&]{
2745ffd83dbSDimitry Andric bool FirstQueue = true;
2755ffd83dbSDimitry Andric Align LastQueueAlignment;
2765ffd83dbSDimitry Andric for (auto &Queue : FlexibleFieldsByAlignment) {
2775ffd83dbSDimitry Andric assert((FirstQueue || Queue.Alignment < LastQueueAlignment) &&
2785ffd83dbSDimitry Andric "bins not in order of descending alignment");
2795ffd83dbSDimitry Andric LastQueueAlignment = Queue.Alignment;
2805ffd83dbSDimitry Andric FirstQueue = false;
2815ffd83dbSDimitry Andric
2825ffd83dbSDimitry Andric assert(Queue.Head && "queue was empty");
2835ffd83dbSDimitry Andric uint64_t LastSize = ~(uint64_t)0;
2845ffd83dbSDimitry Andric for (auto I = Queue.Head; I; I = Queue.getNext(I)) {
2855ffd83dbSDimitry Andric assert(I->Alignment == Queue.Alignment && "bad field in queue");
2865ffd83dbSDimitry Andric assert(I->Size <= LastSize && "queue not in descending size order");
2875ffd83dbSDimitry Andric LastSize = I->Size;
2885ffd83dbSDimitry Andric }
2895ffd83dbSDimitry Andric }
2905ffd83dbSDimitry Andric };
2915ffd83dbSDimitry Andric checkQueues();
2925ffd83dbSDimitry Andric #endif
2935ffd83dbSDimitry Andric
2945ffd83dbSDimitry Andric /// Helper function to remove a field from a queue.
2955ffd83dbSDimitry Andric auto spliceFromQueue = [&](AlignmentQueue *Queue, Field *Last, Field *Cur) {
2965ffd83dbSDimitry Andric assert(Last ? Queue->getNext(Last) == Cur : Queue->Head == Cur);
2975ffd83dbSDimitry Andric
2985ffd83dbSDimitry Andric // If we're removing Cur from a non-initial position, splice it out
2995ffd83dbSDimitry Andric // of the linked list.
3005ffd83dbSDimitry Andric if (Last) {
3015ffd83dbSDimitry Andric Last->Scratch = Cur->Scratch;
3025ffd83dbSDimitry Andric
3035ffd83dbSDimitry Andric // If Cur was the last field in the list, we need to update MinSize.
3045ffd83dbSDimitry Andric // We can just use the last field's size because the list is in
3055ffd83dbSDimitry Andric // descending order of size.
3065ffd83dbSDimitry Andric if (!Cur->Scratch)
3075ffd83dbSDimitry Andric Queue->MinSize = Last->Size;
3085ffd83dbSDimitry Andric
3095ffd83dbSDimitry Andric // Otherwise, replace the head.
3105ffd83dbSDimitry Andric } else {
3115ffd83dbSDimitry Andric if (auto NewHead = Queue->getNext(Cur))
3125ffd83dbSDimitry Andric Queue->Head = NewHead;
3135ffd83dbSDimitry Andric
3145ffd83dbSDimitry Andric // If we just emptied the queue, destroy its bin.
3155ffd83dbSDimitry Andric else
3165ffd83dbSDimitry Andric FlexibleFieldsByAlignment.erase(Queue);
3175ffd83dbSDimitry Andric }
3185ffd83dbSDimitry Andric };
3195ffd83dbSDimitry Andric
3205ffd83dbSDimitry Andric // Do layout into a local array. Doing this in-place on Fields is
3215ffd83dbSDimitry Andric // not really feasible.
3225ffd83dbSDimitry Andric SmallVector<Field, 16> Layout;
3235ffd83dbSDimitry Andric Layout.reserve(Fields.size());
3245ffd83dbSDimitry Andric
3255ffd83dbSDimitry Andric // The offset that we're currently looking to insert at (or after).
3265ffd83dbSDimitry Andric uint64_t LastEnd = 0;
3275ffd83dbSDimitry Andric
3285ffd83dbSDimitry Andric // Helper function to splice Cur out of the given queue and add it
3295ffd83dbSDimitry Andric // to the layout at the given offset.
3305ffd83dbSDimitry Andric auto addToLayout = [&](AlignmentQueue *Queue, Field *Last, Field *Cur,
3315ffd83dbSDimitry Andric uint64_t Offset) -> bool {
3325ffd83dbSDimitry Andric assert(Offset == alignTo(LastEnd, Cur->Alignment));
3335ffd83dbSDimitry Andric
3345ffd83dbSDimitry Andric // Splice out. This potentially invalidates Queue.
3355ffd83dbSDimitry Andric spliceFromQueue(Queue, Last, Cur);
3365ffd83dbSDimitry Andric
3375ffd83dbSDimitry Andric // Add Cur to the layout.
3385ffd83dbSDimitry Andric Layout.push_back(*Cur);
3395ffd83dbSDimitry Andric Layout.back().Offset = Offset;
3405ffd83dbSDimitry Andric LastEnd = Layout.back().getEndOffset();
3415ffd83dbSDimitry Andric
3425ffd83dbSDimitry Andric // Always return true so that we can be tail-called.
3435ffd83dbSDimitry Andric return true;
3445ffd83dbSDimitry Andric };
3455ffd83dbSDimitry Andric
3465ffd83dbSDimitry Andric // Helper function to try to find a field in the given queue that'll
3475ffd83dbSDimitry Andric // fit starting at StartOffset but before EndOffset (if present).
3485ffd83dbSDimitry Andric // Note that this never fails if EndOffset is not provided.
349*bdd1243dSDimitry Andric auto tryAddFillerFromQueue = [&](AlignmentQueue *Queue, uint64_t StartOffset,
350*bdd1243dSDimitry Andric std::optional<uint64_t> EndOffset) -> bool {
3515ffd83dbSDimitry Andric assert(Queue->Head);
3525ffd83dbSDimitry Andric assert(StartOffset == alignTo(LastEnd, Queue->Alignment));
353fe6060f1SDimitry Andric assert(!EndOffset || StartOffset < *EndOffset);
3545ffd83dbSDimitry Andric
3555ffd83dbSDimitry Andric // Figure out the maximum size that a field can be, and ignore this
3565ffd83dbSDimitry Andric // queue if there's nothing in it that small.
3575ffd83dbSDimitry Andric auto MaxViableSize =
3585ffd83dbSDimitry Andric (EndOffset ? *EndOffset - StartOffset : ~(uint64_t)0);
359*bdd1243dSDimitry Andric if (Queue->MinSize > MaxViableSize)
360*bdd1243dSDimitry Andric return false;
3615ffd83dbSDimitry Andric
3625ffd83dbSDimitry Andric // Find the matching field. Note that this should always find
3635ffd83dbSDimitry Andric // something because of the MinSize check above.
3645ffd83dbSDimitry Andric for (Field *Cur = Queue->Head, *Last = nullptr; true;
3655ffd83dbSDimitry Andric Last = Cur, Cur = Queue->getNext(Cur)) {
3665ffd83dbSDimitry Andric assert(Cur && "didn't find a match in queue despite its MinSize");
3675ffd83dbSDimitry Andric if (Cur->Size <= MaxViableSize)
3685ffd83dbSDimitry Andric return addToLayout(Queue, Last, Cur, StartOffset);
3695ffd83dbSDimitry Andric }
3705ffd83dbSDimitry Andric
3715ffd83dbSDimitry Andric llvm_unreachable("didn't find a match in queue despite its MinSize");
3725ffd83dbSDimitry Andric };
3735ffd83dbSDimitry Andric
3745ffd83dbSDimitry Andric // Helper function to find the "best" flexible-offset field according
3755ffd83dbSDimitry Andric // to the criteria described above.
376*bdd1243dSDimitry Andric auto tryAddBestField = [&](std::optional<uint64_t> BeforeOffset) -> bool {
377fe6060f1SDimitry Andric assert(!BeforeOffset || LastEnd < *BeforeOffset);
3785ffd83dbSDimitry Andric auto QueueB = FlexibleFieldsByAlignment.begin();
3795ffd83dbSDimitry Andric auto QueueE = FlexibleFieldsByAlignment.end();
3805ffd83dbSDimitry Andric
3815ffd83dbSDimitry Andric // Start by looking for the most-aligned queue that doesn't need any
3825ffd83dbSDimitry Andric // leading padding after LastEnd.
3835ffd83dbSDimitry Andric auto FirstQueueToSearch = QueueB;
3845ffd83dbSDimitry Andric for (; FirstQueueToSearch != QueueE; ++FirstQueueToSearch) {
3855ffd83dbSDimitry Andric if (isAligned(FirstQueueToSearch->Alignment, LastEnd))
3865ffd83dbSDimitry Andric break;
3875ffd83dbSDimitry Andric }
3885ffd83dbSDimitry Andric
3895ffd83dbSDimitry Andric uint64_t Offset = LastEnd;
3905ffd83dbSDimitry Andric while (true) {
3915ffd83dbSDimitry Andric // Invariant: all of the queues in [FirstQueueToSearch, QueueE)
3925ffd83dbSDimitry Andric // require the same initial padding offset.
3935ffd83dbSDimitry Andric
3945ffd83dbSDimitry Andric // Search those queues in descending order of alignment for a
3955ffd83dbSDimitry Andric // satisfactory field.
3965ffd83dbSDimitry Andric for (auto Queue = FirstQueueToSearch; Queue != QueueE; ++Queue) {
3975ffd83dbSDimitry Andric if (tryAddFillerFromQueue(Queue, Offset, BeforeOffset))
3985ffd83dbSDimitry Andric return true;
3995ffd83dbSDimitry Andric }
4005ffd83dbSDimitry Andric
4015ffd83dbSDimitry Andric // Okay, we don't need to scan those again.
4025ffd83dbSDimitry Andric QueueE = FirstQueueToSearch;
4035ffd83dbSDimitry Andric
4045ffd83dbSDimitry Andric // If we started from the first queue, we're done.
4055ffd83dbSDimitry Andric if (FirstQueueToSearch == QueueB)
4065ffd83dbSDimitry Andric return false;
4075ffd83dbSDimitry Andric
4085ffd83dbSDimitry Andric // Otherwise, scan backwards to find the most-aligned queue that
409fe6060f1SDimitry Andric // still has minimal leading padding after LastEnd. If that
410fe6060f1SDimitry Andric // minimal padding is already at or past the end point, we're done.
4115ffd83dbSDimitry Andric --FirstQueueToSearch;
4125ffd83dbSDimitry Andric Offset = alignTo(LastEnd, FirstQueueToSearch->Alignment);
413fe6060f1SDimitry Andric if (BeforeOffset && Offset >= *BeforeOffset)
414fe6060f1SDimitry Andric return false;
4155ffd83dbSDimitry Andric while (FirstQueueToSearch != QueueB &&
4165ffd83dbSDimitry Andric Offset == alignTo(LastEnd, FirstQueueToSearch[-1].Alignment))
4175ffd83dbSDimitry Andric --FirstQueueToSearch;
4185ffd83dbSDimitry Andric }
4195ffd83dbSDimitry Andric };
4205ffd83dbSDimitry Andric
4215ffd83dbSDimitry Andric // Phase 1: fill the gaps between fixed-offset fields with the best
4225ffd83dbSDimitry Andric // flexible-offset field that fits.
4235ffd83dbSDimitry Andric for (auto I = Fields.begin(); I != FirstFlexible; ++I) {
424fe6060f1SDimitry Andric assert(LastEnd <= I->Offset);
4255ffd83dbSDimitry Andric while (LastEnd != I->Offset) {
4265ffd83dbSDimitry Andric if (!tryAddBestField(I->Offset))
4275ffd83dbSDimitry Andric break;
4285ffd83dbSDimitry Andric }
4295ffd83dbSDimitry Andric Layout.push_back(*I);
4305ffd83dbSDimitry Andric LastEnd = I->getEndOffset();
4315ffd83dbSDimitry Andric }
4325ffd83dbSDimitry Andric
4335ffd83dbSDimitry Andric #ifndef NDEBUG
4345ffd83dbSDimitry Andric checkQueues();
4355ffd83dbSDimitry Andric #endif
4365ffd83dbSDimitry Andric
4375ffd83dbSDimitry Andric // Phase 2: repeatedly add the best flexible-offset field until
4385ffd83dbSDimitry Andric // they're all gone.
4395ffd83dbSDimitry Andric while (!FlexibleFieldsByAlignment.empty()) {
440*bdd1243dSDimitry Andric bool Success = tryAddBestField(std::nullopt);
4415ffd83dbSDimitry Andric assert(Success && "didn't find a field with no fixed limit?");
4425ffd83dbSDimitry Andric (void) Success;
4435ffd83dbSDimitry Andric }
4445ffd83dbSDimitry Andric
4455ffd83dbSDimitry Andric // Copy the layout back into place.
4465ffd83dbSDimitry Andric assert(Layout.size() == Fields.size());
4475ffd83dbSDimitry Andric memcpy(Fields.data(), Layout.data(),
4485ffd83dbSDimitry Andric Fields.size() * sizeof(OptimizedStructLayoutField));
4495ffd83dbSDimitry Andric
4505ffd83dbSDimitry Andric #ifndef NDEBUG
4515ffd83dbSDimitry Andric // Make a final check that the layout is valid.
4525ffd83dbSDimitry Andric checkValidLayout(Fields, LastEnd, MaxAlign);
4535ffd83dbSDimitry Andric #endif
4545ffd83dbSDimitry Andric
4555ffd83dbSDimitry Andric return std::make_pair(LastEnd, MaxAlign);
4565ffd83dbSDimitry Andric }
457