From 92c9b15c040d05d1b82c4caca4fc84c716400234 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Wed, 26 Aug 2026 18:34:33 +0200 Subject: [PATCH 01/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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;