diff --git a/llvm/include/llvm/Support/OpaqueDataStore.h b/llvm/include/llvm/Support/OpaqueDataStore.h
new file mode 100644
--- /dev/null
+++ b/llvm/include/llvm/Support/OpaqueDataStore.h
@@ -0,0 +1,137 @@
+//===-- llvm/Support/OpaqueDataStore.h --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_SUPPORT_OPAQUEDATASTORE_H
+#define LLVM_SUPPORT_OPAQUEDATASTORE_H
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+
+#include <memory>
+
+namespace llvm {
+class raw_ostream;
+
+/// Base class for StoredData.
+class StoredDataBase {
+public:
+  /// Must have a virtual destructor because it is used polymorphically.
+  virtual ~StoredDataBase() = default;
+
+  // Returns the class ID for this type.
+  static const void *classID() { return &ID; }
+
+  // Check whether this instance is a subclass of the class identified by
+  // ClassID.
+  virtual bool isA(const void *const ClassID) const {
+    return ClassID == classID();
+  }
+
+private:
+  virtual void anchor();
+
+  static char ID;
+};
+
+/// Template base helper for StoredData types
+template <typename ThisDataT, typename ParentDataT = StoredDataBase>
+class StoredDataInfo : public ParentDataT {
+public:
+  using ParentDataT::ParentDataT; // inherit constructors
+
+  /// Return the class ID for the metadata type.
+  static const void *classID() { return &ThisDataT::ID; }
+
+  /// Implement isA for this type and check the parent type as well.
+  bool isA(const void *const ClassID) const override {
+    return ClassID == classID() || ParentDataT::isA(ClassID);
+  }
+};
+
+/// Class for storing opaque type-safe data
+class OpaqueDataStore {
+public:
+  OpaqueDataStore() = default;
+
+  template <class... Mods> OpaqueDataStore(Mods... Ms) {
+    add(std::move(Ms)...);
+  }
+
+  /// Get an entry of the specified DataStore type, or insert a new default
+  /// constructed entry.
+  template <typename DataT> DataT &get() {
+    auto It = llvm::find_if(Storage,
+                            [](const std::unique_ptr<StoredDataBase> &Entry) {
+                              return Entry->isA(DataT::classID());
+                            });
+    if (It == Storage.end())
+      return insert<DataT>();
+    return *(DataT *)It->get();
+  }
+
+  /// Get an entry of the specified OpaqueDataStore type if it is stored. Unlike
+  /// get, this method does not construct an entry if it does not already exist,
+  /// but will return an empty optional instead.
+  template <typename DataT> llvm::Optional<const DataT *> find() const {
+    auto It = llvm::find_if(Storage,
+                            [](const std::unique_ptr<StoredDataBase> &Entry) {
+                              return Entry->isA(DataT::classID());
+                            });
+    if (It == Storage.end())
+      return {};
+    return {reinterpret_cast<DataT *>(It->get())};
+  }
+
+  /// Get an entry of the specified OpaqueDataStore type if it is stored. Unlike
+  /// get, this method does not construct an entry if it does not already exist,
+  /// but will return an empty optional instead.
+  template <typename DataT> llvm::Optional<DataT *> find() {
+    auto It = llvm::find_if(Storage,
+                            [](const std::unique_ptr<StoredDataBase> &Entry) {
+                              return Entry->isA(DataT::classID());
+                            });
+    if (It == Storage.end())
+      return {};
+    return {reinterpret_cast<DataT *>(It->get())};
+  }
+
+  template <typename DataT> bool contains() const {
+    return (bool)find<DataT>();
+  }
+
+  /// Returns the size of the OpaqueDataStore collection
+  size_t size() const { return Storage.size(); }
+
+private:
+  /// Insert a new StoredData entry into this collection, and only called via
+  /// get()
+  template <typename DataT, typename... ArgTs> DataT &insert(ArgTs &&...Args) {
+    return *(DataT *)Storage
+                .insert(Storage.end(),
+                        std::make_unique<DataT>(std::forward<ArgTs>(Args)...))
+                ->get();
+  }
+
+  template <typename T, class... Mods> void add(T D, Mods... Ms) {
+    add(std::move(D));
+    add(std::move(Ms)...);
+  }
+
+  /// Get an entry of the specified StoredData type, or insert a new default
+  /// constructed entry.
+  template <typename DataT> DataT &add(DataT D) {
+    assert(!contains<DataT>() && "adding arguments of the same type");
+    return insert<DataT>(std::move(D));
+  }
+
+  SmallVector<std::unique_ptr<StoredDataBase>, 8> Storage;
+};
+
+} // namespace llvm
+
+#endif // LLVM_SUPPORT_OPAQUEDATASTORE_H
diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt
--- a/llvm/lib/Support/CMakeLists.txt
+++ b/llvm/lib/Support/CMakeLists.txt
@@ -188,6 +188,7 @@
   MSP430Attributes.cpp
   MSP430AttributeParser.cpp
   NativeFormatting.cpp
+  OpaqueDataStore.cpp
   OptimizedStructLayout.cpp
   Optional.cpp
   Parallel.cpp
diff --git a/llvm/lib/Support/OpaqueDataStore.cpp b/llvm/lib/Support/OpaqueDataStore.cpp
new file mode 100644
--- /dev/null
+++ b/llvm/lib/Support/OpaqueDataStore.cpp
@@ -0,0 +1,15 @@
+//===-- lib/Support/OpaqueDataStore.cpp -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/OpaqueDataStore.h"
+
+using namespace llvm;
+
+void StoredDataBase::anchor() {}
+
+char StoredDataBase::ID = 0;
diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt
--- a/llvm/unittests/Support/CMakeLists.txt
+++ b/llvm/unittests/Support/CMakeLists.txt
@@ -61,6 +61,7 @@
   MemoryBufferTest.cpp
   MemoryTest.cpp
   NativeFormatTests.cpp
+  OpaqueDataStoreTests.cpp
   OptimizedStructLayoutTest.cpp
   ParallelTest.cpp
   Path.cpp
diff --git a/llvm/unittests/Support/OpaqueDataStoreTests.cpp b/llvm/unittests/Support/OpaqueDataStoreTests.cpp
new file mode 100644
--- /dev/null
+++ b/llvm/unittests/Support/OpaqueDataStoreTests.cpp
@@ -0,0 +1,35 @@
+//===- llvm/unittest/Support/DataStoreTests.cpp ---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/OpaqueDataStore.h"
+
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+class DummyData : public StoredDataInfo<DummyData> {
+public:
+  DummyData() {}
+  static char ID;
+  uint32_t Value;
+};
+char DummyData::ID = 0;
+
+TEST(DataStoreTests, AddData) {
+  OpaqueDataStore Data;
+  EXPECT_FALSE(Data.contains<DummyData>());
+
+  DummyData &V1 = Data.get<DummyData>();
+  EXPECT_TRUE(Data.contains<DummyData>());
+
+  DummyData &V2 = Data.get<DummyData>();
+  EXPECT_EQ(Data.size(), 1ull);
+
+  V1.Value = 0xFEEDFACE;
+  EXPECT_EQ(V2.Value, 0xFEEDFACE);
+}