aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/llvm12/include/llvm/ADT/Waymarking.h
blob: e71ab24f14bc4790fb5ad6fc31a7425e9148a88e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
#pragma once

#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif

//===- Waymarking.h - Array waymarking algorithm ----------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Utility to backtrace an array's head, from a pointer into it. For the
// backtrace to work, we use "Waymarks", which are special tags embedded into
// the array's elements.
//
// A Tag of n-bits (in size) is composed as follows:
//
// bits: |   n-1   |             n-2 ... 0              |
//       .---------.------------------------------------.
//       |Stop Mask|(2^(n-1))-ary numeric system - digit|
//       '---------'------------------------------------'
//
// Backtracing is done as follows:
// Walk back (starting from a given pointer to an element into the array), until
// a tag with a "Stop Mask" is reached. Then start calculating the "Offset" from
// the array's head, by picking up digits along the way, until another stop is
// reached. The "Offset" is then subtracted from the current pointer, and the
// result is the array's head.
// A special case - if we first encounter a Tag with a Stop and a zero digit,
// then this is already the head.
//
// For example:
// In case of 2 bits:
//
// Tags:
// x0 - binary digit 0
// x1 - binary digit 1
// 1x - stop and calculate (s)
//
// Array:
//         .---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.
// head -> |s0 |s1 | 0 |s1 | 0 | 0 |s1 | 1 | 1 |s1 | 0 | 1 | 0 |s1 | 0 | 1 |
//         '---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'
//             |-1 |-2     |-4         |-7         |-10            |-14
//          <_ |   |       |           |           |               |
//          <_____ |       |           |           |               |
//          <_____________ |           |           |               |
//          <_________________________ |           |               |
//          <_____________________________________ |               |
//          <_____________________________________________________ |
//
//
// In case of 3 bits:
//
// Tags:
// x00 - quaternary digit 0
// x01 - quaternary digit 1
// x10 - quaternary digit 2
// x11 - quaternary digit 3
// 1xy - stop and calculate (s)
//
// Array:
//         .---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.
// head -> |s0 |s1 |s2 |s3 | 0 |s1 | 2 |s1 | 0 |s2 | 2 |s2 | 0 |s3 | 2 |s3 |
//         '---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'
//             |-1 |-2 |-3 |-4     |-6     |-8     |-10    |-12    |-14    |-16
//          <_ |   |   |   |       |       |       |       |       |       |
//          <_____ |   |   |       |       |       |       |       |       |
//          <_________ |   |       |       |       |       |       |       |
//          <_____________ |       |       |       |       |       |       |
//          <_____________________ |       |       |       |       |       |
//          <_____________________________ |       |       |       |       |
//          <_____________________________________ |       |       |       |
//          <_____________________________________________ |       |       |
//          <_____________________________________________________ |       |
//          <_____________________________________________________________ |
//
//
// The API introduce 2 functions:
// 1. fillWaymarks
// 2. followWaymarks
//
// Example:
//   int N = 10;
//   int M = 5;
//   int **A = new int *[N + M];   // Define the array.
//   for (int I = 0; I < N + M; ++I)
//     A[I] = new int(I);
//
//   fillWaymarks(A, A + N);       // Set the waymarks for the first N elements
//                                 // of the array.
//                                 // Note that it must be done AFTER we fill
//                                 // the array's elements.
//
//   ...                           // Elements which are not in the range
//                                 // [A, A+N) will not be marked, and we won't
//                                 // be able to call followWaymarks on them.
//
//   ...                           // Elements which will be changed after the
//                                 // call to fillWaymarks, will have to be
//                                 // retagged.
//
//   fillWaymarks(A + N, A + N + M, N); // Set the waymarks of the remaining M
//                                      // elements.
//   ...
//   int **It = A + N + 1;
//   int **B = followWaymarks(It); // Find the head of the array containing It.
//   assert(B == A);
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_ADT_WAYMARKING_H
#define LLVM_ADT_WAYMARKING_H

#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/PointerLikeTypeTraits.h"

