Coverage Report

Created: 2020-06-26 05:44

/home/arjun/llvm-project/mlir/lib/Support/StorageUniquer.cpp
Line
Count
Source (jump to first uncovered line)
1
//===- StorageUniquer.cpp - Common Storage Class Uniquer ------------------===//
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
#include "mlir/Support/StorageUniquer.h"
10
11
#include "mlir/Support/LLVM.h"
12
#include "llvm/Support/RWMutex.h"
13
14
using namespace mlir;
15
using namespace mlir::detail;
16
17
namespace mlir {
18
namespace detail {
19
/// This is the implementation of the StorageUniquer class.
20
struct StorageUniquerImpl {
21
  using BaseStorage = StorageUniquer::BaseStorage;
22
  using StorageAllocator = StorageUniquer::StorageAllocator;
23
24
  /// A lookup key for derived instances of storage objects.
25
  struct LookupKey {
26
    /// The known derived kind for the storage.
27
    unsigned kind;
28
29
    /// The known hash value of the key.
30
    unsigned hashValue;
31
32
    /// An equality function for comparing with an existing storage instance.
33
    function_ref<bool(const BaseStorage *)> isEqual;
34
  };
35
36
  /// A utility wrapper object representing a hashed storage object. This class
37
  /// contains a storage object and an existing computed hash value.
38
  struct HashedStorage {
39
    unsigned hashValue;
40
    BaseStorage *storage;
41
  };
42
43
  /// Get or create an instance of a complex derived type.
44
  BaseStorage *
45
  getOrCreate(unsigned kind, unsigned hashValue,
46
              function_ref<bool(const BaseStorage *)> isEqual,
47
0
              function_ref<BaseStorage *(StorageAllocator &)> ctorFn) {
48
0
    LookupKey lookupKey{kind, hashValue, isEqual};
49
0
    if (!threadingIsEnabled)
50
0
      return getOrCreateUnsafe(kind, hashValue, lookupKey, ctorFn);
51
0
52
0
    // Check for an existing instance in read-only mode.
53
0
    {
54
0
      llvm::sys::SmartScopedReader<true> typeLock(mutex);
55
0
      auto it = storageTypes.find_as(lookupKey);
56
0
      if (it != storageTypes.end())
57
0
        return it->storage;
58
0
    }
59
0
60
0
    // Acquire a writer-lock so that we can safely create the new type instance.
61
0
    llvm::sys::SmartScopedWriter<true> typeLock(mutex);
62
0
    return getOrCreateUnsafe(kind, hashValue, lookupKey, ctorFn);
63
0
  }
64
  /// Get or create an instance of a complex derived type in an unsafe fashion.
65
  BaseStorage *
66
  getOrCreateUnsafe(unsigned kind, unsigned hashValue, LookupKey &lookupKey,
67
0
                    function_ref<BaseStorage *(StorageAllocator &)> ctorFn) {
68
0
    auto existing = storageTypes.insert_as({}, lookupKey);
69
0
    if (!existing.second)
70
0
      return existing.first->storage;
71
0
72
0
    // Otherwise, construct and initialize the derived storage for this type
73
0
    // instance.
74
0
    BaseStorage *storage = initializeStorage(kind, ctorFn);
75
0
    *existing.first = HashedStorage{hashValue, storage};
76
0
    return storage;
77
0
  }
78
79
  /// Get or create an instance of a simple derived type.
80
  BaseStorage *
81
  getOrCreate(unsigned kind,
82
0
              function_ref<BaseStorage *(StorageAllocator &)> ctorFn) {
83
0
    if (!threadingIsEnabled)
84
0
      return getOrCreateUnsafe(kind, ctorFn);
85
0
86
0
    // Check for an existing instance in read-only mode.
87
0
    {
88
0
      llvm::sys::SmartScopedReader<true> typeLock(mutex);
89
0
      auto it = simpleTypes.find(kind);
90
0
      if (it != simpleTypes.end())
91
0
        return it->second;
92
0
    }
93
0
94
0
    // Acquire a writer-lock so that we can safely create the new type instance.
95
0
    llvm::sys::SmartScopedWriter<true> typeLock(mutex);
96
0
    return getOrCreateUnsafe(kind, ctorFn);
97
0
  }
98
  /// Get or create an instance of a simple derived type in an unsafe fashion.
99
  BaseStorage *
100
  getOrCreateUnsafe(unsigned kind,
101
0
                    function_ref<BaseStorage *(StorageAllocator &)> ctorFn) {
102
0
    auto &result = simpleTypes[kind];
103
0
    if (result)
104
0
      return result;
105
0
106
0
    // Otherwise, create and return a new storage instance.
107
0
    return result = initializeStorage(kind, ctorFn);
108
0
  }
109
110
  /// Erase an instance of a complex derived type.
111
  void erase(unsigned kind, unsigned hashValue,
112
             function_ref<bool(const BaseStorage *)> isEqual,
113
0
             function_ref<void(BaseStorage *)> cleanupFn) {
114
0
    LookupKey lookupKey{kind, hashValue, isEqual};
115
0
116
0
    // Acquire a writer-lock so that we can safely erase the type instance.
117
0
    llvm::sys::SmartScopedWriter<true> typeLock(mutex);
118
0
    auto existing = storageTypes.find_as(lookupKey);
119
0
    if (existing == storageTypes.end())
120
0
      return;
121
0
122
0
    // Cleanup the storage and remove it from the map.
123
0
    cleanupFn(existing->storage);
124
0
    storageTypes.erase(existing);
125
0
  }
126
127
  //===--------------------------------------------------------------------===//
128
  // Instance Storage
129
  //===--------------------------------------------------------------------===//
130
131
  /// Utility to create and initialize a storage instance.
132
  BaseStorage *
133
  initializeStorage(unsigned kind,
134
0
                    function_ref<BaseStorage *(StorageAllocator &)> ctorFn) {
135
0
    BaseStorage *storage = ctorFn(allocator);
136
0
    storage->kind = kind;
137
0
    return storage;
138
0
  }
139
140
  /// Storage info for derived TypeStorage objects.
141
  struct StorageKeyInfo : DenseMapInfo<HashedStorage> {
142
0
    static HashedStorage getEmptyKey() {
143
0
      return HashedStorage{0, DenseMapInfo<BaseStorage *>::getEmptyKey()};
144
0
    }
145
0
    static HashedStorage getTombstoneKey() {
146
0
      return HashedStorage{0, DenseMapInfo<BaseStorage *>::getTombstoneKey()};
147
0
    }
148
149
0
    static unsigned getHashValue(const HashedStorage &key) {
150
0
      return key.hashValue;
151
0
    }
152
0
    static unsigned getHashValue(LookupKey key) { return key.hashValue; }
153
154
0
    static bool isEqual(const HashedStorage &lhs, const HashedStorage &rhs) {
155
0
      return lhs.storage == rhs.storage;
156
0
    }
157
0
    static bool isEqual(const LookupKey &lhs, const HashedStorage &rhs) {
158
0
      if (isEqual(rhs, getEmptyKey()) || isEqual(rhs, getTombstoneKey()))
159
0
        return false;
160
0
      // If the lookup kind matches the kind of the storage, then invoke the
161
0
      // equality function on the lookup key.
162
0
      return lhs.kind == rhs.storage->getKind() && lhs.isEqual(rhs.storage);
163
0
    }
164
  };
165
166
  /// Unique types with specific hashing or storage constraints.
167
  using StorageTypeSet = DenseSet<HashedStorage, StorageKeyInfo>;
168
  StorageTypeSet storageTypes;
169
170
  /// Unique types with just the kind.
171
  DenseMap<unsigned, BaseStorage *> simpleTypes;
172
173
  /// Allocator to use when constructing derived type instances.
174
  StorageUniquer::StorageAllocator allocator;
175
176
  /// A mutex to keep type uniquing thread-safe.
177
  llvm::sys::SmartRWMutex<true> mutex;
178
179
  /// Flag specifying if multi-threading is enabled within the uniquer.
180
  bool threadingIsEnabled = true;
181
};
182
} // end namespace detail
183
} // namespace mlir
184
185
0
StorageUniquer::StorageUniquer() : impl(new StorageUniquerImpl()) {}
186
0
StorageUniquer::~StorageUniquer() {}
187
188
/// Set the flag specifying if multi-threading is disabled within the uniquer.
189
0
void StorageUniquer::disableMultithreading(bool disable) {
190
0
  impl->threadingIsEnabled = !disable;
191
0
}
192
193
/// Implementation for getting/creating an instance of a derived type with
194
/// complex storage.
195
auto StorageUniquer::getImpl(
196
    unsigned kind, unsigned hashValue,
197
    function_ref<bool(const BaseStorage *)> isEqual,
198
0
    function_ref<BaseStorage *(StorageAllocator &)> ctorFn) -> BaseStorage * {
199
0
  return impl->getOrCreate(kind, hashValue, isEqual, ctorFn);
200
0
}
201
202
/// Implementation for getting/creating an instance of a derived type with
203
/// default storage.
204
auto StorageUniquer::getImpl(
205
    unsigned kind, function_ref<BaseStorage *(StorageAllocator &)> ctorFn)
206
0
    -> BaseStorage * {
207
0
  return impl->getOrCreate(kind, ctorFn);
208
0
}
209
210
/// Implementation for erasing an instance of a derived type with complex
211
/// storage.
212
void StorageUniquer::eraseImpl(unsigned kind, unsigned hashValue,
213
                               function_ref<bool(const BaseStorage *)> isEqual,
214
0
                               function_ref<void(BaseStorage *)> cleanupFn) {
215
0
  impl->erase(kind, hashValue, isEqual, cleanupFn);
216
0
}