• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===- Waymarking.h - Array waymarking 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 // Utility to backtrace an array's head, from a pointer into it. For the
10 // backtrace to work, we use "Waymarks", which are special tags embedded into
11 // the array's elements.
12 //
13 // A Tag of n-bits (in size) is composed as follows:
14 //
15 // bits: |   n-1   |             n-2 ... 0              |
16 //       .---------.------------------------------------.
17 //       |Stop Mask|(2^(n-1))-ary numeric system - digit|
18 //       '---------'------------------------------------'
19 //
20 // Backtracing is done as follows:
21 // Walk back (starting from a given pointer to an element into the array), until
22 // a tag with a "Stop Mask" is reached. Then start calculating the "Offset" from
23 // the array's head, by picking up digits along the way, until another stop is
24 // reached. The "Offset" is then subtracted from the current pointer, and the
25 // result is the array's head.
26 // A special case - if we first encounter a Tag with a Stop and a zero digit,
27 // then this is already the head.
28 //
29 // For example:
30 // In case of 2 bits:
31 //
32 // Tags:
33 // x0 - binary digit 0
34 // x1 - binary digit 1
35 // 1x - stop and calculate (s)
36 //
37 // Array:
38 //         .---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.
39 // head -> |s0 |s1 | 0 |s1 | 0 | 0 |s1 | 1 | 1 |s1 | 0 | 1 | 0 |s1 | 0 | 1 |
40 //         '---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'
41 //             |-1 |-2     |-4         |-7         |-10            |-14
42 //          <_ |   |       |           |           |               |
43 //          <_____ |       |           |           |               |
44 //          <_____________ |           |           |               |
45 //          <_________________________ |           |               |
46 //          <_____________________________________ |               |
47 //          <_____________________________________________________ |
48 //
49 //
50 // In case of 3 bits:
51 //
52 // Tags:
53 // x00 - quaternary digit 0
54 // x01 - quaternary digit 1
55 // x10 - quaternary digit 2
56 // x11 - quaternary digit 3
57 // 1xy - stop and calculate (s)
58 //
59 // Array:
60 //         .---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.
61 // head -> |s0 |s1 |s2 |s3 | 0 |s1 | 2 |s1 | 0 |s2 | 2 |s2 | 0 |s3 | 2 |s3 |
62 //         '---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'
63 //             |-1 |-2 |-3 |-4     |-6     |-8     |-10    |-12    |-14    |-16
64 //          <_ |   |   |   |       |       |       |       |       |       |
65 //          <_____ |   |   |       |       |       |       |       |       |
66 //          <_________ |   |       |       |       |       |       |       |
67 //          <_____________ |       |       |       |       |       |       |
68 //          <_____________________ |       |       |       |       |       |
69 //          <_____________________________ |       |       |       |       |
70 //          <_____________________________________ |       |       |       |
71 //          <_____________________________________________ |       |       |
72 //          <_____________________________________________________ |       |
73 //          <_____________________________________________________________ |
74 //
75 //
76 // The API introduce 2 functions:
77 // 1. fillWaymarks
78 // 2. followWaymarks
79 //
80 // Example:
81 //   int N = 10;
82 //   int M = 5;
83 //   int **A = new int *[N + M];   // Define the array.
84 //   for (int I = 0; I < N + M; ++I)
85 //     A[I] = new int(I);
86 //
87 //   fillWaymarks(A, A + N);       // Set the waymarks for the first N elements
88 //                                 // of the array.
89 //                                 // Note that it must be done AFTER we fill
90 //                                 // the array's elements.
91 //
92 //   ...                           // Elements which are not in the range
93 //                                 // [A, A+N) will not be marked, and we won't
94 //                                 // be able to call followWaymarks on them.
95 //
96 //   ...                           // Elements which will be changed after the
97 //                                 // call to fillWaymarks, will have to be
98 //                                 // retagged.
99 //
100 //   fillWaymarks(A + N, A + N + M, N); // Set the waymarks of the remaining M
101 //                                      // elements.
102 //   ...
103 //   int **It = A + N + 1;
104 //   int **B = followWaymarks(It); // Find the head of the array containing It.
105 //   assert(B == A);
106 //
107 //===----------------------------------------------------------------------===//
108 
109 #ifndef LLVM_ADT_WAYMARKING_H
110 #define LLVM_ADT_WAYMARKING_H
111 
112 #include "llvm/ADT/STLExtras.h"
113 #include "llvm/Support/PointerLikeTypeTraits.h"
114 
115 namespace llvm {
116 
117 namespace detail {
118 
119 template <unsigned NumBits> struct WaymarkingTraits {
120   enum : unsigned {
121     // The number of bits of a Waymarking Tag.
122     NUM_BITS = NumBits,
123 
124     // A Tag is composed from a Mark and a Stop mask.
125     MARK_SIZE = NUM_BITS - 1,
126     STOP_MASK = (1 << MARK_SIZE),
127     MARK_MASK = (STOP_MASK - 1),
128     TAG_MASK = (MARK_MASK | STOP_MASK),
129 
130     // The number of pre-computed tags (for fast fill).
131     NUM_STATIC_TAGS = 32
132   };
133 
134 private:
135   // Add a new tag, calculated from Count and Stop, to the Vals pack, while
136   // continuing recursively to decrease Len down to 0.
137   template <unsigned Len, bool Stop, unsigned Count, uint8_t... Vals>
138   struct AddTag;
139 
140   // Delegate to the specialized AddTag according to the need of a Stop mask.
141   template <unsigned Len, unsigned Count, uint8_t... Vals> struct GenTag {
142     typedef
143         typename AddTag<Len, (Count <= MARK_MASK), Count, Vals...>::Xdata Xdata;
144   };
145 
146   // Start adding tags while calculating the next Count, which is actually the
147   // number of already calculated tags (equivalent to the position in the
148   // array).
149   template <unsigned Len, uint8_t... Vals> struct GenOffset {
150     typedef typename GenTag<Len, sizeof...(Vals), Vals...>::Xdata Xdata;
151   };
152 
153   // Add the tag and remove it from Count.
154   template <unsigned Len, unsigned Count, uint8_t... Vals>
155   struct AddTag<Len, false, Count, Vals...> {
156     typedef typename GenTag<Len - 1, (Count >> MARK_SIZE), Vals...,
157                             Count & MARK_MASK>::Xdata Xdata;
158   };
159 
160   // We have reached the end of this Count, so start with a new Count.
161   template <unsigned Len, unsigned Count, uint8_t... Vals>
162   struct AddTag<Len, true, Count, Vals...> {
163     typedef typename GenOffset<Len - 1, Vals...,
164                                (Count & MARK_MASK) | STOP_MASK>::Xdata Xdata;
165   };
166 
167   template <unsigned Count, uint8_t... Vals> struct TagsData {
168     // The remaining number for calculating the next tag, following the last one
169     // in Values.
170     static const unsigned Remain = Count;
171 
172     // The array of ordered pre-computed Tags.
173     static const uint8_t Values[sizeof...(Vals)];
174   };
175 
176   // Specialize the case when Len equals 0, as the recursion stop condition.
177   template <unsigned Count, uint8_t... Vals>
178   struct AddTag<0, false, Count, Vals...> {
179     typedef TagsData<Count, Vals...> Xdata;
180   };
181 
182   template <unsigned Count, uint8_t... Vals>
183   struct AddTag<0, true, Count, Vals...> {
184     typedef TagsData<Count, Vals...> Xdata;
185   };
186 
187 public:
188   typedef typename GenOffset<NUM_STATIC_TAGS>::Xdata Tags;
189 };
190 
191 template <unsigned NumBits>
192 template <unsigned Count, uint8_t... Vals>
193 const uint8_t WaymarkingTraits<NumBits>::TagsData<
194     Count, Vals...>::Values[sizeof...(Vals)] = {Vals...};
195 
196 } // end namespace detail
197 
198 /// This class is responsible for tagging (and retrieving the tag of) a given
199 /// element of type T.
200 template <class T, class WTraits = detail::WaymarkingTraits<
201                        PointerLikeTypeTraits<T>::NumLowBitsAvailable>>
202 struct Waymarker {
203   using Traits = WTraits;
204   static void setWaymark(T &N, unsigned Tag) { N.setWaymark(Tag); }
205   static unsigned getWaymark(const T &N) { return N.getWaymark(); }
206 };
207 
208 template <class T, class WTraits> struct Waymarker<T *, WTraits> {
209   using Traits = WTraits;
210   static void setWaymark(T *&N, unsigned Tag) {
211     reinterpret_cast<uintptr_t &>(N) |= static_cast<uintptr_t>(Tag);
212   }
213   static unsigned getWaymark(const T *N) {
214     return static_cast<unsigned>(reinterpret_cast<uintptr_t>(N)) &
215            Traits::TAG_MASK;
216   }
217 };
218 
219 /// Sets up the waymarking algorithm's tags for a given range [Begin, End).
220 ///
221 /// \param Begin The beginning of the range to mark with tags (inclusive).
222 /// \param End The ending of the range to mark with tags (exclusive).
223 /// \param Offset The position in the supposed tags array from which to start
224 /// marking the given range.
225 template <class TIter, class Marker = Waymarker<
226                            typename std::iterator_traits<TIter>::value_type>>
227 void fillWaymarks(TIter Begin, TIter End, size_t Offset = 0) {
228   if (Begin == End)
229     return;
230 
231   size_t Count = Marker::Traits::Tags::Remain;
232   if (Offset <= Marker::Traits::NUM_STATIC_TAGS) {
233     // Start by filling the pre-calculated tags, starting from the given offset.
234     while (Offset != Marker::Traits::NUM_STATIC_TAGS) {
235       Marker::setWaymark(*Begin, Marker::Traits::Tags::Values[Offset]);
236 
237       ++Offset;
238       ++Begin;
239 
240       if (Begin == End)
241         return;
242     }
243   } else {
244     // The given offset is larger than the number of pre-computed tags, so we
245     // must do it the hard way.
246     // Calculate the next remaining Count, as if we have filled the tags up to
247     // the given offset.
248     size_t Off = Marker::Traits::NUM_STATIC_TAGS;
249     do {
250       ++Off;
251 
252       unsigned Tag = Count & Marker::Traits::MARK_MASK;
253 
254       // If the count can fit into the tag, then the counting must stop.
255       if (Count <= Marker::Traits::MARK_MASK) {
256         Tag |= Marker::Traits::STOP_MASK;
257         Count = Off;
258       } else
259         Count >>= Marker::Traits::MARK_SIZE;
260     } while (Off != Offset);
261   }
262 
263   // By now, we have the matching remaining Count for the current offset.
264   do {
265     ++Offset;
266 
267     unsigned Tag = Count & Marker::Traits::MARK_MASK;
268 
269     // If the count can fit into the tag, then the counting must stop.
270     if (Count <= Marker::Traits::MARK_MASK) {
271       Tag |= Marker::Traits::STOP_MASK;
272       Count = Offset;
273     } else
274       Count >>= Marker::Traits::MARK_SIZE;
275 
276     Marker::setWaymark(*Begin, Tag);
277     ++Begin;
278   } while (Begin != End);
279 }
280 
281 /// Sets up the waymarking algorithm's tags for a given range.
282 ///
283 /// \param Range The range to mark with tags.
284 /// \param Offset The position in the supposed tags array from which to start
285 /// marking the given range.
286 template <typename R, class Marker = Waymarker<typename std::remove_reference<
287                           decltype(*std::begin(std::declval<R &>()))>::type>>
288 void fillWaymarks(R &&Range, size_t Offset = 0) {
289   return fillWaymarks<decltype(std::begin(std::declval<R &>())), Marker>(
290       adl_begin(Range), adl_end(Range), Offset);
291 }
292 
293 /// Retrieves the element marked with tag of only STOP_MASK, by following the
294 /// waymarks. This is the first element in a range passed to a previous call to
295 /// \c fillWaymarks with \c Offset 0.
296 ///
297 /// For the trivial usage of calling \c fillWaymarks(Array), and \I is an
298 /// iterator inside \c Array, this function retrieves the head of \c Array, by
299 /// following the waymarks.
300 ///
301 /// \param I The iterator into an array which was marked by the waymarking tags
302 /// (by a previous call to \c fillWaymarks).
303 template <class TIter, class Marker = Waymarker<
304                            typename std::iterator_traits<TIter>::value_type>>
305 TIter followWaymarks(TIter I) {
306   unsigned Tag;
307   do
308     Tag = Marker::getWaymark(*I--);
309   while (!(Tag & Marker::Traits::STOP_MASK));
310 
311   // Special case for the first Use.
312   if (Tag != Marker::Traits::STOP_MASK) {
313     ptrdiff_t Offset = Tag & Marker::Traits::MARK_MASK;
314     while (!((Tag = Marker::getWaymark(*I)) & Marker::Traits::STOP_MASK)) {
315       Offset = (Offset << Marker::Traits::MARK_SIZE) + Tag;
316       --I;
317     }
318     I -= Offset;
319   }
320   return ++I;
321 }
322 
323 } // end namespace llvm
324 
325 #endif // LLVM_ADT_WAYMARKING_H
326