Skip to content

Commit 2dc9ec4

Browse files
authored
[scudo] Refactor allocator config to support optional flags (llvm#81805)
Instead of explicitly disabling a feature by declaring the variable and set it to false, this change supports the optional flags. I.e., you can skip certain flags if you are not using it. This optional feature supports both forms, 1. Value: A parameter for a feature. E.g., EnableRandomOffset 2. Type: A C++ type implementing a feature. E.g., ConditionVariableT On the other hand, to access the flags will be through one of the wrappers, BaseConfig/PrimaryConfig/SecondaryConfig/CacheConfig (CacheConfig is embedded in SecondaryConfig). These wrappers have the getters to access the value and the type. When adding a new feature, we need to add it to `allocator_config.def` and mark the new variable with either *_REQUIRED_* or *_OPTIONAL_* macro so that the accessor will be generated properly. In addition, also remove the need of `UseConditionVariable` to flip on/off of condition variable. Now we only need to define the type of condition variable.
1 parent b87db5b commit 2dc9ec4

15 files changed

+498
-170
lines changed

compiler-rt/lib/scudo/standalone/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ endif()
5858
set(SCUDO_HEADERS
5959
allocator_common.h
6060
allocator_config.h
61+
allocator_config_wrapper.h
6162
atomic_helpers.h
6263
bytemap.h
6364
checksum.h
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
//===-- allocator_config.def ------------------------------------*- 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 defines all the flags and types supported in Scudo. For optional
10+
// flags and types, only explicitly define them when interested (i.e., unused
11+
// optional flags or types can be skipped).
12+
13+
#ifndef BASE_REQUIRED_TEMPLATE_TYPE
14+
#define BASE_REQUIRED_TEMPLATE_TYPE(...)
15+
#endif
16+
#ifndef BASE_OPTIONAL
17+
#define BASE_OPTIONAL(...)
18+
#endif
19+
#ifndef PRIMARY_REQUIRED_TYPE
20+
#define PRIMARY_REQUIRED_TYPE(...)
21+
#endif
22+
#ifndef PRIMARY_REQUIRED
23+
#define PRIMARY_REQUIRED(...)
24+
#endif
25+
#ifndef PRIMARY_OPTIONAL
26+
#define PRIMARY_OPTIONAL(...)
27+
#endif
28+
#ifndef PRIMARY_OPTIONAL_TYPE
29+
#define PRIMARY_OPTIONAL_TYPE(...)
30+
#endif
31+
#ifndef SECONDARY_REQUIRED_TEMPLATE_TYPE
32+
#define SECONDARY_REQUIRED_TEMPLATE_TYPE(...)
33+
#endif
34+
#ifndef SECONDARY_CACHE_OPTIONAL
35+
#define SECONDARY_CACHE_OPTIONAL(...)
36+
#endif
37+
38+
// BASE_REQUIRED_TEMPLATE_TYPE(NAME)
39+
//
40+
// Thread-Specific Data Registry used, shared or exclusive.
41+
BASE_REQUIRED_TEMPLATE_TYPE(TSDRegistryT)
42+
43+
// Defines the type of Primary allocator to use.
44+
BASE_REQUIRED_TEMPLATE_TYPE(PrimaryT)
45+
46+
// Defines the type of Secondary allocator to use.
47+
BASE_REQUIRED_TEMPLATE_TYPE(SecondaryT)
48+
49+
// BASE_OPTIONAL(TYPE, NAME, DEFAULT)
50+
//
51+
// Indicates possible support for Memory Tagging.
52+
BASE_OPTIONAL(const bool, MaySupportMemoryTagging, false)
53+
54+
// PRIMARY_REQUIRED_TYPE(NAME)
55+
//
56+
// SizeClassMap to use with the Primary.
57+
PRIMARY_REQUIRED_TYPE(SizeClassMap)
58+
59+
// Defines the type and scale of a compact pointer. A compact pointer can
60+
// be understood as the offset of a pointer within the region it belongs
61+
// to, in increments of a power-of-2 scale. See `CompactPtrScale` also.
62+
PRIMARY_REQUIRED_TYPE(CompactPtrT)
63+
64+
// PRIMARY_REQUIRED(TYPE, NAME)
65+
//
66+
// The scale of a compact pointer. E.g., Ptr = Base + (CompactPtr << Scale).
67+
PRIMARY_REQUIRED(const uptr, CompactPtrScale)
68+
69+
// Log2 of the size of a size class region, as used by the Primary.
70+
PRIMARY_REQUIRED(const uptr, RegionSizeLog)
71+
72+
// Conceptually, a region will be divided into groups based on the address
73+
// range. Each allocation consumes blocks in the same group until exhaustion
74+
// then it pops out blocks in a new group. Therefore, `GroupSizeLog` is always
75+
// smaller or equal to `RegionSizeLog`. Note that `GroupSizeLog` needs to be
76+
// equal to `RegionSizeLog` for SizeClassAllocator32 because of certain
77+
// constraints.
78+
PRIMARY_REQUIRED(const uptr, GroupSizeLog)
79+
80+
// Call map for user memory with at least this size. Only used with primary64.
81+
PRIMARY_REQUIRED(const uptr, MapSizeIncrement)
82+
83+
// Defines the minimal & maximal release interval that can be set.
84+
PRIMARY_REQUIRED(const s32, MinReleaseToOsIntervalMs)
85+
PRIMARY_REQUIRED(const s32, MaxReleaseToOsIntervalMs)
86+
87+
// PRIMARY_OPTIONAL(TYPE, NAME, DEFAULT)
88+
//
89+
// Indicates support for offsetting the start of a region by a random number of
90+
// pages. Only used with primary64.
91+
PRIMARY_OPTIONAL(const bool, EnableRandomOffset, false)
92+
93+
// PRIMARY_OPTIONAL_TYPE(NAME, DEFAULT)
94+
//
95+
// Use condition variable to shorten the waiting time of refillment of
96+
// freelist. Note that this depends on the implementation of condition
97+
// variable on each platform and the performance may vary so that it does not
98+
// guarantee a performance benefit.
99+
PRIMARY_OPTIONAL_TYPE(ConditionVariableT, ConditionVariableDummy)
100+
101+
// SECONDARY_REQUIRED_TEMPLATE_TYPE(NAME)
102+
//
103+
// Defines the type of Secondary Cache to use.
104+
SECONDARY_REQUIRED_TEMPLATE_TYPE(CacheT)
105+
106+
// SECONDARY_CACHE_OPTIONAL(TYPE, NAME, DEFAULT)
107+
//
108+
// Defines the type of cache used by the Secondary. Some additional
109+
// configuration entries can be necessary depending on the Cache.
110+
SECONDARY_CACHE_OPTIONAL(const u32, EntriesArraySize, 0)
111+
SECONDARY_CACHE_OPTIONAL(const u32, QuarantineSize, 0)
112+
SECONDARY_CACHE_OPTIONAL(const u32, DefaultMaxEntriesCount, 0)
113+
SECONDARY_CACHE_OPTIONAL(const u32, DefaultMaxEntrySize, 0)
114+
SECONDARY_CACHE_OPTIONAL(const s32, MinReleaseToOsIntervalMs, INT32_MIN)
115+
SECONDARY_CACHE_OPTIONAL(const s32, MaxReleaseToOsIntervalMs, INT32_MAX)
116+
117+
#undef SECONDARY_CACHE_OPTIONAL
118+
#undef SECONDARY_REQUIRED_TEMPLATE_TYPE
119+
#undef PRIMARY_OPTIONAL_TYPE
120+
#undef PRIMARY_OPTIONAL
121+
#undef PRIMARY_REQUIRED
122+
#undef PRIMARY_REQUIRED_TYPE
123+
#undef BASE_OPTIONAL
124+
#undef BASE_REQUIRED_TEMPLATE_TYPE

compiler-rt/lib/scudo/standalone/allocator_config.h

Lines changed: 4 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -38,80 +38,10 @@
3838

3939
namespace scudo {
4040

41-
// The combined allocator uses a structure as a template argument that
42-
// specifies the configuration options for the various subcomponents of the
43-
// allocator.
44-
//
45-
// struct ExampleConfig {
46-
// // Indicates possible support for Memory Tagging.
47-
// static const bool MaySupportMemoryTagging = false;
48-
//
49-
// // Thread-Specific Data Registry used, shared or exclusive.
50-
// template <class A> using TSDRegistryT = TSDRegistrySharedT<A, 8U, 4U>;
51-
//
52-
// struct Primary {
53-
// // SizeClassMap to use with the Primary.
54-
// using SizeClassMap = DefaultSizeClassMap;
55-
//
56-
// // Log2 of the size of a size class region, as used by the Primary.
57-
// static const uptr RegionSizeLog = 30U;
58-
//
59-
// // Log2 of the size of block group, as used by the Primary. Each group
60-
// // contains a range of memory addresses, blocks in the range will belong
61-
// // to the same group. In general, single region may have 1 or 2MB group
62-
// // size. Multiple regions will have the group size equal to the region
63-
// // size because the region size is usually smaller than 1 MB.
64-
// // Smaller value gives fine-grained control of memory usage but the
65-
// // trade-off is that it may take longer time of deallocation.
66-
// static const uptr GroupSizeLog = 20U;
67-
//
68-
// // Defines the type and scale of a compact pointer. A compact pointer can
69-
// // be understood as the offset of a pointer within the region it belongs
70-
// // to, in increments of a power-of-2 scale.
71-
// // eg: Ptr = Base + (CompactPtr << Scale).
72-
// typedef u32 CompactPtrT;
73-
// static const uptr CompactPtrScale = SCUDO_MIN_ALIGNMENT_LOG;
74-
//
75-
// // Indicates support for offsetting the start of a region by
76-
// // a random number of pages. Only used with primary64.
77-
// static const bool EnableRandomOffset = true;
78-
//
79-
// // Call map for user memory with at least this size. Only used with
80-
// // primary64.
81-
// static const uptr MapSizeIncrement = 1UL << 18;
82-
//
83-
// // Defines the minimal & maximal release interval that can be set.
84-
// static const s32 MinReleaseToOsIntervalMs = INT32_MIN;
85-
// static const s32 MaxReleaseToOsIntervalMs = INT32_MAX;
86-
//
87-
// // Use condition variable to shorten the waiting time of refillment of
88-
// // freelist. Note that this depends on the implementation of condition
89-
// // variable on each platform and the performance may vary so that it
90-
// // doesn't guarantee a performance benefit.
91-
// // Note that both variables have to be defined to enable it.
92-
// static const bool UseConditionVariable = true;
93-
// using ConditionVariableT = ConditionVariableLinux;
94-
// };
95-
// // Defines the type of Primary allocator to use.
96-
// template <typename Config> using PrimaryT = SizeClassAllocator64<Config>;
97-
//
98-
// // Defines the type of cache used by the Secondary. Some additional
99-
// // configuration entries can be necessary depending on the Cache.
100-
// struct Secondary {
101-
// struct Cache {
102-
// static const u32 EntriesArraySize = 32U;
103-
// static const u32 QuarantineSize = 0U;
104-
// static const u32 DefaultMaxEntriesCount = 32U;
105-
// static const uptr DefaultMaxEntrySize = 1UL << 19;
106-
// static const s32 MinReleaseToOsIntervalMs = INT32_MIN;
107-
// static const s32 MaxReleaseToOsIntervalMs = INT32_MAX;
108-
// };
109-
// // Defines the type of Secondary Cache to use.
110-
// template <typename Config> using CacheT = MapAllocatorCache<Config>;
111-
// };
112-
// // Defines the type of Secondary allocator to use.
113-
// template <typename Config> using SecondaryT = MapAllocator<Config>;
114-
// };
41+
// Scudo uses a structure as a template argument that specifies the
42+
// configuration options for the various subcomponents of the allocator. See the
43+
// following configs as examples and check `allocator_config.def` for all the
44+
// available options.
11545

11646
#ifndef SCUDO_USE_CUSTOM_CONFIG
11747

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
//===-- allocator_config_wrapper.h ------------------------------*- 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+
#ifndef SCUDO_ALLOCATOR_CONFIG_WRAPPER_H_
10+
#define SCUDO_ALLOCATOR_CONFIG_WRAPPER_H_
11+
12+
#include "condition_variable.h"
13+
#include "internal_defs.h"
14+
#include "secondary.h"
15+
16+
namespace {
17+
18+
template <typename T> struct removeConst {
19+
using type = T;
20+
};
21+
template <typename T> struct removeConst<const T> {
22+
using type = T;
23+
};
24+
25+
// This is only used for SFINAE when detecting if a type is defined.
26+
template <typename T> struct voidAdaptor {
27+
using type = void;
28+
};
29+
30+
} // namespace
31+
32+
namespace scudo {
33+
34+
#define OPTIONAL_TEMPLATE(TYPE, NAME, DEFAULT, MEMBER) \
35+
template <typename Config, typename = TYPE> struct NAME##State { \
36+
static constexpr removeConst<TYPE>::type getValue() { return DEFAULT; } \
37+
}; \
38+
template <typename Config> \
39+
struct NAME##State<Config, decltype(Config::MEMBER)> { \
40+
static constexpr removeConst<TYPE>::type getValue() { \
41+
return Config::MEMBER; \
42+
} \
43+
};
44+
45+
#define OPTIONAL_TYPE_TEMPLATE(NAME, DEFAULT, MEMBER) \
46+
template <typename Config, typename Void = void> struct NAME##Type { \
47+
static constexpr bool enabled() { return false; } \
48+
using NAME = DEFAULT; \
49+
}; \
50+
template <typename Config> \
51+
struct NAME##Type<Config, \
52+
typename voidAdaptor<typename Config::MEMBER>::type> { \
53+
static constexpr bool enabled() { return true; } \
54+
using NAME = typename Config::MEMBER; \
55+
};
56+
57+
template <typename AllocatorConfig> struct BaseConfig {
58+
#define BASE_REQUIRED_TEMPLATE_TYPE(NAME) \
59+
template <typename T> using NAME = typename AllocatorConfig::template NAME<T>;
60+
61+
#define BASE_OPTIONAL(TYPE, NAME, DEFAULT) \
62+
OPTIONAL_TEMPLATE(TYPE, NAME, DEFAULT, NAME) \
63+
static constexpr removeConst<TYPE>::type get##NAME() { \
64+
return NAME##State<AllocatorConfig>::getValue(); \
65+
}
66+
67+
#include "allocator_config.def"
68+
}; // BaseConfig
69+
70+
template <typename AllocatorConfig> struct PrimaryConfig {
71+
// TODO: Pass this flag through template argument to remove this hard-coded
72+
// function.
73+
static constexpr bool getMaySupportMemoryTagging() {
74+
return BaseConfig<AllocatorConfig>::getMaySupportMemoryTagging();
75+
}
76+
77+
#define PRIMARY_REQUIRED_TYPE(NAME) \
78+
using NAME = typename AllocatorConfig::Primary::NAME;
79+
80+
#define PRIMARY_REQUIRED(TYPE, NAME) \
81+
static constexpr removeConst<TYPE>::type get##NAME() { \
82+
return AllocatorConfig::Primary::NAME; \
83+
}
84+
85+
#define PRIMARY_OPTIONAL(TYPE, NAME, DEFAULT) \
86+
OPTIONAL_TEMPLATE(TYPE, NAME, DEFAULT, NAME) \
87+
static constexpr removeConst<TYPE>::type get##NAME() { \
88+
return NAME##State<typename AllocatorConfig::Primary>::getValue(); \
89+
}
90+
91+
#define PRIMARY_OPTIONAL_TYPE(NAME, DEFAULT) \
92+
OPTIONAL_TYPE_TEMPLATE(NAME, DEFAULT, NAME) \
93+
static constexpr bool has##NAME() { \
94+
return NAME##Type<typename AllocatorConfig::Primary>::enabled(); \
95+
} \
96+
using NAME = typename NAME##Type<typename AllocatorConfig::Primary>::NAME;
97+
98+
#include "allocator_config.def"
99+
100+
}; // PrimaryConfig
101+
102+
template <typename AllocatorConfig> struct SecondaryConfig {
103+
// TODO: Pass this flag through template argument to remove this hard-coded
104+
// function.
105+
static constexpr bool getMaySupportMemoryTagging() {
106+
return BaseConfig<AllocatorConfig>::getMaySupportMemoryTagging();
107+
}
108+
109+
#define SECONDARY_REQUIRED_TEMPLATE_TYPE(NAME) \
110+
template <typename T> \
111+
using NAME = typename AllocatorConfig::Secondary::template NAME<T>;
112+
#include "allocator_config.def"
113+
114+
struct CacheConfig {
115+
// TODO: Pass this flag through template argument to remove this hard-coded
116+
// function.
117+
static constexpr bool getMaySupportMemoryTagging() {
118+
return BaseConfig<AllocatorConfig>::getMaySupportMemoryTagging();
119+
}
120+
121+
#define SECONDARY_CACHE_OPTIONAL(TYPE, NAME, DEFAULT) \
122+
OPTIONAL_TEMPLATE(TYPE, NAME, DEFAULT, Cache::NAME) \
123+
static constexpr removeConst<TYPE>::type get##NAME() { \
124+
return NAME##State<typename AllocatorConfig::Secondary>::getValue(); \
125+
}
126+
#include "allocator_config.def"
127+
}; // CacheConfig
128+
}; // SecondaryConfig
129+
130+
#undef OPTIONAL_TEMPLATE
131+
#undef OPTIONAL_TEMPLATE_TYPE
132+
133+
} // namespace scudo
134+
135+
#endif // SCUDO_ALLOCATOR_CONFIG_WRAPPER_H_

0 commit comments

Comments
 (0)