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