Home | History | Annotate | Line # | Download | only in tsan
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