1 //===-- xray_segmented_array.h ---------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This file is a part of XRay, a dynamic runtime instrumentation system. 11 // 12 // Defines the implementation of a segmented array, with fixed-size segments 13 // backing the segments. 14 // 15 //===----------------------------------------------------------------------===// 16 #ifndef XRAY_SEGMENTED_ARRAY_H 17 #define XRAY_SEGMENTED_ARRAY_H 18 19 #include "sanitizer_common/sanitizer_allocator.h" 20 #include "xray_allocator.h" 21 #include "xray_utils.h" 22 #include <cassert> 23 #include <type_traits> 24 #include <utility> 25 26 namespace __xray { 27 28 /// The Array type provides an interface similar to std::vector<...> but does 29 /// not shrink in size. Once constructed, elements can be appended but cannot be 30 /// removed. The implementation is heavily dependent on the contract provided by 31 /// the Allocator type, in that all memory will be released when the Allocator 32 /// is destroyed. When an Array is destroyed, it will destroy elements in the 33 /// backing store but will not free the memory. 34 template <class T> class Array { 35 struct SegmentBase { 36 SegmentBase *Prev; 37 SegmentBase *Next; 38 }; 39 40 // We want each segment of the array to be cache-line aligned, and elements of 41 // the array be offset from the beginning of the segment. 42 struct Segment : SegmentBase { 43 char Data[1]; 44 }; 45 46 public: 47 // Each segment of the array will be laid out with the following assumptions: 48 // 49 // - Each segment will be on a cache-line address boundary (kCacheLineSize 50 // aligned). 51 // 52 // - The elements will be accessed through an aligned pointer, dependent on 53 // the alignment of T. 54 // 55 // - Each element is at least two-pointers worth from the beginning of the 56 // Segment, aligned properly, and the rest of the elements are accessed 57 // through appropriate alignment. 58 // 59 // We then compute the size of the segment to follow this logic: 60 // 61 // - Compute the number of elements that can fit within 62 // kCacheLineSize-multiple segments, minus the size of two pointers. 63 // 64 // - Request cacheline-multiple sized elements from the allocator. 65 static constexpr size_t AlignedElementStorageSize = 66 sizeof(typename std::aligned_storage<sizeof(T), alignof(T)>::type); 67 68 static constexpr size_t SegmentSize = 69 nearest_boundary(sizeof(Segment) + next_pow2(sizeof(T)), kCacheLineSize); 70 71 using AllocatorType = Allocator<SegmentSize>; 72 73 static constexpr size_t ElementsPerSegment = 74 (SegmentSize - sizeof(Segment)) / next_pow2(sizeof(T)); 75 76 static_assert(ElementsPerSegment > 0, 77 "Must have at least 1 element per segment."); 78 79 static SegmentBase SentinelSegment; 80 81 using size_type = size_t; 82 83 private: 84 AllocatorType *Alloc; 85 SegmentBase *Head = &SentinelSegment; 86 SegmentBase *Tail = &SentinelSegment; 87 size_t Size = 0; 88 89 // Here we keep track of segments in the freelist, to allow us to re-use 90 // segments when elements are trimmed off the end. 91 SegmentBase *Freelist = &SentinelSegment; 92 93 Segment *NewSegment() XRAY_NEVER_INSTRUMENT { 94 // We need to handle the case in which enough elements have been trimmed to 95 // allow us to re-use segments we've allocated before. For this we look into 96 // the Freelist, to see whether we need to actually allocate new blocks or 97 // just re-use blocks we've already seen before. 98 if (Freelist != &SentinelSegment) { 99 auto *FreeSegment = Freelist; 100 Freelist = FreeSegment->Next; 101 FreeSegment->Next = &SentinelSegment; 102 Freelist->Prev = &SentinelSegment; 103 return static_cast<Segment *>(FreeSegment); 104 } 105 106 auto SegmentBlock = Alloc->Allocate(); 107 if (SegmentBlock.Data == nullptr) 108 return nullptr; 109 110 // Placement-new the Segment element at the beginning of the SegmentBlock. 111 auto S = reinterpret_cast<Segment *>(SegmentBlock.Data); 112 new (S) SegmentBase{&SentinelSegment, &SentinelSegment}; 113 return S; 114 } 115 116 Segment *InitHeadAndTail() XRAY_NEVER_INSTRUMENT { 117 DCHECK_EQ(Head, &SentinelSegment); 118 DCHECK_EQ(Tail, &SentinelSegment); 119 auto Segment = NewSegment(); 120 if (Segment == nullptr) 121 return nullptr; 122 DCHECK_EQ(Segment->Next, &SentinelSegment); 123 DCHECK_EQ(Segment->Prev, &SentinelSegment); 124 Head = Tail = static_cast<SegmentBase *>(Segment); 125 return Segment; 126 } 127 128 Segment *AppendNewSegment() XRAY_NEVER_INSTRUMENT { 129 auto S = NewSegment(); 130 if (S == nullptr) 131 return nullptr; 132 DCHECK_NE(Tail, &SentinelSegment); 133 DCHECK_EQ(Tail->Next, &SentinelSegment); 134 DCHECK_EQ(S->Prev, &SentinelSegment); 135 DCHECK_EQ(S->Next, &SentinelSegment); 136 Tail->Next = S; 137 S->Prev = Tail; 138 Tail = S; 139 return static_cast<Segment *>(Tail); 140 } 141 142 // This Iterator models a BidirectionalIterator. 143 template <class U> class Iterator { 144 SegmentBase *S = &SentinelSegment; 145 size_t Offset = 0; 146 size_t Size = 0; 147 148 public: 149 Iterator(SegmentBase *IS, size_t Off, size_t S) XRAY_NEVER_INSTRUMENT 150 : S(IS), 151 Offset(Off), 152 Size(S) {} 153 Iterator(const Iterator &) NOEXCEPT XRAY_NEVER_INSTRUMENT = default; 154 Iterator() NOEXCEPT XRAY_NEVER_INSTRUMENT = default; 155 Iterator(Iterator &&) NOEXCEPT XRAY_NEVER_INSTRUMENT = default; 156 Iterator &operator=(const Iterator &) XRAY_NEVER_INSTRUMENT = default; 157 Iterator &operator=(Iterator &&) XRAY_NEVER_INSTRUMENT = default; 158 ~Iterator() XRAY_NEVER_INSTRUMENT = default; 159 160 Iterator &operator++() XRAY_NEVER_INSTRUMENT { 161 if (++Offset % ElementsPerSegment || Offset == Size) 162 return *this; 163 164 // At this point, we know that Offset % N == 0, so we must advance the 165 // segment pointer. 166 DCHECK_EQ(Offset % ElementsPerSegment, 0); 167 DCHECK_NE(Offset, Size); 168 DCHECK_NE(S, &SentinelSegment); 169 DCHECK_NE(S->Next, &SentinelSegment); 170 S = S->Next; 171 DCHECK_NE(S, &SentinelSegment); 172 return *this; 173 } 174 175 Iterator &operator--() XRAY_NEVER_INSTRUMENT { 176 DCHECK_NE(S, &SentinelSegment); 177 DCHECK_GT(Offset, 0); 178 179 auto PreviousOffset = Offset--; 180 if (PreviousOffset != Size && PreviousOffset % ElementsPerSegment == 0) { 181 DCHECK_NE(S->Prev, &SentinelSegment); 182 S = S->Prev; 183 } 184 185 return *this; 186 } 187 188 Iterator operator++(int) XRAY_NEVER_INSTRUMENT { 189 Iterator Copy(*this); 190 ++(*this); 191 return Copy; 192 } 193 194 Iterator operator--(int) XRAY_NEVER_INSTRUMENT { 195 Iterator Copy(*this); 196 --(*this); 197 return Copy; 198 } 199 200 template <class V, class W> 201 friend bool operator==(const Iterator<V> &L, 202 const Iterator<W> &R) XRAY_NEVER_INSTRUMENT { 203 return L.S == R.S && L.Offset == R.Offset; 204 } 205 206 template <class V, class W> 207 friend bool operator!=(const Iterator<V> &L, 208 const Iterator<W> &R) XRAY_NEVER_INSTRUMENT { 209 return !(L == R); 210 } 211 212 U &operator*() const XRAY_NEVER_INSTRUMENT { 213 DCHECK_NE(S, &SentinelSegment); 214 auto RelOff = Offset % ElementsPerSegment; 215 216 // We need to compute the character-aligned pointer, offset from the 217 // segment's Data location to get the element in the position of Offset. 218 auto Base = static_cast<Segment *>(S)->Data; 219 auto AlignedOffset = Base + (RelOff * AlignedElementStorageSize); 220 return *reinterpret_cast<U *>(AlignedOffset); 221 } 222 223 U *operator->() const XRAY_NEVER_INSTRUMENT { return &(**this); } 224 }; 225 226 public: 227 explicit Array(AllocatorType &A) XRAY_NEVER_INSTRUMENT : Alloc(&A) {} 228 229 Array(const Array &) = delete; 230 Array(Array &&O) NOEXCEPT : Alloc(O.Alloc), 231 Head(O.Head), 232 Tail(O.Tail), 233 Size(O.Size) { 234 O.Head = &SentinelSegment; 235 O.Tail = &SentinelSegment; 236 O.Size = 0; 237 } 238 239 bool empty() const XRAY_NEVER_INSTRUMENT { return Size == 0; } 240 241 AllocatorType &allocator() const XRAY_NEVER_INSTRUMENT { 242 DCHECK_NE(Alloc, nullptr); 243 return *Alloc; 244 } 245 246 size_t size() const XRAY_NEVER_INSTRUMENT { return Size; } 247 248 T *Append(const T &E) XRAY_NEVER_INSTRUMENT { 249 if (UNLIKELY(Head == &SentinelSegment)) 250 if (InitHeadAndTail() == nullptr) 251 return nullptr; 252 253 auto Offset = Size % ElementsPerSegment; 254 if (UNLIKELY(Size != 0 && Offset == 0)) 255 if (AppendNewSegment() == nullptr) 256 return nullptr; 257 258 auto Base = static_cast<Segment *>(Tail)->Data; 259 auto AlignedOffset = Base + (Offset * AlignedElementStorageSize); 260 auto Position = reinterpret_cast<T *>(AlignedOffset); 261 *Position = E; 262 ++Size; 263 return Position; 264 } 265 266 template <class... Args> 267 T *AppendEmplace(Args &&... args) XRAY_NEVER_INSTRUMENT { 268 if (UNLIKELY(Head == &SentinelSegment)) 269 if (InitHeadAndTail() == nullptr) 270 return nullptr; 271 272 auto Offset = Size % ElementsPerSegment; 273 auto *LatestSegment = Tail; 274 if (UNLIKELY(Size != 0 && Offset == 0)) { 275 LatestSegment = AppendNewSegment(); 276 if (LatestSegment == nullptr) 277 return nullptr; 278 } 279 280 DCHECK_NE(Tail, &SentinelSegment); 281 auto Base = static_cast<Segment *>(LatestSegment)->Data; 282 auto AlignedOffset = Base + (Offset * AlignedElementStorageSize); 283 auto Position = reinterpret_cast<T *>(AlignedOffset); 284 285 // In-place construct at Position. 286 new (Position) T{std::forward<Args>(args)...}; 287 ++Size; 288 return reinterpret_cast<T *>(Position); 289 } 290 291 T &operator[](size_t Offset) const XRAY_NEVER_INSTRUMENT { 292 DCHECK_LE(Offset, Size); 293 // We need to traverse the array enough times to find the element at Offset. 294 auto S = Head; 295 while (Offset >= ElementsPerSegment) { 296 S = S->Next; 297 Offset -= ElementsPerSegment; 298 DCHECK_NE(S, &SentinelSegment); 299 } 300 auto Base = static_cast<Segment *>(S)->Data; 301 auto AlignedOffset = Base + (Offset * AlignedElementStorageSize); 302 auto Position = reinterpret_cast<T *>(AlignedOffset); 303 return *reinterpret_cast<T *>(Position); 304 } 305 306 T &front() const XRAY_NEVER_INSTRUMENT { 307 DCHECK_NE(Head, &SentinelSegment); 308 DCHECK_NE(Size, 0u); 309 return *begin(); 310 } 311 312 T &back() const XRAY_NEVER_INSTRUMENT { 313 DCHECK_NE(Tail, &SentinelSegment); 314 DCHECK_NE(Size, 0u); 315 auto It = end(); 316 --It; 317 return *It; 318 } 319 320 template <class Predicate> 321 T *find_element(Predicate P) const XRAY_NEVER_INSTRUMENT { 322 if (empty()) 323 return nullptr; 324 325 auto E = end(); 326 for (auto I = begin(); I != E; ++I) 327 if (P(*I)) 328 return &(*I); 329 330 return nullptr; 331 } 332 333 /// Remove N Elements from the end. This leaves the blocks behind, and not 334 /// require allocation of new blocks for new elements added after trimming. 335 void trim(size_t Elements) XRAY_NEVER_INSTRUMENT { 336 if (Elements == 0) 337 return; 338 339 auto OldSize = Size; 340 Elements = Elements >= Size ? Size : Elements; 341 Size -= Elements; 342 343 DCHECK_NE(Head, &SentinelSegment); 344 DCHECK_NE(Tail, &SentinelSegment); 345 346 for (auto SegmentsToTrim = (nearest_boundary(OldSize, ElementsPerSegment) - 347 nearest_boundary(Size, ElementsPerSegment)) / 348 ElementsPerSegment; 349 SegmentsToTrim > 0; --SegmentsToTrim) { 350 351 // We want to short-circuit if the trace is already empty. 352 if (Head == &SentinelSegment && Head == Tail) 353 return; 354 355 // Put the tail into the Freelist. 356 auto *FreeSegment = Tail; 357 Tail = Tail->Prev; 358 if (Tail == &SentinelSegment) 359 Head = Tail; 360 else 361 Tail->Next = &SentinelSegment; 362 363 DCHECK_EQ(Tail->Next, &SentinelSegment); 364 FreeSegment->Next = Freelist; 365 FreeSegment->Prev = &SentinelSegment; 366 if (Freelist != &SentinelSegment) 367 Freelist->Prev = FreeSegment; 368 Freelist = FreeSegment; 369 } 370 } 371 372 // Provide iterators. 373 Iterator<T> begin() const XRAY_NEVER_INSTRUMENT { 374 return Iterator<T>(Head, 0, Size); 375 } 376 Iterator<T> end() const XRAY_NEVER_INSTRUMENT { 377 return Iterator<T>(Tail, Size, Size); 378 } 379 Iterator<const T> cbegin() const XRAY_NEVER_INSTRUMENT { 380 return Iterator<const T>(Head, 0, Size); 381 } 382 Iterator<const T> cend() const XRAY_NEVER_INSTRUMENT { 383 return Iterator<const T>(Tail, Size, Size); 384 } 385 }; 386 387 // We need to have this storage definition out-of-line so that the compiler can 388 // ensure that storage for the SentinelSegment is defined and has a single 389 // address. 390 template <class T> 391 typename Array<T>::SegmentBase Array<T>::SentinelSegment{ 392 &Array<T>::SentinelSegment, &Array<T>::SentinelSegment}; 393 394 } // namespace __xray 395 396 #endif // XRAY_SEGMENTED_ARRAY_H 397