Skip to content

Commit 8e84946

Browse files
ckennellycopybara-github
authored andcommitted
Strongly type alignment inside of TCMalloc.
PiperOrigin-RevId: 855477238 Change-Id: I3565a35159f1820b4815bbc3f956b2d52bd79e9b
1 parent de67824 commit 8e84946

10 files changed

Lines changed: 78 additions & 54 deletions

tcmalloc/allocation_sampling.h

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,7 @@ ABSL_ATTRIBUTE_NOINLINE sized_ptr_t SampleifyAllocation(
116116
stack_trace.depth = absl::GetStackTrace(stack_trace.stack, kMaxStackDepth, 0);
117117

118118
if (policy.has_explicit_alignment()) {
119-
// TODO(b/404341539): Make policy.align() a std::align_val_t.
120-
stack_trace.requested_alignment =
121-
static_cast<std::align_val_t>(policy.align());
119+
stack_trace.requested_alignment = policy.align();
122120
} else {
123121
stack_trace.requested_alignment = std::nullopt;
124122
}
@@ -146,8 +144,7 @@ ABSL_ATTRIBUTE_NOINLINE sized_ptr_t SampleifyAllocation(
146144
stack_trace.cold_allocated = IsExpandedSizeClass(size_class);
147145

148146
Length num_pages = BytesToLengthCeil(stack_trace.allocated_size);
149-
size_t sample_alignment = policy.align();
150-
if (sample_alignment == 1) sample_alignment = 0;
147+
std::align_val_t sample_alignment = policy.align();
151148
alloc_with_status = state.guardedpage_allocator().TrySample(
152149
requested_size, sample_alignment, num_pages, stack_trace);
153150
if (alloc_with_status.status == Profile::Sample::GuardedStatus::Guarded) {
@@ -382,8 +379,7 @@ void MaybeUnsampleAllocation(Static& state, Policy policy,
382379
std::align_val_t{1}));
383380
const std::optional<std::align_val_t> deallocated_alignment =
384381
policy.has_explicit_alignment()
385-
? std::make_optional<std::align_val_t>(
386-
static_cast<std::align_val_t>(policy.align()))
382+
? std::make_optional<std::align_val_t>(policy.align())
387383
: std::nullopt;
388384

389385
if ((size.has_value() ||

tcmalloc/guarded_page_allocator.cc

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ void GuardedPageAllocator::Reset() {
9696
}
9797

9898
GuardedAllocWithStatus GuardedPageAllocator::TrySample(
99-
size_t size, size_t alignment, Length num_pages,
99+
size_t size, std::align_val_t alignment, Length num_pages,
100100
const StackTrace& stack_trace) {
101101
if (num_pages > Length(1)) {
102102
skipped_allocations_toolarge_.Add(1);
@@ -165,16 +165,17 @@ GuardedAllocWithStatus GuardedPageAllocator::TrySample(
165165
}
166166

167167
GuardedAllocWithStatus GuardedPageAllocator::Allocate(
168-
size_t size, size_t alignment, const StackTrace& stack_trace) {
168+
size_t size, std::align_val_t alignment, const StackTrace& stack_trace) {
169169
const ssize_t free_slot = ReserveFreeSlot();
170170
if (free_slot == -1) {
171171
// All slots are reserved.
172172
return {nullptr, Profile::Sample::GuardedStatus::NoAvailableSlots};
173173
}
174174

175175
TC_ASSERT_LE(size, page_size_);
176-
TC_ASSERT_LE(alignment, page_size_);
177-
TC_ASSERT(alignment == 0 || absl::has_single_bit(alignment));
176+
TC_ASSERT_LE(static_cast<size_t>(alignment), page_size_);
177+
TC_ASSERT(static_cast<size_t>(alignment) == 0 ||
178+
absl::has_single_bit(static_cast<size_t>(alignment)));
178179
void* result = reinterpret_cast<void*>(SlotToAddr(free_slot));
179180

180181
// For size == 0, the page remains protected.
@@ -206,13 +207,13 @@ GuardedAllocWithStatus GuardedPageAllocator::Allocate(
206207
d.dealloc_trace.depth = 0;
207208
d.requested_size = size;
208209
d.requested_alignment = static_cast<std::align_val_t>(std::max(
209-
alignment,
210-
std::max(static_cast<size_t>(kAlignment),
211-
static_cast<size_t>(__STDCPP_DEFAULT_NEW_ALIGNMENT__))));
210+
alignment, std::max(kAlignment, static_cast<std::align_val_t>(
211+
__STDCPP_DEFAULT_NEW_ALIGNMENT__))));
212212
d.allocation_start = reinterpret_cast<uintptr_t>(result);
213213
d.dealloc_count.store(0, std::memory_order_relaxed);
214214
TC_ASSERT(!d.write_overflow_detected);
215-
TC_ASSERT(!alignment || d.allocation_start % alignment == 0);
215+
TC_ASSERT(static_cast<size_t>(alignment) == 0 ||
216+
d.allocation_start % static_cast<size_t>(alignment) == 0);
216217

217218
stacktrace_filter_.Add({stack_trace.stack, stack_trace.depth}, 1);
218219
return {result, Profile::Sample::GuardedStatus::Guarded};
@@ -535,7 +536,8 @@ size_t GuardedPageAllocator::AddrToSlot(uintptr_t addr) const {
535536
}
536537

537538
void GuardedPageAllocator::MaybeRightAlign(size_t slot, size_t size,
538-
size_t alignment, void** ptr) {
539+
std::align_val_t alignment,
540+
void** ptr) {
539541
if (!ShouldRightAlign(slot)) return;
540542
uintptr_t adjusted_ptr =
541543
reinterpret_cast<uintptr_t>(*ptr) + page_size_ - size;
@@ -552,8 +554,9 @@ void GuardedPageAllocator::MaybeRightAlign(size_t slot, size_t size,
552554
static_cast<size_t>(__STDCPP_DEFAULT_NEW_ALIGNMENT__)));
553555

554556
// Ensure valid alignment.
555-
alignment = std::max(alignment, default_alignment);
556-
uintptr_t alignment_padding = adjusted_ptr & (alignment - 1);
557+
const uintptr_t mask =
558+
std::max(static_cast<size_t>(alignment), default_alignment) - 1;
559+
uintptr_t alignment_padding = adjusted_ptr & mask;
557560
adjusted_ptr -= alignment_padding;
558561

559562
// Write magic bytes in alignment padding to detect small overflow writes.

tcmalloc/guarded_page_allocator.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ class GuardedPageAllocator {
117117
// returns an instance of GuardedAllocWithStatus, that includes guarded
118118
// allocation Span and guarded status. Otherwise, returns nullptr and the
119119
// status indicating why the allocation may not be guarded.
120-
GuardedAllocWithStatus TrySample(size_t size, size_t alignment,
120+
GuardedAllocWithStatus TrySample(size_t size, std::align_val_t alignment,
121121
Length num_pages,
122122
const StackTrace& stack_trace);
123123

@@ -133,7 +133,7 @@ class GuardedPageAllocator {
133133
//
134134
// Precondition: size and alignment <= page_size_
135135
// Precondition: alignment is 0 or a power of 2
136-
GuardedAllocWithStatus Allocate(size_t size, size_t alignment,
136+
GuardedAllocWithStatus Allocate(size_t size, std::align_val_t alignment,
137137
const StackTrace& stack_trace)
138138
ABSL_LOCKS_EXCLUDED(guarded_page_lock_);
139139

@@ -263,7 +263,7 @@ class GuardedPageAllocator {
263263
// If slot is marked for right alignment, moves the allocation in *ptr to the
264264
// right end of the slot, maintaining the specified size and alignment. Magic
265265
// bytes are written in any alignment padding.
266-
void MaybeRightAlign(size_t slot, size_t size, size_t alignment,
266+
void MaybeRightAlign(size_t slot, size_t size, std::align_val_t alignment,
267267
void** absl_nonnull ptr);
268268

269269
uintptr_t SlotToAddr(size_t slot) const;

tcmalloc/guarded_page_allocator_benchmark.cc

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <cstddef>
1818
#include <functional>
1919
#include <memory>
20+
#include <new>
2021
#include <vector>
2122

2223
#include "benchmark/benchmark.h"
@@ -79,7 +80,7 @@ void BM_AllocDealloc(benchmark::State& state) {
7980
auto gpa = GetGuardedPageAllocator();
8081
for (auto _ : state) {
8182
char* ptr = reinterpret_cast<char*>(
82-
gpa->Allocate(alloc_size, 0, GetStackTrace(0)).alloc);
83+
gpa->Allocate(alloc_size, std::align_val_t{0}, GetStackTrace(0)).alloc);
8384
TC_CHECK_NE(ptr, nullptr);
8485
ptr[0] = 'X'; // Page fault first page.
8586
ptr[alloc_size - 1] = 'X'; // Page fault last page.
@@ -103,7 +104,8 @@ void ReservePool(const benchmark::State&) {
103104
auto deleter = [gpa](void* p) { gpa->Deallocate(p); };
104105

105106
for (size_t stack_idx = 0;;) {
106-
auto alloc = gpa->TrySample(1, 0, Length(1), GetStackTrace(stack_idx));
107+
auto alloc = gpa->TrySample(1, std::align_val_t{0}, Length(1),
108+
GetStackTrace(stack_idx));
107109
switch (alloc.status) {
108110
case GuardedStatus::NoAvailableSlots:
109111
TC_CHECK(!GetReserved().empty());
@@ -143,7 +145,8 @@ void BM_TrySample(benchmark::State& state) {
143145

144146
for (auto _ : state) {
145147
StackTrace& stack_trace = GetStackTrace(stack_idx++ % GetReserved().size());
146-
auto alloc = gpa->TrySample(alloc_size, 0, Length(1), stack_trace);
148+
auto alloc =
149+
gpa->TrySample(alloc_size, std::align_val_t{0}, Length(1), stack_trace);
147150

148151
switch (alloc.status) {
149152
case GuardedStatus::RateLimited:

tcmalloc/guarded_page_allocator_test.cc

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ class GuardedPageAllocatorParamTest
8383
};
8484

8585
TEST_F(GuardedPageAllocatorTest, SingleAllocDealloc) {
86-
auto alloc_with_status = gpa_.Allocate(PageSize(), 0, GetStackTrace());
86+
auto alloc_with_status =
87+
gpa_.Allocate(PageSize(), std::align_val_t{0}, GetStackTrace());
8788
EXPECT_EQ(alloc_with_status.status, Profile::Sample::GuardedStatus::Guarded);
8889
EXPECT_EQ(gpa_.successful_allocations(), 1);
8990
char* buf = static_cast<char*>(alloc_with_status.alloc);
@@ -116,7 +117,8 @@ TEST_F(GuardedPageAllocatorTest, NoAlignmentProvided) {
116117
// Make several allocation attempts to encounter left/right-alignment in
117118
// the guarded region.
118119
for (int i = 0; i < kElements; i++) {
119-
auto alloc_with_status = gpa_.Allocate(size, 0, GetStackTrace());
120+
auto alloc_with_status =
121+
gpa_.Allocate(size, std::align_val_t{0}, GetStackTrace());
120122
EXPECT_EQ(alloc_with_status.status,
121123
Profile::Sample::GuardedStatus::Guarded);
122124
ptrs[i] = alloc_with_status.alloc;
@@ -140,7 +142,8 @@ TEST_F(GuardedPageAllocatorTest, NoAlignmentProvided) {
140142
TEST_F(GuardedPageAllocatorTest, AllocDeallocAligned) {
141143
for (size_t align = 1; align <= PageSize(); align <<= 1) {
142144
constexpr size_t alloc_size = 1;
143-
auto alloc_with_status = gpa_.Allocate(alloc_size, align, GetStackTrace());
145+
auto alloc_with_status = gpa_.Allocate(
146+
alloc_size, static_cast<std::align_val_t>(align), GetStackTrace());
144147
EXPECT_EQ(alloc_with_status.status,
145148
Profile::Sample::GuardedStatus::Guarded);
146149
EXPECT_NE(alloc_with_status.alloc, nullptr);
@@ -157,8 +160,8 @@ TEST_F(GuardedPageAllocatorTest, MismatchedAlignment) {
157160
for (size_t align = 1; align <= PageSize(); align <<= 1) {
158161
for (size_t misalign = 1; misalign <= align; misalign <<= 1) {
159162
constexpr size_t alloc_size = 1;
160-
auto alloc_with_status =
161-
gpa_.Allocate(alloc_size, align, GetStackTrace());
163+
auto alloc_with_status = gpa_.Allocate(
164+
alloc_size, static_cast<std::align_val_t>(align), GetStackTrace());
162165
EXPECT_EQ(alloc_with_status.status,
163166
Profile::Sample::GuardedStatus::Guarded);
164167
EXPECT_NE(alloc_with_status.alloc, nullptr);
@@ -182,20 +185,22 @@ TEST_P(GuardedPageAllocatorParamTest, AllocDeallocAllPages) {
182185
size_t num_pages = GetParam();
183186
char* bufs[kMaxGpaPages];
184187
for (size_t i = 0; i < num_pages; i++) {
185-
auto alloc_with_status = gpa_.Allocate(1, 0, GetStackTrace());
188+
auto alloc_with_status =
189+
gpa_.Allocate(1, std::align_val_t{0}, GetStackTrace());
186190
EXPECT_EQ(alloc_with_status.status,
187191
Profile::Sample::GuardedStatus::Guarded);
188192
bufs[i] = reinterpret_cast<char*>(alloc_with_status.alloc);
189193
EXPECT_NE(bufs[i], nullptr);
190194
EXPECT_TRUE(gpa_.PointerIsMine(bufs[i]));
191195
}
192196
EXPECT_EQ(gpa_.successful_allocations(), num_pages);
193-
auto alloc_with_status = gpa_.Allocate(1, 0, GetStackTrace());
197+
auto alloc_with_status =
198+
gpa_.Allocate(1, std::align_val_t{0}, GetStackTrace());
194199
EXPECT_EQ(alloc_with_status.status,
195200
Profile::Sample::GuardedStatus::NoAvailableSlots);
196201
EXPECT_EQ(alloc_with_status.alloc, nullptr);
197202
gpa_.Deallocate(bufs[0]);
198-
alloc_with_status = gpa_.Allocate(1, 0, GetStackTrace());
203+
alloc_with_status = gpa_.Allocate(1, std::align_val_t{0}, GetStackTrace());
199204
EXPECT_EQ(alloc_with_status.status, Profile::Sample::GuardedStatus::Guarded);
200205
bufs[0] = reinterpret_cast<char*>(alloc_with_status.alloc);
201206
EXPECT_NE(bufs[0], nullptr);
@@ -210,7 +215,8 @@ INSTANTIATE_TEST_SUITE_P(VaryNumPages, GuardedPageAllocatorParamTest,
210215
testing::Values(1, kMaxGpaPages / 2, kMaxGpaPages));
211216

212217
TEST_F(GuardedPageAllocatorTest, PointerIsMine) {
213-
auto alloc_with_status = gpa_.Allocate(1, 0, GetStackTrace());
218+
auto alloc_with_status =
219+
gpa_.Allocate(1, std::align_val_t{0}, GetStackTrace());
214220
EXPECT_EQ(alloc_with_status.status, Profile::Sample::GuardedStatus::Guarded);
215221
EXPECT_EQ(gpa_.successful_allocations(), 1);
216222
void* buf = alloc_with_status.alloc;
@@ -229,7 +235,8 @@ TEST_F(GuardedPageAllocatorTest, Print) {
229235
}
230236

231237
TEST_F(GuardedPageAllocatorTest, ZeroByteAllocationAndDeallocation) {
232-
auto alloc_with_status = gpa_.Allocate(0, 0, GetStackTrace());
238+
auto alloc_with_status =
239+
gpa_.Allocate(0, std::align_val_t{0}, GetStackTrace());
233240
EXPECT_EQ(alloc_with_status.status, Profile::Sample::GuardedStatus::Guarded);
234241
EXPECT_NE(alloc_with_status.alloc, nullptr);
235242
void* ptr = alloc_with_status.alloc;
@@ -247,7 +254,8 @@ TEST_F(GuardedPageAllocatorTest, ZeroByteAllocationAndDeallocation) {
247254
}
248255

249256
TEST_F(GuardedPageAllocatorTest, ZeroByteUseAfterFree) {
250-
auto alloc_with_status = gpa_.Allocate(0, 0, GetStackTrace());
257+
auto alloc_with_status =
258+
gpa_.Allocate(0, std::align_val_t{0}, GetStackTrace());
251259
EXPECT_EQ(alloc_with_status.status, Profile::Sample::GuardedStatus::Guarded);
252260
ASSERT_NE(alloc_with_status.alloc, nullptr);
253261
void* ptr = alloc_with_status.alloc;
@@ -260,7 +268,8 @@ TEST_F(GuardedPageAllocatorTest, ZeroByteUseAfterFree) {
260268
}
261269

262270
TEST_F(GuardedPageAllocatorTest, ZeroByteDoubleFree) {
263-
auto alloc_with_status = gpa_.Allocate(0, 0, GetStackTrace());
271+
auto alloc_with_status =
272+
gpa_.Allocate(0, std::align_val_t{0}, GetStackTrace());
264273
EXPECT_EQ(alloc_with_status.status, Profile::Sample::GuardedStatus::Guarded);
265274
ASSERT_NE(alloc_with_status.alloc, nullptr);
266275
void* ptr = alloc_with_status.alloc;
@@ -280,7 +289,8 @@ TEST_F(GuardedPageAllocatorTest, ThreadedAllocCount) {
280289
for (size_t i = 0; i < kNumThreads; i++) {
281290
threads.push_back(std::thread([this, &allocations, i]() {
282291
for (size_t j = 0; j < kMaxGpaPages; j++) {
283-
allocations[i][j] = gpa_.Allocate(1, 0, GetStackTrace()).alloc;
292+
allocations[i][j] =
293+
gpa_.Allocate(1, std::align_val_t{0}, GetStackTrace()).alloc;
284294
}
285295
}));
286296
}
@@ -311,7 +321,8 @@ TEST_F(GuardedPageAllocatorTest, ThreadedHighContention) {
311321
threads.push_back(std::thread([this]() {
312322
char* buf;
313323
while (true) {
314-
auto alloc_with_status = gpa_.Allocate(1, 0, GetStackTrace());
324+
auto alloc_with_status =
325+
gpa_.Allocate(1, std::align_val_t{0}, GetStackTrace());
315326
if (alloc_with_status.status ==
316327
Profile::Sample::GuardedStatus::Guarded) {
317328
buf = reinterpret_cast<char*>(alloc_with_status.alloc);
@@ -341,7 +352,8 @@ TEST_F(GuardedPageAllocatorTest, ThreadedHighContention) {
341352
}
342353
// Verify all pages have been deallocated now that all threads are done.
343354
for (size_t i = 0; i < kMaxGpaPages; i++) {
344-
auto alloc_with_status = gpa_.Allocate(1, 0, GetStackTrace());
355+
auto alloc_with_status =
356+
gpa_.Allocate(1, std::align_val_t{0}, GetStackTrace());
345357
EXPECT_EQ(alloc_with_status.status,
346358
Profile::Sample::GuardedStatus::Guarded);
347359
EXPECT_NE(alloc_with_status.alloc, nullptr);

tcmalloc/pages.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <cstddef>
2020
#include <cstdint>
2121
#include <limits>
22+
#include <new>
2223
#include <string>
2324

2425
#include "absl/strings/numbers.h"
@@ -193,6 +194,11 @@ inline constexpr Length BytesToLengthCeil(size_t bytes) {
193194
((bytes & (kPageSize - 1)) > 0 ? 1 : 0));
194195
}
195196

197+
TCMALLOC_ATTRIBUTE_CONST
198+
inline constexpr Length BytesToLengthCeil(std::align_val_t bytes) {
199+
return BytesToLengthCeil(static_cast<size_t>(bytes));
200+
}
201+
196202
TCMALLOC_ATTRIBUTE_CONST
197203
inline constexpr Length BytesToLengthFloor(size_t bytes) {
198204
return Length(bytes >> kPageShift);

tcmalloc/segv_handler_test.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <sys/syscall.h>
1919

2020
#include <cstddef>
21+
#include <new>
2122

2223
#include "gtest/gtest.h"
2324
#include "tcmalloc/internal/logging.h"
@@ -40,7 +41,8 @@ TEST(SegvHandlerTest, SignalHandlerStackConsumption) {
4041
stack_trace.stack[0] = reinterpret_cast<void*>(&&self);
4142
stack_trace.depth = 1;
4243

43-
auto ptr = tc_globals.guardedpage_allocator().Allocate(1, 0, stack_trace);
44+
auto ptr = tc_globals.guardedpage_allocator().Allocate(1, std::align_val_t{0},
45+
stack_trace);
4446
if (ptr.status != Profile::Sample::GuardedStatus::Guarded) {
4547
GTEST_SKIP() << "did not get a guarded allocation";
4648
}

tcmalloc/sizemap.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ class SizeMap {
223223
[[nodiscard]] ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED
224224
ABSL_ATTRIBUTE_ALWAYS_INLINE inline SizeMapResult GetSizeClass(
225225
Policy policy, size_t size) const {
226-
const size_t align = policy.align();
226+
const size_t align = static_cast<size_t>(policy.align());
227227
TC_ASSERT(absl::has_single_bit(align));
228228

229229
if (ABSL_PREDICT_FALSE(align > kPageSize)) {

tcmalloc/tcmalloc.cc

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,8 @@ ABSL_ATTRIBUTE_NOINLINE static void free_non_normal(void* ptr, size_t size,
816816
policy.InSamePartitionAs(ptr).AccessAsCold(), size);
817817
if (ABSL_PREDICT_FALSE(!is_small)) {
818818
// We couldn't calculate the size class, which means size > kMaxSize.
819-
TC_ASSERT(size > kMaxSize || policy.align() > alignof(std::max_align_t));
819+
TC_ASSERT(size > kMaxSize ||
820+
policy.align() > std::align_val_t{alignof(std::max_align_t)});
820821
static_assert(kMaxSize >= kPageSize, "kMaxSize must be at least kPageSize");
821822
return InvokeHooksAndFreePages(ptr, size, policy);
822823
}
@@ -876,7 +877,8 @@ inline ABSL_ATTRIBUTE_ALWAYS_INLINE void do_free_with_size(void* ptr,
876877
tc_globals.sizemap().GetSizeClass(policy.InSamePartitionAs(ptr), size);
877878
if (ABSL_PREDICT_FALSE(!is_small)) {
878879
// We couldn't calculate the size class, which means size > kMaxSize.
879-
TC_ASSERT(size > kMaxSize || policy.align() > alignof(std::max_align_t));
880+
TC_ASSERT(size > kMaxSize ||
881+
policy.align() > std::align_val_t{alignof(std::max_align_t)});
880882
static_assert(kMaxSize >= kPageSize, "kMaxSize must be at least kPageSize");
881883
SLOW_PATH_BARRIER();
882884
return InvokeHooksAndFreePages(ptr, size, policy);
@@ -944,7 +946,7 @@ bool CorrectSize(void* ptr, const size_t provided_size, Policy policy) {
944946
}
945947

946948
if (size_class > 0) {
947-
if (policy.align() > static_cast<size_t>(kAlignment)) {
949+
if (policy.align() > kAlignment) {
948950
// Nontrivial alignment. We might have used a larger size to satisify it.
949951
minimum_size = 0;
950952
} else {

0 commit comments

Comments
 (0)