Skip to content

Commit 4a9aef6

Browse files
authored
DynamicAPInt: optimize size of structure (#97831)
Reuse the APInt::BitWidth to eliminate DynamicAPInt::HoldsLarge, cutting the size of DynamicAPInt by four bytes. This is implemented by making DynamicAPInt a friend of SlowDynamicAPInt and APInt, so it can directly access SlowDynamicAPInt::Val and APInt::BitWidth. We get a speedup of 4% with this patch.
1 parent be3a8b8 commit 4a9aef6

File tree

4 files changed

+40
-11
lines changed

4 files changed

+40
-11
lines changed

llvm/include/llvm/ADT/APInt.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class StringRef;
3030
class hash_code;
3131
class raw_ostream;
3232
struct Align;
33+
class DynamicAPInt;
3334

3435
template <typename T> class SmallVectorImpl;
3536
template <typename T> class ArrayRef;
@@ -1895,6 +1896,9 @@ class [[nodiscard]] APInt {
18951896
friend struct DenseMapInfo<APInt, void>;
18961897
friend class APSInt;
18971898

1899+
// Make DynamicAPInt a friend so it can access BitWidth directly.
1900+
friend DynamicAPInt;
1901+
18981902
/// This constructor is used only internally for speed of construction of
18991903
/// temporaries. It is unsafe since it takes ownership of the pointer, so it
19001904
/// is not public.

llvm/include/llvm/ADT/DynamicAPInt.h

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,21 +35,23 @@ namespace llvm {
3535
/// We always_inline all operations; removing these results in a 1.5x
3636
/// performance slowdown.
3737
///
38-
/// When HoldsLarge is true, a SlowMPInt is held in the union. If it is false,
39-
/// the int64_t is held. Using std::variant instead would lead to significantly
40-
/// worse performance.
38+
/// When isLarge returns true, a SlowMPInt is held in the union. If isSmall
39+
/// returns true, the int64_t is held. We don't have a separate field for
40+
/// indicating this, and instead "steal" memory from ValLarge when it is not in
41+
/// use because we know that the memory layout of APInt is such that BitWidth
42+
/// doesn't overlap with ValSmall (see static_assert_layout). Using std::variant
43+
/// instead would lead to significantly worse performance.
4144
class DynamicAPInt {
4245
union {
4346
int64_t ValSmall;
4447
detail::SlowDynamicAPInt ValLarge;
4548
};
46-
unsigned HoldsLarge;
4749

4850
LLVM_ATTRIBUTE_ALWAYS_INLINE void initSmall(int64_t O) {
4951
if (LLVM_UNLIKELY(isLarge()))
5052
ValLarge.detail::SlowDynamicAPInt::~SlowDynamicAPInt();
5153
ValSmall = O;
52-
HoldsLarge = false;
54+
ValLarge.Val.BitWidth = 0;
5355
}
5456
LLVM_ATTRIBUTE_ALWAYS_INLINE void
5557
initLarge(const detail::SlowDynamicAPInt &O) {
@@ -66,14 +68,17 @@ class DynamicAPInt {
6668
// and leak it.
6769
ValLarge = O;
6870
}
69-
HoldsLarge = true;
7071
}
7172

7273
LLVM_ATTRIBUTE_ALWAYS_INLINE explicit DynamicAPInt(
7374
const detail::SlowDynamicAPInt &Val)
74-
: ValLarge(Val), HoldsLarge(true) {}
75-
LLVM_ATTRIBUTE_ALWAYS_INLINE bool isSmall() const { return !HoldsLarge; }
76-
LLVM_ATTRIBUTE_ALWAYS_INLINE bool isLarge() const { return HoldsLarge; }
75+
: ValLarge(Val) {}
76+
LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr bool isSmall() const {
77+
return ValLarge.Val.BitWidth == 0;
78+
}
79+
LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr bool isLarge() const {
80+
return !isSmall();
81+
}
7782
/// Get the stored value. For getSmall/Large,
7883
/// the stored value should be small/large.
7984
LLVM_ATTRIBUTE_ALWAYS_INLINE int64_t getSmall() const {
@@ -105,14 +110,17 @@ class DynamicAPInt {
105110

106111
public:
107112
LLVM_ATTRIBUTE_ALWAYS_INLINE explicit DynamicAPInt(int64_t Val)
108-
: ValSmall(Val), HoldsLarge(false) {}
113+
: ValSmall(Val) {
114+
ValLarge.Val.BitWidth = 0;
115+
}
109116
LLVM_ATTRIBUTE_ALWAYS_INLINE DynamicAPInt() : DynamicAPInt(0) {}
110117
LLVM_ATTRIBUTE_ALWAYS_INLINE ~DynamicAPInt() {
111118
if (LLVM_UNLIKELY(isLarge()))
112119
ValLarge.detail::SlowDynamicAPInt::~SlowDynamicAPInt();
113120
}
114121
LLVM_ATTRIBUTE_ALWAYS_INLINE DynamicAPInt(const DynamicAPInt &O)
115-
: ValSmall(O.ValSmall), HoldsLarge(false) {
122+
: ValSmall(O.ValSmall) {
123+
ValLarge.Val.BitWidth = 0;
116124
if (LLVM_UNLIKELY(O.isLarge()))
117125
initLarge(O.ValLarge);
118126
}
@@ -203,6 +211,8 @@ class DynamicAPInt {
203211

204212
friend hash_code hash_value(const DynamicAPInt &x); // NOLINT
205213

214+
void static_assert_layout(); // NOLINT
215+
206216
raw_ostream &print(raw_ostream &OS) const;
207217
LLVM_DUMP_METHOD void dump() const;
208218
};

llvm/include/llvm/ADT/SlowDynamicAPInt.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
#include "llvm/ADT/APInt.h"
2222
#include "llvm/Support/raw_ostream.h"
2323

24+
namespace llvm {
25+
class DynamicAPInt;
26+
} // namespace llvm
27+
2428
namespace llvm::detail {
2529
/// A simple class providing dynamic arbitrary-precision arithmetic. Internally,
2630
/// it stores an APInt, whose width is doubled whenever an overflow occurs at a
@@ -69,6 +73,9 @@ class SlowDynamicAPInt {
6973
/// Overload to compute a hash_code for a SlowDynamicAPInt value.
7074
friend hash_code hash_value(const SlowDynamicAPInt &X); // NOLINT
7175

76+
// Make DynamicAPInt a friend so it can access Val directly.
77+
friend DynamicAPInt;
78+
7279
unsigned getBitWidth() const { return Val.getBitWidth(); }
7380

7481
void print(raw_ostream &OS) const;

llvm/lib/Support/DynamicAPInt.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ hash_code llvm::hash_value(const DynamicAPInt &X) {
1818
return detail::hash_value(X.getLarge());
1919
}
2020

21+
void DynamicAPInt::static_assert_layout() {
22+
constexpr size_t ValLargeOffset =
23+
offsetof(DynamicAPInt, ValLarge.Val.BitWidth);
24+
constexpr size_t ValSmallOffset = offsetof(DynamicAPInt, ValSmall);
25+
constexpr size_t ValSmallSize = sizeof(ValSmall);
26+
static_assert(ValLargeOffset >= ValSmallOffset + ValSmallSize);
27+
}
28+
2129
raw_ostream &DynamicAPInt::print(raw_ostream &OS) const {
2230
if (isSmall())
2331
return OS << ValSmall;

0 commit comments

Comments
 (0)