diff --git a/clang/lib/Tooling/JSONCompilationDatabase.cpp b/clang/lib/Tooling/JSONCompilationDatabase.cpp --- a/clang/lib/Tooling/JSONCompilationDatabase.cpp +++ b/clang/lib/Tooling/JSONCompilationDatabase.cpp @@ -179,8 +179,11 @@ // Register the JSONCompilationDatabasePlugin with the // CompilationDatabasePluginRegistry using this statically initialized variable. +// Using 10 as the priority is a little arbitary, but it ensures this will +// register before the FixedCompilationDatabasePlugin. static CompilationDatabasePluginRegistry::Add -X("json-compilation-database", "Reads JSON formatted compilation databases"); + X("json-compilation-database", "Reads JSON formatted compilation databases", + 10); namespace clang { namespace tooling { diff --git a/llvm/include/llvm/Support/Registry.h b/llvm/include/llvm/Support/Registry.h --- a/llvm/include/llvm/Support/Registry.h +++ b/llvm/include/llvm/Support/Registry.h @@ -21,21 +21,28 @@ #include namespace llvm { - /// A simple registry entry which provides only a name, description, and - /// no-argument constructor. - template - class SimpleRegistryEntry { - StringRef Name, Desc; - std::unique_ptr (*Ctor)(); - public: - SimpleRegistryEntry(StringRef N, StringRef D, std::unique_ptr (*C)()) - : Name(N), Desc(D), Ctor(C) {} +template class Registry; - StringRef getName() const { return Name; } - StringRef getDesc() const { return Desc; } - std::unique_ptr instantiate() const { return Ctor(); } - }; +/// A simple registry entry which provides only a name, description, and +/// no-argument constructor. +template class SimpleRegistryEntry { + StringRef Name, Desc; + unsigned Order; + friend class Registry; + std::unique_ptr (*Ctor)(); + +public: + SimpleRegistryEntry(StringRef N, StringRef D, unsigned O, + std::unique_ptr (*C)()) + : Name(N), Desc(D), Order(O), Ctor(C) {} + SimpleRegistryEntry(StringRef N, StringRef D, std::unique_ptr (*C)()) + : SimpleRegistryEntry(N, D, ~0U, C) {} + + StringRef getName() const { return Name; } + StringRef getDesc() const { return Desc; } + std::unique_ptr instantiate() const { return Ctor(); } +}; /// A global registry used in conjunction with static constructors to make /// pluggable components (like targets or garbage collectors) "just work" when @@ -122,10 +129,57 @@ : Entry(Name, Desc, CtorFn), Node(Entry) { add_node(&Node); } + Add(StringRef Name, StringRef Desc, unsigned Order) + : Entry(Name, Desc, Order, CtorFn), Node(Entry) { + add_node(&Node); + } }; }; } // end namespace llvm +// Strictly a testing aid, do not use outside tests. +#define LLVM_INSTANTIATE_REGISTRY_BASE() \ + namespace llvm { \ + template \ + typename Registry::node *Registry::Head = nullptr; \ + template \ + typename Registry::node *Registry::Tail = nullptr; \ + template \ + void Registry::add_node(typename Registry::node *N) { \ + if (N->Val.Order == ~0U) { \ + if (Tail) \ + Tail->Next = N; \ + else \ + Head = N; \ + Tail = N; \ + return; \ + } \ + auto Ptr = &Head; \ + while (*Ptr) { \ + if ((*Ptr)->Val.Order > N->Val.Order) \ + break; \ + Ptr = &(*Ptr)->Next; \ + } \ + N->Next = *Ptr; \ + if (*Ptr == Tail) \ + Tail = N; \ + *Ptr = N; \ + } \ + template typename Registry::iterator Registry::begin() { \ + return iterator(Head); \ + } \ + } + +// Strictly a testing aid, do not use outside tests. +#define LLVM_INSTANTIATE_REGISTRY_IMPL(REGISTRY_CLASS) \ + namespace llvm { \ + template REGISTRY_CLASS::node *Registry::Head; \ + template REGISTRY_CLASS::node *Registry::Tail; \ + template void \ + Registry::add_node(REGISTRY_CLASS::node *); \ + template REGISTRY_CLASS::iterator Registry::begin(); \ + } + /// Instantiate a registry class. /// /// This provides template definitions of add_node, begin, and the Head and Tail @@ -134,26 +188,8 @@ /// strictly speaking that's not allowed by the C++ standard (we would need to /// have explicit specialization declarations in all translation units where the /// specialization is used) so we don't. -#define LLVM_INSTANTIATE_REGISTRY(REGISTRY_CLASS) \ - namespace llvm { \ - template typename Registry::node *Registry::Head = nullptr;\ - template typename Registry::node *Registry::Tail = nullptr;\ - template \ - void Registry::add_node(typename Registry::node *N) { \ - if (Tail) \ - Tail->Next = N; \ - else \ - Head = N; \ - Tail = N; \ - } \ - template typename Registry::iterator Registry::begin() { \ - return iterator(Head); \ - } \ - template REGISTRY_CLASS::node *Registry::Head; \ - template REGISTRY_CLASS::node *Registry::Tail; \ - template \ - void Registry::add_node(REGISTRY_CLASS::node*); \ - template REGISTRY_CLASS::iterator Registry::begin(); \ - } +#define LLVM_INSTANTIATE_REGISTRY(REGISTRY_CLASS) \ + LLVM_INSTANTIATE_REGISTRY_BASE() \ + LLVM_INSTANTIATE_REGISTRY_IMPL(REGISTRY_CLASS) #endif // LLVM_SUPPORT_REGISTRY_H 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 @@ -62,6 +62,7 @@ ProcessTest.cpp ProgramTest.cpp RegexTest.cpp + RegistryTest.cpp ReverseIterationTest.cpp ReplaceFileTest.cpp RISCVAttributeParserTest.cpp diff --git a/llvm/unittests/Support/RegistryTest.cpp b/llvm/unittests/Support/RegistryTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/Support/RegistryTest.cpp @@ -0,0 +1,122 @@ +//===- llvm/unittest/Support/RegistryTest.cpp - Registry tests ------------===// +// +// 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/Registry.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using ::testing::ElementsAre; +using ::testing::UnorderedElementsAre; + +LLVM_INSTANTIATE_REGISTRY_BASE() + +#define CREATE_TEST_PLUGIN_AND_REGISTRY(Name) \ + struct Name##Plugin { \ + unsigned Value; \ + Name##Plugin(unsigned Value) : Value(Value) {} \ + }; \ + using Name##PluginRegistry = llvm::Registry; \ + LLVM_INSTANTIATE_REGISTRY_IMPL(Name##PluginRegistry) + +// Preprocessor hack to ensure the counter macro is handled correctly +#define JOIN(X, Y) X##Y + +#define ORDERED_PLUGIN_IMPL(Name, Value, UniqID) \ + struct JOIN(OrderedPlugin, UniqID) : Name##Plugin { \ + JOIN(OrderedPlugin, UniqID)() : Name##Plugin(Value) {} \ + }; \ + Name##PluginRegistry::Add JOIN( \ + Instance, UniqID)("OrderedPlugin", "OrderedPlugin", Value); + +#define UNORDERED_PLUGIN_IMPL(Name, Value, UniqID) \ + struct JOIN(UnorderedPlugin, UniqID) : Name##Plugin { \ + JOIN(UnorderedPlugin, UniqID)() : Name##Plugin(Value) {} \ + }; \ + Name##PluginRegistry::Add JOIN( \ + Instance, UniqID)("UnorderedPlugin", "UnorderedPlugin"); + +#define ORDERED_PLUGIN(Name, Value) ORDERED_PLUGIN_IMPL(Name, Value, __LINE__) + +#define UNORDERED_PLUGIN_WITH_VALUE(Name, Value) \ + UNORDERED_PLUGIN_IMPL(Name, Value, __LINE__) + +#define UNORDERED_PLUGIN(Name) UNORDERED_PLUGIN_WITH_VALUE(Name, 0) + +CREATE_TEST_PLUGIN_AND_REGISTRY(AllUnordered) + +UNORDERED_PLUGIN_WITH_VALUE(AllUnordered, 34) +UNORDERED_PLUGIN_WITH_VALUE(AllUnordered, 99) +UNORDERED_PLUGIN_WITH_VALUE(AllUnordered, 52) +UNORDERED_PLUGIN_WITH_VALUE(AllUnordered, 81) +UNORDERED_PLUGIN(AllUnordered) + +TEST(RegistryTest, AllUnordered) { + llvm::SmallVector Values; + for (const auto &Entry : AllUnorderedPluginRegistry::entries()) + Values.push_back(Entry.instantiate()->Value); + EXPECT_THAT(Values, UnorderedElementsAre(0, 34, 52, 81, 99)); +} + +CREATE_TEST_PLUGIN_AND_REGISTRY(AllOrdered) +ORDERED_PLUGIN(AllOrdered, 10) +ORDERED_PLUGIN(AllOrdered, 25) +ORDERED_PLUGIN(AllOrdered, 5) +ORDERED_PLUGIN(AllOrdered, 3) +ORDERED_PLUGIN(AllOrdered, 9) + +TEST(RegistryTest, AllOrdered) { + llvm::SmallVector Values; + for (const auto &Entry : AllOrderedPluginRegistry::entries()) + Values.push_back(Entry.instantiate()->Value); + EXPECT_THAT(Values, ElementsAre(3, 5, 9, 10, 25)); +} + +/// When a plugin registry mixes its ordering across entries. All entries that +/// specify an order should be registered first, all others will have an +/// undefined order at the end of the registry. +/// \code +/// Registry<...>::Add<...> Foo("Name", "Desc") +/// Registry<...>::Add<...> Bar("Name", "Desc", 10) +/// \endcode +/// In this example Bar will appear first + +CREATE_TEST_PLUGIN_AND_REGISTRY(MixedUnorderedStart) +UNORDERED_PLUGIN_WITH_VALUE(MixedUnorderedStart, 43) +UNORDERED_PLUGIN_WITH_VALUE(MixedUnorderedStart, 86) +ORDERED_PLUGIN(MixedUnorderedStart, 44) +ORDERED_PLUGIN(MixedUnorderedStart, 85) +UNORDERED_PLUGIN_WITH_VALUE(MixedUnorderedStart, 64) +ORDERED_PLUGIN(MixedUnorderedStart, 20) + +TEST(RegistryTest, MixedUnorderedStart) { + llvm::SmallVector Values; + for (const auto &Entry : MixedUnorderedStartPluginRegistry::entries()) + Values.push_back(Entry.instantiate()->Value); + EXPECT_THAT(makeArrayRef(Values).take_front(3), ElementsAre(20, 44, 85)); + EXPECT_THAT(makeArrayRef(Values).take_back(3), + UnorderedElementsAre(43, 86, 64)); +} + +CREATE_TEST_PLUGIN_AND_REGISTRY(MixedOrderedStart) +ORDERED_PLUGIN(MixedOrderedStart, 44) +UNORDERED_PLUGIN_WITH_VALUE(MixedOrderedStart, 43) +UNORDERED_PLUGIN_WITH_VALUE(MixedOrderedStart, 86) +ORDERED_PLUGIN(MixedOrderedStart, 85) +UNORDERED_PLUGIN_WITH_VALUE(MixedOrderedStart, 64) +ORDERED_PLUGIN(MixedOrderedStart, 20) + +TEST(RegistryTest, MixedOrderedStart) { + llvm::SmallVector Values; + for (const auto &Entry : MixedOrderedStartPluginRegistry::entries()) + Values.push_back(Entry.instantiate()->Value); + EXPECT_THAT(makeArrayRef(Values).take_front(3), ElementsAre(20, 44, 85)); + EXPECT_THAT(makeArrayRef(Values).take_back(3), + UnorderedElementsAre(43, 86, 64)); +}