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