Skip to content

Commit 49e5a97

Browse files
committed
Add an algorithm for performing "optimal" layout of a struct.
The algorithm supports both assigning a fixed offset to a field prior to layout and allowing fields to have sizes that aren't multiples of their required alignments. This means that the well-known algorithm of sorting by decreasing alignment isn't always good enough. Still, we start with that, and only if that leaves padding around do we fall back on a greedy padding-minimizing algorithm. There is no known efficient algorithm for producing a guaranteed-minimal layout in all cases. In fact, allowing arbitrary fixed-offset fields means there's a straightforward reduction from bin-packing, making this NP-hard. But as usual with such problems, we can still efficiently produce adequate solutions to the cases that matter most to us. I intend to use this in coroutine frame layout, where the retcon lowerings very badly want to minimize total space usage, and where the switch lowering can indeed produce a header with interior padding if the promise field is highly-aligned. But it may be useful in a much wider variety of situations.
1 parent 98fa615 commit 49e5a97

File tree

5 files changed

+716
-0
lines changed

5 files changed

+716
-0
lines changed
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
//===-- OptimalLayout.h - Optimal data layout algorithm -----------*- C++ -*-=//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
///
9+
/// This file provides an interface for laying out a sequence of fields
10+
/// as a struct in a way that attempts to minimizes the total space
11+
/// requirements of the struct.
12+
///
13+
/// The word "optimal" is a misnomer in several ways. First, minimizing
14+
/// space usage doesn't necessarily yield optimal performance because it
15+
/// may decrease locality. Second, there is no known efficient algorithm
16+
/// that guarantees a minimal layout for arbitrary inputs. Nonetheless,
17+
/// this algorithm is likely to produce much more compact layouts than
18+
/// would be produced by just allocating space in a buffer.
19+
///
20+
//===----------------------------------------------------------------------===//
21+
22+
#ifndef LLVM_SUPPORT_OPTIMALLAYOUT_H
23+
#define LLVM_SUPPORT_OPTIMALLAYOUT_H
24+
25+
#include "llvm/Support/Alignment.h"
26+
#include "llvm/ADT/ArrayRef.h"
27+
#include <utility>
28+
29+
namespace llvm {
30+
31+
/// A field in a structure.
32+
struct OptimalLayoutField {
33+
/// A special value for Offset indicating that the field can be moved
34+
/// anywhere.
35+
static constexpr uint64_t FlexibleOffset = ~(uint64_t)0;
36+
37+
OptimalLayoutField(const void *Id, uint64_t Size, Align Alignment,
38+
uint64_t FixedOffset = FlexibleOffset)
39+
: Offset(FixedOffset), Size(Size), Id(Id), Alignment(Alignment) {
40+
assert(Size > 0 && "adding an empty field to the layout");
41+
}
42+
43+
/// The offset of this field in the final layout. If this is
44+
/// initialized to FlexibleOffset, layout will overwrite it with
45+
/// the assigned offset of the field.
46+
uint64_t Offset;
47+
48+
/// The required size of this field in bytes. Does not have to be
49+
/// a multiple of Alignment. Must be non-zero.
50+
uint64_t Size;
51+
52+
/// A opaque value which uniquely identifies this field.
53+
const void *Id;
54+
55+
/// Private scratch space for the algorithm. The implementation
56+
/// must treat this as uninitialized memory on entry.
57+
void *Scratch;
58+
59+
/// The required alignment of this field.
60+
Align Alignment;
61+
62+
/// Return true if this field has been assigned a fixed offset.
63+
/// After layout, this will be true of all the fields.
64+
bool hasFixedOffset() const {
65+
return (Offset != FlexibleOffset);
66+
}
67+
68+
/// Given that this field has a fixed offset, return the offset
69+
/// of the first byte following it.
70+
uint64_t getEndOffset() const {
71+
assert(hasFixedOffset());
72+
return Offset + Size;
73+
}
74+
};
75+
76+
/// Compute a layout for a struct containing the given fields, making a
77+
/// best-effort attempt to minimize the amount of space required.
78+
///
79+
/// Two features are supported which require a more careful solution
80+
/// than the well-known "sort by decreasing alignment" solution:
81+
///
82+
/// - Fields may be assigned a fixed offset in the layout. If there are
83+
/// gaps among the fixed-offset fields, the algorithm may attempt
84+
/// to allocate flexible-offset fields into those gaps. If that's
85+
/// undesirable, the caller should "block out" those gaps by e.g.
86+
/// just creating a single fixed-offset field that represents the
87+
/// entire "header".
88+
///
89+
/// - The size of a field is not required to be a multiple of, or even
90+
/// greater than, the field's required alignment. The only constraint
91+
/// on fields is that they must not be zero-sized.
92+
///
93+
/// To simplify the implementation, any fixed-offset fields in the
94+
/// layout must appear at the start of the field array, and they must
95+
/// be ordered by increasing offset.
96+
///
97+
/// The algorithm will produce a guaranteed-minimal layout with no
98+
/// interior padding in the following "C-style" case:
99+
///
100+
/// - every field's size is a multiple of its required alignment and
101+
/// - either no fields have initially fixed offsets, or the fixed-offset
102+
/// fields have no interior padding and end at an offset that is at
103+
/// least as aligned as all the flexible-offset fields.
104+
///
105+
/// Otherwise, while the algorithm will make a best-effort attempt to
106+
/// avoid padding, it cannot guarantee a minimal layout, as there is
107+
/// no known efficient algorithm for doing so.
108+
///
109+
/// The layout produced by this algorithm may not be stable across LLVM
110+
/// releases. Do not use this anywhere where ABI stability is required.
111+
///
112+
/// Flexible-offset fields with the same size and alignment will be ordered
113+
/// the same way they were in the initial array. Otherwise the current
114+
/// algorithm makes no effort to preserve the initial order of
115+
/// flexible-offset fields.
116+
///
117+
/// On return, all fields will have been assigned a fixed offset, and the
118+
/// array will be sorted in order of ascending offsets. Note that this
119+
/// means that the fixed-offset fields may no longer form a strict prefix
120+
/// if there's any padding before they end.
121+
///
122+
/// The return value is the total size of the struct and its required
123+
/// alignment. Note that the total size is not rounded up to a multiple
124+
/// of the required alignment; clients which require this can do so easily.
125+
std::pair<uint64_t, Align>
126+
performOptimalLayout(MutableArrayRef<OptimalLayoutField> Fields);
127+
128+
} // namespace llvm
129+
130+
#endif

llvm/lib/Support/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ add_llvm_component_library(LLVMSupport
115115
MemoryBuffer.cpp
116116
MD5.cpp
117117
NativeFormatting.cpp
118+
OptimalLayout.cpp
118119
Optional.cpp
119120
Parallel.cpp
120121
PluginLoader.cpp

0 commit comments

Comments
 (0)