xref: /freebsd-src/contrib/llvm-project/llvm/lib/Support/OptimizedStructLayout.cpp (revision 5ffd83dbcc34f10e07f6d3e968ae6365869615f4)
1*5ffd83dbSDimitry Andric //===--- OptimizedStructLayout.cpp - Optimal data layout algorithm ----------------===//
2*5ffd83dbSDimitry Andric //
3*5ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*5ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*5ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*5ffd83dbSDimitry Andric //
7*5ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
8*5ffd83dbSDimitry Andric //
9*5ffd83dbSDimitry Andric // This file implements the performOptimizedStructLayout interface.
10*5ffd83dbSDimitry Andric //
11*5ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
12*5ffd83dbSDimitry Andric 
13*5ffd83dbSDimitry Andric #include "llvm/Support/OptimizedStructLayout.h"
14*5ffd83dbSDimitry Andric 
15*5ffd83dbSDimitry Andric using namespace llvm;
16*5ffd83dbSDimitry Andric 
17*5ffd83dbSDimitry Andric using Field = OptimizedStructLayoutField;
18*5ffd83dbSDimitry Andric 
19*5ffd83dbSDimitry Andric #ifndef NDEBUG
20*5ffd83dbSDimitry Andric static void checkValidLayout(ArrayRef<Field> Fields, uint64_t Size,
21*5ffd83dbSDimitry Andric                              Align MaxAlign) {
22*5ffd83dbSDimitry Andric   uint64_t LastEnd = 0;
23*5ffd83dbSDimitry Andric   Align ComputedMaxAlign;
24*5ffd83dbSDimitry Andric   for (auto &Field : Fields) {
25*5ffd83dbSDimitry Andric     assert(Field.hasFixedOffset() &&
26*5ffd83dbSDimitry Andric            "didn't assign a fixed offset to field");
27*5ffd83dbSDimitry Andric     assert(isAligned(Field.Alignment, Field.Offset) &&
28*5ffd83dbSDimitry Andric            "didn't assign a correctly-aligned offset to field");
29*5ffd83dbSDimitry Andric     assert(Field.Offset >= LastEnd &&
30*5ffd83dbSDimitry Andric            "didn't assign offsets in ascending order");
31*5ffd83dbSDimitry Andric     LastEnd = Field.getEndOffset();
32*5ffd83dbSDimitry Andric     assert(Field.Alignment <= MaxAlign &&
33*5ffd83dbSDimitry Andric            "didn't compute MaxAlign correctly");
34*5ffd83dbSDimitry Andric     ComputedMaxAlign = std::max(Field.Alignment, MaxAlign);
35*5ffd83dbSDimitry Andric   }
36*5ffd83dbSDimitry Andric   assert(LastEnd == Size && "didn't compute LastEnd correctly");
37*5ffd83dbSDimitry Andric   assert(ComputedMaxAlign == MaxAlign && "didn't compute MaxAlign correctly");
38*5ffd83dbSDimitry Andric }
39*5ffd83dbSDimitry Andric #endif
40*5ffd83dbSDimitry Andric 
41*5ffd83dbSDimitry Andric std::pair<uint64_t, Align>
42*5ffd83dbSDimitry Andric llvm::performOptimizedStructLayout(MutableArrayRef<Field> Fields) {
43*5ffd83dbSDimitry Andric #ifndef NDEBUG
44*5ffd83dbSDimitry Andric   // Do some simple precondition checks.
45*5ffd83dbSDimitry Andric   {
46*5ffd83dbSDimitry Andric     bool InFixedPrefix = true;
47*5ffd83dbSDimitry Andric     size_t LastEnd = 0;
48*5ffd83dbSDimitry Andric     for (auto &Field : Fields) {
49*5ffd83dbSDimitry Andric       assert(Field.Size > 0 && "field of zero size");
50*5ffd83dbSDimitry Andric       if (Field.hasFixedOffset()) {
51*5ffd83dbSDimitry Andric         assert(InFixedPrefix &&
52*5ffd83dbSDimitry Andric                "fixed-offset fields are not a strict prefix of array");
53*5ffd83dbSDimitry Andric         assert(LastEnd <= Field.Offset &&
54*5ffd83dbSDimitry Andric                "fixed-offset fields overlap or are not in order");
55*5ffd83dbSDimitry Andric         LastEnd = Field.getEndOffset();
56*5ffd83dbSDimitry Andric         assert(LastEnd > Field.Offset &&
57*5ffd83dbSDimitry Andric                "overflow in fixed-offset end offset");
58*5ffd83dbSDimitry Andric       } else {
59*5ffd83dbSDimitry Andric         InFixedPrefix = false;
60*5ffd83dbSDimitry Andric       }
61*5ffd83dbSDimitry Andric     }
62*5ffd83dbSDimitry Andric   }
63*5ffd83dbSDimitry Andric #endif
64*5ffd83dbSDimitry Andric 
65*5ffd83dbSDimitry Andric   // Do an initial pass over the fields.
66*5ffd83dbSDimitry Andric   Align MaxAlign;
67*5ffd83dbSDimitry Andric 
68*5ffd83dbSDimitry Andric   // Find the first flexible-offset field, tracking MaxAlign.
69*5ffd83dbSDimitry Andric   auto FirstFlexible = Fields.begin(), E = Fields.end();
70*5ffd83dbSDimitry Andric   while (FirstFlexible != E && FirstFlexible->hasFixedOffset()) {
71*5ffd83dbSDimitry Andric     MaxAlign = std::max(MaxAlign, FirstFlexible->Alignment);
72*5ffd83dbSDimitry Andric     ++FirstFlexible;
73*5ffd83dbSDimitry Andric   }
74*5ffd83dbSDimitry Andric 
75*5ffd83dbSDimitry Andric   // If there are no flexible fields, we're done.
76*5ffd83dbSDimitry Andric   if (FirstFlexible == E) {
77*5ffd83dbSDimitry Andric     uint64_t Size = 0;
78*5ffd83dbSDimitry Andric     if (!Fields.empty())
79*5ffd83dbSDimitry Andric       Size = Fields.back().getEndOffset();
80*5ffd83dbSDimitry Andric 
81*5ffd83dbSDimitry Andric #ifndef NDEBUG
82*5ffd83dbSDimitry Andric     checkValidLayout(Fields, Size, MaxAlign);
83*5ffd83dbSDimitry Andric #endif
84*5ffd83dbSDimitry Andric     return std::make_pair(Size, MaxAlign);
85*5ffd83dbSDimitry Andric   }
86*5ffd83dbSDimitry Andric 
87*5ffd83dbSDimitry Andric   // Walk over the flexible-offset fields, tracking MaxAlign and
88*5ffd83dbSDimitry Andric   // assigning them a unique number in order of their appearance.
89*5ffd83dbSDimitry Andric   // We'll use this unique number in the comparison below so that
90*5ffd83dbSDimitry Andric   // we can use array_pod_sort, which isn't stable.  We won't use it
91*5ffd83dbSDimitry Andric   // past that point.
92*5ffd83dbSDimitry Andric   {
93*5ffd83dbSDimitry Andric     uintptr_t UniqueNumber = 0;
94*5ffd83dbSDimitry Andric     for (auto I = FirstFlexible; I != E; ++I) {
95*5ffd83dbSDimitry Andric       I->Scratch = reinterpret_cast<void*>(UniqueNumber++);
96*5ffd83dbSDimitry Andric       MaxAlign = std::max(MaxAlign, I->Alignment);
97*5ffd83dbSDimitry Andric     }
98*5ffd83dbSDimitry Andric   }
99*5ffd83dbSDimitry Andric 
100*5ffd83dbSDimitry Andric   // Sort the flexible elements in order of decreasing alignment,
101*5ffd83dbSDimitry Andric   // then decreasing size, and then the original order as recorded
102*5ffd83dbSDimitry Andric   // in Scratch.  The decreasing-size aspect of this is only really
103*5ffd83dbSDimitry Andric   // important if we get into the gap-filling stage below, but it
104*5ffd83dbSDimitry Andric   // doesn't hurt here.
105*5ffd83dbSDimitry Andric   array_pod_sort(FirstFlexible, E,
106*5ffd83dbSDimitry Andric                  [](const Field *lhs, const Field *rhs) -> int {
107*5ffd83dbSDimitry Andric     // Decreasing alignment.
108*5ffd83dbSDimitry Andric     if (lhs->Alignment != rhs->Alignment)
109*5ffd83dbSDimitry Andric       return (lhs->Alignment < rhs->Alignment ? 1 : -1);
110*5ffd83dbSDimitry Andric 
111*5ffd83dbSDimitry Andric     // Decreasing size.
112*5ffd83dbSDimitry Andric     if (lhs->Size != rhs->Size)
113*5ffd83dbSDimitry Andric       return (lhs->Size < rhs->Size ? 1 : -1);
114*5ffd83dbSDimitry Andric 
115*5ffd83dbSDimitry Andric     // Original order.
116*5ffd83dbSDimitry Andric     auto lhsNumber = reinterpret_cast<uintptr_t>(lhs->Scratch);
117*5ffd83dbSDimitry Andric     auto rhsNumber = reinterpret_cast<uintptr_t>(rhs->Scratch);
118*5ffd83dbSDimitry Andric     if (lhsNumber != rhsNumber)
119*5ffd83dbSDimitry Andric       return (lhsNumber < rhsNumber ? -1 : 1);
120*5ffd83dbSDimitry Andric 
121*5ffd83dbSDimitry Andric     return 0;
122*5ffd83dbSDimitry Andric   });
123*5ffd83dbSDimitry Andric 
124*5ffd83dbSDimitry Andric   // Do a quick check for whether that sort alone has given us a perfect
125*5ffd83dbSDimitry Andric   // layout with no interior padding.  This is very common: if the
126*5ffd83dbSDimitry Andric   // fixed-layout fields have no interior padding, and they end at a
127*5ffd83dbSDimitry Andric   // sufficiently-aligned offset for all the flexible-layout fields,
128*5ffd83dbSDimitry Andric   // and the flexible-layout fields all have sizes that are multiples
129*5ffd83dbSDimitry Andric   // of their alignment, then this will reliably trigger.
130*5ffd83dbSDimitry Andric   {
131*5ffd83dbSDimitry Andric     bool HasPadding = false;
132*5ffd83dbSDimitry Andric     uint64_t LastEnd = 0;
133*5ffd83dbSDimitry Andric 
134*5ffd83dbSDimitry Andric     // Walk the fixed-offset fields.
135*5ffd83dbSDimitry Andric     for (auto I = Fields.begin(); I != FirstFlexible; ++I) {
136*5ffd83dbSDimitry Andric       assert(I->hasFixedOffset());
137*5ffd83dbSDimitry Andric       if (LastEnd != I->Offset) {
138*5ffd83dbSDimitry Andric         HasPadding = true;
139*5ffd83dbSDimitry Andric         break;
140*5ffd83dbSDimitry Andric       }
141*5ffd83dbSDimitry Andric       LastEnd = I->getEndOffset();
142*5ffd83dbSDimitry Andric     }
143*5ffd83dbSDimitry Andric 
144*5ffd83dbSDimitry Andric     // Walk the flexible-offset fields, optimistically assigning fixed
145*5ffd83dbSDimitry Andric     // offsets.  Note that we maintain a strict division between the
146*5ffd83dbSDimitry Andric     // fixed-offset and flexible-offset fields, so if we end up
147*5ffd83dbSDimitry Andric     // discovering padding later in this loop, we can just abandon this
148*5ffd83dbSDimitry Andric     // work and we'll ignore the offsets we already assigned.
149*5ffd83dbSDimitry Andric     if (!HasPadding) {
150*5ffd83dbSDimitry Andric       for (auto I = FirstFlexible; I != E; ++I) {
151*5ffd83dbSDimitry Andric         auto Offset = alignTo(LastEnd, I->Alignment);
152*5ffd83dbSDimitry Andric         if (LastEnd != Offset) {
153*5ffd83dbSDimitry Andric           HasPadding = true;
154*5ffd83dbSDimitry Andric           break;
155*5ffd83dbSDimitry Andric         }
156*5ffd83dbSDimitry Andric         I->Offset = Offset;
157*5ffd83dbSDimitry Andric         LastEnd = I->getEndOffset();
158*5ffd83dbSDimitry Andric       }
159*5ffd83dbSDimitry Andric     }
160*5ffd83dbSDimitry Andric 
161*5ffd83dbSDimitry Andric     // If we already have a perfect layout, we're done.
162*5ffd83dbSDimitry Andric     if (!HasPadding) {
163*5ffd83dbSDimitry Andric #ifndef NDEBUG
164*5ffd83dbSDimitry Andric       checkValidLayout(Fields, LastEnd, MaxAlign);
165*5ffd83dbSDimitry Andric #endif
166*5ffd83dbSDimitry Andric       return std::make_pair(LastEnd, MaxAlign);
167*5ffd83dbSDimitry Andric     }
168*5ffd83dbSDimitry Andric   }
169*5ffd83dbSDimitry Andric 
170*5ffd83dbSDimitry Andric   // The algorithm sketch at this point is as follows.
171*5ffd83dbSDimitry Andric   //
172*5ffd83dbSDimitry Andric   // Consider the padding gaps between fixed-offset fields in ascending
173*5ffd83dbSDimitry Andric   // order.  Let LastEnd be the offset of the first byte following the
174*5ffd83dbSDimitry Andric   // field before the gap, or 0 if the gap is at the beginning of the
175*5ffd83dbSDimitry Andric   // structure.  Find the "best" flexible-offset field according to the
176*5ffd83dbSDimitry Andric   // criteria below.  If no such field exists, proceed to the next gap.
177*5ffd83dbSDimitry Andric   // Otherwise, add the field at the first properly-aligned offset for
178*5ffd83dbSDimitry Andric   // that field that is >= LastEnd, then update LastEnd and repeat in
179*5ffd83dbSDimitry Andric   // order to fill any remaining gap following that field.
180*5ffd83dbSDimitry Andric   //
181*5ffd83dbSDimitry Andric   // Next, let LastEnd to be the offset of the first byte following the
182*5ffd83dbSDimitry Andric   // last fixed-offset field, or 0 if there are no fixed-offset fields.
183*5ffd83dbSDimitry Andric   // While there are flexible-offset fields remaining, find the "best"
184*5ffd83dbSDimitry Andric   // flexible-offset field according to the criteria below, add it at
185*5ffd83dbSDimitry Andric   // the first properly-aligned offset for that field that is >= LastEnd,
186*5ffd83dbSDimitry Andric   // and update LastEnd to the first byte following the field.
187*5ffd83dbSDimitry Andric   //
188*5ffd83dbSDimitry Andric   // The "best" field is chosen by the following criteria, considered
189*5ffd83dbSDimitry Andric   // strictly in order:
190*5ffd83dbSDimitry Andric   //
191*5ffd83dbSDimitry Andric   // - When filling a gap betweeen fields, the field must fit.
192*5ffd83dbSDimitry Andric   // - A field is preferred if it requires less padding following LastEnd.
193*5ffd83dbSDimitry Andric   // - A field is preferred if it is more aligned.
194*5ffd83dbSDimitry Andric   // - A field is preferred if it is larger.
195*5ffd83dbSDimitry Andric   // - A field is preferred if it appeared earlier in the initial order.
196*5ffd83dbSDimitry Andric   //
197*5ffd83dbSDimitry Andric   // Minimizing leading padding is a greedy attempt to avoid padding
198*5ffd83dbSDimitry Andric   // entirely.  Preferring more-aligned fields is an attempt to eliminate
199*5ffd83dbSDimitry Andric   // stricter constraints earlier, with the idea that weaker alignment
200*5ffd83dbSDimitry Andric   // constraints may be resolvable with less padding elsewhere.  These
201*5ffd83dbSDimitry Andric   // These two rules are sufficient to ensure that we get the optimal
202*5ffd83dbSDimitry Andric   // layout in the "C-style" case.  Preferring larger fields tends to take
203*5ffd83dbSDimitry Andric   // better advantage of large gaps and may be more likely to have a size
204*5ffd83dbSDimitry Andric   // that's a multiple of a useful alignment.  Preferring the initial
205*5ffd83dbSDimitry Andric   // order may help somewhat with locality but is mostly just a way of
206*5ffd83dbSDimitry Andric   // ensuring deterministic output.
207*5ffd83dbSDimitry Andric   //
208*5ffd83dbSDimitry Andric   // Note that this algorithm does not guarantee a minimal layout.  Picking
209*5ffd83dbSDimitry Andric   // a larger object greedily may leave a gap that cannot be filled as
210*5ffd83dbSDimitry Andric   // efficiently.  Unfortunately, solving this perfectly is an NP-complete
211*5ffd83dbSDimitry Andric   // problem (by reduction from bin-packing: let B_i be the bin sizes and
212*5ffd83dbSDimitry Andric   // O_j be the object sizes; add fixed-offset fields such that the gaps
213*5ffd83dbSDimitry Andric   // between them have size B_i, and add flexible-offset fields with
214*5ffd83dbSDimitry Andric   // alignment 1 and size O_j; if the layout size is equal to the end of
215*5ffd83dbSDimitry Andric   // the last fixed-layout field, the objects fit in the bins; note that
216*5ffd83dbSDimitry Andric   // this doesn't even require the complexity of alignment).
217*5ffd83dbSDimitry Andric 
218*5ffd83dbSDimitry Andric   // The implementation below is essentially just an optimized version of
219*5ffd83dbSDimitry Andric   // scanning the list of remaining fields looking for the best, which
220*5ffd83dbSDimitry Andric   // would be O(n^2).  In the worst case, it doesn't improve on that.
221*5ffd83dbSDimitry Andric   // However, in practice it'll just scan the array of alignment bins
222*5ffd83dbSDimitry Andric   // and consider the first few elements from one or two bins.  The
223*5ffd83dbSDimitry Andric   // number of bins is bounded by a small constant: alignments are powers
224*5ffd83dbSDimitry Andric   // of two that are vanishingly unlikely to be over 64 and fairly unlikely
225*5ffd83dbSDimitry Andric   // to be over 8.  And multiple elements only need to be considered when
226*5ffd83dbSDimitry Andric   // filling a gap between fixed-offset fields, which doesn't happen very
227*5ffd83dbSDimitry Andric   // often.  We could use a data structure within bins that optimizes for
228*5ffd83dbSDimitry Andric   // finding the best-sized match, but it would require allocating memory
229*5ffd83dbSDimitry Andric   // and copying data, so it's unlikely to be worthwhile.
230*5ffd83dbSDimitry Andric 
231*5ffd83dbSDimitry Andric 
232*5ffd83dbSDimitry Andric   // Start by organizing the flexible-offset fields into bins according to
233*5ffd83dbSDimitry Andric   // their alignment.  We expect a small enough number of bins that we
234*5ffd83dbSDimitry Andric   // don't care about the asymptotic costs of walking this.
235*5ffd83dbSDimitry Andric   struct AlignmentQueue {
236*5ffd83dbSDimitry Andric     /// The minimum size of anything currently in this queue.
237*5ffd83dbSDimitry Andric     uint64_t MinSize;
238*5ffd83dbSDimitry Andric 
239*5ffd83dbSDimitry Andric     /// The head of the queue.  A singly-linked list.  The order here should
240*5ffd83dbSDimitry Andric     /// be consistent with the earlier sort, i.e. the elements should be
241*5ffd83dbSDimitry Andric     /// monotonically descending in size and otherwise in the original order.
242*5ffd83dbSDimitry Andric     ///
243*5ffd83dbSDimitry Andric     /// We remove the queue from the array as soon as this is empty.
244*5ffd83dbSDimitry Andric     OptimizedStructLayoutField *Head;
245*5ffd83dbSDimitry Andric 
246*5ffd83dbSDimitry Andric     /// The alignment requirement of the queue.
247*5ffd83dbSDimitry Andric     Align Alignment;
248*5ffd83dbSDimitry Andric 
249*5ffd83dbSDimitry Andric     static Field *getNext(Field *Cur) {
250*5ffd83dbSDimitry Andric       return static_cast<Field *>(Cur->Scratch);
251*5ffd83dbSDimitry Andric     }
252*5ffd83dbSDimitry Andric   };
253*5ffd83dbSDimitry Andric   SmallVector<AlignmentQueue, 8> FlexibleFieldsByAlignment;
254*5ffd83dbSDimitry Andric   for (auto I = FirstFlexible; I != E; ) {
255*5ffd83dbSDimitry Andric     auto Head = I;
256*5ffd83dbSDimitry Andric     auto Alignment = I->Alignment;
257*5ffd83dbSDimitry Andric 
258*5ffd83dbSDimitry Andric     uint64_t MinSize = I->Size;
259*5ffd83dbSDimitry Andric     auto LastInQueue = I;
260*5ffd83dbSDimitry Andric     for (++I; I != E && I->Alignment == Alignment; ++I) {
261*5ffd83dbSDimitry Andric       LastInQueue->Scratch = I;
262*5ffd83dbSDimitry Andric       LastInQueue = I;
263*5ffd83dbSDimitry Andric       MinSize = std::min(MinSize, I->Size);
264*5ffd83dbSDimitry Andric     }
265*5ffd83dbSDimitry Andric     LastInQueue->Scratch = nullptr;
266*5ffd83dbSDimitry Andric 
267*5ffd83dbSDimitry Andric     FlexibleFieldsByAlignment.push_back({MinSize, Head, Alignment});
268*5ffd83dbSDimitry Andric   }
269*5ffd83dbSDimitry Andric 
270*5ffd83dbSDimitry Andric #ifndef NDEBUG
271*5ffd83dbSDimitry Andric   // Verify that we set the queues up correctly.
272*5ffd83dbSDimitry Andric   auto checkQueues = [&]{
273*5ffd83dbSDimitry Andric     bool FirstQueue = true;
274*5ffd83dbSDimitry Andric     Align LastQueueAlignment;
275*5ffd83dbSDimitry Andric     for (auto &Queue : FlexibleFieldsByAlignment) {
276*5ffd83dbSDimitry Andric       assert((FirstQueue || Queue.Alignment < LastQueueAlignment) &&
277*5ffd83dbSDimitry Andric              "bins not in order of descending alignment");
278*5ffd83dbSDimitry Andric       LastQueueAlignment = Queue.Alignment;
279*5ffd83dbSDimitry Andric       FirstQueue = false;
280*5ffd83dbSDimitry Andric 
281*5ffd83dbSDimitry Andric       assert(Queue.Head && "queue was empty");
282*5ffd83dbSDimitry Andric       uint64_t LastSize = ~(uint64_t)0;
283*5ffd83dbSDimitry Andric       for (auto I = Queue.Head; I; I = Queue.getNext(I)) {
284*5ffd83dbSDimitry Andric         assert(I->Alignment == Queue.Alignment && "bad field in queue");
285*5ffd83dbSDimitry Andric         assert(I->Size <= LastSize && "queue not in descending size order");
286*5ffd83dbSDimitry Andric         LastSize = I->Size;
287*5ffd83dbSDimitry Andric       }
288*5ffd83dbSDimitry Andric     }
289*5ffd83dbSDimitry Andric   };
290*5ffd83dbSDimitry Andric   checkQueues();
291*5ffd83dbSDimitry Andric #endif
292*5ffd83dbSDimitry Andric 
293*5ffd83dbSDimitry Andric   /// Helper function to remove a field from a queue.
294*5ffd83dbSDimitry Andric   auto spliceFromQueue = [&](AlignmentQueue *Queue, Field *Last, Field *Cur) {
295*5ffd83dbSDimitry Andric     assert(Last ? Queue->getNext(Last) == Cur : Queue->Head == Cur);
296*5ffd83dbSDimitry Andric 
297*5ffd83dbSDimitry Andric     // If we're removing Cur from a non-initial position, splice it out
298*5ffd83dbSDimitry Andric     // of the linked list.
299*5ffd83dbSDimitry Andric     if (Last) {
300*5ffd83dbSDimitry Andric       Last->Scratch = Cur->Scratch;
301*5ffd83dbSDimitry Andric 
302*5ffd83dbSDimitry Andric       // If Cur was the last field in the list, we need to update MinSize.
303*5ffd83dbSDimitry Andric       // We can just use the last field's size because the list is in
304*5ffd83dbSDimitry Andric       // descending order of size.
305*5ffd83dbSDimitry Andric       if (!Cur->Scratch)
306*5ffd83dbSDimitry Andric         Queue->MinSize = Last->Size;
307*5ffd83dbSDimitry Andric 
308*5ffd83dbSDimitry Andric     // Otherwise, replace the head.
309*5ffd83dbSDimitry Andric     } else {
310*5ffd83dbSDimitry Andric       if (auto NewHead = Queue->getNext(Cur))
311*5ffd83dbSDimitry Andric         Queue->Head = NewHead;
312*5ffd83dbSDimitry Andric 
313*5ffd83dbSDimitry Andric       // If we just emptied the queue, destroy its bin.
314*5ffd83dbSDimitry Andric       else
315*5ffd83dbSDimitry Andric         FlexibleFieldsByAlignment.erase(Queue);
316*5ffd83dbSDimitry Andric     }
317*5ffd83dbSDimitry Andric   };
318*5ffd83dbSDimitry Andric 
319*5ffd83dbSDimitry Andric   // Do layout into a local array.  Doing this in-place on Fields is
320*5ffd83dbSDimitry Andric   // not really feasible.
321*5ffd83dbSDimitry Andric   SmallVector<Field, 16> Layout;
322*5ffd83dbSDimitry Andric   Layout.reserve(Fields.size());
323*5ffd83dbSDimitry Andric 
324*5ffd83dbSDimitry Andric   // The offset that we're currently looking to insert at (or after).
325*5ffd83dbSDimitry Andric   uint64_t LastEnd = 0;
326*5ffd83dbSDimitry Andric 
327*5ffd83dbSDimitry Andric   // Helper function to splice Cur out of the given queue and add it
328*5ffd83dbSDimitry Andric   // to the layout at the given offset.
329*5ffd83dbSDimitry Andric   auto addToLayout = [&](AlignmentQueue *Queue, Field *Last, Field *Cur,
330*5ffd83dbSDimitry Andric                          uint64_t Offset) -> bool {
331*5ffd83dbSDimitry Andric     assert(Offset == alignTo(LastEnd, Cur->Alignment));
332*5ffd83dbSDimitry Andric 
333*5ffd83dbSDimitry Andric     // Splice out.  This potentially invalidates Queue.
334*5ffd83dbSDimitry Andric     spliceFromQueue(Queue, Last, Cur);
335*5ffd83dbSDimitry Andric 
336*5ffd83dbSDimitry Andric     // Add Cur to the layout.
337*5ffd83dbSDimitry Andric     Layout.push_back(*Cur);
338*5ffd83dbSDimitry Andric     Layout.back().Offset = Offset;
339*5ffd83dbSDimitry Andric     LastEnd = Layout.back().getEndOffset();
340*5ffd83dbSDimitry Andric 
341*5ffd83dbSDimitry Andric     // Always return true so that we can be tail-called.
342*5ffd83dbSDimitry Andric     return true;
343*5ffd83dbSDimitry Andric   };
344*5ffd83dbSDimitry Andric 
345*5ffd83dbSDimitry Andric   // Helper function to try to find a field in the given queue that'll
346*5ffd83dbSDimitry Andric   // fit starting at StartOffset but before EndOffset (if present).
347*5ffd83dbSDimitry Andric   // Note that this never fails if EndOffset is not provided.
348*5ffd83dbSDimitry Andric   auto tryAddFillerFromQueue = [&](AlignmentQueue *Queue,
349*5ffd83dbSDimitry Andric                                    uint64_t StartOffset,
350*5ffd83dbSDimitry Andric                                    Optional<uint64_t> EndOffset) -> bool {
351*5ffd83dbSDimitry Andric     assert(Queue->Head);
352*5ffd83dbSDimitry Andric     assert(StartOffset == alignTo(LastEnd, Queue->Alignment));
353*5ffd83dbSDimitry Andric 
354*5ffd83dbSDimitry Andric     // Figure out the maximum size that a field can be, and ignore this
355*5ffd83dbSDimitry Andric     // queue if there's nothing in it that small.
356*5ffd83dbSDimitry Andric     auto MaxViableSize =
357*5ffd83dbSDimitry Andric       (EndOffset ? *EndOffset - StartOffset : ~(uint64_t)0);
358*5ffd83dbSDimitry Andric     if (Queue->MinSize > MaxViableSize) return false;
359*5ffd83dbSDimitry Andric 
360*5ffd83dbSDimitry Andric     // Find the matching field.  Note that this should always find
361*5ffd83dbSDimitry Andric     // something because of the MinSize check above.
362*5ffd83dbSDimitry Andric     for (Field *Cur = Queue->Head, *Last = nullptr; true;
363*5ffd83dbSDimitry Andric            Last = Cur, Cur = Queue->getNext(Cur)) {
364*5ffd83dbSDimitry Andric       assert(Cur && "didn't find a match in queue despite its MinSize");
365*5ffd83dbSDimitry Andric       if (Cur->Size <= MaxViableSize)
366*5ffd83dbSDimitry Andric         return addToLayout(Queue, Last, Cur, StartOffset);
367*5ffd83dbSDimitry Andric     }
368*5ffd83dbSDimitry Andric 
369*5ffd83dbSDimitry Andric     llvm_unreachable("didn't find a match in queue despite its MinSize");
370*5ffd83dbSDimitry Andric   };
371*5ffd83dbSDimitry Andric 
372*5ffd83dbSDimitry Andric   // Helper function to find the "best" flexible-offset field according
373*5ffd83dbSDimitry Andric   // to the criteria described above.
374*5ffd83dbSDimitry Andric   auto tryAddBestField = [&](Optional<uint64_t> BeforeOffset) -> bool {
375*5ffd83dbSDimitry Andric     auto QueueB = FlexibleFieldsByAlignment.begin();
376*5ffd83dbSDimitry Andric     auto QueueE = FlexibleFieldsByAlignment.end();
377*5ffd83dbSDimitry Andric 
378*5ffd83dbSDimitry Andric     // Start by looking for the most-aligned queue that doesn't need any
379*5ffd83dbSDimitry Andric     // leading padding after LastEnd.
380*5ffd83dbSDimitry Andric     auto FirstQueueToSearch = QueueB;
381*5ffd83dbSDimitry Andric     for (; FirstQueueToSearch != QueueE; ++FirstQueueToSearch) {
382*5ffd83dbSDimitry Andric       if (isAligned(FirstQueueToSearch->Alignment, LastEnd))
383*5ffd83dbSDimitry Andric         break;
384*5ffd83dbSDimitry Andric     }
385*5ffd83dbSDimitry Andric 
386*5ffd83dbSDimitry Andric     uint64_t Offset = LastEnd;
387*5ffd83dbSDimitry Andric     while (true) {
388*5ffd83dbSDimitry Andric       // Invariant: all of the queues in [FirstQueueToSearch, QueueE)
389*5ffd83dbSDimitry Andric       // require the same initial padding offset.
390*5ffd83dbSDimitry Andric 
391*5ffd83dbSDimitry Andric       // Search those queues in descending order of alignment for a
392*5ffd83dbSDimitry Andric       // satisfactory field.
393*5ffd83dbSDimitry Andric       for (auto Queue = FirstQueueToSearch; Queue != QueueE; ++Queue) {
394*5ffd83dbSDimitry Andric         if (tryAddFillerFromQueue(Queue, Offset, BeforeOffset))
395*5ffd83dbSDimitry Andric           return true;
396*5ffd83dbSDimitry Andric       }
397*5ffd83dbSDimitry Andric 
398*5ffd83dbSDimitry Andric       // Okay, we don't need to scan those again.
399*5ffd83dbSDimitry Andric       QueueE = FirstQueueToSearch;
400*5ffd83dbSDimitry Andric 
401*5ffd83dbSDimitry Andric       // If we started from the first queue, we're done.
402*5ffd83dbSDimitry Andric       if (FirstQueueToSearch == QueueB)
403*5ffd83dbSDimitry Andric         return false;
404*5ffd83dbSDimitry Andric 
405*5ffd83dbSDimitry Andric       // Otherwise, scan backwards to find the most-aligned queue that
406*5ffd83dbSDimitry Andric       // still has minimal leading padding after LastEnd.
407*5ffd83dbSDimitry Andric       --FirstQueueToSearch;
408*5ffd83dbSDimitry Andric       Offset = alignTo(LastEnd, FirstQueueToSearch->Alignment);
409*5ffd83dbSDimitry Andric       while (FirstQueueToSearch != QueueB &&
410*5ffd83dbSDimitry Andric              Offset == alignTo(LastEnd, FirstQueueToSearch[-1].Alignment))
411*5ffd83dbSDimitry Andric         --FirstQueueToSearch;
412*5ffd83dbSDimitry Andric     }
413*5ffd83dbSDimitry Andric   };
414*5ffd83dbSDimitry Andric 
415*5ffd83dbSDimitry Andric   // Phase 1: fill the gaps between fixed-offset fields with the best
416*5ffd83dbSDimitry Andric   // flexible-offset field that fits.
417*5ffd83dbSDimitry Andric   for (auto I = Fields.begin(); I != FirstFlexible; ++I) {
418*5ffd83dbSDimitry Andric     while (LastEnd != I->Offset) {
419*5ffd83dbSDimitry Andric       if (!tryAddBestField(I->Offset))
420*5ffd83dbSDimitry Andric         break;
421*5ffd83dbSDimitry Andric     }
422*5ffd83dbSDimitry Andric     Layout.push_back(*I);
423*5ffd83dbSDimitry Andric     LastEnd = I->getEndOffset();
424*5ffd83dbSDimitry Andric   }
425*5ffd83dbSDimitry Andric 
426*5ffd83dbSDimitry Andric #ifndef NDEBUG
427*5ffd83dbSDimitry Andric   checkQueues();
428*5ffd83dbSDimitry Andric #endif
429*5ffd83dbSDimitry Andric 
430*5ffd83dbSDimitry Andric   // Phase 2: repeatedly add the best flexible-offset field until
431*5ffd83dbSDimitry Andric   // they're all gone.
432*5ffd83dbSDimitry Andric   while (!FlexibleFieldsByAlignment.empty()) {
433*5ffd83dbSDimitry Andric     bool Success = tryAddBestField(None);
434*5ffd83dbSDimitry Andric     assert(Success && "didn't find a field with no fixed limit?");
435*5ffd83dbSDimitry Andric     (void) Success;
436*5ffd83dbSDimitry Andric   }
437*5ffd83dbSDimitry Andric 
438*5ffd83dbSDimitry Andric   // Copy the layout back into place.
439*5ffd83dbSDimitry Andric   assert(Layout.size() == Fields.size());
440*5ffd83dbSDimitry Andric   memcpy(Fields.data(), Layout.data(),
441*5ffd83dbSDimitry Andric          Fields.size() * sizeof(OptimizedStructLayoutField));
442*5ffd83dbSDimitry Andric 
443*5ffd83dbSDimitry Andric #ifndef NDEBUG
444*5ffd83dbSDimitry Andric   // Make a final check that the layout is valid.
445*5ffd83dbSDimitry Andric   checkValidLayout(Fields, LastEnd, MaxAlign);
446*5ffd83dbSDimitry Andric #endif
447*5ffd83dbSDimitry Andric 
448*5ffd83dbSDimitry Andric   return std::make_pair(LastEnd, MaxAlign);
449*5ffd83dbSDimitry Andric }
450