]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/compiler-rt/lib/xray/xray_segmented_array.h
Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp
[FreeBSD/FreeBSD.git] / contrib / compiler-rt / lib / xray / xray_segmented_array.h
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 Segment {
36     Segment *Prev;
37     Segment *Next;
38     char Data[1];
39   };
40
41 public:
42   // Each segment of the array will be laid out with the following assumptions:
43   //
44   //   - Each segment will be on a cache-line address boundary (kCacheLineSize
45   //     aligned).
46   //
47   //   - The elements will be accessed through an aligned pointer, dependent on
48   //     the alignment of T.
49   //
50   //   - Each element is at least two-pointers worth from the beginning of the
51   //     Segment, aligned properly, and the rest of the elements are accessed
52   //     through appropriate alignment.
53   //
54   // We then compute the size of the segment to follow this logic:
55   //
56   //   - Compute the number of elements that can fit within
57   //     kCacheLineSize-multiple segments, minus the size of two pointers.
58   //
59   //   - Request cacheline-multiple sized elements from the allocator.
60   static constexpr uint64_t AlignedElementStorageSize =
61       sizeof(typename std::aligned_storage<sizeof(T), alignof(T)>::type);
62
63   static constexpr uint64_t SegmentControlBlockSize = sizeof(Segment *) * 2;
64
65   static constexpr uint64_t SegmentSize = nearest_boundary(
66       SegmentControlBlockSize + next_pow2(sizeof(T)), kCacheLineSize);
67
68   using AllocatorType = Allocator<SegmentSize>;
69
70   static constexpr uint64_t ElementsPerSegment =
71       (SegmentSize - SegmentControlBlockSize) / next_pow2(sizeof(T));
72
73   static_assert(ElementsPerSegment > 0,
74                 "Must have at least 1 element per segment.");
75
76   static Segment SentinelSegment;
77
78   using size_type = uint64_t;
79
80 private:
81   // This Iterator models a BidirectionalIterator.
82   template <class U> class Iterator {
83     Segment *S = &SentinelSegment;
84     uint64_t Offset = 0;
85     uint64_t Size = 0;
86
87   public:
88     Iterator(Segment *IS, uint64_t Off, uint64_t S) XRAY_NEVER_INSTRUMENT
89         : S(IS),
90           Offset(Off),
91           Size(S) {}
92     Iterator(const Iterator &) NOEXCEPT XRAY_NEVER_INSTRUMENT = default;
93     Iterator() NOEXCEPT XRAY_NEVER_INSTRUMENT = default;
94     Iterator(Iterator &&) NOEXCEPT XRAY_NEVER_INSTRUMENT = default;
95     Iterator &operator=(const Iterator &) XRAY_NEVER_INSTRUMENT = default;
96     Iterator &operator=(Iterator &&) XRAY_NEVER_INSTRUMENT = default;
97     ~Iterator() XRAY_NEVER_INSTRUMENT = default;
98
99     Iterator &operator++() XRAY_NEVER_INSTRUMENT {
100       if (++Offset % ElementsPerSegment || Offset == Size)
101         return *this;
102
103       // At this point, we know that Offset % N == 0, so we must advance the
104       // segment pointer.
105       DCHECK_EQ(Offset % ElementsPerSegment, 0);
106       DCHECK_NE(Offset, Size);
107       DCHECK_NE(S, &SentinelSegment);
108       DCHECK_NE(S->Next, &SentinelSegment);
109       S = S->Next;
110       DCHECK_NE(S, &SentinelSegment);
111       return *this;
112     }
113
114     Iterator &operator--() XRAY_NEVER_INSTRUMENT {
115       DCHECK_NE(S, &SentinelSegment);
116       DCHECK_GT(Offset, 0);
117
118       auto PreviousOffset = Offset--;
119       if (PreviousOffset != Size && PreviousOffset % ElementsPerSegment == 0) {
120         DCHECK_NE(S->Prev, &SentinelSegment);
121         S = S->Prev;
122       }
123
124       return *this;
125     }
126
127     Iterator operator++(int) XRAY_NEVER_INSTRUMENT {
128       Iterator Copy(*this);
129       ++(*this);
130       return Copy;
131     }
132
133     Iterator operator--(int) XRAY_NEVER_INSTRUMENT {
134       Iterator Copy(*this);
135       --(*this);
136       return Copy;
137     }
138
139     template <class V, class W>
140     friend bool operator==(const Iterator<V> &L,
141                            const Iterator<W> &R) XRAY_NEVER_INSTRUMENT {
142       return L.S == R.S && L.Offset == R.Offset;
143     }
144
145     template <class V, class W>
146     friend bool operator!=(const Iterator<V> &L,
147                            const Iterator<W> &R) XRAY_NEVER_INSTRUMENT {
148       return !(L == R);
149     }
150
151     U &operator*() const XRAY_NEVER_INSTRUMENT {
152       DCHECK_NE(S, &SentinelSegment);
153       auto RelOff = Offset % ElementsPerSegment;
154
155       // We need to compute the character-aligned pointer, offset from the
156       // segment's Data location to get the element in the position of Offset.
157       auto Base = &S->Data;
158       auto AlignedOffset = Base + (RelOff * AlignedElementStorageSize);
159       return *reinterpret_cast<U *>(AlignedOffset);
160     }
161
162     U *operator->() const XRAY_NEVER_INSTRUMENT { return &(**this); }
163   };
164
165   AllocatorType *Alloc;
166   Segment *Head;
167   Segment *Tail;
168
169   // Here we keep track of segments in the freelist, to allow us to re-use
170   // segments when elements are trimmed off the end.
171   Segment *Freelist;
172   uint64_t Size;
173
174   // ===============================
175   // In the following implementation, we work through the algorithms and the
176   // list operations using the following notation:
177   //
178   //   - pred(s) is the predecessor (previous node accessor) and succ(s) is
179   //     the successor (next node accessor).
180   //
181   //   - S is a sentinel segment, which has the following property:
182   //
183   //         pred(S) == succ(S) == S
184   //
185   //   - @ is a loop operator, which can imply pred(s) == s if it appears on
186   //     the left of s, or succ(s) == S if it appears on the right of s.
187   //
188   //   - sL <-> sR : means a bidirectional relation between sL and sR, which
189   //     means:
190   //
191   //         succ(sL) == sR && pred(SR) == sL
192   //
193   //   - sL -> sR : implies a unidirectional relation between sL and SR,
194   //     with the following properties:
195   //
196   //         succ(sL) == sR
197   //
198   //     sL <- sR : implies a unidirectional relation between sR and sL,
199   //     with the following properties:
200   //
201   //         pred(sR) == sL
202   //
203   // ===============================
204
205   Segment *NewSegment() XRAY_NEVER_INSTRUMENT {
206     // We need to handle the case in which enough elements have been trimmed to
207     // allow us to re-use segments we've allocated before. For this we look into
208     // the Freelist, to see whether we need to actually allocate new blocks or
209     // just re-use blocks we've already seen before.
210     if (Freelist != &SentinelSegment) {
211       // The current state of lists resemble something like this at this point:
212       //
213       //   Freelist: @S@<-f0->...<->fN->@S@
214       //                  ^ Freelist
215       //
216       // We want to perform a splice of `f0` from Freelist to a temporary list,
217       // which looks like:
218       //
219       //   Templist: @S@<-f0->@S@
220       //                  ^ FreeSegment
221       //
222       // Our algorithm preconditions are:
223       DCHECK_EQ(Freelist->Prev, &SentinelSegment);
224
225       // Then the algorithm we implement is:
226       //
227       //   SFS = Freelist
228       //   Freelist = succ(Freelist)
229       //   if (Freelist != S)
230       //     pred(Freelist) = S
231       //   succ(SFS) = S
232       //   pred(SFS) = S
233       //
234       auto *FreeSegment = Freelist;
235       Freelist = Freelist->Next;
236
237       // Note that we need to handle the case where Freelist is now pointing to
238       // S, which we don't want to be overwriting.
239       // TODO: Determine whether the cost of the branch is higher than the cost
240       // of the blind assignment.
241       if (Freelist != &SentinelSegment)
242         Freelist->Prev = &SentinelSegment;
243
244       FreeSegment->Next = &SentinelSegment;
245       FreeSegment->Prev = &SentinelSegment;
246
247       // Our postconditions are:
248       DCHECK_EQ(Freelist->Prev, &SentinelSegment);
249       DCHECK_NE(FreeSegment, &SentinelSegment);
250       return FreeSegment;
251     }
252
253     auto SegmentBlock = Alloc->Allocate();
254     if (SegmentBlock.Data == nullptr)
255       return nullptr;
256
257     // Placement-new the Segment element at the beginning of the SegmentBlock.
258     new (SegmentBlock.Data) Segment{&SentinelSegment, &SentinelSegment, {0}};
259     auto SB = reinterpret_cast<Segment *>(SegmentBlock.Data);
260     return SB;
261   }
262
263   Segment *InitHeadAndTail() XRAY_NEVER_INSTRUMENT {
264     DCHECK_EQ(Head, &SentinelSegment);
265     DCHECK_EQ(Tail, &SentinelSegment);
266     auto S = NewSegment();
267     if (S == nullptr)
268       return nullptr;
269     DCHECK_EQ(S->Next, &SentinelSegment);
270     DCHECK_EQ(S->Prev, &SentinelSegment);
271     DCHECK_NE(S, &SentinelSegment);
272     Head = S;
273     Tail = S;
274     DCHECK_EQ(Head, Tail);
275     DCHECK_EQ(Tail->Next, &SentinelSegment);
276     DCHECK_EQ(Tail->Prev, &SentinelSegment);
277     return S;
278   }
279
280   Segment *AppendNewSegment() XRAY_NEVER_INSTRUMENT {
281     auto S = NewSegment();
282     if (S == nullptr)
283       return nullptr;
284     DCHECK_NE(Tail, &SentinelSegment);
285     DCHECK_EQ(Tail->Next, &SentinelSegment);
286     DCHECK_EQ(S->Prev, &SentinelSegment);
287     DCHECK_EQ(S->Next, &SentinelSegment);
288     S->Prev = Tail;
289     Tail->Next = S;
290     Tail = S;
291     DCHECK_EQ(S, S->Prev->Next);
292     DCHECK_EQ(Tail->Next, &SentinelSegment);
293     return S;
294   }
295
296 public:
297   explicit Array(AllocatorType &A) XRAY_NEVER_INSTRUMENT
298       : Alloc(&A),
299         Head(&SentinelSegment),
300         Tail(&SentinelSegment),
301         Freelist(&SentinelSegment),
302         Size(0) {}
303
304   Array() XRAY_NEVER_INSTRUMENT : Alloc(nullptr),
305                                   Head(&SentinelSegment),
306                                   Tail(&SentinelSegment),
307                                   Freelist(&SentinelSegment),
308                                   Size(0) {}
309
310   Array(const Array &) = delete;
311   Array &operator=(const Array &) = delete;
312
313   Array(Array &&O) XRAY_NEVER_INSTRUMENT : Alloc(O.Alloc),
314                                            Head(O.Head),
315                                            Tail(O.Tail),
316                                            Freelist(O.Freelist),
317                                            Size(O.Size) {
318     O.Alloc = nullptr;
319     O.Head = &SentinelSegment;
320     O.Tail = &SentinelSegment;
321     O.Size = 0;
322     O.Freelist = &SentinelSegment;
323   }
324
325   Array &operator=(Array &&O) XRAY_NEVER_INSTRUMENT {
326     Alloc = O.Alloc;
327     O.Alloc = nullptr;
328     Head = O.Head;
329     O.Head = &SentinelSegment;
330     Tail = O.Tail;
331     O.Tail = &SentinelSegment;
332     Freelist = O.Freelist;
333     O.Freelist = &SentinelSegment;
334     Size = O.Size;
335     O.Size = 0;
336     return *this;
337   }
338
339   ~Array() XRAY_NEVER_INSTRUMENT {
340     for (auto &E : *this)
341       (&E)->~T();
342   }
343
344   bool empty() const XRAY_NEVER_INSTRUMENT { return Size == 0; }
345
346   AllocatorType &allocator() const XRAY_NEVER_INSTRUMENT {
347     DCHECK_NE(Alloc, nullptr);
348     return *Alloc;
349   }
350
351   uint64_t size() const XRAY_NEVER_INSTRUMENT { return Size; }
352
353   template <class... Args>
354   T *AppendEmplace(Args &&... args) XRAY_NEVER_INSTRUMENT {
355     DCHECK((Size == 0 && Head == &SentinelSegment && Head == Tail) ||
356            (Size != 0 && Head != &SentinelSegment && Tail != &SentinelSegment));
357     if (UNLIKELY(Head == &SentinelSegment)) {
358       auto R = InitHeadAndTail();
359       if (R == nullptr)
360         return nullptr;
361     }
362
363     DCHECK_NE(Head, &SentinelSegment);
364     DCHECK_NE(Tail, &SentinelSegment);
365
366     auto Offset = Size % ElementsPerSegment;
367     if (UNLIKELY(Size != 0 && Offset == 0))
368       if (AppendNewSegment() == nullptr)
369         return nullptr;
370
371     DCHECK_NE(Tail, &SentinelSegment);
372     auto Base = &Tail->Data;
373     auto AlignedOffset = Base + (Offset * AlignedElementStorageSize);
374     DCHECK_LE(AlignedOffset + sizeof(T),
375               reinterpret_cast<unsigned char *>(Base) + SegmentSize);
376
377     // In-place construct at Position.
378     new (AlignedOffset) T{std::forward<Args>(args)...};
379     ++Size;
380     return reinterpret_cast<T *>(AlignedOffset);
381   }
382
383   T *Append(const T &E) XRAY_NEVER_INSTRUMENT {
384     // FIXME: This is a duplication of AppenEmplace with the copy semantics
385     // explicitly used, as a work-around to GCC 4.8 not invoking the copy
386     // constructor with the placement new with braced-init syntax.
387     DCHECK((Size == 0 && Head == &SentinelSegment && Head == Tail) ||
388            (Size != 0 && Head != &SentinelSegment && Tail != &SentinelSegment));
389     if (UNLIKELY(Head == &SentinelSegment)) {
390       auto R = InitHeadAndTail();
391       if (R == nullptr)
392         return nullptr;
393     }
394
395     DCHECK_NE(Head, &SentinelSegment);
396     DCHECK_NE(Tail, &SentinelSegment);
397
398     auto Offset = Size % ElementsPerSegment;
399     if (UNLIKELY(Size != 0 && Offset == 0))
400       if (AppendNewSegment() == nullptr)
401         return nullptr;
402
403     DCHECK_NE(Tail, &SentinelSegment);
404     auto Base = &Tail->Data;
405     auto AlignedOffset = Base + (Offset * AlignedElementStorageSize);
406     DCHECK_LE(AlignedOffset + sizeof(T),
407               reinterpret_cast<unsigned char *>(Tail) + SegmentSize);
408
409     // In-place construct at Position.
410     new (AlignedOffset) T(E);
411     ++Size;
412     return reinterpret_cast<T *>(AlignedOffset);
413   }
414
415   T &operator[](uint64_t Offset) const XRAY_NEVER_INSTRUMENT {
416     DCHECK_LE(Offset, Size);
417     // We need to traverse the array enough times to find the element at Offset.
418     auto S = Head;
419     while (Offset >= ElementsPerSegment) {
420       S = S->Next;
421       Offset -= ElementsPerSegment;
422       DCHECK_NE(S, &SentinelSegment);
423     }
424     auto Base = &S->Data;
425     auto AlignedOffset = Base + (Offset * AlignedElementStorageSize);
426     auto Position = reinterpret_cast<T *>(AlignedOffset);
427     return *reinterpret_cast<T *>(Position);
428   }
429
430   T &front() const XRAY_NEVER_INSTRUMENT {
431     DCHECK_NE(Head, &SentinelSegment);
432     DCHECK_NE(Size, 0u);
433     return *begin();
434   }
435
436   T &back() const XRAY_NEVER_INSTRUMENT {
437     DCHECK_NE(Tail, &SentinelSegment);
438     DCHECK_NE(Size, 0u);
439     auto It = end();
440     --It;
441     return *It;
442   }
443
444   template <class Predicate>
445   T *find_element(Predicate P) const XRAY_NEVER_INSTRUMENT {
446     if (empty())
447       return nullptr;
448
449     auto E = end();
450     for (auto I = begin(); I != E; ++I)
451       if (P(*I))
452         return &(*I);
453
454     return nullptr;
455   }
456
457   /// Remove N Elements from the end. This leaves the blocks behind, and not
458   /// require allocation of new blocks for new elements added after trimming.
459   void trim(uint64_t Elements) XRAY_NEVER_INSTRUMENT {
460     auto OldSize = Size;
461     Elements = Elements > Size ? Size : Elements;
462     Size -= Elements;
463
464     // We compute the number of segments we're going to return from the tail by
465     // counting how many elements have been trimmed. Given the following:
466     //
467     // - Each segment has N valid positions, where N > 0
468     // - The previous size > current size
469     //
470     // To compute the number of segments to return, we need to perform the
471     // following calculations for the number of segments required given 'x'
472     // elements:
473     //
474     //   f(x) = {
475     //            x == 0          : 0
476     //          , 0 < x <= N      : 1
477     //          , N < x <= max    : x / N + (x % N ? 1 : 0)
478     //          }
479     //
480     // We can simplify this down to:
481     //
482     //   f(x) = {
483     //            x == 0          : 0,
484     //          , 0 < x <= max    : x / N + (x < N || x % N ? 1 : 0)
485     //          }
486     //
487     // And further down to:
488     //
489     //   f(x) = x ? x / N + (x < N || x % N ? 1 : 0) : 0
490     //
491     // We can then perform the following calculation `s` which counts the number
492     // of segments we need to remove from the end of the data structure:
493     //
494     //   s(p, c) = f(p) - f(c)
495     //
496     // If we treat p = previous size, and c = current size, and given the
497     // properties above, the possible range for s(...) is [0..max(typeof(p))/N]
498     // given that typeof(p) == typeof(c).
499     auto F = [](uint64_t X) {
500       return X ? (X / ElementsPerSegment) +
501                      (X < ElementsPerSegment || X % ElementsPerSegment ? 1 : 0)
502                : 0;
503     };
504     auto PS = F(OldSize);
505     auto CS = F(Size);
506     DCHECK_GE(PS, CS);
507     auto SegmentsToTrim = PS - CS;
508     for (auto I = 0uL; I < SegmentsToTrim; ++I) {
509       // Here we place the current tail segment to the freelist. To do this
510       // appropriately, we need to perform a splice operation on two
511       // bidirectional linked-lists. In particular, we have the current state of
512       // the doubly-linked list of segments:
513       //
514       //   @S@ <- s0 <-> s1 <-> ... <-> sT -> @S@
515       //
516       DCHECK_NE(Head, &SentinelSegment);
517       DCHECK_NE(Tail, &SentinelSegment);
518       DCHECK_EQ(Tail->Next, &SentinelSegment);
519
520       if (Freelist == &SentinelSegment) {
521         // Our two lists at this point are in this configuration:
522         //
523         //   Freelist: (potentially) @S@
524         //   Mainlist: @S@<-s0<->s1<->...<->sPT<->sT->@S@
525         //                  ^ Head                ^ Tail
526         //
527         // The end state for us will be this configuration:
528         //
529         //   Freelist: @S@<-sT->@S@
530         //   Mainlist: @S@<-s0<->s1<->...<->sPT->@S@
531         //                  ^ Head          ^ Tail
532         //
533         // The first step for us is to hold a reference to the tail of Mainlist,
534         // which in our notation is represented by sT. We call this our "free
535         // segment" which is the segment we are placing on the Freelist.
536         //
537         //   sF = sT
538         //
539         // Then, we also hold a reference to the "pre-tail" element, which we
540         // call sPT:
541         //
542         //   sPT = pred(sT)
543         //
544         // We want to splice sT into the beginning of the Freelist, which in
545         // an empty Freelist means placing a segment whose predecessor and
546         // successor is the sentinel segment.
547         //
548         // The splice operation then can be performed in the following
549         // algorithm:
550         //
551         //   succ(sPT) = S
552         //   pred(sT) = S
553         //   succ(sT) = Freelist
554         //   Freelist = sT
555         //   Tail = sPT
556         //
557         auto SPT = Tail->Prev;
558         SPT->Next = &SentinelSegment;
559         Tail->Prev = &SentinelSegment;
560         Tail->Next = Freelist;
561         Freelist = Tail;
562         Tail = SPT;
563
564         // Our post-conditions here are:
565         DCHECK_EQ(Tail->Next, &SentinelSegment);
566         DCHECK_EQ(Freelist->Prev, &SentinelSegment);
567       } else {
568         // In the other case, where the Freelist is not empty, we perform the
569         // following transformation instead:
570         //
571         // This transforms the current state:
572         //
573         //   Freelist: @S@<-f0->@S@
574         //                  ^ Freelist
575         //   Mainlist: @S@<-s0<->s1<->...<->sPT<->sT->@S@
576         //                  ^ Head                ^ Tail
577         //
578         // Into the following:
579         //
580         //   Freelist: @S@<-sT<->f0->@S@
581         //                  ^ Freelist
582         //   Mainlist: @S@<-s0<->s1<->...<->sPT->@S@
583         //                  ^ Head          ^ Tail
584         //
585         // The algorithm is:
586         //
587         //   sFH = Freelist
588         //   sPT = pred(sT)
589         //   pred(SFH) = sT
590         //   succ(sT) = Freelist
591         //   pred(sT) = S
592         //   succ(sPT) = S
593         //   Tail = sPT
594         //   Freelist = sT
595         //
596         auto SFH = Freelist;
597         auto SPT = Tail->Prev;
598         auto ST = Tail;
599         SFH->Prev = ST;
600         ST->Next = Freelist;
601         ST->Prev = &SentinelSegment;
602         SPT->Next = &SentinelSegment;
603         Tail = SPT;
604         Freelist = ST;
605
606         // Our post-conditions here are:
607         DCHECK_EQ(Tail->Next, &SentinelSegment);
608         DCHECK_EQ(Freelist->Prev, &SentinelSegment);
609         DCHECK_EQ(Freelist->Next->Prev, Freelist);
610       }
611     }
612
613     // Now in case we've spliced all the segments in the end, we ensure that the
614     // main list is "empty", or both the head and tail pointing to the sentinel
615     // segment.
616     if (Tail == &SentinelSegment)
617       Head = Tail;
618
619     DCHECK(
620         (Size == 0 && Head == &SentinelSegment && Tail == &SentinelSegment) ||
621         (Size != 0 && Head != &SentinelSegment && Tail != &SentinelSegment));
622     DCHECK(
623         (Freelist != &SentinelSegment && Freelist->Prev == &SentinelSegment) ||
624         (Freelist == &SentinelSegment && Tail->Next == &SentinelSegment));
625   }
626
627   // Provide iterators.
628   Iterator<T> begin() const XRAY_NEVER_INSTRUMENT {
629     return Iterator<T>(Head, 0, Size);
630   }
631   Iterator<T> end() const XRAY_NEVER_INSTRUMENT {
632     return Iterator<T>(Tail, Size, Size);
633   }
634   Iterator<const T> cbegin() const XRAY_NEVER_INSTRUMENT {
635     return Iterator<const T>(Head, 0, Size);
636   }
637   Iterator<const T> cend() const XRAY_NEVER_INSTRUMENT {
638     return Iterator<const T>(Tail, Size, Size);
639   }
640 };
641
642 // We need to have this storage definition out-of-line so that the compiler can
643 // ensure that storage for the SentinelSegment is defined and has a single
644 // address.
645 template <class T>
646 typename Array<T>::Segment Array<T>::SentinelSegment{
647     &Array<T>::SentinelSegment, &Array<T>::SentinelSegment, {'\0'}};
648
649 } // namespace __xray
650
651 #endif // XRAY_SEGMENTED_ARRAY_H