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