xref: /freebsd-src/contrib/llvm-project/llvm/lib/Support/OptimizedStructLayout.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
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