//===------ SymbolLookupSet.h - Symbol set for ORC lookups ------*- 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 // //===----------------------------------------------------------------------===// // // SymbolLookupSet class. // //===----------------------------------------------------------------------===// #ifndef LLVM_EXECUTIONENGINE_ORC_SYMBOLLOOKUPSET_H #define LLVM_EXECUTIONENGINE_ORC_SYMBOLLOOKUPSET_H #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ExecutionEngine/Orc/CoreContainers.h" #include "llvm/Support/Error.h" #include #include #include #include namespace llvm::orc { /// Lookup flags that apply to each symbol in a lookup. /// /// If RequiredSymbol is used (the default) for a given symbol then that symbol /// must be found during the lookup or the lookup will fail returning a /// SymbolNotFound error. If WeaklyReferencedSymbol is used and the given /// symbol is not found then the query will continue, and no result for the /// missing symbol will be present in the result (assuming the rest of the /// lookup succeeds). enum class SymbolLookupFlags { RequiredSymbol, WeaklyReferencedSymbol }; /// A set of symbols to look up, each associated with a SymbolLookupFlags /// value. /// /// This class is backed by a vector and optimized for fast insertion, /// deletion and iteration. It does not guarantee a stable order between /// operations, and will not automatically detect duplicate elements (they /// can be manually checked by calling the validate method). class SymbolLookupSet { public: using value_type = std::pair; using UnderlyingVector = std::vector; using iterator = UnderlyingVector::iterator; using const_iterator = UnderlyingVector::const_iterator; SymbolLookupSet() = default; SymbolLookupSet(std::initializer_list Elems) { for (auto &E : Elems) Symbols.push_back(std::move(E)); } explicit SymbolLookupSet( SymbolStringPtr Name, SymbolLookupFlags Flags = SymbolLookupFlags::RequiredSymbol) { add(std::move(Name), Flags); } /// Construct a SymbolLookupSet from an initializer list of SymbolStringPtrs. explicit SymbolLookupSet( std::initializer_list Names, SymbolLookupFlags Flags = SymbolLookupFlags::RequiredSymbol) { Symbols.reserve(Names.size()); for (const auto &Name : Names) add(std::move(Name), Flags); } /// Construct a SymbolLookupSet from a SymbolNameSet with the given /// Flags used for each value. explicit SymbolLookupSet( const SymbolNameSet &Names, SymbolLookupFlags Flags = SymbolLookupFlags::RequiredSymbol) { Symbols.reserve(Names.size()); for (const auto &Name : Names) add(Name, Flags); } /// Construct a SymbolLookupSet from a vector of symbols with the given Flags /// used for each value. /// If the ArrayRef contains duplicates it is up to the client to remove these /// before using this instance for lookup. explicit SymbolLookupSet( ArrayRef Names, SymbolLookupFlags Flags = SymbolLookupFlags::RequiredSymbol) { Symbols.reserve(Names.size()); for (const auto &Name : Names) add(Name, Flags); } /// Construct a SymbolLookupSet from DenseMap keys. template static SymbolLookupSet fromMapKeys(const DenseMap &M, SymbolLookupFlags Flags = SymbolLookupFlags::RequiredSymbol) { SymbolLookupSet Result; Result.Symbols.reserve(M.size()); for (const auto &[Name, Val] : M) Result.add(Name, Flags); return Result; } /// Add an element to the set. The client is responsible for checking that /// duplicates are not added. SymbolLookupSet & add(SymbolStringPtr Name, SymbolLookupFlags Flags = SymbolLookupFlags::RequiredSymbol) { Symbols.push_back(std::make_pair(std::move(Name), Flags)); return *this; } /// Quickly append one lookup set to another. SymbolLookupSet &append(SymbolLookupSet Other) { Symbols.reserve(Symbols.size() + Other.size()); for (auto &KV : Other) Symbols.push_back(std::move(KV)); return *this; } bool empty() const { return Symbols.empty(); } UnderlyingVector::size_type size() const { return Symbols.size(); } iterator begin() { return Symbols.begin(); } iterator end() { return Symbols.end(); } const_iterator begin() const { return Symbols.begin(); } const_iterator end() const { return Symbols.end(); } /// Removes the Ith element of the vector, replacing it with the last element. void remove(UnderlyingVector::size_type I) { std::swap(Symbols[I], Symbols.back()); Symbols.pop_back(); } /// Removes the element pointed to by the given iterator. This iterator and /// all subsequent ones (including end()) are invalidated. void remove(iterator I) { remove(I - begin()); } /// Removes all elements matching the given predicate, which must be callable /// as bool(const SymbolStringPtr &, SymbolLookupFlags Flags). template void remove_if(PredFn &&Pred) { UnderlyingVector::size_type I = 0; while (I != Symbols.size()) { const auto &Name = Symbols[I].first; auto Flags = Symbols[I].second; if (Pred(Name, Flags)) remove(I); else ++I; } } /// Loop over the elements of this SymbolLookupSet, applying the Body function /// to each one. Body must be callable as /// bool(const SymbolStringPtr &, SymbolLookupFlags). /// If Body returns true then the element just passed in is removed from the /// set. If Body returns false then the element is retained. template auto forEachWithRemoval(BodyFn &&Body) -> std::enable_if_t< std::is_same(), std::declval())), bool>::value> { UnderlyingVector::size_type I = 0; while (I != Symbols.size()) { const auto &Name = Symbols[I].first; auto Flags = Symbols[I].second; if (Body(Name, Flags)) remove(I); else ++I; } } /// Loop over the elements of this SymbolLookupSet, applying the Body function /// to each one. Body must be callable as /// Expected(const SymbolStringPtr &, SymbolLookupFlags). /// If Body returns a failure value, the loop exits immediately. If Body /// returns true then the element just passed in is removed from the set. If /// Body returns false then the element is retained. template auto forEachWithRemoval(BodyFn &&Body) -> std::enable_if_t< std::is_same(), std::declval())), Expected>::value, Error> { UnderlyingVector::size_type I = 0; while (I != Symbols.size()) { const auto &Name = Symbols[I].first; auto Flags = Symbols[I].second; auto Remove = Body(Name, Flags); if (!Remove) return Remove.takeError(); if (*Remove) remove(I); else ++I; } return Error::success(); } /// Construct a SymbolNameVector from this instance by dropping the Flags /// values. SymbolNameVector getSymbolNames() const { SymbolNameVector Names; Names.reserve(Symbols.size()); for (const auto &KV : Symbols) Names.push_back(KV.first); return Names; } /// Sort the lookup set by pointer value. This sort is fast but sensitive to /// allocation order and so should not be used where a consistent order is /// required. void sortByAddress() { llvm::sort(Symbols, llvm::less_first()); } /// Sort the lookup set lexicographically. This sort is slow but the order /// is unaffected by allocation order. void sortByName() { llvm::sort(Symbols, [](const value_type &LHS, const value_type &RHS) { return *LHS.first < *RHS.first; }); } /// Remove any duplicate elements. If a SymbolLookupSet is not duplicate-free /// by construction, this method can be used to turn it into a proper set. void removeDuplicates() { sortByAddress(); auto LastI = llvm::unique(Symbols); Symbols.erase(LastI, Symbols.end()); } #ifndef NDEBUG /// Returns true if this set contains any duplicates. This should only be used /// in assertions. bool containsDuplicates() { if (Symbols.size() < 2) return false; sortByAddress(); for (UnderlyingVector::size_type I = 1; I != Symbols.size(); ++I) if (Symbols[I].first == Symbols[I - 1].first) return true; return false; } #endif private: UnderlyingVector Symbols; }; } // namespace llvm::orc #endif // LLVM_EXECUTIONENGINE_ORC_SYMBOLLOOKUPSET_H