diff --git a/include/phasar/DataFlow/IfdsIde/Solver/Compressor.h b/include/phasar/DataFlow/IfdsIde/Solver/Compressor.h index d0f9472d9c..29c900e0bb 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/Compressor.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/Compressor.h @@ -3,158 +3,12 @@ #include "phasar/DB/ProjectIRDBBase.h" #include "phasar/Utils/ByRef.h" -#include "phasar/Utils/TypeTraits.h" - -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/DenseMapInfo.h" -#include "llvm/ADT/SmallVector.h" +#include "phasar/Utils/Compressor.h" #include -#include -#include -#include #include namespace psr { -template class Compressor; - -/// \brief A utility class that assigns a sequential Id to every inserted -/// object. -/// -/// This specialization handles types that can be efficiently passed by value -template -class Compressor>> { -public: - void reserve(size_t Capacity) { - assert(Capacity <= UINT32_MAX); - ToInt.reserve(Capacity); - FromInt.reserve(Capacity); - } - - uint32_t getOrInsert(T Elem) { - auto [It, Inserted] = ToInt.try_emplace(Elem, ToInt.size()); - if (Inserted) { - FromInt.push_back(Elem); - } - return It->second; - } - - std::optional getOrNull(T Elem) const { - if (auto It = ToInt.find(Elem); It != ToInt.end()) { - return It->second; - } - return std::nullopt; - } - - T operator[](size_t Idx) const noexcept { - assert(Idx < FromInt.size()); - return FromInt[Idx]; - } - - [[nodiscard]] size_t size() const noexcept { return FromInt.size(); } - [[nodiscard]] size_t capacity() const noexcept { - return FromInt.capacity() + - ToInt.getMemorySize() / sizeof(typename decltype(ToInt)::value_type); - } - - auto begin() const noexcept { return FromInt.begin(); } - auto end() const noexcept { return FromInt.end(); } - -private: - llvm::DenseMap ToInt; - llvm::SmallVector FromInt; -}; - -/// \brief A utility class that assigns a sequential Id to every inserted -/// object. -/// -/// This specialization handles types that cannot be efficiently passed by value -template -class Compressor>> { -public: - void reserve(size_t Capacity) { - assert(Capacity <= UINT32_MAX); - ToInt.reserve(Capacity); - } - - /// Returns the index of the given element in the compressors storage. If the - /// element isn't present yet, it will be added first and its index will - /// then be returned. - uint32_t getOrInsert(const T &Elem) { - if (auto It = ToInt.find(&Elem); It != ToInt.end()) { - return It->second; - } - auto Ret = FromInt.size(); - auto *Ins = &FromInt.emplace_back(Elem); - ToInt[Ins] = Ret; - return Ret; - } - - /// Returns the index of the given element in the compressors storage. If the - /// element isn't present yet, it will be added first and its index will - /// then be returned. - uint32_t getOrInsert(T &&Elem) { - if (auto It = ToInt.find(&Elem); It != ToInt.end()) { - return It->second; - } - auto Ret = FromInt.size(); - auto *Ins = &FromInt.emplace_back(std::move(Elem)); - ToInt[Ins] = Ret; - return Ret; - } - - /// Returns the index of the given element in the compressors storage. If the - /// element isn't present, std::nullopt will be returned - std::optional getOrNull(const T &Elem) const { - if (auto It = ToInt.find(&Elem); It != ToInt.end()) { - return It->second; - } - return std::nullopt; - } - - const T &operator[](size_t Idx) const noexcept { - assert(Idx < FromInt.size()); - return FromInt[Idx]; - } - - [[nodiscard]] size_t size() const noexcept { return FromInt.size(); } - [[nodiscard]] size_t capacity() const noexcept { - return FromInt.size() + - ToInt.getMemorySize() / sizeof(typename decltype(ToInt)::value_type); - } - - auto begin() const noexcept { return FromInt.begin(); } - auto end() const noexcept { return FromInt.end(); } - -private: - struct DSI : llvm::DenseMapInfo { - static auto getHashValue(const T *Elem) noexcept { - assert(Elem != nullptr); - if constexpr (has_llvm_dense_map_info) { - return llvm::DenseMapInfo::getHashValue(*Elem); - } else { - return std::hash{}(*Elem); - } - } - static auto isEqual(const T *LHS, const T *RHS) noexcept { - if (LHS == RHS) { - return true; - } - if (LHS == DSI::getEmptyKey() || LHS == DSI::getTombstoneKey() || - RHS == DSI::getEmptyKey() || RHS == DSI::getTombstoneKey()) { - return false; - } - if constexpr (has_llvm_dense_map_info) { - return llvm::DenseMapInfo::isEqual(*LHS, *RHS); - } else { - return *LHS == *RHS; - } - } - }; - - std::deque FromInt; - llvm::DenseMap ToInt; -}; struct NoneCompressor final { constexpr NoneCompressor() noexcept = default; diff --git a/include/phasar/DataFlow/Mono/Contexts/CallStringCTX.h b/include/phasar/DataFlow/Mono/Contexts/CallStringCTX.h index c7326729b8..42ac02b425 100644 --- a/include/phasar/DataFlow/Mono/Contexts/CallStringCTX.h +++ b/include/phasar/DataFlow/Mono/Contexts/CallStringCTX.h @@ -3,10 +3,9 @@ #include "phasar/Utils/Printer.h" +#include "llvm/ADT/Hashing.h" #include "llvm/Support/raw_ostream.h" -#include "boost/functional/hash.hpp" - #include #include #include @@ -94,11 +93,9 @@ namespace std { template struct hash> { size_t operator()(const psr::CallStringCTX &CS) const noexcept { - boost::hash> HashDeque; - std::hash HashUnsigned; - size_t U = HashUnsigned(K); - size_t H = HashDeque(CS.CallString); - return U ^ (H << 1); + auto H = + llvm::hash_combine_range(CS.CallString.begin(), CS.CallString.end()); + return llvm::hash_combine(K, H); } }; diff --git a/include/phasar/Utils/BitVectorSet.h b/include/phasar/Utils/BitVectorSet.h index b11786e847..3ed4fe474b 100644 --- a/include/phasar/Utils/BitVectorSet.h +++ b/include/phasar/Utils/BitVectorSet.h @@ -7,22 +7,25 @@ * Philipp Schubert and Richard Leer *****************************************************************************/ -#ifndef PHASAR_UTILS_BITVECTORSET_H_ -#define PHASAR_UTILS_BITVECTORSET_H_ +#ifndef PHASAR_UTILS_BITVECTORSET_H +#define PHASAR_UTILS_BITVECTORSET_H + +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/Compressor.h" +#include "phasar/Utils/Fn.h" #include "llvm/ADT/BitVector.h" #include "llvm/ADT/Hashing.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallBitVector.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" -#include "boost/bimap.hpp" -#include "boost/bimap/unordered_set_of.hpp" - #include #include #include #include +#include namespace psr { namespace internal { @@ -61,122 +64,68 @@ inline llvm::ArrayRef getWords(const llvm::SmallBitVector &BV, } } // namespace internal -/** - * BitVectorSet implements a set that requires minimal space. Elements are - * kept in a static map and the set itself only stores a vector of bits which - * indicate whether elements are contained in the set. - * - * @brief Implements a set that requires minimal space. - */ +/// BitVectorSet implements a set that requires minimal space. Elements are +/// kept in a static map and the set itself only stores a vector of bits which +/// indicate whether elements are contained in the set. +/// +/// \brief Implements a set that requires minimal space. +/// \attention This data-structure is NOT thread-safe, since it relies on static +/// storage! +/// template class BitVectorSet { -public: - // Using boost::hash causes ambiguity for hash_value(): - // - - // - - // - - using bimap_t = boost::bimap>, - boost::bimaps::unordered_set_of>; -private: - inline static bimap_t Position; // NOLINT - BitVectorTy Bits; + static ByConstRef iterTransform(uint32_t Idx) { + assert(Position.inbounds(Idx)); + return Position[Idx]; + } - template class BitVectorSetIterator { - BitVectorTy Bits; + // using IteratorTy = + // llvm::mapped_iterator>; + // Unfortunately cannot use llvm::mapped_iterator, since the + // const_set_bits_iterator does not properly implement the iterator interface + // (typename iterator_category is missing) + class IteratorTy { public: + using value_type = T; + using reference = ByConstRef; + using pointer = const T *; + using difference_type = ptrdiff_t; using iterator_category = std::forward_iterator_tag; - using value_type = D; - using difference_type = std::ptrdiff_t; - using pointer = D *; - using reference = D &; - BitVectorSetIterator(D Ptr = nullptr) : PosPtr(Ptr) {} - - BitVectorSetIterator &operator=(D *Ptr) { - PosPtr = Ptr; - return *this; - } - - void setBits(const BitVectorTy &OtherBits) { Bits = OtherBits; } - bool operator==(const BitVectorSetIterator &OtherIterator) const { - return PosPtr == OtherIterator.getPtr(); - } - - bool operator!=(const BitVectorSetIterator &OtherIterator) const { - return !(*this == OtherIterator); - } - - BitVectorSetIterator &operator+=(const difference_type &Movement) { - for (difference_type I = 0; I < Movement; I++) { - PosPtr++; - } - return *this; - } - - BitVectorSetIterator &operator++() { - do { - int NextIdx = Bits.find_next(PosPtr->first); - - if (NextIdx <= static_cast(PosPtr->first)) { - PosPtr = Position.right.find(Bits.size()); - break; - } - - PosPtr = Position.right.find(NextIdx); - - assert(PosPtr->first < Bits.size() && - "pos_ptr->first index into BitVector out of range"); - } while (!Bits[PosPtr->first]); + constexpr IteratorTy(typename BitVectorTy::const_set_bits_iterator It, + fn_t Txn = {}) noexcept + : It(std::move(It)), Txn(Txn) {} + IteratorTy &operator++() { + ++It; return *this; } - - BitVectorSetIterator operator++(int) { - auto Temp(*this); + IteratorTy operator++(int) { + auto Cpy = *this; ++*this; - return Temp; + return Cpy; } - BitVectorSetIterator operator+(const difference_type &Movement) { - auto OldPtr = PosPtr; - for (difference_type I = 0; I < Movement; I++) { - PosPtr++; - } - auto Temp(*this); - PosPtr = OldPtr; - return Temp; - } + [[nodiscard]] reference operator*() const { return Txn(*It); } - difference_type operator-(const BitVectorSetIterator &OtherIterator) { - return std::distance(OtherIterator.getPtr(), this->getPtr()); + [[nodiscard]] bool operator==(const IteratorTy &Other) const { + return It == Other.It; + } + [[nodiscard]] bool operator!=(const IteratorTy &Other) const { + return !(*this == Other); } - - // T& operator*(){return pos_ptr->second;} - - const T &operator*() const { return PosPtr->second; } - - D *operator->() { return PosPtr; } - - [[nodiscard]] D getPtr() const { return PosPtr; } - - // const D* getConstPtr()const{return pos_ptr;} - - // T getPos() {return pos_ptr->first;} - - // T getVal() {return pos_ptr->second;} - - [[nodiscard]] const BitVectorTy &getBits() const { return Bits; } private: - D PosPtr; + typename BitVectorTy::const_set_bits_iterator It; + [[no_unique_address]] fn_t Txn; }; public: - using iterator = BitVectorSetIterator; - using const_iterator = - BitVectorSetIterator; + using const_iterator = IteratorTy; + using iterator = const_iterator; using value_type = T; BitVectorSet() = default; @@ -187,11 +136,12 @@ class BitVectorSet { insert(IList.begin(), IList.end()); } - template BitVectorSet(InputIt First, InputIt Last) { + template + explicit BitVectorSet(InputIt First, InputIt Last) { insert(First, Last); } - static BitVectorSet fromBits(BitVectorTy Bits) { + [[nodiscard]] static BitVectorSet fromBits(BitVectorTy Bits) { BitVectorSet Ret; Ret.Bits = std::move(Bits); return Ret; @@ -225,22 +175,13 @@ class BitVectorSet { } void insert(const T &Data) { - auto Search = Position.left.find(Data); - // Data already known - if (Search != Position.left.end()) { - if (Bits.size() <= Search->second) { - Bits.resize(Search->second + 1); - } - Bits.set(Search->second); - } else { - // Data unknown - size_t Idx = Position.left.size(); - Position.left.insert(std::make_pair(Data, Position.left.size())); - if (Bits.size() <= Position.left.size()) { - Bits.resize(Position.left.size()); - } - Bits.set(Idx); + uint32_t Idx = Position.getOrInsert(Data); + + if (Idx >= Bits.size()) { + Bits.resize(Idx + 1); } + + Bits.set(Idx); } void insert(const BitVectorSet &Other) { Bits |= Other.Bits; } @@ -253,10 +194,9 @@ class BitVectorSet { } void erase(const T &Data) noexcept { - auto Search = Position.left.find(Data); - if (Search != Position.left.end()) { - if (Bits.size() > Search->second) { - Bits.reset(Search->second); + if (auto Idx = Position.getOrNull(Data)) { + if (*Idx < Bits.size()) { + Bits.reset(*Idx); } } } @@ -281,12 +221,10 @@ class BitVectorSet { [[nodiscard]] bool find(const T &Data) const noexcept { return count(Data); } [[nodiscard]] size_t count(const T &Data) const noexcept { - auto Search = Position.left.find(Data); - if (Search != Position.left.end()) { - if (Bits.size() > Search->second) { - return Bits[Search->second]; - } + if (auto Idx = Position.getOrNull(Data)) { + return (*Idx < Bits.size() && Bits.test(*Idx)) ? 1 : 0; } + return 0; } @@ -347,56 +285,28 @@ class BitVectorSet { friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const BitVectorSet &B) { OS << '<'; - size_t Idx = 0; - for (auto &Position : B.Position.left) { - if (Position.second < B.Bits.size() && B.Bits[Position.second]) { - ++Idx; - OS << Position.first; - if (Idx < B.size()) { - OS << ", "; - } - } - } - OS << '>'; - return OS; + llvm::interleaveComma(B, OS); + return OS << '>'; } - [[nodiscard]] iterator begin() { - int Index = Bits.find_first(); - if (Index == -1) { - Index = Bits.size(); - } - iterator BeginIter(Position.right.find(Index)); - BeginIter.setBits(Bits); - return BeginIter; + [[nodiscard]] const_iterator begin() const noexcept { + return {Bits.set_bits_begin(), {}}; } - - [[nodiscard]] iterator end() { - iterator EndIter(Position.right.find(Bits.size())); - EndIter.setBits(Bits); - return EndIter; + [[nodiscard]] const_iterator end() const noexcept { + return {Bits.set_bits_end(), {}}; } - [[nodiscard]] const_iterator begin() const { - int Index = Bits.find_first(); - if (Index == -1) { - Index = Bits.size(); - } - const_iterator BeginIter(Position.right.find(Index)); - BeginIter.setBits(Bits); - return BeginIter; + [[nodiscard]] iterator begin() noexcept { + return {Bits.set_bits_begin(), {}}; } + [[nodiscard]] iterator end() noexcept { return {Bits.set_bits_end(), {}}; } - [[nodiscard]] const_iterator end() const { - const_iterator EndIter(Position.right.find(Bits.size())); - EndIter.setBits(Bits); - return EndIter; - } + static void clearPosition() noexcept { Position.clear(); } - static void clearPosition() { - Position.left.clear(); - Position.right.clear(); - } +private: + inline static Compressor Position; + + BitVectorTy Bits; }; // Overloads with the other intersectWith functions from Utilities.h diff --git a/include/phasar/Utils/Compressor.h b/include/phasar/Utils/Compressor.h new file mode 100644 index 0000000000..296dee5e08 --- /dev/null +++ b/include/phasar/Utils/Compressor.h @@ -0,0 +1,179 @@ +#ifndef PHASAR_UTILS_COMPRESSOR_H +#define PHASAR_UTILS_COMPRESSOR_H + +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/TypeTraits.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/ADT/SmallVector.h" + +#include +#include +#include +#include +#include + +namespace psr { +template +class Compressor; + +/// \brief A utility class that assigns a sequential Id to every inserted +/// object. +/// +/// This specialization handles types that can be efficiently passed by value +template +class Compressor>> { +public: + void reserve(size_t Capacity) { + assert(Capacity <= UINT32_MAX); + ToInt.reserve(Capacity); + FromInt.reserve(Capacity); + } + + IdT getOrInsert(T Elem) { + auto [It, Inserted] = ToInt.try_emplace(Elem, IdT(ToInt.size())); + if (Inserted) { + FromInt.push_back(Elem); + } + return It->second; + } + + std::optional getOrNull(T Elem) const { + if (auto It = ToInt.find(Elem); It != ToInt.end()) { + return It->second; + } + return std::nullopt; + } + + [[nodiscard]] bool inbounds(IdT Idx) const noexcept { + return size_t(Idx) < FromInt.size(); + } + + T operator[](IdT Idx) const noexcept { + assert(inbounds(Idx)); + return FromInt[size_t(Idx)]; + } + + [[nodiscard]] size_t size() const noexcept { return FromInt.size(); } + [[nodiscard]] size_t capacity() const noexcept { + return FromInt.capacity() + + ToInt.getMemorySize() / sizeof(typename decltype(ToInt)::value_type); + } + + auto begin() const noexcept { return FromInt.begin(); } + auto end() const noexcept { return FromInt.end(); } + + void clear() noexcept { + ToInt.clear(); + FromInt.clear(); + } + +private: + llvm::DenseMap ToInt; + llvm::SmallVector FromInt; +}; + +/// \brief A utility class that assigns a sequential Id to every inserted +/// object. +/// +/// This specialization handles types that cannot be efficiently passed by value +template +class Compressor>> { +public: + void reserve(size_t Capacity) { + assert(Capacity <= UINT32_MAX); + ToInt.reserve(Capacity); + } + + /// Returns the index of the given element in the compressors storage. If the + /// element isn't present yet, it will be added first and its index will + /// then be returned. + IdT getOrInsert(const T &Elem) { + if (auto It = ToInt.find(&Elem); It != ToInt.end()) { + return It->second; + } + auto Ret = IdT(FromInt.size()); + auto *Ins = &FromInt.emplace_back(Elem); + ToInt[Ins] = Ret; + return Ret; + } + + /// Returns the index of the given element in the compressors storage. If the + /// element isn't present yet, it will be added first and its index will + /// then be returned. + IdT getOrInsert(T &&Elem) { + if (auto It = ToInt.find(&Elem); It != ToInt.end()) { + return It->second; + } + auto Ret = IdT(FromInt.size()); + auto *Ins = &FromInt.emplace_back(std::move(Elem)); + ToInt[Ins] = Ret; + return Ret; + } + + /// Returns the index of the given element in the compressors storage. If the + /// element isn't present, std::nullopt will be returned + std::optional getOrNull(const T &Elem) const { + if (auto It = ToInt.find(&Elem); It != ToInt.end()) { + return It->second; + } + return std::nullopt; + } + + [[nodiscard]] bool inbounds(IdT Idx) const noexcept { + return size_t(Idx) < FromInt.size(); + } + + const T &operator[](IdT Idx) const noexcept { + assert(inbounds(Idx)); + return FromInt[size_t(Idx)]; + } + + [[nodiscard]] size_t size() const noexcept { return FromInt.size(); } + [[nodiscard]] size_t capacity() const noexcept { + return FromInt.size() + + ToInt.getMemorySize() / sizeof(typename decltype(ToInt)::value_type); + } + + auto begin() const noexcept { return FromInt.begin(); } + auto end() const noexcept { return FromInt.end(); } + + void clear() noexcept { + ToInt.clear(); + FromInt.clear(); + } + +private: + struct DSI : llvm::DenseMapInfo { + static auto getHashValue(const T *Elem) noexcept { + assert(Elem != nullptr); + if constexpr (has_llvm_dense_map_info) { + return llvm::DenseMapInfo::getHashValue(*Elem); + } else { + return std::hash{}(*Elem); + } + } + static auto isEqual(const T *LHS, const T *RHS) noexcept { + if (LHS == RHS) { + return true; + } + if (LHS == DSI::getEmptyKey() || LHS == DSI::getTombstoneKey() || + RHS == DSI::getEmptyKey() || RHS == DSI::getTombstoneKey()) { + return false; + } + if constexpr (has_llvm_dense_map_info) { + return llvm::DenseMapInfo::isEqual(*LHS, *RHS); + } else { + return *LHS == *RHS; + } + } + }; + + std::deque FromInt; + llvm::DenseMap ToInt; +}; + +} // namespace psr + +#endif // PHASAR_DATAFLOW_IFDSIDE_SOLVER_COMPRESSOR_H diff --git a/include/phasar/Utils/Fn.h b/include/phasar/Utils/Fn.h index 55cc644149..060a2f7b77 100644 --- a/include/phasar/Utils/Fn.h +++ b/include/phasar/Utils/Fn.h @@ -24,8 +24,8 @@ namespace psr { template struct fn_t { // NOLINT(readability-identifier-naming) template constexpr std::invoke_result_t - operator()(ArgsT &&...Args) noexcept( - std::is_nothrow_invocable_v) { + operator()(ArgsT &&...Args) const + noexcept(std::is_nothrow_invocable_v) { return std::invoke(F, PSR_FWD(Args)...); } }; diff --git a/lib/Utils/Utilities.cpp b/lib/Utils/Utilities.cpp index 0c2aeedaca..0689e09d70 100644 --- a/lib/Utils/Utilities.cpp +++ b/lib/Utils/Utilities.cpp @@ -12,11 +12,10 @@ #include "phasar/Utils/Logger.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Demangle/Demangle.h" #include "llvm/IR/DerivedTypes.h" -#include "boost/algorithm/string/find.hpp" - #include #include @@ -43,21 +42,19 @@ bool isConstructor(llvm::StringRef MangledName) { // see https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling // This version will not work in some edge cases - auto Constructor = boost::algorithm::find_last(MangledName, "C2E"); - if (Constructor.begin() != Constructor.end()) { + auto Constructor = MangledName.rfind("C2E"); + if (Constructor != llvm::StringRef::npos) { return true; } - Constructor = boost::algorithm::find_last(MangledName, "C1E"); - - if (Constructor.begin() != Constructor.end()) { + Constructor = MangledName.rfind("C1E"); + if (Constructor != llvm::StringRef::npos) { return true; } - Constructor = boost::algorithm::find_last(MangledName, "C2E"); - - if (Constructor.begin() != Constructor.end()) { + Constructor = MangledName.rfind("C3E"); + if (Constructor != llvm::StringRef::npos) { return true; } diff --git a/unittests/Utils/BitVectorSetTest.cpp b/unittests/Utils/BitVectorSetTest.cpp index 9f9a073b03..f85dba2d8b 100644 --- a/unittests/Utils/BitVectorSetTest.cpp +++ b/unittests/Utils/BitVectorSetTest.cpp @@ -6,6 +6,7 @@ #include "gtest/gtest.h" +#include #include #include #include @@ -345,8 +346,8 @@ TEST(BitVectorSet, iterator_movement) { auto IteratorA = A.begin(); auto IteratorB = B.begin(); - IteratorA += 4; - IteratorB += 2; + std::advance(IteratorA, 4); + std::advance(IteratorB, 2); EXPECT_EQ(*IteratorA, *IteratorB); EXPECT_EQ(A.count(*IteratorA), 1U); IteratorB++; @@ -382,7 +383,6 @@ TEST(BitVectorSet, rangeFor) { const BitVectorSet D({5, 6, 7, 8, 42, 13}); std::set DS; std::set DSGT = {5, 6, 7, 8, 42, 13}; - auto I = D.begin(); for (auto I : D) { DS.insert(I); }