namespace llvm {

namespace detail {

template <unsigned NumBits> struct WaymarkingTraits {
  enum : unsigned {
    // The number of bits of a Waymarking Tag.
    NUM_BITS = NumBits,

    // A Tag is composed from a Mark and a Stop mask.
    MARK_SIZE = NUM_BITS - 1,
    STOP_MASK = (1 << MARK_SIZE),
    MARK_MASK = (STOP_MASK - 1),
    TAG_MASK = (MARK_MASK | STOP_MASK),

    // The number of pre-computed tags (for fast fill).
    NUM_STATIC_TAGS = 32
  };

private:
  // Add a new tag, calculated from Count and Stop, to the Vals pack, while
  // continuing recursively to decrease Len down to 0.
  template <unsigned Len, bool Stop, unsigned Count, uint8_t... Vals>
  struct AddTag;

  // Delegate to the specialized AddTag according to the need of a Stop mask.
  template <unsigned Len, unsigned Count, uint8_t... Vals> struct GenTag {
    typedef
        typename AddTag<Len, (Count <= MARK_MASK), Count, Vals...>::Xdata Xdata;
  };

  // Start adding tags while calculating the next Count, which is actually the
  // number of already calculated tags (equivalent to the position in the
  // array).
  template <unsigned Len, uint8_t... Vals> struct GenOffset {
    typedef typename GenTag<Len, sizeof...(Vals), Vals...>::Xdata Xdata;
  };

  // Add the tag and remove it from Count.
  template <unsigned Len, unsigned Count, uint8_t... Vals>
  struct AddTag<Len, false, Count, Vals...> {
    typedef typename GenTag<Len - 1, (Count >> MARK_SIZE), Vals...,
                            Count & MARK_MASK>::Xdata Xdata;
  };

  // We have reached the end of this Count, so start with a new Count.
  template <unsigned Len, unsigned Count, uint8_t... Vals>
  struct AddTag<Len, true, Count, Vals...> {
    typedef typename GenOffset<Len - 1, Vals...,
                               (Count & MARK_MASK) | STOP_MASK>::Xdata Xdata;
  };

  template <unsigned Count, uint8_t... Vals> struct TagsData {
    // The remaining number for calculating the next tag, following the last one
    // in Values.
    static const unsigned Remain = Count;

    // The array of ordered pre-computed Tags.
    static const uint8_t Values[sizeof...(Vals)];
  };

  // Specialize the case when Len equals 0, as the recursion stop condition.
  template <unsigned Count, uint8_t... Vals>
  struct AddTag<0, false, Count, Vals...> {
    typedef TagsData<Count, Vals...> Xdata;
  };

  template <unsigned Count, uint8_t... Vals>
  struct AddTag<0, true, Count, Vals...> {
    typedef TagsData<Count, Vals...> Xdata;
  };

public:
  typedef typename GenOffset<NUM_STATIC_TAGS>::Xdata Tags;
};

template <unsigned NumBits>
template <unsigned Count, uint8_t... Vals>
const uint8_t WaymarkingTraits<NumBits>::TagsData<
    Count, Vals...>::Values[sizeof...(Vals)] = {Vals...};

} // end namespace detail

/// This class is responsible for tagging (and retrieving the tag of) a given
/// element of type T.
template <class T, class WTraits = detail::WaymarkingTraits<
                       PointerLikeTypeTraits<T>::NumLowBitsAvailable>>
struct Waymarker {
  using Traits = WTraits;
  static void setWaymark(T &N, unsigned Tag) { N.setWaymark(Tag); }
  static unsigned getWaymark(const T &N) { return N.getWaymark(); }
};

template <class T, class WTraits> struct Waymarker<T *, WTraits> {
  using Traits = WTraits;
  static void setWaymark(T *&N, unsigned Tag) {
    reinterpret_cast<uintptr_t &>(N) |= static_cast<uintptr_t>(Tag);
  }
  static unsigned getWaymark(const T *N) {
    return static_cast<unsigned>(reinterpret_cast<uintptr_t>(N)) &
           Traits::TAG_MASK;
  }
};

/// Sets up the waymarking algorithm's tags for a given range [Begin, End).
///
/// \param Begin The beginning of the range to mark with tags (inclusive).
/// \param End The ending of the range to mark with tags (exclusive).
/// \param Offset The position in the supposed tags array from which to start
/// marking the given range.
template <class TIter, class Marker = Waymarker<
                           typename std::iterator_traits<TIter>::value_type>>
void fillWaymarks(TIter Begin, TIter End, size_t Offset = 0) {
  if (Begin == End)
    return;

  size_t Count = Marker::Traits::Tags::Remain;
  if (Offset <= Marker::Traits::NUM_STATIC_TAGS) {
    // Start by filling the pre-calculated tags, starting from the given offset.
    while (Offset != Marker::Traits::NUM_STATIC_TAGS) {
      Marker::setWaymark(*Begin, Marker::Traits::Tags::Values[Offset]);

      ++Offset;
      ++Begin;

      if (Begin == End)
        return;
    }
  } else {
    // The given offset is larger than the number of pre-computed tags, so we
    // must do it the hard way.
    // Calculate the next remaining Count, as if we have filled the tags up to
    // the given offset.
    size_t Off = Marker::Traits::NUM_STATIC_TAGS;
    do {
      ++Off;

      unsigned Tag = Count & Marker::Traits::MARK_MASK;

      // If the count can fit into the tag, then the counting must stop.
      if (Count <= Marker::Traits::MARK_MASK) {
        Tag |= Marker::Traits::STOP_MASK;
        Count = Off;
      } else
        Count >>= Marker::Traits::MARK_SIZE;
    } while (Off != Offset);
  }

  // By now, we have the matching remaining Count for the current offset.
  do {
    ++Offset;

    unsigned Tag = Count & Marker::Traits::MARK_MASK;

    // If the count can fit into the tag, then the counting must stop.
    if (Count <= Marker::Traits::MARK_MASK) {
      Tag |= Marker::Traits::STOP_MASK;
      Count = Offset;
    } else
      Count >>= Marker::Traits::MARK_SIZE;

    Marker::setWaymark(*Begin, Tag);
    ++Begin;
  } while (Begin != End);
}

/// Sets up the waymarking algorithm's tags for a given range.
///
/// \param Range The range to mark with tags.
/// \param Offset The position in the supposed tags array from which to start
/// marking the given range.
template <typename R, class Marker = Waymarker<typename std::remove_reference<
                          decltype(*std::begin(std::declval<R &>()))>::type>>
void fillWaymarks(R &&Range, size_t Offset = 0) {
  return fillWaymarks<decltype(std::begin(std::declval<R &>())), Marker>(
      adl_begin(Range), adl_end(Range), Offset);
}

/// Retrieves the element marked with tag of only STOP_MASK, by following the
/// waymarks. This is the first element in a range passed to a previous call to
/// \c fillWaymarks with \c Offset 0.
///
/// For the trivial usage of calling \c fillWaymarks(Array), and \I is an
/// iterator inside \c Array, this function retrieves the head of \c Array, by
/// following the waymarks.
///
/// \param I The iterator into an array which was marked by the waymarking tags
/// (by a previous call to \c fillWaymarks).
template <class TIter, class Marker = Waymarker<
                           typename std::iterator_traits<TIter>::value_type>>
TIter followWaymarks(TIter I) {
  unsigned Tag;
  do
    Tag = Marker::getWaymark(*I--);
  while (!(Tag & Marker::Traits::STOP_MASK));

  // Special case for the first Use.
  if (Tag != Marker::Traits::STOP_MASK) {
    ptrdiff_t Offset = Tag & Marker::Traits::MARK_MASK;
    while (!((Tag = Marker::getWaymark(*I)) & Marker::Traits::STOP_MASK)) {
      Offset = (Offset << Marker::Traits::MARK_SIZE) + Tag;
      --I;
    }
    I -= Offset;
  }
  return ++I;
}

} // end namespace llvm

#endif // LLVM_ADT_WAYMARKING_H

#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif