tsan_shadow.h revision 1.1 1 //===-- tsan_shadow.h -------------------------------------------*- 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 #ifndef TSAN_SHADOW_H
10 #define TSAN_SHADOW_H
11
12 #include "tsan_defs.h"
13 #include "tsan_trace.h"
14
15 namespace __tsan {
16
17 // FastState (from most significant bit):
18 // ignore : 1
19 // tid : kTidBits
20 // unused : -
21 // history_size : 3
22 // epoch : kClkBits
23 class FastState {
24 public:
25 FastState(u64 tid, u64 epoch) {
26 x_ = tid << kTidShift;
27 x_ |= epoch;
28 DCHECK_EQ(tid, this->tid());
29 DCHECK_EQ(epoch, this->epoch());
30 DCHECK_EQ(GetIgnoreBit(), false);
31 }
32
33 explicit FastState(u64 x) : x_(x) {}
34
35 u64 raw() const { return x_; }
36
37 u64 tid() const {
38 u64 res = (x_ & ~kIgnoreBit) >> kTidShift;
39 return res;
40 }
41
42 u64 TidWithIgnore() const {
43 u64 res = x_ >> kTidShift;
44 return res;
45 }
46
47 u64 epoch() const {
48 u64 res = x_ & ((1ull << kClkBits) - 1);
49 return res;
50 }
51
52 void IncrementEpoch() {
53 u64 old_epoch = epoch();
54 x_ += 1;
55 DCHECK_EQ(old_epoch + 1, epoch());
56 (void)old_epoch;
57 }
58
59 void SetIgnoreBit() { x_ |= kIgnoreBit; }
60 void ClearIgnoreBit() { x_ &= ~kIgnoreBit; }
61 bool GetIgnoreBit() const { return (s64)x_ < 0; }
62
63 void SetHistorySize(int hs) {
64 CHECK_GE(hs, 0);
65 CHECK_LE(hs, 7);
66 x_ = (x_ & ~(kHistoryMask << kHistoryShift)) | (u64(hs) << kHistoryShift);
67 }
68
69 ALWAYS_INLINE
70 int GetHistorySize() const {
71 return (int)((x_ >> kHistoryShift) & kHistoryMask);
72 }
73
74 void ClearHistorySize() { SetHistorySize(0); }
75
76 ALWAYS_INLINE
77 u64 GetTracePos() const {
78 const int hs = GetHistorySize();
79 // When hs == 0, the trace consists of 2 parts.
80 const u64 mask = (1ull << (kTracePartSizeBits + hs + 1)) - 1;
81 return epoch() & mask;
82 }
83
84 private:
85 friend class Shadow;
86 static const int kTidShift = 64 - kTidBits - 1;
87 static const u64 kIgnoreBit = 1ull << 63;
88 static const u64 kFreedBit = 1ull << 63;
89 static const u64 kHistoryShift = kClkBits;
90 static const u64 kHistoryMask = 7;
91 u64 x_;
92 };
93
94 // Shadow (from most significant bit):
95 // freed : 1
96 // tid : kTidBits
97 // is_atomic : 1
98 // is_read : 1
99 // size_log : 2
100 // addr0 : 3
101 // epoch : kClkBits
102 class Shadow : public FastState {
103 public:
104 explicit Shadow(u64 x) : FastState(x) {}
105
106 explicit Shadow(const FastState &s) : FastState(s.x_) { ClearHistorySize(); }
107
108 void SetAddr0AndSizeLog(u64 addr0, unsigned kAccessSizeLog) {
109 DCHECK_EQ((x_ >> kClkBits) & 31, 0);
110 DCHECK_LE(addr0, 7);
111 DCHECK_LE(kAccessSizeLog, 3);
112 x_ |= ((kAccessSizeLog << 3) | addr0) << kClkBits;
113 DCHECK_EQ(kAccessSizeLog, size_log());
114 DCHECK_EQ(addr0, this->addr0());
115 }
116
117 void SetWrite(unsigned kAccessIsWrite) {
118 DCHECK_EQ(x_ & kReadBit, 0);
119 if (!kAccessIsWrite)
120 x_ |= kReadBit;
121 DCHECK_EQ(kAccessIsWrite, IsWrite());
122 }
123
124 void SetAtomic(bool kIsAtomic) {
125 DCHECK(!IsAtomic());
126 if (kIsAtomic)
127 x_ |= kAtomicBit;
128 DCHECK_EQ(IsAtomic(), kIsAtomic);
129 }
130
131 bool IsAtomic() const { return x_ & kAtomicBit; }
132
133 bool IsZero() const { return x_ == 0; }
134
135 static inline bool TidsAreEqual(const Shadow s1, const Shadow s2) {
136 u64 shifted_xor = (s1.x_ ^ s2.x_) >> kTidShift;
137 DCHECK_EQ(shifted_xor == 0, s1.TidWithIgnore() == s2.TidWithIgnore());
138 return shifted_xor == 0;
139 }
140
141 static ALWAYS_INLINE bool Addr0AndSizeAreEqual(const Shadow s1,
142 const Shadow s2) {
143 u64 masked_xor = ((s1.x_ ^ s2.x_) >> kClkBits) & 31;
144 return masked_xor == 0;
145 }
146
147 static ALWAYS_INLINE bool TwoRangesIntersect(Shadow s1, Shadow s2,
148 unsigned kS2AccessSize) {
149 bool res = false;
150 u64 diff = s1.addr0() - s2.addr0();
151 if ((s64)diff < 0) { // s1.addr0 < s2.addr0
152 // if (s1.addr0() + size1) > s2.addr0()) return true;
153 if (s1.size() > -diff)
154 res = true;
155 } else {
156 // if (s2.addr0() + kS2AccessSize > s1.addr0()) return true;
157 if (kS2AccessSize > diff)
158 res = true;
159 }
160 DCHECK_EQ(res, TwoRangesIntersectSlow(s1, s2));
161 DCHECK_EQ(res, TwoRangesIntersectSlow(s2, s1));
162 return res;
163 }
164
165 u64 ALWAYS_INLINE addr0() const { return (x_ >> kClkBits) & 7; }
166 u64 ALWAYS_INLINE size() const { return 1ull << size_log(); }
167 bool ALWAYS_INLINE IsWrite() const { return !IsRead(); }
168 bool ALWAYS_INLINE IsRead() const { return x_ & kReadBit; }
169
170 // The idea behind the freed bit is as follows.
171 // When the memory is freed (or otherwise unaccessible) we write to the shadow
172 // values with tid/epoch related to the free and the freed bit set.
173 // During memory accesses processing the freed bit is considered
174 // as msb of tid. So any access races with shadow with freed bit set
175 // (it is as if write from a thread with which we never synchronized before).
176 // This allows us to detect accesses to freed memory w/o additional
177 // overheads in memory access processing and at the same time restore
178 // tid/epoch of free.
179 void MarkAsFreed() { x_ |= kFreedBit; }
180
181 bool IsFreed() const { return x_ & kFreedBit; }
182
183 bool GetFreedAndReset() {
184 bool res = x_ & kFreedBit;
185 x_ &= ~kFreedBit;
186 return res;
187 }
188
189 bool ALWAYS_INLINE IsBothReadsOrAtomic(bool kIsWrite, bool kIsAtomic) const {
190 bool v = x_ & ((u64(kIsWrite ^ 1) << kReadShift) |
191 (u64(kIsAtomic) << kAtomicShift));
192 DCHECK_EQ(v, (!IsWrite() && !kIsWrite) || (IsAtomic() && kIsAtomic));
193 return v;
194 }
195
196 bool ALWAYS_INLINE IsRWNotWeaker(bool kIsWrite, bool kIsAtomic) const {
197 bool v = ((x_ >> kReadShift) & 3) <= u64((kIsWrite ^ 1) | (kIsAtomic << 1));
198 DCHECK_EQ(v, (IsAtomic() < kIsAtomic) ||
199 (IsAtomic() == kIsAtomic && !IsWrite() <= !kIsWrite));
200 return v;
201 }
202
203 bool ALWAYS_INLINE IsRWWeakerOrEqual(bool kIsWrite, bool kIsAtomic) const {
204 bool v = ((x_ >> kReadShift) & 3) >= u64((kIsWrite ^ 1) | (kIsAtomic << 1));
205 DCHECK_EQ(v, (IsAtomic() > kIsAtomic) ||
206 (IsAtomic() == kIsAtomic && !IsWrite() >= !kIsWrite));
207 return v;
208 }
209
210 private:
211 static const u64 kReadShift = 5 + kClkBits;
212 static const u64 kReadBit = 1ull << kReadShift;
213 static const u64 kAtomicShift = 6 + kClkBits;
214 static const u64 kAtomicBit = 1ull << kAtomicShift;
215
216 u64 size_log() const { return (x_ >> (3 + kClkBits)) & 3; }
217
218 static bool TwoRangesIntersectSlow(const Shadow s1, const Shadow s2) {
219 if (s1.addr0() == s2.addr0())
220 return true;
221 if (s1.addr0() < s2.addr0() && s1.addr0() + s1.size() > s2.addr0())
222 return true;
223 if (s2.addr0() < s1.addr0() && s2.addr0() + s2.size() > s1.addr0())
224 return true;
225 return false;
226 }
227 };
228
229 const RawShadow kShadowRodata = (RawShadow)-1; // .rodata shadow marker
230
231 } // namespace __tsan
232
233 #endif
234