Add a HHIR-level peephole optimization to reorder CheckTypes
[hiphop-php.git] / hphp / util / radix-map.h
blob037c3aaef7a8f898ff2d3c5f2fe699f4fac75890
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #pragma once
19 #include "hphp/util/compilation-flags.h"
20 #include "hphp/util/assertions.h"
22 namespace HPHP {
25 * RadixMap stores blocks of memory represented by [ptr,size), where both
26 * ptr and size have at least LgAlign zeroed low-bits. It supports
27 * insert, erase, get, iterate, and find. find(addr) retreives
28 * the [ptr,size) block that contains addr.
30 * Internally, the tree consists of nodes with Radix = 2^LgRadix slots each;
31 * larger values of LgRadix result in a wider, shallower tree. Each tree level
32 * represents LgRadix number of bits of the pointer stored at that level,
33 * with each slot representing a unique group of bits of the pointer.
34 * Together with LgAlign, the bits of a pointer are treated as follows:
36 * [prefix][LgRadix]...[LgRadix][LgAlign]
37 * ^-- msb lsb --^
39 * The low LgAlign bits are ignored, and each interior LgRadix bits are
40 * used as the index into the node at that level. Low order bits index
41 * into leaf nodes in the tree. The highest order index accesses the root
42 * node of the tree. All pointers in the tree share the same common prefix,
43 * which is not used other than to recreate pointers and detect when the tree
44 * needs to be made deeper.
46 template<class T, size_t LgAlign = 3, size_t LgRadix = 4>
47 struct RadixMap {
48 static_assert(LgAlign >= 1, "need one ptr/size bit for internal use");
49 static_assert(std::is_pointer<T>::value, "T must be a pointer type");
50 static constexpr size_t Radix = 1<<LgRadix;
51 static constexpr size_t Align = 1<<LgAlign;
53 // initial root_prefix has bits set that cannot occur in any ptr
54 static constexpr size_t EmptyPrefix = ~0ul;
56 // Implementation notes.
58 // All accessors work by descending the tree, doing their work.
59 // by convention, when "scale" is tracked, it is the tree level (starting
60 // at 0 for nodes that can't have children) times LgRadix. Each slot
61 // represents (1<<scale)+LgAlign bytes.
63 // Each slot can be in one of three states:
64 // empty - 0
65 // leaf - size, representing a [ptr,size) rmap entry
66 // child - pointer to child node
68 // root_prefix contains the high order bits common to every ptr.
70 struct Node {
71 Node() {}
72 bool is_leaf(size_t i) const {
73 return slots[i].size & 1;
75 bool is_child(size_t i) const {
76 return !is_leaf(i) && slots[i].child != nullptr;
78 // Initialize slot i as a leaf. Caller must first call destroy_node()
79 // if the slot previously contained a child pointer.
80 void set_size(size_t i, size_t size) {
81 assert(size != 0 && size % Align == 0);
82 slots[i].size = size + 1; // +1 to set the leaf flag
84 void set_child(size_t i, Node* child) {
85 assert(!is_child(i));
86 slots[i].child = child;
88 void erase(size_t i) {
89 assert(is_leaf(i));
90 slots[i].size = 0;
92 // Return the child at slot i, or null if the slot doesn't hold a child
93 // pointer. It's a bug to call this function on a leaf (ie a nonempty slot).
94 Node* child(size_t i) const {
95 assert(!is_leaf(i));
96 return slots[i].child;
98 size_t size(size_t i) const {
99 assert(is_leaf(i));
100 return slots[i].size - 1; // -1 to remove the leaf flag.
102 public:
103 union {
104 Node* child;
105 size_t size; // if leaf
106 } slots[Radix];
109 // recursively destroy node's children (if any), then free node.
110 NEVER_INLINE void destroy_node(Node* node, int scale) {
111 assert(scale % LgRadix == 0);
112 if (scale > LgRadix) {
113 for (size_t i = 0; i < Radix; ++i) {
114 if (node->is_child(i)) {
115 destroy_node(node->child(i), scale - LgRadix);
118 } else if (scale == LgRadix) {
119 for (size_t i = 0; i < Radix; ++i) {
120 if (node->is_child(i)) {
121 free(node->child(i));
122 --m_node_count;
126 assert(node != m_root || m_node_count == 1);
127 free(node);
128 --m_node_count;
131 ~RadixMap() {
132 if (m_root) destroy_node(m_root, m_root_scale);
135 void clear() {
136 if (m_root) {
137 destroy_node(m_root, m_root_scale);
138 m_root = nullptr;
139 m_root_prefix = EmptyPrefix;
140 m_root_scale = 0;
141 m_node_count = 0;
145 bool empty() const {
146 return !m_root;
149 // Iteratively count the number of blocks currently stored.
150 size_t countBlocks() const {
151 size_t n = 0;
152 iterate([&](T, size_t) { ++n; });
153 return n;
157 * Insert [ptr,size). Assumes ptr not already present, and [ptr,size)
158 * does not overlap any existing range.
160 void insert(T ptr, const size_t size) {
161 assert(size >= Align);
162 // valid pointers cannot have EmptyPrefix as their upper bits.
163 assert(upper(toKey(ptr), m_root_scale) != EmptyPrefix);
164 assert(uintptr_t(ptr) % Align == 0 && size % Align == 0);
165 // this assert is too expensive to leave enabled by default.
166 //assert(!m_root || !find(ptr).ptr);
167 auto const k = toKey(ptr);
168 // compute the max scale at which [ptr,size) would fit perfectly;
169 // ie scale <= the lsb of both k and size/Align.
170 auto max_scale = __builtin_ffsl(k | (size >> LgAlign)) - 1;
171 auto n = m_root;
172 // if necessary, make the tree deeper to expand addressable range
173 while (upper(k, m_root_scale) != m_root_prefix ||
174 m_root_scale + LgRadix <= max_scale) {
175 if (!n) {
176 n = m_root = make_node();
177 // make tree deeper until block can be stored at its natural level
178 while (m_root_scale + LgRadix <= max_scale) {
179 m_root_scale += LgRadix;
181 m_root_prefix = upper(k, m_root_scale);
182 break;
184 // deepen tree while new pointer is out of root's range, or if
185 // it should be stored at a higher level anyway.
186 n = make_node();
187 auto scale = m_root_scale + LgRadix;
188 n->set_child(index(m_root_prefix, scale), m_root);
189 m_root_prefix = upper(m_root_prefix, scale);
190 m_root_scale = scale;
191 m_root = n;
193 // walk down tree, insert new range as high as possible
194 for (auto scale = m_root_scale;; scale -= LgRadix) {
195 auto i = index(k, scale);
196 auto child = n->child(i);
197 if (scale <= max_scale) {
198 if (child) {
199 destroy_node(child, scale - LgRadix);
201 return n->set_size(i, size);
203 if (!child) {
204 child = make_node();
205 n->set_child(i, child);
207 n = child;
209 not_reached();
213 * Return the size associated with ptr, 0 if no range is found.
214 * Ptr must be exactly equal the start of a range stored in the map.
216 size_t get(const void* ptr) const {
217 // if !m_root, then m_root_prefix cannot match any pointer
218 assert(m_root || (m_root_prefix == EmptyPrefix && m_root_scale == 0));
219 auto const k = toKey(ptr);
220 auto scale = m_root_scale;
221 if (upper(k, scale) == m_root_prefix) {
222 for (auto n = m_root; n != nullptr;) {
223 auto i = index(k, scale);
224 if (n->is_leaf(i)) {
225 return lower(ptr, scale) == 0 ? n->size(i) : 0;
227 scale -= LgRadix;
228 n = n->child(i);
231 return 0;
235 * If ptr exactly equals the start of a range stored in the map, erase it
236 * from the map and return its size. Otherwise do nothing and return 0.
238 size_t erase(const void* ptr) {
239 // if !m_root, then m_root_prefix cannot match any pointer
240 assert(m_root || (m_root_prefix == EmptyPrefix && m_root_scale == 0));
241 auto const k = toKey(ptr);
242 auto scale = m_root_scale;
243 // if the tree is empty, this check will fail
244 if (upper(k, scale) == m_root_prefix) {
245 for (auto n = m_root; n != nullptr;) {
246 auto i = index(k, scale);
247 if (n->is_leaf(i)) {
248 if (lower(ptr, scale) == 0) {
249 auto size = n->size(i);
250 n->erase(i);
251 return size;
253 return 0;
255 scale -= LgRadix;
256 n = n->child(i);
259 return 0;
263 * Iterate through the map in address order.
265 template<class Fn> void iterate(Fn fn) const {
266 if (!m_root) return;
267 for (size_t k = m_root_prefix, e = k + slot_size(m_root_scale + LgRadix);
268 k < e;) {
269 auto n = m_root;
270 auto scale = m_root_scale;
271 for (size_t i = index(k, scale); i < Radix;) {
272 if (n->is_leaf(i)) {
273 auto size = n->size(i);
274 fn((T)(k << LgAlign), size);
275 k += size >> LgAlign;
276 i += size >> (scale + LgAlign);
277 } else if (auto child = n->child(i)) {
278 // down to next level without changing k
279 n = child;
280 i = index(k, scale -= LgRadix);
281 } else {
282 // move to start of next slot at this level
283 k = (k + slot_size(scale)) & ~(slot_size(scale) - 1);
284 ++i;
291 * Return the entry enclosing ptr, or nullptr if no such entry exists.
293 struct FindResult { T ptr; size_t size; };
294 FindResult find(const void* ptr) const {
295 // if !m_root, then m_root_prefix cannot match any pointer
296 assert(m_root || (m_root_prefix == EmptyPrefix && m_root_scale == 0));
297 auto end = m_root_prefix + slot_size(m_root_scale + LgRadix) - 1;
298 auto needle = toKey(ptr);
299 for (auto k = std::min(needle, end);
300 upper(k, m_root_scale) == m_root_prefix;) {
301 auto n = m_root;
302 auto scale = m_root_scale;
303 for (ssize_t i = index(k, scale); i >= 0;) {
304 if (n->is_leaf(i)) {
305 k &= ~(slot_size(scale) - 1);
306 auto size = n->size(i);
307 return needle >= k + (size >> LgAlign) ? FindResult{nullptr, 0} :
308 FindResult{(T)(k << LgAlign), size};
310 if (auto child = n->child(i)) {
311 // down to next level without changing k
312 n = child;
313 i = index(k, scale -= LgRadix);
314 } else {
315 // move to end of previous slot at this level
316 k = (k & ~(slot_size(scale) - 1)) - 1;
317 --i;
318 assert(i==-1 || i == index(k,scale));
322 return {nullptr, 0};
325 private:
326 static size_t toKey(const void* ptr) {
327 return size_t(ptr) >> LgAlign;
330 // extract middle bits; 0 = lowest LgRadix bits
331 static int index(size_t k, size_t scale) {
332 return (k >> scale) & (Radix - 1);
335 // extract upper scale+LgRadix bits
336 static size_t upper(size_t k, size_t scale) {
337 return k & ~(slot_size(scale + LgRadix) - 1);
340 // extract the lower scale+LgAlign bits
341 static size_t lower(const void* ptr, size_t scale) {
342 return uintptr_t(ptr) & ((1UL << (scale + LgAlign)) - 1);
345 // return the number of leaf slots represented by each slot at this level.
346 // each leaf slot represents Align bytes.
347 static size_t slot_size(size_t scale) {
348 return 1UL << scale;
351 Node* make_node() {
352 ++m_node_count;
353 return new (calloc(1, sizeof(Node))) Node();
356 private:
357 Node* m_root{nullptr};
358 size_t m_root_prefix{EmptyPrefix};
359 int m_root_scale{0};
360 int m_node_count{0};