From 92c9b15c040d05d1b82c4caca4fc84c716400234 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Wed, 26 Aug 2026 18:34:33 +0200 Subject: [PATCH 01/14] Remove TBB requirement, which is unused --- CMakeLists.txt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3182ee87c7..1a28946c99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -271,9 +271,6 @@ include(add_nlohmann_json) add_nlohmann_json() add_json_schema_validator() -# TBB -find_package(TBB) - # Coverage if (CODE_COVERAGE) set(CODE_COVERAGE_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/code-coverage.cmake") @@ -428,10 +425,6 @@ target_sources(phasar_interface INTERFACE BASE_DIRS "${PHASAR_SRC_DIR}/include" "${PHASAR_BINARY_DIR}/include" FILES ${PHASAR_PUBLIC_HEADERS} "${PHASAR_BINARY_DIR}/include/phasar/Config/phasar-config.h" ) -if (TARGET TBB::tbb) - target_link_libraries(phasar_interface INTERFACE TBB::tbb) -endif() - # Some preprocessor symbols that need to be available in phasar sources, but should not be installed add_cxx_compile_definitions(PHASAR_SRC_DIR="${CMAKE_SOURCE_DIR}") From fbf2ae0f946c7ff63699bdd4cd0b1d18cfa17d16 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Wed, 26 Aug 2026 18:41:23 +0200 Subject: [PATCH 02/14] A moved-from BitSet must be empty to prevent UB in ValueIdMap's dtor with non-trivial value-type --- include/phasar/Utils/BitSet.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/include/phasar/Utils/BitSet.h b/include/phasar/Utils/BitSet.h index 80f07eba0b..e8c046ea72 100644 --- a/include/phasar/Utils/BitSet.h +++ b/include/phasar/Utils/BitSet.h @@ -24,6 +24,7 @@ #include #include #include +#include namespace psr { @@ -99,6 +100,19 @@ class BitSet { explicit BitSet(size_t InitialCapacity, bool InitialValue) : Bits(InitialCapacity, InitialValue) {} + // Moved-from llvm::BitVector is not empty. ValueIdMap requires this in its + // dtor. + BitSet(BitSet &&Other) noexcept : Bits(std::exchange(Other.Bits, {})) {} + BitSet &operator=(BitSet &&Other) noexcept { + std::swap(Bits, Other.Bits); + return *this; + } + + BitSet(const BitSet &) = default; + BitSet &operator=(const BitSet &) = default; + + ~BitSet() = default; + void reserve(size_t Cap) { if (Bits.size() < Cap) { Bits.resize(Cap); From 5d6f2a53a6de580533d2332f500563b5c29b5ff9 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Wed, 26 Aug 2026 19:01:26 +0200 Subject: [PATCH 03/14] Improve .foreach()-style iteration with early-exit + add ValueIdMap::foreach --- include/phasar/Utils/BitSet.h | 8 ++++-- include/phasar/Utils/SmallArraySet.h | 6 ++--- include/phasar/Utils/SparseBitSet.h | 18 +++++-------- include/phasar/Utils/Utilities.h | 38 ++++++++++++++++++++++++++++ include/phasar/Utils/ValueIdMap.h | 14 +++++++++- 5 files changed, 65 insertions(+), 19 deletions(-) diff --git a/include/phasar/Utils/BitSet.h b/include/phasar/Utils/BitSet.h index e8c046ea72..0d36eb2550 100644 --- a/include/phasar/Utils/BitSet.h +++ b/include/phasar/Utils/BitSet.h @@ -11,6 +11,7 @@ #define PHASAR_UTILS_BITSET_H #include "phasar/Utils/TypeTraits.h" +#include "phasar/Utils/Utilities.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallBitVector.h" @@ -220,7 +221,7 @@ class BitSet { /// /// This is likely faster than using iterators. template HandlerFn> - void foreach (HandlerFn Handler) const + bool foreach (HandlerFn Handler) const noexcept(std::is_nothrow_invocable_v) { uintptr_t Store{}; auto Words = getWords(Bits, Store); @@ -229,11 +230,14 @@ class BitSet { while (W) { auto Curr = std::countr_zero(W) + Offset; W &= W - 1; - std::invoke(Handler, IdT(Curr)); + if (!invokeControlFlow(Handler, IdT(Curr))) { + return false; + } } Offset += sizeof(W) * CHAR_BIT; } + return true; } /// Same as mergeWith() diff --git a/include/phasar/Utils/SmallArraySet.h b/include/phasar/Utils/SmallArraySet.h index 9022c6c1c8..e5494ed1d7 100644 --- a/include/phasar/Utils/SmallArraySet.h +++ b/include/phasar/Utils/SmallArraySet.h @@ -268,11 +268,9 @@ class SmallArraySet : private llvm::SmallVector { std::ranges::sort(base()); } - LLVM_ATTRIBUTE_ALWAYS_INLINE void foreach ( + LLVM_ATTRIBUTE_ALWAYS_INLINE auto foreach ( std::invocable auto Handler) const { - for (const auto &Elem : base()) { - std::invoke(Handler, Elem); - } + return psr::foreachInRange(base(), std::move(Handler)); } [[nodiscard]] friend auto hash_value(const SmallArraySet &Set) noexcept { diff --git a/include/phasar/Utils/SparseBitSet.h b/include/phasar/Utils/SparseBitSet.h index f09a0d151b..bbbe30dc2f 100644 --- a/include/phasar/Utils/SparseBitSet.h +++ b/include/phasar/Utils/SparseBitSet.h @@ -37,19 +37,13 @@ template class SparseBitSet { } template HandlerFn> - LLVM_ATTRIBUTE_ALWAYS_INLINE void foreach (HandlerFn Handler) const { - return Bits.iterate( - [](uint32_t Id, void *HandlerPtr) { + LLVM_ATTRIBUTE_ALWAYS_INLINE bool foreach (HandlerFn Handler) const { + // Unfortunately, Bits.iterate() returns void... + return roaring::api::roaring_iterate( + &Bits.roaring, + [](uint32_t Id, void *HandlerPtr) -> bool { auto &Handler = *(HandlerFn *)HandlerPtr; - if constexpr (std::convertible_to< - std::invoke_result_t, bool>) { - if (!std::invoke(Handler, IdT(Id))) { - return false; - } - } else { - std::invoke(Handler, IdT(Id)); - } - return true; + return invokeControlFlow(Handler, IdT(Id)); }, &Handler); } diff --git a/include/phasar/Utils/Utilities.h b/include/phasar/Utils/Utilities.h index c122152674..6101150b9a 100644 --- a/include/phasar/Utils/Utilities.h +++ b/include/phasar/Utils/Utilities.h @@ -11,13 +11,17 @@ #define PHASAR_UTILS_UTILITIES_H_ #include "phasar/Utils/ByRef.h" +#include "phasar/Utils/Macros.h" #include "phasar/Utils/TypeTraits.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" +#include +#include #include +#include #include #include #include @@ -362,6 +366,40 @@ template return {std::move(First), std::move(Second)}; } +template + requires(std::invocable) +[[nodiscard]] constexpr auto invokeControlFlow( + FnT &&Fn, + ArgsT &&...Args) noexcept(std::is_nothrow_invocable_v) { + using ResultT = std::invoke_result_t; + if constexpr (std::is_void_v) { + std::invoke(PSR_FWD(Fn), PSR_FWD(Args)...); + return std::bool_constant{}; + } else { + static_assert(std::convertible_to); + return std::invoke(PSR_FWD(Fn), PSR_FWD(Args)...); + } +} + +template +constexpr auto foreachInRange(RangeT &&Range, FnT &&Fn) { + using std::begin; + using ElemT = decltype(*begin(Range)); + using ResultT = std::invoke_result_t; + if constexpr (std::is_void_v) { + using std::end; + std::for_each(begin(Range), end(Range), copyOrRef(Fn)); + return std::bool_constant{}; + } else { + for (auto &&Elem : PSR_FWD(Range)) { + if (!std::invoke(Fn, PSR_FWD(Elem))) { + return false; + } + } + return true; + } +} + } // namespace psr #endif diff --git a/include/phasar/Utils/ValueIdMap.h b/include/phasar/Utils/ValueIdMap.h index bce7bd6cf6..bb949bfd5c 100644 --- a/include/phasar/Utils/ValueIdMap.h +++ b/include/phasar/Utils/ValueIdMap.h @@ -13,6 +13,7 @@ #include "phasar/Utils/BitSet.h" #include "phasar/Utils/Macros.h" #include "phasar/Utils/TypeTraits.h" +#include "phasar/Utils/Utilities.h" #include #include @@ -478,8 +479,19 @@ class ValueIdMap { } [[nodiscard]] constexpr const_iterator cend() const noexcept { return end(); } + auto foreach (std::invocable auto Handler) { + return foreachImpl(*this, std::move(Handler)); + } + auto foreach (std::invocable auto Handler) const { + return foreachImpl(*this, std::move(Handler)); + } + private: - // ── Private helpers ──────────────────────────────────────────────────────── + static auto foreachImpl(auto &&Self, auto &&Handler) { + return Self.IsSet.foreach ([&Self, Handler{copyOrRef(Handler)}](IdT Id) { + return psr::invokeControlFlow(Handler, Id, *Self.slot(Id)); + }); + } [[nodiscard]] constexpr ValueT *slot(IdT Key) noexcept { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) From 16fc8f58561a48fef9871888d1b55676267121d4 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Wed, 26 Aug 2026 19:03:32 +0200 Subject: [PATCH 04/14] Fix DefaultIDESolverConfig with problem-concepts --- .../DataFlow/IfdsIde/Solver/StaticIDESolverConfig.h | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/include/phasar/DataFlow/IfdsIde/Solver/StaticIDESolverConfig.h b/include/phasar/DataFlow/IfdsIde/Solver/StaticIDESolverConfig.h index 3a4c274e30..86b421fa00 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/StaticIDESolverConfig.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/StaticIDESolverConfig.h @@ -115,14 +115,11 @@ struct PSR_PREFERRED_NAME(IFDSSolverConfigWithStatsAndGC) WithGCMode : Base { static constexpr JumpFunctionGCMode EnableJumpFunctionGC = GCMode; }; -template -struct DefaultIDESolverConfig : IDESolverConfig {}; - -template - requires std::is_base_of_v< - IFDSTabulationProblem, - ProblemTy> -struct DefaultIDESolverConfig : IFDSSolverConfig {}; +template +struct DefaultIDESolverConfig : IFDSSolverConfig {}; + +template +struct DefaultIDESolverConfig : IDESolverConfig {}; } // namespace psr From 2b44a71c5b668531b8ddcfa9e6cff26b61bdbbe2 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Thu, 27 Aug 2026 18:47:53 +0200 Subject: [PATCH 05/14] Some fixes in sparse WPDS --- include/phasar/DataFlow/WPDS/IfdsIdeRuleProvider.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/include/phasar/DataFlow/WPDS/IfdsIdeRuleProvider.h b/include/phasar/DataFlow/WPDS/IfdsIdeRuleProvider.h index d27120da00..5a292cc7df 100644 --- a/include/phasar/DataFlow/WPDS/IfdsIdeRuleProvider.h +++ b/include/phasar/DataFlow/WPDS/IfdsIdeRuleProvider.h @@ -154,9 +154,10 @@ class IfdsIdeRuleProvider { } }(); for (const auto &Succ : RetSites) { - const auto FctSucc = factSucc(Succ, Fct); + // Note: No sparsification here, because getPopRules() needs the + // predecessor of Succ for (const auto &EntrySE : EntrySEs) { - Outs.emplace_back(Fct, FctSucc, EntrySE, W); + Outs.emplace_back(Fct, Succ, EntrySE, W); } } } @@ -240,13 +241,15 @@ class IfdsIdeRuleProvider { return Outs; } - [[nodiscard]] constexpr auto &problem() const noexcept { return *Problem; } + [[nodiscard]] constexpr auto &problem() const noexcept { + return Problem.ideProblem(); + } private: [[nodiscard]] auto factSucc(stack_element_type Succ, ByConstRef CL) { if constexpr (has_advanceToNextUser_v) { - return ICF->advancetoNextUser(Succ, CL); + return ICF->advanceToNextUser(Succ, CL); } else { return Succ; } From 287c1a0716d0563d18aaa37f0e9ad2b867073669 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Thu, 27 Aug 2026 18:50:24 +0200 Subject: [PATCH 06/14] Make CachedLLVMAliasIterator conform to the IsAliasInfo concept --- .../Pointer/CachedLLVMAliasIterator.h | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/include/phasar/PhasarLLVM/Pointer/CachedLLVMAliasIterator.h b/include/phasar/PhasarLLVM/Pointer/CachedLLVMAliasIterator.h index 130c11f8f8..cd771e0087 100644 --- a/include/phasar/PhasarLLVM/Pointer/CachedLLVMAliasIterator.h +++ b/include/phasar/PhasarLLVM/Pointer/CachedLLVMAliasIterator.h @@ -12,8 +12,10 @@ #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include "phasar/Pointer/AliasAnalysisType.h" +#include "phasar/Pointer/AliasInfoBase.h" #include "phasar/Pointer/AliasInfoTraits.h" #include "phasar/Pointer/AliasSetOwner.h" +#include "phasar/Utils/AnalysisProperties.h" #include "llvm/IR/Function.h" @@ -31,7 +33,8 @@ struct AliasInfoTraits /// \note Currently assumes that the underlying alias information is /// flow-insensitive and the granularity of different alias-information per /// instruction is actually at function-level -class CachedLLVMAliasIterator { +class CachedLLVMAliasIterator + : public AnalysisPropertiesMixin { public: using alias_traits_t = AliasInfoTraits; using n_t = alias_traits_t::n_t; @@ -44,11 +47,12 @@ class CachedLLVMAliasIterator { // --- API Functions: - [[nodiscard]] inline bool isInterProcedural() const noexcept { + [[nodiscard]] constexpr bool isInterProcedural() const noexcept { return false; // No idea, so be conservative here }; - [[nodiscard]] AliasAnalysisType getAliasAnalysisType() const noexcept { + [[nodiscard]] constexpr AliasAnalysisType + getAliasAnalysisType() const noexcept { return AliasAnalysisType::Invalid; // No idea } @@ -83,7 +87,8 @@ class CachedLLVMAliasIterator { void printAsJson(llvm::raw_ostream &OS = llvm::outs()) const; - [[nodiscard]] AnalysisProperties getAnalysisProperties() const noexcept { + [[nodiscard]] constexpr AnalysisProperties + getAnalysisProperties() const noexcept { return AnalysisProperties::None; } @@ -98,18 +103,18 @@ class CachedLLVMAliasIterator { }; struct ReachableAllocationSitesKeyDMI { - inline static ReachableAllocationSitesKey getEmptyKey() noexcept { + static ReachableAllocationSitesKey getEmptyKey() noexcept { return {{}, llvm::DenseMapInfo::getEmptyKey()}; } - inline static ReachableAllocationSitesKey getTombstoneKey() noexcept { + static ReachableAllocationSitesKey getTombstoneKey() noexcept { return {{}, llvm::DenseMapInfo::getTombstoneKey()}; } - inline static auto getHashValue(ReachableAllocationSitesKey Key) noexcept { + static auto getHashValue(ReachableAllocationSitesKey Key) noexcept { return llvm::hash_combine(Key.FunAndIntraProcOnly.getOpaqueValue(), Key.Value); } - inline static bool isEqual(ReachableAllocationSitesKey Key1, - ReachableAllocationSitesKey Key2) noexcept { + static bool isEqual(ReachableAllocationSitesKey Key1, + ReachableAllocationSitesKey Key2) noexcept { return Key1.FunAndIntraProcOnly == Key2.FunAndIntraProcOnly && Key1.Value == Key2.Value; } @@ -124,6 +129,8 @@ class CachedLLVMAliasIterator { ReachableAllocationSitesKeyDMI> ReachableAllocationSitesMap; }; + +static_assert(IsAliasInfo); } // namespace psr #endif // PHASAR_PHASARLLVM_POINTER_CACHEDALIASITERATOR_H From e79fe5f266b0a42e652709df0e1c2f502010027c Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Thu, 27 Aug 2026 18:52:34 +0200 Subject: [PATCH 07/14] Better CanEfficientlyPassByValue --- include/phasar/Utils/ByRef.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/phasar/Utils/ByRef.h b/include/phasar/Utils/ByRef.h index a713344474..6bab046ad8 100644 --- a/include/phasar/Utils/ByRef.h +++ b/include/phasar/Utils/ByRef.h @@ -18,7 +18,9 @@ namespace psr { template concept CanEfficientlyPassByValue = - sizeof(T) <= 2 * sizeof(void *) && std::is_trivially_copyable_v; + std::is_reference_v || (sizeof(T) <= 2 * sizeof(void *) && + std::is_trivially_copy_constructible_v && + std::is_trivially_destructible_v); template using ByConstRef = From 3973b87c299e025b2d79f2fa67f313aa5e44ff26 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Thu, 27 Aug 2026 19:00:37 +0200 Subject: [PATCH 08/14] minor --- include/phasar/Utils/Compressor.h | 2 +- include/phasar/Utils/Utilities.h | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/phasar/Utils/Compressor.h b/include/phasar/Utils/Compressor.h index a7d4fc1893..2a2eb6ea3d 100644 --- a/include/phasar/Utils/Compressor.h +++ b/include/phasar/Utils/Compressor.h @@ -167,7 +167,7 @@ class Compressor { } IdT insertDummy(std::convertible_to auto &&Elem) { - auto Ret = Id(FromInt.size()); + auto Ret = IdT(FromInt.size()); FromInt.emplace_back(PSR_FWD(Elem)); return Ret; } diff --git a/include/phasar/Utils/Utilities.h b/include/phasar/Utils/Utilities.h index 6101150b9a..b9659a7133 100644 --- a/include/phasar/Utils/Utilities.h +++ b/include/phasar/Utils/Utilities.h @@ -161,8 +161,9 @@ struct StringIDLess { /// See template class scope_exit { // NOLINT public: - template ()())> - scope_exit(FFn &&F) noexcept(std::is_nothrow_constructible_v) + template FFn> + constexpr scope_exit(FFn &&F) noexcept( + std::is_nothrow_constructible_v) : F(std::forward(F)) {} ~scope_exit() noexcept { F(); } From 05c89721a4ca58bd1fa899b69d1aef057ea2d1ec Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Thu, 27 Aug 2026 19:07:09 +0200 Subject: [PATCH 09/14] Small improvement in MemSSA --- .../phasar/PhasarLLVM/Pointer/MemSSAUtils.h | 3 ++- lib/PhasarLLVM/Pointer/MemSSAUtils.cpp | 18 +++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/include/phasar/PhasarLLVM/Pointer/MemSSAUtils.h b/include/phasar/PhasarLLVM/Pointer/MemSSAUtils.h index f9fd5aa5c9..ca91c10498 100644 --- a/include/phasar/PhasarLLVM/Pointer/MemSSAUtils.h +++ b/include/phasar/PhasarLLVM/Pointer/MemSSAUtils.h @@ -42,7 +42,8 @@ struct MemSSABundle { /// Returns true if a LiveOnEntry def is reachable (value may come from outside /// the function). In that case, ReachingDefs may be incompletely populated. [[nodiscard]] bool collectReachingDefs( - llvm::MemoryAccess *MA, const llvm::MemorySSA &MSSA, + llvm::MemoryAccess *MA, llvm::MemorySSA &MSSA, + const llvm::MemoryLocation &Loc, llvm::SmallPtrSetImpl &ReachingDefs, llvm::SmallPtrSetImpl &Visited); diff --git a/lib/PhasarLLVM/Pointer/MemSSAUtils.cpp b/lib/PhasarLLVM/Pointer/MemSSAUtils.cpp index 0d1f024ef9..abac86abad 100644 --- a/lib/PhasarLLVM/Pointer/MemSSAUtils.cpp +++ b/lib/PhasarLLVM/Pointer/MemSSAUtils.cpp @@ -33,7 +33,8 @@ MemSSABundle::MemSSABundle(llvm::Function &F, } bool psr::collectReachingDefs( - llvm::MemoryAccess *MA, const llvm::MemorySSA &MSSA, + llvm::MemoryAccess *MA, llvm::MemorySSA &MSSA, + const llvm::MemoryLocation &Loc, llvm::SmallPtrSetImpl &ReachingDefs, llvm::SmallPtrSetImpl &Visited) { if (!Visited.insert(MA).second) { @@ -53,9 +54,15 @@ bool psr::collectReachingDefs( } if (auto *Phi = llvm::dyn_cast(MA)) { for (const auto &Inc : Phi->incoming_values()) { - bool LOE = collectReachingDefs(llvm::cast(Inc.get()), - MSSA, ReachingDefs, Visited); - if (LOE) { + auto *IncMA = llvm::cast(Inc.get()); + // The def that immediately precedes the phi on this path need not + // clobber Loc at all, so ask the walker again instead of taking it. + // Without this, an unrelated store shadows the actual definition. + if (llvm::isa(IncMA) && + !MSSA.isLiveOnEntryDef(IncMA)) { + IncMA = MSSA.getWalker()->getClobberingMemoryAccess(IncMA, Loc); + } + if (collectReachingDefs(IncMA, MSSA, Loc, ReachingDefs, Visited)) { return true; } } @@ -69,7 +76,8 @@ bool psr::collectReachingDefs( if (auto *Access = MSSA.getMemoryAccess(Load)) { auto *Clobber = MSSA.getWalker()->getClobberingMemoryAccess(Access); llvm::SmallPtrSet Visited; - return collectReachingDefs(Clobber, MSSA, ReachingDefs, Visited); + return collectReachingDefs(Clobber, MSSA, llvm::MemoryLocation::get(Load), + ReachingDefs, Visited); } return true; From 30791d993dba9f2f46b43ad4176433da2e13d221 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Thu, 27 Aug 2026 19:52:48 +0200 Subject: [PATCH 10/14] Improve sparsity in sparse-IDE/IFDS/WPDS --- .../PhasarLLVM/ControlFlow/LLVMBasedCFG.h | 5 +- .../ControlFlow/SparseLLVMControlFlow.h | 20 ++++++- .../phasar/PhasarLLVM/Utils/LLVMShorthands.h | 39 +++++++++----- lib/PhasarLLVM/ControlFlow/LLVMBasedCFG.cpp | 16 ------ .../ControlFlow/SparseLLVMControlFlow.cpp | 53 ++++++++++--------- 5 files changed, 78 insertions(+), 55 deletions(-) diff --git a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h index 6bc968f4fc..f654a771d3 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h +++ b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h @@ -11,6 +11,7 @@ #define PHASAR_PHASARLLVM_CONTROLFLOW_LLVMBASEDCFG_H_ #include "phasar/ControlFlow/CFGBase.h" +#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" @@ -76,7 +77,9 @@ template class LLVMBasedCFGImpl : public CFGBase { [[nodiscard]] bool isExitInstImpl(n_t Inst) const noexcept { return llvm::isa(Inst); } - [[nodiscard]] bool isStartPointImpl(n_t Inst) const noexcept; + [[nodiscard]] bool isStartPointImpl(n_t Inst) const noexcept { + return isStartInst(Inst); + } [[nodiscard]] bool isFieldLoadImpl(n_t Inst) const noexcept; [[nodiscard]] bool isFieldStoreImpl(n_t Inst) const noexcept; [[nodiscard]] bool isFallThroughSuccessorImpl(n_t Inst, diff --git a/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMControlFlow.h b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMControlFlow.h index 4e344cca61..ebc4613fa2 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMControlFlow.h +++ b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMControlFlow.h @@ -11,9 +11,13 @@ #include "phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedCFGProvider.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" +#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" +#include "llvm/IR/CFG.h" #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Value.h" namespace psr { @@ -37,12 +41,24 @@ class SparseLLVMControlFlow { LLVMAliasInfoRef AI); private: + [[nodiscard]] static bool isNoopIntrinsic(n_t Inst) { + // isAssumeLikeIntrinsic() alone is not enough: llvm.objectsize and + // llvm.ptr_annotation derive their result from a pointer operand + const auto *II = llvm::dyn_cast(Inst); + return II && II->isAssumeLikeIntrinsic() && II->getType()->isVoidTy(); + } + + [[nodiscard]] static bool isExitInst(n_t Inst) { + return llvm::isa( + Inst); + } + [[nodiscard]] static n_t advanceToNextUserImpl(n_t Succ, v_t Fact, LLVMAliasInfoRef AI) { - if (Succ == Fact || !Succ->getPrevNode() || !Succ->getNextNode()) { + if (Succ == Fact || isExitInst(Succ) || isStartInst(Succ)) { return Succ; } - if (llvm::isa(Succ)) { + if (llvm::isa(Succ) && !isNoopIntrinsic(Succ)) { if (llvm::isa(Fact)) { return Succ; } diff --git a/include/phasar/PhasarLLVM/Utils/LLVMShorthands.h b/include/phasar/PhasarLLVM/Utils/LLVMShorthands.h index 897f6dfd51..453c0c2b00 100644 --- a/include/phasar/PhasarLLVM/Utils/LLVMShorthands.h +++ b/include/phasar/PhasarLLVM/Utils/LLVMShorthands.h @@ -26,6 +26,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/IR/Argument.h" +#include "llvm/IR/CFG.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/GlobalObject.h" @@ -66,14 +67,15 @@ class LLVMProjectIRDB; * @return True, if given LLVM Type is a struct like this %TSi = type <{ i64 }>. * False, otherwise. */ -bool isIntegerLikeType(const llvm::Type *T) noexcept; +[[nodiscard]] bool isIntegerLikeType(const llvm::Type *T) noexcept; /** * @brief Checks if the given LLVM Value is either a alloca instruction or a * heap allocation function, e.g. new, new[], malloc, realloc or calloc. */ -bool isAllocaInstOrHeapAllocaFunction(const llvm::Value *V) noexcept; -bool isHeapAllocatingFunction(const llvm::Function *F) noexcept; +[[nodiscard]] bool +isAllocaInstOrHeapAllocaFunction(const llvm::Value *V) noexcept; +[[nodiscard]] bool isHeapAllocatingFunction(const llvm::Function *F) noexcept; /// Returns true if the provided function and the function type are both not /// null and have the same number of parameters and the same return type. If the @@ -101,7 +103,8 @@ LLVM_DEPRECATED("With opaque pointers, this function is not very useful. Use " bool matchesSignature(const llvm::FunctionType *FType1, const llvm::FunctionType *FType2); -llvm::ModuleSlotTracker &getModuleSlotTrackerFor(const llvm::Value *V); +[[nodiscard]] llvm::ModuleSlotTracker & +getModuleSlotTrackerFor(const llvm::Value *V); /** * @brief Returns a string representation of a LLVM Value. @@ -119,7 +122,7 @@ llvm::ModuleSlotTracker &getModuleSlotTrackerFor(const llvm::Value *V); * @brief Same as llvmIRToString() but tries to shorten the * resulting string */ -std::string llvmIRToShortString(const llvm::Value *V); +[[nodiscard]] std::string llvmIRToShortString(const llvm::Value *V); /** * @brief Returns a string-representation of a LLVM type. @@ -230,7 +233,7 @@ const llvm::Instruction *getNthTermInstruction(const llvm::Function *F, const llvm::StoreInst *getNthStoreInstruction(const llvm::Function *F, unsigned StoNo); -llvm::SmallVector +[[nodiscard]] llvm::SmallVector getAllExitPoints(const llvm::Function *F, bool IncludeResume = true); void appendAllExitPoints( const llvm::Function *F, @@ -273,13 +276,14 @@ std::size_t computeModuleHash(const llvm::Module *M); * @brief True, iff V is the compiler-generated guard variable for the * thread-safe initialization of function-local static variables. */ -bool isGuardVariable(const llvm::Value *V); +[[nodiscard]] bool isGuardVariable(const llvm::Value *V); /** * @brief True, iff V is the compiler-generated branch that leads to the lazy * initialization of a function-local static variable. */ -bool isStaticVariableLazyInitializationBranch(const llvm::BranchInst *Inst); +[[nodiscard]] bool +isStaticVariableLazyInitializationBranch(const llvm::BranchInst *Inst); [[nodiscard]] inline bool definitelyContainsNoPointerFast(const llvm::Type *Ty) noexcept { @@ -370,7 +374,7 @@ void forEachPointerOperand(const llvm::Value *V, HandlerT Handler) { */ bool isVarAnnotationIntrinsic(const llvm::Function *F); -inline const llvm::Function *getFunction(const llvm::Value *V) { +[[nodiscard]] inline const llvm::Function *getFunction(const llvm::Value *V) { if (!V) { return nullptr; } @@ -382,7 +386,8 @@ inline const llvm::Function *getFunction(const llvm::Value *V) { } return nullptr; } -inline const llvm::Function *getFunction(const llvm::Instruction *Inst) { +[[nodiscard]] inline const llvm::Function * +getFunction(const llvm::Instruction *Inst) { if (!Inst) { return nullptr; } @@ -390,9 +395,19 @@ inline const llvm::Function *getFunction(const llvm::Instruction *Inst) { return Inst->getFunction(); } -const llvm::DIType *stripMemberAndTypedef(const llvm::DIType *Ty); +[[nodiscard]] inline bool isStartInst(const llvm::Instruction *Inst) { + return +#if LLVM_VERSION_MAJOR <= 18 + !Inst->getPrevNonDebugInstruction() +#else + !Inst->getPrevNode() +#endif + && llvm::pred_empty(Inst->getParent()); +} + +[[nodiscard]] const llvm::DIType *stripMemberAndTypedef(const llvm::DIType *Ty); -inline bool isPointerTy(const llvm::DIType *Ty) { +[[nodiscard]] inline bool isPointerTy(const llvm::DIType *Ty) { if (const auto *DerivedTy = llvm::dyn_cast(stripMemberAndTypedef(Ty))) { return DerivedTy->getTag() == llvm::dwarf::DW_TAG_pointer_type || diff --git a/lib/PhasarLLVM/ControlFlow/LLVMBasedCFG.cpp b/lib/PhasarLLVM/ControlFlow/LLVMBasedCFG.cpp index 898e1e2ee3..93ae6188ba 100644 --- a/lib/PhasarLLVM/ControlFlow/LLVMBasedCFG.cpp +++ b/lib/PhasarLLVM/ControlFlow/LLVMBasedCFG.cpp @@ -185,22 +185,6 @@ auto detail::LLVMBasedCFGImpl::getExitPointsOfImpl(f_t Fun) const return {}; } -template -bool detail::LLVMBasedCFGImpl::isStartPointImpl( - n_t Inst) const noexcept { - auto FirstInst = &Inst->getFunction()->front().front(); - if (Inst == FirstInst) { - return true; - } -#if LLVM_VERSION_MAJOR <= 18 - if (llvm::isa(FirstInst)) { - FirstInst = FirstInst->getNextNonDebugInstruction(false); - } -#endif - - return Inst == FirstInst; -} - template bool detail::LLVMBasedCFGImpl::isFieldLoadImpl( n_t Inst) const noexcept { diff --git a/lib/PhasarLLVM/ControlFlow/SparseLLVMControlFlow.cpp b/lib/PhasarLLVM/ControlFlow/SparseLLVMControlFlow.cpp index e83bf6c1a8..5733dfe344 100644 --- a/lib/PhasarLLVM/ControlFlow/SparseLLVMControlFlow.cpp +++ b/lib/PhasarLLVM/ControlFlow/SparseLLVMControlFlow.cpp @@ -1,9 +1,12 @@ #include "phasar/PhasarLLVM/ControlFlow/SparseLLVMControlFlow.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" +#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/IR/CFG.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" using namespace psr; @@ -71,35 +74,17 @@ static bool mayAlias(const llvm::Value *Ptr1, const llvm::Value *Ptr2, return AliasAnalysis.alias(Ptr1, Ptr2) != AliasResult::NoAlias; } -static bool isFirstInBB(const llvm::Instruction *Inst) { - return !Inst->getPrevNode(); -} - -static bool isLastInBB(const llvm::Instruction *Inst, const llvm::Value *Val) { - if (Inst->getNextNode()) { - return false; - } - - if (Val->getType()->isPointerTy()) { - return true; - } - - const auto *InstBB = Inst->getParent(); - for (const auto *User : Val->users()) { - const auto *UserInst = llvm::dyn_cast(User); - if (!UserInst || UserInst->getParent() != InstBB) { - return true; - } - } - return llvm::succ_empty(Inst); -} - bool SparseLLVMControlFlow::shouldKeepInst(n_t Inst, v_t Val, LLVMAliasInfoRef AI) { - if (Inst == Val || isFirstInBB(Inst) || isLastInBB(Inst, Val)) { + if (Inst == Val || isExitInst(Inst) || isStartInst(Inst)) { // First in BB always stays for now return true; } + + if (isNoopIntrinsic(Inst)) { + return false; + } + if (llvm::isa(Inst)) { if (llvm::isa(Val)) { // We cannot know, whether the callee uses the global @@ -135,6 +120,7 @@ bool SparseLLVMControlFlow::shouldKeepInst(n_t Inst, v_t Val, auto psr::SparseLLVMControlFlow::advanceToNextUserImplInternal( n_t Succ, v_t Fact, LLVMAliasInfoRef AI) -> n_t { + const auto *Save = Succ; while (!shouldKeepInst(Succ, Fact, AI)) { n_t NextSucc = #if LLVM_VERSION_MAJOR <= 18 @@ -143,6 +129,25 @@ auto psr::SparseLLVMControlFlow::advanceToNextUserImplInternal( Succ->getNextNode(); #endif if (!NextSucc) { + const auto *Parent = Succ->getParent(); + if (llvm::succ_size(Parent) == 1) { + const auto *SuccBB = *llvm::succ_begin(Parent); + Succ = &SuccBB->front(); +#if LLVM_VERSION_MAJOR <= 18 + if (llvm::isa(Succ)) { + Succ = Succ->getNextNonDebugInstruction(); + } +#endif + + if (Succ != Save && llvm::pred_size(SuccBB) == 1) { + // just a simple chain, no merge point. + continue; + } + + // merge-point + return Succ; + } + break; } Succ = NextSucc; From 83668a29c441ca1b61867dd8dc01b573eaa48535 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Wed, 14 Oct 2026 17:40:52 +0200 Subject: [PATCH 11/14] Set of misc changes --- CMakeLists.txt | 2 +- .../IfdsIde/Solver/IterativeIDESolver.h | 2 +- .../IfdsIde/CFLFieldSensIFDSProblem.h | 2 +- .../PhasarLLVM/Pointer/LLVMRawAAResults.h | 2 +- .../PhasarLLVM/Pointer/LLVMRawAliasSet.h | 4 ++- .../TaintConfig/TaintConfigUtilities.h | 15 ++++++++++ include/phasar/Utils/LibrarySummary.h | 4 +++ include/phasar/Utils/SmallArraySet.h | 26 ++++++++--------- include/phasar/Utils/StrongTypeDef.h | 7 +++-- include/phasar/Utils/ValueIdMap.h | 12 ++++++-- lib/PhasarLLVM/Pointer/AndersenOTFAA.cpp | 10 +++++-- lib/Utils/LibrarySummary.cpp | 29 +++++++++++++++++-- .../Pointer/FilteredLLVMAliasSetTest.cpp | 3 ++ 13 files changed, 91 insertions(+), 27 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 74e89fdef9..efe360619a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -271,7 +271,7 @@ add_nlohmann_json() add_json_schema_validator() # TBB -find_package(TBB) # needed by GCC +find_package(TBB QUIET) # needed by GCC # Coverage if (CODE_COVERAGE) diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h index e6ef1bad99..590b4ff686 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h @@ -992,7 +992,7 @@ class IterativeIDESolver for (ByConstRef RetFact : RetFacts) { auto RetFactId = FactCompressor.getOrInsert(RetFact); - auto EF = [&]() mutable { + auto EF = [&] { if constexpr (ComputeValues) { auto RetEF = FECache.getReturnEdgeFunction( Problem, CallSite, Callee, ExitInst, SummaryFact, RetSite, diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/CFLFieldSensIFDSProblem.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/CFLFieldSensIFDSProblem.h index 3d5d9a7e8f..6a5399ceea 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/CFLFieldSensIFDSProblem.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/CFLFieldSensIFDSProblem.h @@ -140,7 +140,7 @@ class FieldStringManager { } auto Kills = KillsCompressor[KS]; - Kills.insert(Offs); + Kills.insertUnique(Offs); return KillsCompressor.getOrInsert(std::move(Kills)); } diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMRawAAResults.h b/include/phasar/PhasarLLVM/Pointer/LLVMRawAAResults.h index d4460a880e..f0aafcb3c3 100644 --- a/include/phasar/PhasarLLVM/Pointer/LLVMRawAAResults.h +++ b/include/phasar/PhasarLLVM/Pointer/LLVMRawAAResults.h @@ -26,7 +26,7 @@ namespace psr { constexpr std::invocable auto llvmRawAliasHandler(const ValueCompressor &VC, std::invocable auto Callback) { - return [&VC, Callback{copyOrRef(Callback)}](ValueId Alias) { + return [&VC, Callback{std::move(Callback)}](ValueId Alias) { for (auto V : VC.id2vars(Alias)) { if (const auto *LLVMVar = V.valueOrNull()) [[likely]] { std::invoke(Callback, LLVMVar); diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMRawAliasSet.h b/include/phasar/PhasarLLVM/Pointer/LLVMRawAliasSet.h index 6760a74768..4042f0da29 100644 --- a/include/phasar/PhasarLLVM/Pointer/LLVMRawAliasSet.h +++ b/include/phasar/PhasarLLVM/Pointer/LLVMRawAliasSet.h @@ -27,6 +27,7 @@ #include "phasar/Utils/TypedVector.h" #include "phasar/Utils/ValueCompressor.h" +#include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Instructions.h" #include "llvm/Support/TypeName.h" @@ -226,7 +227,8 @@ class LLVMRawAliasSet : public LLVMRawAliasSetBase, if (!Val->getType()->isPointerTy()) { return false; } - if (llvm::isa(Val)) { + if (llvm::isa( + Val)) { return true; } if (const auto *Call = llvm::dyn_cast(Val)) { diff --git a/include/phasar/PhasarLLVM/TaintConfig/TaintConfigUtilities.h b/include/phasar/PhasarLLVM/TaintConfig/TaintConfigUtilities.h index 797d460938..28786e1185 100644 --- a/include/phasar/PhasarLLVM/TaintConfig/TaintConfigUtilities.h +++ b/include/phasar/PhasarLLVM/TaintConfig/TaintConfigUtilities.h @@ -39,6 +39,9 @@ void forallGeneratedFacts( std::invoke(WithGenFact, CB); } + if (!Callee) { + return; + } for (unsigned I = 0, End = Callee->arg_size(); I < End; ++I) { if (Config.isSource(Callee->getArg(I))) { std::invoke(WithGenFact, CB->getArgOperand(I)); @@ -61,6 +64,9 @@ void collectGeneratedFacts(ContainerTy &Dest, const LLVMTaintConfig &Config, Dest.insert(CB); } + if (!Callee) { + return; + } for (unsigned I = 0, End = Callee->arg_size(); I < End; ++I) { if (Config.isSource(Callee->getArg(I))) { Dest.insert(CB->getArgOperand(I)); @@ -80,6 +86,9 @@ void forallLeakedFacts(const LLVMTaintConfig &Config, const llvm::CallBase *CB, llvm::for_each(llvm::make_filter_range(CBLeaks, LeakIf), WithLeakFact); } + if (!Callee) { + return; + } for (unsigned I = 0, End = Callee->arg_size(); I < End; ++I) { if (Config.isSink(Callee->getArg(I)) && std::invoke(LeakIf, CB->getArgOperand(I))) { @@ -101,6 +110,9 @@ void collectLeakedFacts(ContainerTy &Dest, const LLVMTaintConfig &Config, std::inserter(Dest, Dest.end()), LeakIf); } + if (!Callee) { + return; + } for (unsigned I = 0, End = Callee->arg_size(); I < End; ++I) { if (Config.isSink(Callee->getArg(I)) && LeakIf(CB->getArgOperand(I))) { Dest.insert(CB->getArgOperand(I)); @@ -122,6 +134,9 @@ template void collectSanitizedFacts(ContainerTy &Dest, const LLVMTaintConfig &Config, const llvm::CallBase *CB, const llvm::Function *Callee) { + if (!Callee) { + return; + } for (unsigned I = 0, End = Callee->arg_size(); I < End; ++I) { if (Config.isSanitizer(Callee->getArg(I))) { Dest.insert(CB->getArgOperand(I)); diff --git a/include/phasar/Utils/LibrarySummary.h b/include/phasar/Utils/LibrarySummary.h index 348ce0eedd..84be9dd5b5 100644 --- a/include/phasar/Utils/LibrarySummary.h +++ b/include/phasar/Utils/LibrarySummary.h @@ -22,6 +22,10 @@ namespace psr { /// return the same pointer [[nodiscard]] bool isSingletonReturningFunction(llvm::StringRef FName) noexcept; +/// Checks whether a function with the given (mangled) name may return the +/// block its first argument points to, similar to realloc. +[[nodiscard]] bool isReallocatingFunction(llvm::StringRef FName) noexcept; + } // namespace psr #endif // PHASAR_UTILS_LIBRARY_SUMMARY_H diff --git a/include/phasar/Utils/SmallArraySet.h b/include/phasar/Utils/SmallArraySet.h index e5494ed1d7..85af075520 100644 --- a/include/phasar/Utils/SmallArraySet.h +++ b/include/phasar/Utils/SmallArraySet.h @@ -63,7 +63,7 @@ class SmallArraySet : private llvm::SmallVector { [[nodiscard]] static SmallArraySet fromSorted(llvm::SmallVectorImpl &&Vec) { - assert(std::ranges::is_sorted(Vec) && "Vec is not sorted"); + assert(std::ranges::is_sorted(Vec, std::less{}) && "Vec is not sorted"); SmallArraySet Ret(std::move(Vec), /*PreSortedAndUniqued=*/std::true_type{}); Ret.base().erase(std::unique(begin(), end()), end()); @@ -73,8 +73,8 @@ class SmallArraySet : private llvm::SmallVector { [[nodiscard]] static SmallArraySet fromSortedUniqued(llvm::SmallVectorImpl &&Vec) { - assert(std::ranges::is_sorted(Vec) && "Vec is not sorted"); - assert(std::ranges::adjacent_find(Vec) == Vec.end() && + assert(std::ranges::is_sorted(Vec, std::less{}) && "Vec is not sorted"); + assert(std::ranges::adjacent_find(Vec, std::less{}) == Vec.end() && "Vec is not uniqued"); SmallArraySet Ret(std::move(Vec), /*PreSortedAndUniqued=*/std::true_type{}); @@ -114,7 +114,7 @@ class SmallArraySet : private llvm::SmallVector { this->push_back(Val); if (Size + 1 == LinearThreshold) [[unlikely]] { - std::ranges::sort(base()); + std::ranges::sort(base(), std::less{}); } return true; @@ -154,7 +154,7 @@ class SmallArraySet : private llvm::SmallVector { } if (I == OtherSize) { if (this->size() == LinearThreshold) [[unlikely]] { - std::ranges::sort(base()); + std::ranges::sort(base(), std::less{}); } return; } @@ -221,8 +221,8 @@ class SmallArraySet : private llvm::SmallVector { SmallArraySet Merged{}; Merged.resize_for_overwrite(std::min(Size, OtherSize)); - auto [Unused1, Unused2, LastOut] = - std::ranges::set_intersection(*this, Other, Merged.begin()); + auto [Unused1, Unused2, LastOut] = std::ranges::set_intersection( + *this, Other, Merged.begin(), std::less{}); Merged.base().erase(LastOut, Merged.end()); *this = std::move(Merged); return Size != size(); @@ -245,8 +245,8 @@ class SmallArraySet : private llvm::SmallVector { SmallArraySet Merged{}; Merged.resize_for_overwrite(std::min(Size, OtherSize)); - auto [Unused1, Unused2, LastOut] = - std::ranges::set_intersection(*this, Other, Merged.begin()); + auto [Unused1, Unused2, LastOut] = std::ranges::set_intersection( + *this, Other, Merged.begin(), std::less{}); Merged.base().erase(LastOut, Merged.end()); return Merged; } @@ -256,7 +256,7 @@ class SmallArraySet : private llvm::SmallVector { return llvm::is_contained(base(), Val); } - return std::ranges::binary_search(base(), Val); + return std::ranges::binary_search(base(), Val, std::less{}); } void sort() { @@ -265,7 +265,7 @@ class SmallArraySet : private llvm::SmallVector { return; } - std::ranges::sort(base()); + std::ranges::sort(base(), std::less{}); } LLVM_ATTRIBUTE_ALWAYS_INLINE auto foreach ( @@ -278,7 +278,7 @@ class SmallArraySet : private llvm::SmallVector { if constexpr (std::is_trivially_copyable_v) { T Arr[LinearThreshold]{}; memcpy(&Arr, Set.data(), Set.size_in_bytes()); - std::ranges::sort(Arr, Arr + Set.size()); + std::ranges::sort(Arr, Arr + Set.size(), std::less{}); return llvm::hash_combine_range(Arr, Arr + Set.size()); } else { // Some reduction that ignores the order @@ -322,7 +322,7 @@ class SmallArraySet : private llvm::SmallVector { : llvm::SmallVector(std::move(Elems)) {} bool insertImpl(ByConstRef Val) { - const auto It = std::ranges::lower_bound(base(), Val); + const auto It = std::ranges::lower_bound(base(), Val, std::less{}); if (It != this->end() && *It == Val) { return false; } diff --git a/include/phasar/Utils/StrongTypeDef.h b/include/phasar/Utils/StrongTypeDef.h index 94a3fe1bf5..cbad7a9a8a 100644 --- a/include/phasar/Utils/StrongTypeDef.h +++ b/include/phasar/Utils/StrongTypeDef.h @@ -22,6 +22,9 @@ #define PHASAR_STRONG_TYPEDEF(NAMESPACE, TYPE, NAME, ...) \ namespace NAMESPACE { \ enum class PSR_ENUM_EXTENSIBILITY(open) NAME : TYPE { __VA_ARGS__ }; \ + [[nodiscard]] inline auto hash_value(NAME Id) noexcept { \ + return llvm::hash_value(TYPE(Id)); \ + } \ } \ namespace llvm { \ template <> struct DenseMapInfo { \ @@ -36,9 +39,7 @@ ? std::numeric_limits::min() + 1 \ : std::numeric_limits::max() - 1); \ } \ - static auto getHashValue(QUAL_NAME Id) noexcept { \ - return llvm::hash_value(TYPE(Id)); \ - } \ + static auto getHashValue(QUAL_NAME Id) noexcept { return hash_value(Id); } \ static constexpr bool isEqual(QUAL_NAME Id1, QUAL_NAME Id2) noexcept { \ return Id1 == Id2; \ } \ diff --git a/include/phasar/Utils/ValueIdMap.h b/include/phasar/Utils/ValueIdMap.h index bb949bfd5c..40f93c7163 100644 --- a/include/phasar/Utils/ValueIdMap.h +++ b/include/phasar/Utils/ValueIdMap.h @@ -203,7 +203,7 @@ class ValueIdMap { /// Move constructor. The allocator is move-constructed from \p Other. ValueIdMap(ValueIdMap &&Other) noexcept - : Alloc(std::move(Other.Alloc)), IsSet(std::move(Other.IsSet)), + : Alloc(std::move(Other.Alloc)), IsSet(std::exchange(Other.IsSet, {})), Capacity(std::exchange(Other.Capacity, 0)), Slots(std::exchange(Other.Slots, nullptr)) {} @@ -479,6 +479,14 @@ class ValueIdMap { } [[nodiscard]] constexpr const_iterator cend() const noexcept { return end(); } + [[nodiscard]] constexpr const BitSet &keys() const & noexcept { + return IsSet; + } + + [[nodiscard]] constexpr BitSet keys() && noexcept { + return std::move(IsSet); + } + auto foreach (std::invocable auto Handler) { return foreachImpl(*this, std::move(Handler)); } @@ -527,7 +535,7 @@ class ValueIdMap { /// Takes ownership of Other's storage (Other must have been cleared first). void stealFrom(ValueIdMap &Other) noexcept { - IsSet = std::move(Other.IsSet); + IsSet = std::exchange(Other.IsSet, {}); Capacity = std::exchange(Other.Capacity, 0); Slots = std::exchange(Other.Slots, nullptr); } diff --git a/lib/PhasarLLVM/Pointer/AndersenOTFAA.cpp b/lib/PhasarLLVM/Pointer/AndersenOTFAA.cpp index 28872793a0..bf13e57ba5 100644 --- a/lib/PhasarLLVM/Pointer/AndersenOTFAA.cpp +++ b/lib/PhasarLLVM/Pointer/AndersenOTFAA.cpp @@ -1214,8 +1214,7 @@ struct [[clang::internal_linkage]] AndersenOTFSolver::SolverData { if (const auto *CB = llvm::dyn_cast(V)) { const auto *Callee = llvm::dyn_cast_or_null( CB->getCalledOperand()->stripPointerCastsAndAliases()); - if (!Callee || - !(psr::isHeapAllocatingFunction(Callee) || isAllocWrapper(Callee))) { + if (!Callee || !isAllocWrapper(Callee)) { return false; } llvm::SmallPtrSet EscVisited; @@ -1266,6 +1265,13 @@ struct [[clang::internal_linkage]] AndersenOTFSolver::SolverData { } bool isAllocWrapper(const llvm::Function *F) { + if (psr::isHeapAllocatingFunction(F)) { + // The allocator list wins over the body, which may hide the allocation, + // e.g., gnulib's xmalloc passes it through check_nonnull. A realloc-like + // allocator may return the block it got instead. + return !psr::isReallocatingFunction(F->getName()); + } + if (F->isDeclaration()) { return false; // real allocators are handled via isHeapAllocatingFunction } diff --git a/lib/Utils/LibrarySummary.cpp b/lib/Utils/LibrarySummary.cpp index b6850eb4df..788f87077c 100644 --- a/lib/Utils/LibrarySummary.cpp +++ b/lib/Utils/LibrarySummary.cpp @@ -141,6 +141,24 @@ static constexpr llvm::StringLiteral SingletonReturningFunctions[] = { "zError", }; +static constexpr llvm::StringLiteral ReallocatingFunNames[] = { + // C functions: + "realloc", + "reallocarray", + + // gnulib and libiberty: + "irealloc", + "ireallocarray", + "x2nrealloc", + "x2realloc", + "xirealloc", + "xireallocarray", + "xnrealloc", + "xpalloc", + "xrealloc", + "xreallocarray", +}; + bool psr::isHeapAllocatingFunction(llvm::StringRef FName) noexcept { // Note: For a performance comparison of different search strategies, see // https://quick-bench.com/q/lNjDT6z-M-L08h372fbCfYjwM64 @@ -148,7 +166,7 @@ bool psr::isHeapAllocatingFunction(llvm::StringRef FName) noexcept { static const llvm::DenseSet HAFs( std::begin(HeapAllocatingFunNames), std::end(HeapAllocatingFunNames)); - return HAFs.count(FName); + return HAFs.contains(FName); } bool psr::isSingletonReturningFunction(llvm::StringRef FName) noexcept { @@ -156,5 +174,12 @@ bool psr::isSingletonReturningFunction(llvm::StringRef FName) noexcept { std::begin(SingletonReturningFunctions), std::end(SingletonReturningFunctions)); - return SRFs.count(FName); + return SRFs.contains(FName); +} + +bool psr::isReallocatingFunction(llvm::StringRef FName) noexcept { + static const llvm::DenseSet RFs( + std::begin(ReallocatingFunNames), std::end(ReallocatingFunNames)); + + return RFs.contains(FName); } diff --git a/unittests/PhasarLLVM/Pointer/FilteredLLVMAliasSetTest.cpp b/unittests/PhasarLLVM/Pointer/FilteredLLVMAliasSetTest.cpp index d4f0e8b599..c0e27a8a27 100644 --- a/unittests/PhasarLLVM/Pointer/FilteredLLVMAliasSetTest.cpp +++ b/unittests/PhasarLLVM/Pointer/FilteredLLVMAliasSetTest.cpp @@ -8,6 +8,7 @@ #include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" #include "phasar/PhasarLLVM/TaintConfig/LLVMTaintConfig.h" #include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" +#include "phasar/Pointer/AliasInfoBase.h" #include "llvm/ADT/StringRef.h" #include "llvm/IR/InstrTypes.h" @@ -17,6 +18,8 @@ using namespace psr; +static_assert(IsAliasInfo); + namespace { static LLVMTaintConfig getDefaultConfig() { From 59a8d37a073f865c4a170dc0f8984bb0fb673716 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Wed, 14 Oct 2026 17:56:14 +0200 Subject: [PATCH 12/14] Add call-graph iteration + fix installation with tbb --- Config.cmake.in | 2 ++ .../export_callsite_cg_streaming.cpp | 7 ++++--- include/phasar/ControlFlow/CallGraph.h | 20 +++++++++++++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/Config.cmake.in b/Config.cmake.in index 80e4b57cd1..34d1d26a94 100644 --- a/Config.cmake.in +++ b/Config.cmake.in @@ -11,6 +11,8 @@ set(PHASAR_LLVM_VERSION @PHASAR_LLVM_VERSION@) include("${CMAKE_CURRENT_LIST_DIR}/PhasarDepsExports.cmake") find_dependency(LLVM ${PHASAR_LLVM_VERSION} REQUIRED CONFIG) +find_dependency(TBB QUIET) + set(PHASAR_USE_LLVM_FAT_LIB @USE_LLVM_FAT_LIB@) set(PHASAR_BUILD_DYNLIB @PHASAR_BUILD_DYNLIB@) set(PHASAR_USE_Z3 @PHASAR_USE_Z3@) diff --git a/examples/how-to/09-export-callsite-cg/export_callsite_cg_streaming.cpp b/examples/how-to/09-export-callsite-cg/export_callsite_cg_streaming.cpp index 0fd7955cee..c51d6659c1 100644 --- a/examples/how-to/09-export-callsite-cg/export_callsite_cg_streaming.cpp +++ b/examples/how-to/09-export-callsite-cg/export_callsite_cg_streaming.cpp @@ -115,12 +115,13 @@ int main(int Argc, char **Argv) { // Walk every function PhASAR's call-graph knows about, and ask it for all // known callers, writing (and forgetting) each result immediately instead of // accumulating all of them. - for (const llvm::Function *Fun : ICF.getAllVertexFunctions()) { + ICF.getCallGraph().iterateCallersView([&](const llvm::Function *Fun, + const auto &Callers) { auto FunName = Fun->getName(); ++FnCount; ResolvedLoc CalleeLoc = resolveLocation(Fun); - for (const auto *CS : ICF.getCallersOf(Fun)) { + for (const auto *CS : Callers) { ResolvedLoc CallerLoc = resolveLocation(CS); OS.write_escaped(CS->getFunction()->getName()) << ',' << CallerLoc << ','; @@ -137,7 +138,7 @@ int main(int Argc, char **Argv) { << ICF.getNumVertexFunctions() << " functions processed so far\n"; } } - } + }); OS.close(); diff --git a/include/phasar/ControlFlow/CallGraph.h b/include/phasar/ControlFlow/CallGraph.h index 67cdc04e38..59409fd82b 100644 --- a/include/phasar/ControlFlow/CallGraph.h +++ b/include/phasar/ControlFlow/CallGraph.h @@ -17,8 +17,10 @@ #include "phasar/Utils/StableVector.h" #include "phasar/Utils/Utilities.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/IR/Function.h" +#include #include #include #include @@ -52,6 +54,24 @@ class CallGraph : public CallGraphBase> { /// Creates a new, empty call-graph CallGraph() noexcept = default; + auto + iterateCalleesView(std::invocable> auto Fn) const { + return psr::foreachInRange( + CalleesAt, [Fn = copyOrRef(Fn)](const auto &Elem) { + const auto &[CS, Callees] = Elem; + return psr::invokeControlFlow(Fn, CS, llvm::ArrayRef(*Callees)); + }); + } + + auto + iterateCallersView(std::invocable> auto Fn) const { + return psr::foreachInRange( + CallersOf, [Fn = copyOrRef(Fn)](const auto &Elem) { + const auto &[CS, Callers] = Elem; + return psr::invokeControlFlow(Fn, CS, llvm::ArrayRef(*Callers)); + }); + } + /// Deserializes a previously computed call-graph template [[nodiscard]] static CallGraph From 9582101618fc3119b6decb7b89f723c650cfbae3 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Mon, 19 Oct 2026 18:42:00 +0200 Subject: [PATCH 13/14] Fix function types in TypeTraits + deprecate psr::identity --- .../PathSensitivityManagerBase.h | 3 ++- include/phasar/Utils/TypeTraits.h | 18 +++++++++++------- include/phasar/Utils/Utilities.h | 2 +- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/include/phasar/DataFlow/PathSensitivity/PathSensitivityManagerBase.h b/include/phasar/DataFlow/PathSensitivity/PathSensitivityManagerBase.h index 3a58bae1e4..f1f0bf572b 100644 --- a/include/phasar/DataFlow/PathSensitivity/PathSensitivityManagerBase.h +++ b/include/phasar/DataFlow/PathSensitivity/PathSensitivityManagerBase.h @@ -12,6 +12,7 @@ #include "phasar/Utils/AdjacencyList.h" #include "phasar/Utils/Logger.h" +#include "phasar/Utils/TypeTraits.h" #include "phasar/Utils/Utilities.h" #include "llvm/ADT/BitVector.h" @@ -180,7 +181,7 @@ template class PathSensitivityManagerBase { [[nodiscard]] static graph_type reverseDAG(graph_type &&Dag, size_t MaxDepth) { auto Sz = graph_traits_t::size(Dag); - return reverseDAG(std::move(Dag), identity{}, Sz, MaxDepth); + return reverseDAG(std::move(Dag), IdentityFn{}, Sz, MaxDepth); } }; diff --git a/include/phasar/Utils/TypeTraits.h b/include/phasar/Utils/TypeTraits.h index 158a072ba7..c52ebe174f 100644 --- a/include/phasar/Utils/TypeTraits.h +++ b/include/phasar/Utils/TypeTraits.h @@ -241,14 +241,16 @@ using ElementType = typename detail::ElementType::type; struct TrueFn { template - [[nodiscard]] bool operator()(const Args &.../*unused*/) const noexcept { + [[nodiscard]] constexpr bool + operator()(const Args &.../*unused*/) const noexcept { return true; } }; struct FalseFn { template - [[nodiscard]] bool operator()(const Args &.../*unused*/) const noexcept { + [[nodiscard]] constexpr bool + operator()(const Args &.../*unused*/) const noexcept { return false; } }; @@ -256,14 +258,15 @@ struct FalseFn { /// Delegates to the ctor of T template struct DefaultConstruct { template - [[nodiscard]] T - operator()(U &&...Val) noexcept(std::is_nothrow_constructible_v) { + [[nodiscard]] constexpr T operator()(U &&...Val) const + noexcept(std::is_nothrow_constructible_v) { return T(std::forward(Val)...); } }; struct IgnoreArgs { - template void operator()(U &&.../*Val*/) noexcept {} + template + constexpr void operator()(U &&.../*Val*/) const noexcept {} }; template @@ -289,13 +292,14 @@ template } struct IdentityFn { - template decltype(auto) operator()(T &&Val) const noexcept { + template + constexpr decltype(auto) operator()(T &&Val) const noexcept { return std::forward(Val); } }; template struct DummyFn { - void operator()(ArgsT... Arg) const noexcept {} + constexpr void operator()(ArgsT... Arg) const noexcept {} }; /// True if T can be relocated by copying its bytes (e.g. via memcpy) without diff --git a/include/phasar/Utils/Utilities.h b/include/phasar/Utils/Utilities.h index b9659a7133..979e23363e 100644 --- a/include/phasar/Utils/Utilities.h +++ b/include/phasar/Utils/Utilities.h @@ -290,7 +290,7 @@ template constexpr auto copyOrRef(T &Val) noexcept { } } -struct identity { +struct [[deprecated("Use IdentityFn from TypeTraits.h instead")]] identity { template decltype(auto) operator()(T &&Val) const noexcept { return std::forward(Val); } From 5b024065ffc731856fe25085a92d5f73af2ad392 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Mon, 19 Oct 2026 19:12:22 +0200 Subject: [PATCH 14/14] Fix tbb handling for install target --- CMakeLists.txt | 1 + Config.cmake.in | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index efe360619a..f02b3cc2e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -272,6 +272,7 @@ add_json_schema_validator() # TBB find_package(TBB QUIET) # needed by GCC +set(PHASAR_USE_TBB ${TBB_FOUND}) # Coverage if (CODE_COVERAGE) diff --git a/Config.cmake.in b/Config.cmake.in index 34d1d26a94..da9ffd1ffa 100644 --- a/Config.cmake.in +++ b/Config.cmake.in @@ -11,8 +11,6 @@ set(PHASAR_LLVM_VERSION @PHASAR_LLVM_VERSION@) include("${CMAKE_CURRENT_LIST_DIR}/PhasarDepsExports.cmake") find_dependency(LLVM ${PHASAR_LLVM_VERSION} REQUIRED CONFIG) -find_dependency(TBB QUIET) - set(PHASAR_USE_LLVM_FAT_LIB @USE_LLVM_FAT_LIB@) set(PHASAR_BUILD_DYNLIB @PHASAR_BUILD_DYNLIB@) set(PHASAR_USE_Z3 @PHASAR_USE_Z3@) @@ -26,6 +24,10 @@ else() find_dependency(roaring REQUIRED CONFIG) endif() +if (@PHASAR_USE_TBB@) + find_dependency(TBB) +endif() + if (PHASAR_USE_Z3) find_dependency(Z3 REQUIRED) endif()