github.com/johnnyeven/libtools@v0.0.0-20191126065708-61829c1adf46/third_party/mlir/lib/Support/StorageUniquer.cpp (about)

     1  //===- StorageUniquer.cpp - Common Storage Class Uniquer ------------------===//
     2  //
     3  // Copyright 2019 The MLIR Authors.
     4  //
     5  // Licensed under the Apache License, Version 2.0 (the "License");
     6  // you may not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //   http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing, software
    12  // distributed under the License is distributed on an "AS IS" BASIS,
    13  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  // See the License for the specific language governing permissions and
    15  // limitations under the License.
    16  // =============================================================================
    17  
    18  #include "mlir/Support/StorageUniquer.h"
    19  #include "mlir/Support/LLVM.h"
    20  #include "llvm/ADT/DenseMap.h"
    21  #include "llvm/Support/Allocator.h"
    22  #include "llvm/Support/RWMutex.h"
    23  
    24  using namespace mlir;
    25  using namespace mlir::detail;
    26  
    27  namespace mlir {
    28  namespace detail {
    29  /// This is the implementation of the StorageUniquer class.
    30  struct StorageUniquerImpl {
    31    using BaseStorage = StorageUniquer::BaseStorage;
    32    using StorageAllocator = StorageUniquer::StorageAllocator;
    33  
    34    /// A lookup key for derived instances of storage objects.
    35    struct LookupKey {
    36      /// The known derived kind for the storage.
    37      unsigned kind;
    38  
    39      /// The known hash value of the key.
    40      unsigned hashValue;
    41  
    42      /// An equality function for comparing with an existing storage instance.
    43      llvm::function_ref<bool(const BaseStorage *)> isEqual;
    44    };
    45  
    46    /// A utility wrapper object representing a hashed storage object. This class
    47    /// contains a storage object and an existing computed hash value.
    48    struct HashedStorage {
    49      unsigned hashValue;
    50      BaseStorage *storage;
    51    };
    52  
    53    /// Get or create an instance of a complex derived type.
    54    BaseStorage *
    55    getOrCreate(unsigned kind, unsigned hashValue,
    56                llvm::function_ref<bool(const BaseStorage *)> isEqual,
    57                llvm::function_ref<BaseStorage *(StorageAllocator &)> ctorFn) {
    58      LookupKey lookupKey{kind, hashValue, isEqual};
    59  
    60      // Check for an existing instance in read-only mode.
    61      {
    62        llvm::sys::SmartScopedReader<true> typeLock(mutex);
    63        auto it = storageTypes.find_as(lookupKey);
    64        if (it != storageTypes.end())
    65          return it->storage;
    66      }
    67  
    68      // Acquire a writer-lock so that we can safely create the new type instance.
    69      llvm::sys::SmartScopedWriter<true> typeLock(mutex);
    70  
    71      // Check for an existing instance again here, because another writer thread
    72      // may have already created one.
    73      auto existing = storageTypes.insert_as({}, lookupKey);
    74      if (!existing.second)
    75        return existing.first->storage;
    76  
    77      // Otherwise, construct and initialize the derived storage for this type
    78      // instance.
    79      BaseStorage *storage = initializeStorage(kind, ctorFn);
    80      *existing.first = HashedStorage{hashValue, storage};
    81      return storage;
    82    }
    83  
    84    /// Get or create an instance of a simple derived type.
    85    BaseStorage *
    86    getOrCreate(unsigned kind,
    87                llvm::function_ref<BaseStorage *(StorageAllocator &)> ctorFn) {
    88      // Check for an existing instance in read-only mode.
    89      {
    90        llvm::sys::SmartScopedReader<true> typeLock(mutex);
    91        auto it = simpleTypes.find(kind);
    92        if (it != simpleTypes.end())
    93          return it->second;
    94      }
    95  
    96      // Acquire a writer-lock so that we can safely create the new type instance.
    97      llvm::sys::SmartScopedWriter<true> typeLock(mutex);
    98  
    99      // Check for an existing instance again here, because another writer thread
   100      // may have already created one.
   101      auto &result = simpleTypes[kind];
   102      if (result)
   103        return result;
   104  
   105      // Otherwise, create and return a new storage instance.
   106      return result = initializeStorage(kind, ctorFn);
   107    }
   108  
   109    /// Erase an instance of a complex derived type.
   110    void erase(unsigned kind, unsigned hashValue,
   111               llvm::function_ref<bool(const BaseStorage *)> isEqual,
   112               llvm::function_ref<void(BaseStorage *)> cleanupFn) {
   113      LookupKey lookupKey{kind, hashValue, isEqual};
   114  
   115      // Acquire a writer-lock so that we can safely erase the type instance.
   116      llvm::sys::SmartScopedWriter<true> typeLock(mutex);
   117      auto existing = storageTypes.find_as(lookupKey);
   118      if (existing == storageTypes.end())
   119        return;
   120  
   121      // Cleanup the storage and remove it from the map.
   122      cleanupFn(existing->storage);
   123      storageTypes.erase(existing);
   124    }
   125  
   126    //===--------------------------------------------------------------------===//
   127    // Instance Storage
   128    //===--------------------------------------------------------------------===//
   129  
   130    /// Utility to create and initialize a storage instance.
   131    BaseStorage *initializeStorage(
   132        unsigned kind,
   133        llvm::function_ref<BaseStorage *(StorageAllocator &)> ctorFn) {
   134      BaseStorage *storage = ctorFn(allocator);
   135      storage->kind = kind;
   136      return storage;
   137    }
   138  
   139    /// Storage info for derived TypeStorage objects.
   140    struct StorageKeyInfo : DenseMapInfo<HashedStorage> {
   141      static HashedStorage getEmptyKey() {
   142        return HashedStorage{0, DenseMapInfo<BaseStorage *>::getEmptyKey()};
   143      }
   144      static HashedStorage getTombstoneKey() {
   145        return HashedStorage{0, DenseMapInfo<BaseStorage *>::getTombstoneKey()};
   146      }
   147  
   148      static unsigned getHashValue(const HashedStorage &key) {
   149        return key.hashValue;
   150      }
   151      static unsigned getHashValue(LookupKey key) { return key.hashValue; }
   152  
   153      static bool isEqual(const HashedStorage &lhs, const HashedStorage &rhs) {
   154        return lhs.storage == rhs.storage;
   155      }
   156      static bool isEqual(const LookupKey &lhs, const HashedStorage &rhs) {
   157        if (isEqual(rhs, getEmptyKey()) || isEqual(rhs, getTombstoneKey()))
   158          return false;
   159        // If the lookup kind matches the kind of the storage, then invoke the
   160        // equality function on the lookup key.
   161        return lhs.kind == rhs.storage->getKind() && lhs.isEqual(rhs.storage);
   162      }
   163    };
   164  
   165    // Unique types with specific hashing or storage constraints.
   166    using StorageTypeSet = llvm::DenseSet<HashedStorage, StorageKeyInfo>;
   167    StorageTypeSet storageTypes;
   168  
   169    // Unique types with just the kind.
   170    llvm::DenseMap<unsigned, BaseStorage *> simpleTypes;
   171  
   172    // Allocator to use when constructing derived type instances.
   173    StorageUniquer::StorageAllocator allocator;
   174  
   175    // A mutex to keep type uniquing thread-safe.
   176    llvm::sys::SmartRWMutex<true> mutex;
   177  };
   178  } // end namespace detail
   179  } // namespace mlir
   180  
   181  StorageUniquer::StorageUniquer() : impl(new StorageUniquerImpl()) {}
   182  StorageUniquer::~StorageUniquer() {}
   183  
   184  /// Implementation for getting/creating an instance of a derived type with
   185  /// complex storage.
   186  auto StorageUniquer::getImpl(
   187      unsigned kind, unsigned hashValue,
   188      llvm::function_ref<bool(const BaseStorage *)> isEqual,
   189      std::function<BaseStorage *(StorageAllocator &)> ctorFn) -> BaseStorage * {
   190    return impl->getOrCreate(kind, hashValue, isEqual, ctorFn);
   191  }
   192  
   193  /// Implementation for getting/creating an instance of a derived type with
   194  /// default storage.
   195  auto StorageUniquer::getImpl(
   196      unsigned kind, std::function<BaseStorage *(StorageAllocator &)> ctorFn)
   197      -> BaseStorage * {
   198    return impl->getOrCreate(kind, ctorFn);
   199  }
   200  
   201  /// Implementation for erasing an instance of a derived type with complex
   202  /// storage.
   203  void StorageUniquer::eraseImpl(
   204      unsigned kind, unsigned hashValue,
   205      llvm::function_ref<bool(const BaseStorage *)> isEqual,
   206      std::function<void(BaseStorage *)> cleanupFn) {
   207    impl->erase(kind, hashValue, isEqual, cleanupFn);
   208  }