From c5fea5bbd98c7e6eb3cc5591d8e8cbbbffd097fd Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Tue, 17 Jun 2025 20:37:30 +0200 Subject: [PATCH 01/11] Add AliasIterator --- include/phasar/Pointer/AliasInfo.h | 15 +++- include/phasar/Pointer/AliasInfoBase.h | 13 +++ include/phasar/Pointer/AliasIterator.h | 119 +++++++++++++++++++++++++ 3 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 include/phasar/Pointer/AliasIterator.h diff --git a/include/phasar/Pointer/AliasInfo.h b/include/phasar/Pointer/AliasInfo.h index a5f2f5f577..a18dfbebf3 100644 --- a/include/phasar/Pointer/AliasInfo.h +++ b/include/phasar/Pointer/AliasInfo.h @@ -10,11 +10,11 @@ #ifndef PHASAR_POINTER_ALIASINFO_H #define PHASAR_POINTER_ALIASINFO_H +#include "phasar/Pointer/AliasInfoBase.h" #include "phasar/Pointer/AliasInfoTraits.h" #include "phasar/Pointer/AliasResult.h" #include "phasar/Utils/AnalysisProperties.h" #include "phasar/Utils/ByRef.h" -#include "phasar/Utils/TypeTraits.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" @@ -59,6 +59,8 @@ struct AliasInfoTraits> : DefaultAATraits {}; template class AliasInfoRef : public AnalysisPropertiesMixin> { friend class AliasInfo; + template friend class AliasIteratorRef; + using traits_t = AliasInfoTraits>; public: @@ -193,6 +195,7 @@ class AliasInfoRef : public AnalysisPropertiesMixin> { bool (*IsInReachableAllocationSites)(void *, ByConstRef, ByConstRef, bool, ByConstRef); + void (*AliasesOf)(void *, v_t, n_t, llvm::function_ref); void (*Print)(const void *, llvm::raw_ostream &); void (*PrintAsJson)(const void *, llvm::raw_ostream &); void (*MergeWith)(void *, void *); @@ -230,6 +233,16 @@ class AliasInfoRef : public AnalysisPropertiesMixin> { return static_cast(AA)->isInReachableAllocationSites( Pointer1, Pointer2, IntraProcOnly, AtInstruction); }, + [](void *AA, v_t Of, n_t At, llvm::function_ref WithAlias) { + if constexpr (IsAliasIterator) { + return static_cast(AA)->aliasesof(Of, At, WithAlias); + } else { + auto AliasSetPtr = static_cast(AA)->getAliasSet(Of, At); + for (auto &&Alias : *AliasSetPtr) { + WithAlias(PSR_FWD(Alias)); + } + } + }, [](const void *AA, llvm::raw_ostream &OS) { static_cast(AA)->print(OS); }, diff --git a/include/phasar/Pointer/AliasInfoBase.h b/include/phasar/Pointer/AliasInfoBase.h index 0775346d79..27570110bf 100644 --- a/include/phasar/Pointer/AliasInfoBase.h +++ b/include/phasar/Pointer/AliasInfoBase.h @@ -72,9 +72,22 @@ struct IsAliasInfo< decltype(testAliasInfo(std::declval(), std::declval()))>>> : std::true_type { }; + +template +struct IsAliasIterator : std::false_type {}; + +template +struct IsAliasIterator< + T, std::void_t().aliasesof( + std::declval(), std::declval(), + std::declval>()))>> + : std::true_type {}; + } // namespace detail template PSR_CONCEPT IsAliasInfo = detail::IsAliasInfo::value; +template +PSR_CONCEPT IsAliasIterator = detail::IsAliasIterator::value; } // namespace psr diff --git a/include/phasar/Pointer/AliasIterator.h b/include/phasar/Pointer/AliasIterator.h new file mode 100644 index 0000000000..1766b199bc --- /dev/null +++ b/include/phasar/Pointer/AliasIterator.h @@ -0,0 +1,119 @@ +/****************************************************************************** + * Copyright (c) 2025 Fabian Schíebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel and others + *****************************************************************************/ + +#ifndef PHASAR_POINTER_ALIASITERATOR_H +#define PHASAR_POINTER_ALIASITERATOR_H + +#include "phasar/Pointer/AliasInfo.h" +#include "phasar/Utils/Macros.h" +#include "phasar/Utils/Utilities.h" + +#include "llvm/ADT/STLFunctionalExtras.h" + +#include +#include + +namespace psr { + +template struct ReachableAllocationSitesIterator { + using n_t = typename UnderlyingAA::n_t; + using v_t = typename UnderlyingAA::v_t; + + void aliasesOf(v_t Of, n_t At, llvm::function_ref WithAlias) { + assert(AA != nullptr); + + auto AliasSetPtr = AA->getReachableAllocationSites(Of, true, At); + if (!AliasSetPtr || AliasSetPtr->empty()) { + // The alias-relation should be reflexive + WithAlias(Of); + return; + } + + for (auto &&Alias : *AliasSetPtr) { + WithAlias(PSR_FWD(Alias)); + } + } + + UnderlyingAA *AA{}; +}; + +template +ReachableAllocationSitesIterator(UnderlyingAA *) + -> ReachableAllocationSitesIterator; + +template class AliasIteratorRef { +public: + using n_t = N; + using v_t = V; + + template && + std::is_same_v && + std::is_same_v>> + constexpr AliasIteratorRef(ConcreteAA *AA) noexcept + : AA(&psr::assertNotNull(AA)), AliasesOf(&aliasesOfThunk) { + static_assert(IsAliasIterator); + } + + template + constexpr AliasIteratorRef( + ReachableAllocationSitesIterator RAS) noexcept + : AA(&psr::assertNotNull(RAS.AA)), + AliasesOf(&allocSitesOfThunk) {} + + constexpr AliasIteratorRef(AliasInfoRef AS) noexcept + : AA(&psr::assertNotNull(AS.AA)), AliasesOf(AS.VT->AliasesOf) {} + + constexpr AliasIteratorRef(const AliasIteratorRef &) noexcept = default; + constexpr AliasIteratorRef & + operator=(const AliasIteratorRef &) noexcept = default; + ~AliasIteratorRef() = default; + + void aliasesOf(v_t Of, n_t At, llvm::function_ref WithAlias) { + assert(AliasesOf); + AliasesOf(AA, std::move(Of), std::move(At), WithAlias); + } + + template > + [[nodiscard]] SetT asSet(v_t Of, n_t At) { + SetT Set; + aliasesOf(Of, At, [&Set](v_t Alias) { Set.insert(std::move(Alias)); }); + return Set; + } + +private: + template + static void aliasesOfThunk(void *AA, v_t Of, n_t At, + llvm::function_ref WithAlias) { + if constexpr (IsAliasIterator) { + return static_cast(AA)->aliasesof(Of, At, WithAlias); + } else { + auto AliasSetPtr = static_cast(AA)->getAliasSet(Of, At); + for (auto &&Alias : *AliasSetPtr) { + WithAlias(PSR_FWD(Alias)); + } + } + } + + template + static void allocSitesOfThunk(void *AA, v_t Of, n_t At, + llvm::function_ref WithAlias) { + ReachableAllocationSitesIterator{ + static_cast(AA)} + .aliasesOf(Of, At, WithAlias); + } + + void *AA{}; + void (*AliasesOf)(void *, v_t, n_t, llvm::function_ref){}; +}; + +} // namespace psr + +#endif // PHASAR_POINTER_ALIASITERATOR_H From 36ab0546c1d8c4a03fff73f10a346f9ea940bc88 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Wed, 18 Jun 2025 14:14:36 +0200 Subject: [PATCH 02/11] Add alias() check as optional member to AliasIterator --- include/phasar/Pointer/AliasInfo.h | 6 +- include/phasar/Pointer/AliasInfoBase.h | 16 ++++ include/phasar/Pointer/AliasIterator.h | 120 ++++++++++++++++++++----- 3 files changed, 117 insertions(+), 25 deletions(-) diff --git a/include/phasar/Pointer/AliasInfo.h b/include/phasar/Pointer/AliasInfo.h index a18dfbebf3..55b7932621 100644 --- a/include/phasar/Pointer/AliasInfo.h +++ b/include/phasar/Pointer/AliasInfo.h @@ -195,7 +195,8 @@ class AliasInfoRef : public AnalysisPropertiesMixin> { bool (*IsInReachableAllocationSites)(void *, ByConstRef, ByConstRef, bool, ByConstRef); - void (*AliasesOf)(void *, v_t, n_t, llvm::function_ref); + void (*AliasesOf)(void *, ByConstRef, ByConstRef, + llvm::function_ref); void (*Print)(const void *, llvm::raw_ostream &); void (*PrintAsJson)(const void *, llvm::raw_ostream &); void (*MergeWith)(void *, void *); @@ -233,7 +234,8 @@ class AliasInfoRef : public AnalysisPropertiesMixin> { return static_cast(AA)->isInReachableAllocationSites( Pointer1, Pointer2, IntraProcOnly, AtInstruction); }, - [](void *AA, v_t Of, n_t At, llvm::function_ref WithAlias) { + [](void *AA, ByConstRef Of, ByConstRef At, + llvm::function_ref WithAlias) { if constexpr (IsAliasIterator) { return static_cast(AA)->aliasesof(Of, At, WithAlias); } else { diff --git a/include/phasar/Pointer/AliasInfoBase.h b/include/phasar/Pointer/AliasInfoBase.h index 27570110bf..9e2bff7600 100644 --- a/include/phasar/Pointer/AliasInfoBase.h +++ b/include/phasar/Pointer/AliasInfoBase.h @@ -83,6 +83,22 @@ struct IsAliasIterator< std::declval>()))>> : std::true_type {}; +template +struct HasAlias : std::false_type {}; +template +struct HasAlias().alias( + std::declval(), + std::declval(), + std::declval()))> : std::true_type {}; + +template +struct HasGetAliasSet : std::false_type {}; +template +struct HasGetAliasSet< + T, std::void_t().getAliasSet( + std::declval(), std::declval()))>> + : std::true_type {}; + } // namespace detail template PSR_CONCEPT IsAliasInfo = detail::IsAliasInfo::value; diff --git a/include/phasar/Pointer/AliasIterator.h b/include/phasar/Pointer/AliasIterator.h index 1766b199bc..706edb7343 100644 --- a/include/phasar/Pointer/AliasIterator.h +++ b/include/phasar/Pointer/AliasIterator.h @@ -11,6 +11,9 @@ #define PHASAR_POINTER_ALIASITERATOR_H #include "phasar/Pointer/AliasInfo.h" +#include "phasar/Pointer/AliasInfoBase.h" +#include "phasar/Pointer/AliasResult.h" +#include "phasar/Utils/ByRef.h" #include "phasar/Utils/Macros.h" #include "phasar/Utils/Utilities.h" @@ -40,6 +43,20 @@ template struct ReachableAllocationSitesIterator { } } + [[nodiscard]] AliasResult alias(v_t Ptr, v_t Alias, n_t At) { + assert(AA != nullptr); + + if (Ptr == Alias) { + return AliasResult::MustAlias; + } + + if (AA->isInReachableAllocationSites(Ptr, Alias, true, At)) { + return AliasResult::MayAlias; + } + + return AliasResult::NoAlias; + } + UnderlyingAA *AA{}; }; @@ -48,6 +65,30 @@ ReachableAllocationSitesIterator(UnderlyingAA *) -> ReachableAllocationSitesIterator; template class AliasIteratorRef { + template + static constexpr bool CanSSO = std::is_trivially_copyable_v && + sizeof(ConcreteAA) <= sizeof(void *); + + template + constexpr static ConcreteAA *fromOpaquePtr(void *&EF) noexcept { + if constexpr (CanSSO) { + return static_cast(static_cast(&EF)); + } else { + return static_cast(EF); + } + } + + template + constexpr void *getOpaquePtr(ConcreteAA &AA) noexcept { + if constexpr (CanSSO) { + void *Ret{}; + ::new (&Ret) ConcreteAA(AA); + return Ret; + } else { + return &AA; + } + } + public: using n_t = N; using v_t = V; @@ -58,61 +99,94 @@ template class AliasIteratorRef { std::is_same_v && std::is_same_v>> constexpr AliasIteratorRef(ConcreteAA *AA) noexcept - : AA(&psr::assertNotNull(AA)), AliasesOf(&aliasesOfThunk) { + : AA(getOpaquePtr(psr::assertNotNull(AA))), VT(&VtableFor) { static_assert(IsAliasIterator); } - template - constexpr AliasIteratorRef( - ReachableAllocationSitesIterator RAS) noexcept - : AA(&psr::assertNotNull(RAS.AA)), - AliasesOf(&allocSitesOfThunk) {} - constexpr AliasIteratorRef(AliasInfoRef AS) noexcept - : AA(&psr::assertNotNull(AS.AA)), AliasesOf(AS.VT->AliasesOf) {} + : AA(&psr::assertNotNull(AS.AA)), VT(AS.VT->AliasesOf, AS.VT.Alias) {} constexpr AliasIteratorRef(const AliasIteratorRef &) noexcept = default; constexpr AliasIteratorRef & operator=(const AliasIteratorRef &) noexcept = default; ~AliasIteratorRef() = default; - void aliasesOf(v_t Of, n_t At, llvm::function_ref WithAlias) { - assert(AliasesOf); - AliasesOf(AA, std::move(Of), std::move(At), WithAlias); + void aliasesOf(ByConstRef Of, ByConstRef At, + llvm::function_ref WithAlias) { + assert(VT != nullptr); + VT->AliasesOf(AA, Of, At, WithAlias); } template > - [[nodiscard]] SetT asSet(v_t Of, n_t At) { + [[nodiscard]] SetT asSet(ByConstRef Of, ByConstRef At) { SetT Set; aliasesOf(Of, At, [&Set](v_t Alias) { Set.insert(std::move(Alias)); }); return Set; } + [[nodiscard]] AliasResult alias(ByConstRef Ptr, ByConstRef Alias, + ByConstRef At) { + assert(VT != nullptr); + return VT->Alias(AA, Ptr, Alias, At); + } + private: + struct VTable { + void (*AliasesOf)(void *, ByConstRef, ByConstRef, + llvm::function_ref); + AliasResult (*Alias)(void *, ByConstRef, ByConstRef, + ByConstRef); + }; + template - static void aliasesOfThunk(void *AA, v_t Of, n_t At, + static void aliasesOfThunk(void *AA, ByConstRef Of, ByConstRef At, llvm::function_ref WithAlias) { + const auto *CAA = fromOpaquePtr(AA); if constexpr (IsAliasIterator) { - return static_cast(AA)->aliasesof(Of, At, WithAlias); + return CAA->aliasesof(Of, At, WithAlias); } else { - auto AliasSetPtr = static_cast(AA)->getAliasSet(Of, At); + auto AliasSetPtr = CAA->getAliasSet(Of, At); for (auto &&Alias : *AliasSetPtr) { WithAlias(PSR_FWD(Alias)); } } } - template - static void allocSitesOfThunk(void *AA, v_t Of, n_t At, - llvm::function_ref WithAlias) { - ReachableAllocationSitesIterator{ - static_cast(AA)} - .aliasesOf(Of, At, WithAlias); + template + static AliasResult aliasThunk(void *AA, ByConstRef Ptr, + ByConstRef Alias, ByConstRef At) { + if (Ptr == Alias) { + return AliasResult::MustAlias; + } + + const auto *CAA = fromOpaquePtr(AA); + if constexpr (detail::HasAlias::value) { + return CAA->alias(Ptr, Alias, At); + } else if constexpr (detail::HasGetAliasSet::value) { + auto AliasSetPtr = CAA->getAliasSet(Ptr, At); + return AliasSetPtr->count(Alias); + } else { + AliasResult Ret = AliasResult::NoAlias; + + CAA->aliasesof(Ptr, At, [&Ret, Alias](v_t A) { + if (A == Alias) { + Ret = AliasResult::MayAlias; + } + }); + + return Ret; + } } + template + constexpr static VTable VtableFor = { + &aliasesOfThunk, + &aliasThunk, + }; + void *AA{}; - void (*AliasesOf)(void *, v_t, n_t, llvm::function_ref){}; -}; + const VTable *VT{}; +}; // namespace psr } // namespace psr From 14fc564825744d9e8cc8d9114c6f8e2109217d1f Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Wed, 18 Jun 2025 14:53:12 +0200 Subject: [PATCH 03/11] Integrate AliasIterator into Default[IFDS/IDE]AliasAwareFlowFunctions --- .../IfdsIde/DefaultAliasAwareIDEProblem.h | 15 +++-- .../phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h | 4 ++ include/phasar/Pointer/AliasInfo.h | 52 +++++++++-------- include/phasar/Pointer/AliasInfoBase.h | 9 ++- include/phasar/Pointer/AliasIterator.h | 57 ++++++++++++++----- include/phasar/Utils/Utilities.h | 4 +- .../DefaultAliasAwareIDEFlowFunctions.cpp | 23 +++----- 7 files changed, 102 insertions(+), 62 deletions(-) diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h index 9a73977f97..05ff4b9a53 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h @@ -27,14 +27,13 @@ class IDEAliasAwareDefaultFlowFunctionsImpl using IDENoAliasDefaultFlowFunctionsImpl::isFunctionModeled; - [[nodiscard]] constexpr LLVMAliasInfoRef getAliasInfo() const noexcept { + [[nodiscard]] constexpr LLVMAliasIteratorRef getAliasInfo() const noexcept { return AS; } - constexpr IDEAliasAwareDefaultFlowFunctionsImpl(LLVMAliasInfoRef AS) noexcept - : AS(AS) { - assert(AS && "You must provide an alias information handle!"); - } + constexpr IDEAliasAwareDefaultFlowFunctionsImpl( + LLVMAliasIteratorRef AS) noexcept + : AS(AS) {} [[nodiscard]] FlowFunctionPtrType getNormalFlowFunctionImpl(n_t Curr, n_t /*Succ*/); @@ -49,7 +48,7 @@ class IDEAliasAwareDefaultFlowFunctionsImpl llvm::ArrayRef /*Callees*/); private: - LLVMAliasInfoRef AS; + LLVMAliasIteratorRef AS; }; } // namespace detail @@ -81,7 +80,7 @@ class DefaultAliasAwareIDEProblem /// \note It is useful to use an instance of FilteredAliasSet for the alias /// information to lower suprious aliases explicit DefaultAliasAwareIDEProblem( - const ProjectIRDBBase *IRDB, LLVMAliasInfoRef AS, + const ProjectIRDBBase *IRDB, LLVMAliasIteratorRef AS, std::vector EntryPoints, std::optional ZeroValue) noexcept(std::is_nothrow_move_constructible_v) @@ -123,7 +122,7 @@ class DefaultAliasAwareIFDSProblem /// \note It is useful to use an instance of FilteredAliasSet for the alias /// information to lower suprious aliases explicit DefaultAliasAwareIFDSProblem( - const ProjectIRDBBase *IRDB, LLVMAliasInfoRef AS, + const ProjectIRDBBase *IRDB, LLVMAliasIteratorRef AS, std::vector EntryPoints, d_t ZeroValue) noexcept(std::is_nothrow_move_constructible_v) : IFDSTabulationProblem(IRDB, std::move(EntryPoints), ZeroValue), diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h b/include/phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h index 43d83a1f68..bbe69a0f74 100644 --- a/include/phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h +++ b/include/phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h @@ -11,6 +11,7 @@ #define PHASAR_PHASARLLVM_POINTER_LLVMALIASINFO_H_ #include "phasar/Pointer/AliasInfo.h" +#include "phasar/Pointer/AliasIterator.h" namespace llvm { class Function; @@ -20,6 +21,9 @@ class Value; namespace psr { +using LLVMAliasIteratorRef = + AliasIteratorRef; + using LLVMAliasInfoRef = AliasInfoRef; diff --git a/include/phasar/Pointer/AliasInfo.h b/include/phasar/Pointer/AliasInfo.h index 55b7932621..cdb3f9faad 100644 --- a/include/phasar/Pointer/AliasInfo.h +++ b/include/phasar/Pointer/AliasInfo.h @@ -51,13 +51,14 @@ struct AliasInfoTraits> : DefaultAATraits {}; /// Example: /// \code /// LLVMAliasSet ASet(...); -/// AliasInfoRef AA = &ASet; +/// LLVMAliasInfoRef AA = &ASet; /// \endcode /// /// NOTE: AliasInfoRef::mergeWith() only works if supplied with a compatible /// other AliasInfo. Otherwise, it asserts out template -class AliasInfoRef : public AnalysisPropertiesMixin> { +class [[gsl::Pointer]] AliasInfoRef + : public AnalysisPropertiesMixin> { friend class AliasInfo; template friend class AliasIteratorRef; @@ -183,11 +184,9 @@ class AliasInfoRef : public AnalysisPropertiesMixin> { } private: - struct VTable { + struct VTable : detail::AliasIteratorVTableBase { bool (*IsInterProcedural)(const void *) noexcept; AliasAnalysisType (*GetAliasAnalysisType)(const void *) noexcept; - AliasResult (*Alias)(void *, ByConstRef, ByConstRef, - ByConstRef); AliasSetPtrTy (*GetAliasSet)(void *, ByConstRef, ByConstRef); AllocationSiteSetPtrTy (*GetReachableAllocationSites)(void *, ByConstRef, bool, @@ -195,8 +194,6 @@ class AliasInfoRef : public AnalysisPropertiesMixin> { bool (*IsInReachableAllocationSites)(void *, ByConstRef, ByConstRef, bool, ByConstRef); - void (*AliasesOf)(void *, ByConstRef, ByConstRef, - llvm::function_ref); void (*Print)(const void *, llvm::raw_ostream &); void (*PrintAsJson)(const void *, llvm::raw_ostream &); void (*MergeWith)(void *, void *); @@ -209,17 +206,32 @@ class AliasInfoRef : public AnalysisPropertiesMixin> { template static constexpr VTable VTableFor = { + { + [](void *AA, ByConstRef Of, ByConstRef At, + llvm::function_ref WithAlias) { + if constexpr (IsAliasIterator) { + return static_cast(AA)->aliasesof(Of, At, + WithAlias); + } else { + auto AliasSetPtr = + static_cast(AA)->getAliasSet(Of, At); + for (auto &&Alias : *AliasSetPtr) { + WithAlias(PSR_FWD(Alias)); + } + } + }, + [](void *AA, ByConstRef Pointer1, ByConstRef Pointer2, + ByConstRef AtInstruction) { + return static_cast(AA)->alias(Pointer1, Pointer2, + AtInstruction); + }, + }, [](const void *AA) noexcept { return static_cast(AA)->isInterProcedural(); }, [](const void *AA) noexcept { return static_cast(AA)->getAliasAnalysisType(); }, - [](void *AA, ByConstRef Pointer1, ByConstRef Pointer2, - ByConstRef AtInstruction) { - return static_cast(AA)->alias(Pointer1, Pointer2, - AtInstruction); - }, [](void *AA, ByConstRef Pointer, ByConstRef AtInstruction) { return static_cast(AA)->getAliasSet(Pointer, AtInstruction); @@ -234,17 +246,6 @@ class AliasInfoRef : public AnalysisPropertiesMixin> { return static_cast(AA)->isInReachableAllocationSites( Pointer1, Pointer2, IntraProcOnly, AtInstruction); }, - [](void *AA, ByConstRef Of, ByConstRef At, - llvm::function_ref WithAlias) { - if constexpr (IsAliasIterator) { - return static_cast(AA)->aliasesof(Of, At, WithAlias); - } else { - auto AliasSetPtr = static_cast(AA)->getAliasSet(Of, At); - for (auto &&Alias : *AliasSetPtr) { - WithAlias(PSR_FWD(Alias)); - } - } - }, [](const void *AA, llvm::raw_ostream &OS) { static_cast(AA)->print(OS); }, @@ -267,7 +268,7 @@ class AliasInfoRef : public AnalysisPropertiesMixin> { [](const void *AA) noexcept { delete static_cast(AA); }, - }; + }; // namespace psr // -- @@ -287,7 +288,8 @@ class AliasInfoRef : public AnalysisPropertiesMixin> { /// \endcode /// template -class [[clang::trivial_abi]] AliasInfo final : public AliasInfoRef { +class [[clang::trivial_abi, gsl::Owner]] AliasInfo final + : public AliasInfoRef { using base_t = AliasInfoRef; public: diff --git a/include/phasar/Pointer/AliasInfoBase.h b/include/phasar/Pointer/AliasInfoBase.h index 9e2bff7600..0ebe25c4b9 100644 --- a/include/phasar/Pointer/AliasInfoBase.h +++ b/include/phasar/Pointer/AliasInfoBase.h @@ -11,6 +11,7 @@ #define PHASAR_POINTER_ALIASINFOBASE_H #include "phasar/Pointer/AliasInfoTraits.h" +#include "phasar/Utils/ByRef.h" #include "phasar/Utils/Macros.h" #include "llvm/Support/raw_ostream.h" @@ -78,7 +79,7 @@ struct IsAliasIterator : std::false_type {}; template struct IsAliasIterator< - T, std::void_t().aliasesof( + T, std::void_t().aliasesOf( std::declval(), std::declval(), std::declval>()))>> : std::true_type {}; @@ -99,6 +100,12 @@ struct HasGetAliasSet< std::declval(), std::declval()))>> : std::true_type {}; +template struct AliasIteratorVTableBase { + void (*AliasesOf)(void *, ByConstRef, ByConstRef, + llvm::function_ref); + AliasResult (*Alias)(void *, ByConstRef, ByConstRef, ByConstRef); +}; + } // namespace detail template PSR_CONCEPT IsAliasInfo = detail::IsAliasInfo::value; diff --git a/include/phasar/Pointer/AliasIterator.h b/include/phasar/Pointer/AliasIterator.h index 706edb7343..f4fd04201b 100644 --- a/include/phasar/Pointer/AliasIterator.h +++ b/include/phasar/Pointer/AliasIterator.h @@ -64,7 +64,20 @@ template ReachableAllocationSitesIterator(UnderlyingAA *) -> ReachableAllocationSitesIterator; -template class AliasIteratorRef { +/// \brief A type-erased reference to any object implementing the +/// IsAliasIterator interface. Use this, if your alias-aware analysis just needs +/// a minimal interface to work with aliases and does not require the +/// versatility of AliasInfoRef. +/// +/// This is a *non-owning* reference similar to std::string_view and +/// llvm::ArrayRef. Pass values of this type by value. +/// +/// Example: +/// \code +/// LLVMAliasSet ASet(...); +/// LLVMAliasIteratorRef AA = &ASet; +/// \endcode +template class [[gsl::Pointer]] AliasIteratorRef { template static constexpr bool CanSSO = std::is_trivially_copyable_v && sizeof(ConcreteAA) <= sizeof(void *); @@ -104,19 +117,38 @@ template class AliasIteratorRef { } constexpr AliasIteratorRef(AliasInfoRef AS) noexcept - : AA(&psr::assertNotNull(AS.AA)), VT(AS.VT->AliasesOf, AS.VT.Alias) {} + : AA(AS.AA), VT(AS.VT) { + assert(AS != nullptr); + } constexpr AliasIteratorRef(const AliasIteratorRef &) noexcept = default; constexpr AliasIteratorRef & operator=(const AliasIteratorRef &) noexcept = default; ~AliasIteratorRef() = default; + /// \brief Invokes the callback WithAlias for all aliases of Of at the + /// instruction At. + /// + /// Note: The alias-relation is reflexive, so WithAlias is also called with + /// Of. + /// + /// \param Of The pointer, for which the aliases should be iterated + /// \param At The instruction, where the alias-query is raised. + /// Implementations may ignore this parameter + /// \param WithAlias Callback to invoke for each alias of Of void aliasesOf(ByConstRef Of, ByConstRef At, llvm::function_ref WithAlias) { assert(VT != nullptr); VT->AliasesOf(AA, Of, At, WithAlias); } + /// \brief Convenience function to aggregate all aliases of Of in a set. + /// + /// \param Of The pointer, for which the aliases should be iterated + /// \param At The instruction, where the alias-query is raised. + /// Implementations may ignore this parameter + /// \tparam SetT The set-type of the set to create + /// \returns A set of type SetT containing all aliases of Of template > [[nodiscard]] SetT asSet(ByConstRef Of, ByConstRef At) { SetT Set; @@ -124,6 +156,12 @@ template class AliasIteratorRef { return Set; } + /// \brief Checks, whether Ptr and Alias may/must/partial/no-alias at + /// instruction At. + /// + /// \param Ptr The pointer, for which the aliases should be iterated + /// \param Alias A pointer, which may be a potential alias of Ptr + /// \param At The instruction, where the alias-query is raised. [[nodiscard]] AliasResult alias(ByConstRef Ptr, ByConstRef Alias, ByConstRef At) { assert(VT != nullptr); @@ -131,17 +169,10 @@ template class AliasIteratorRef { } private: - struct VTable { - void (*AliasesOf)(void *, ByConstRef, ByConstRef, - llvm::function_ref); - AliasResult (*Alias)(void *, ByConstRef, ByConstRef, - ByConstRef); - }; - template static void aliasesOfThunk(void *AA, ByConstRef Of, ByConstRef At, llvm::function_ref WithAlias) { - const auto *CAA = fromOpaquePtr(AA); + auto *CAA = fromOpaquePtr(AA); if constexpr (IsAliasIterator) { return CAA->aliasesof(Of, At, WithAlias); } else { @@ -159,7 +190,7 @@ template class AliasIteratorRef { return AliasResult::MustAlias; } - const auto *CAA = fromOpaquePtr(AA); + auto *CAA = fromOpaquePtr(AA); if constexpr (detail::HasAlias::value) { return CAA->alias(Ptr, Alias, At); } else if constexpr (detail::HasGetAliasSet::value) { @@ -179,13 +210,13 @@ template class AliasIteratorRef { } template - constexpr static VTable VtableFor = { + constexpr static detail::AliasIteratorVTableBase VtableFor = { &aliasesOfThunk, &aliasThunk, }; void *AA{}; - const VTable *VT{}; + const detail::AliasIteratorVTableBase *VT{}; }; // namespace psr } // namespace psr diff --git a/include/phasar/Utils/Utilities.h b/include/phasar/Utils/Utilities.h index 612732679f..109654b6a5 100644 --- a/include/phasar/Utils/Utilities.h +++ b/include/phasar/Utils/Utilities.h @@ -287,7 +287,9 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, return OS; } -template LLVM_ATTRIBUTE_ALWAYS_INLINE T &assertNotNull(T &Value) { +template +LLVM_ATTRIBUTE_ALWAYS_INLINE std::enable_if_t, T &> +assertNotNull(T &Value) { return Value; } diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEFlowFunctions.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEFlowFunctions.cpp index ed18352cee..f3d0cb4d20 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEFlowFunctions.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEFlowFunctions.cpp @@ -16,10 +16,7 @@ auto detail::IDEAliasAwareDefaultFlowFunctionsImpl::getNormalFlowFunctionImpl( n_t Curr, n_t Succ) -> FlowFunctionPtrType { if (const auto *Store = llvm::dyn_cast(Curr)) { - container_type Gen; - - auto AliasSet = AS.getAliasSet(Store->getPointerOperand(), Store); - Gen.insert(AliasSet->begin(), AliasSet->end()); + auto Gen = AS.asSet(Store->getPointerOperand(), Store); Gen.insert(Store->getValueOperand()); return FFTemplates::lambdaFlow( @@ -45,31 +42,29 @@ auto detail::IDEAliasAwareDefaultFlowFunctionsImpl::getCallFlowFunctionImpl( CallInst, CalleeFun); } -static void populateWithMayAliases(LLVMAliasInfoRef AS, container_type &Facts, +static void populateWithMayAliases(LLVMAliasIteratorRef AS, + container_type &Facts, const llvm::Instruction *Context) { container_type Tmp = Facts; - for (const auto *Fact : Facts) { - auto Aliases = AS.getAliasSet(Fact, Context); - for (const auto *Alias : *Aliases) { + for (const auto *Fact : Tmp) { + AS.aliasesOf(Fact, Context, [&Facts, Context](const auto *Alias) { if (const auto *Inst = llvm::dyn_cast(Alias)) { if (Inst->getParent() == Context->getParent() && Context->comesBefore(Inst)) { // We will see that inst later - continue; + return; } } if (const auto *Load = llvm::dyn_cast(Alias)) { // Handle at least one level of indirection... const auto *PointerOp = Load->getPointerOperand()->stripPointerCasts(); - Tmp.insert(PointerOp); + Facts.insert(PointerOp); } - Tmp.insert(Alias); - } + Facts.insert(Alias); + }); } - - Facts = std::move(Tmp); } auto detail::IDEAliasAwareDefaultFlowFunctionsImpl::getRetFlowFunctionImpl( From 035fdf562501293ca1ecfd97e10087c7b0491d0f Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Wed, 18 Jun 2025 17:30:36 +0200 Subject: [PATCH 04/11] Some restructure --- include/phasar/Pointer/AliasInfo.h | 18 ++- include/phasar/Pointer/AliasInfoBase.h | 35 ----- include/phasar/Pointer/AliasIterator.h | 139 ++++++++++++------ .../PhasarLLVM/Pointer/LLVMAliasSetTest.cpp | 10 ++ 4 files changed, 114 insertions(+), 88 deletions(-) diff --git a/include/phasar/Pointer/AliasInfo.h b/include/phasar/Pointer/AliasInfo.h index cdb3f9faad..2f268b07d5 100644 --- a/include/phasar/Pointer/AliasInfo.h +++ b/include/phasar/Pointer/AliasInfo.h @@ -12,6 +12,7 @@ #include "phasar/Pointer/AliasInfoBase.h" #include "phasar/Pointer/AliasInfoTraits.h" +#include "phasar/Pointer/AliasIterator.h" #include "phasar/Pointer/AliasResult.h" #include "phasar/Utils/AnalysisProperties.h" #include "phasar/Utils/ByRef.h" @@ -60,7 +61,6 @@ template class [[gsl::Pointer]] AliasInfoRef : public AnalysisPropertiesMixin> { friend class AliasInfo; - template friend class AliasIteratorRef; using traits_t = AliasInfoTraits>; @@ -71,14 +71,14 @@ class [[gsl::Pointer]] AliasInfoRef using n_t = typename traits_t::n_t; using v_t = typename traits_t::v_t; - AliasInfoRef() noexcept = default; - AliasInfoRef(std::nullptr_t) noexcept : AliasInfoRef() {} + constexpr AliasInfoRef() noexcept = default; + constexpr AliasInfoRef(std::nullptr_t) noexcept : AliasInfoRef() {} template && std::is_same_v && std::is_same_v>> - AliasInfoRef(ConcreteAA *AA) noexcept + constexpr AliasInfoRef(ConcreteAA *AA) noexcept : AA(AA), VT((std::is_empty_v || AA) ? &VTableFor : nullptr) {} @@ -87,12 +87,16 @@ class [[gsl::Pointer]] AliasInfoRef AliasInfoRef(AliasInfo &&) = delete; AliasInfoRef &operator=(AliasInfo &&) = delete; - AliasInfoRef(const AliasInfoRef &) noexcept = default; - AliasInfoRef &operator=(const AliasInfoRef &) noexcept = default; + constexpr AliasInfoRef(const AliasInfoRef &) noexcept = default; + constexpr AliasInfoRef &operator=(const AliasInfoRef &) noexcept = default; ~AliasInfoRef() noexcept = default; explicit operator bool() const noexcept { return VT != nullptr; } + constexpr operator AliasIteratorRef() const noexcept { + return AliasIteratorRef(AA, VT); + } + // -- Impl for IsAliasInfo: [[nodiscard]] bool isInterProcedural() const noexcept { @@ -184,7 +188,7 @@ class [[gsl::Pointer]] AliasInfoRef } private: - struct VTable : detail::AliasIteratorVTableBase { + struct VTable : AliasIteratorRef::VTable { bool (*IsInterProcedural)(const void *) noexcept; AliasAnalysisType (*GetAliasAnalysisType)(const void *) noexcept; AliasSetPtrTy (*GetAliasSet)(void *, ByConstRef, ByConstRef); diff --git a/include/phasar/Pointer/AliasInfoBase.h b/include/phasar/Pointer/AliasInfoBase.h index 0ebe25c4b9..b0fff91fc8 100644 --- a/include/phasar/Pointer/AliasInfoBase.h +++ b/include/phasar/Pointer/AliasInfoBase.h @@ -11,7 +11,6 @@ #define PHASAR_POINTER_ALIASINFOBASE_H #include "phasar/Pointer/AliasInfoTraits.h" -#include "phasar/Utils/ByRef.h" #include "phasar/Utils/Macros.h" #include "llvm/Support/raw_ostream.h" @@ -74,43 +73,9 @@ struct IsAliasInfo< std::declval()))>>> : std::true_type { }; -template -struct IsAliasIterator : std::false_type {}; - -template -struct IsAliasIterator< - T, std::void_t().aliasesOf( - std::declval(), std::declval(), - std::declval>()))>> - : std::true_type {}; - -template -struct HasAlias : std::false_type {}; -template -struct HasAlias().alias( - std::declval(), - std::declval(), - std::declval()))> : std::true_type {}; - -template -struct HasGetAliasSet : std::false_type {}; -template -struct HasGetAliasSet< - T, std::void_t().getAliasSet( - std::declval(), std::declval()))>> - : std::true_type {}; - -template struct AliasIteratorVTableBase { - void (*AliasesOf)(void *, ByConstRef, ByConstRef, - llvm::function_ref); - AliasResult (*Alias)(void *, ByConstRef, ByConstRef, ByConstRef); -}; - } // namespace detail template PSR_CONCEPT IsAliasInfo = detail::IsAliasInfo::value; -template -PSR_CONCEPT IsAliasIterator = detail::IsAliasIterator::value; } // namespace psr diff --git a/include/phasar/Pointer/AliasIterator.h b/include/phasar/Pointer/AliasIterator.h index f4fd04201b..0630ddc214 100644 --- a/include/phasar/Pointer/AliasIterator.h +++ b/include/phasar/Pointer/AliasIterator.h @@ -10,8 +10,6 @@ #ifndef PHASAR_POINTER_ALIASITERATOR_H #define PHASAR_POINTER_ALIASITERATOR_H -#include "phasar/Pointer/AliasInfo.h" -#include "phasar/Pointer/AliasInfoBase.h" #include "phasar/Pointer/AliasResult.h" #include "phasar/Utils/ByRef.h" #include "phasar/Utils/Macros.h" @@ -24,45 +22,37 @@ namespace psr { -template struct ReachableAllocationSitesIterator { - using n_t = typename UnderlyingAA::n_t; - using v_t = typename UnderlyingAA::v_t; - - void aliasesOf(v_t Of, n_t At, llvm::function_ref WithAlias) { - assert(AA != nullptr); - - auto AliasSetPtr = AA->getReachableAllocationSites(Of, true, At); - if (!AliasSetPtr || AliasSetPtr->empty()) { - // The alias-relation should be reflexive - WithAlias(Of); - return; - } - - for (auto &&Alias : *AliasSetPtr) { - WithAlias(PSR_FWD(Alias)); - } - } - - [[nodiscard]] AliasResult alias(v_t Ptr, v_t Alias, n_t At) { - assert(AA != nullptr); - - if (Ptr == Alias) { - return AliasResult::MustAlias; - } - - if (AA->isInReachableAllocationSites(Ptr, Alias, true, At)) { - return AliasResult::MayAlias; - } - - return AliasResult::NoAlias; - } - - UnderlyingAA *AA{}; -}; - -template -ReachableAllocationSitesIterator(UnderlyingAA *) - -> ReachableAllocationSitesIterator; +namespace detail { + +template +struct IsAliasIterator : std::false_type {}; + +template +struct IsAliasIterator< + T, std::void_t().aliasesOf( + std::declval(), std::declval(), + std::declval>()))>> + : std::true_type {}; + +template +struct HasAlias : std::false_type {}; +template +struct HasAlias().alias( + std::declval(), + std::declval(), + std::declval()))> : std::true_type {}; + +template +struct HasGetAliasSet : std::false_type {}; +template +struct HasGetAliasSet< + T, std::void_t().getAliasSet( + std::declval(), std::declval()))>> + : std::true_type {}; + +} // namespace detail +template +PSR_CONCEPT IsAliasIterator = detail::IsAliasIterator::value; /// \brief A type-erased reference to any object implementing the /// IsAliasIterator interface. Use this, if your alias-aware analysis just needs @@ -106,6 +96,12 @@ template class [[gsl::Pointer]] AliasIteratorRef { using n_t = N; using v_t = V; + struct VTable { + void (*AliasesOf)(void *, ByConstRef, ByConstRef, + llvm::function_ref); + AliasResult (*Alias)(void *, ByConstRef, ByConstRef, ByConstRef); + }; + template && @@ -115,10 +111,21 @@ template class [[gsl::Pointer]] AliasIteratorRef { : AA(getOpaquePtr(psr::assertNotNull(AA))), VT(&VtableFor) { static_assert(IsAliasIterator); } + template < + typename ConcreteAA, + typename = std::enable_if_t< + !std::is_base_of_v && + std::is_same_v && + std::is_same_v && CanSSO>> + constexpr AliasIteratorRef(ConcreteAA AA) noexcept + : AA(getOpaquePtr(AA)), VT(&VtableFor) { + static_assert(IsAliasIterator); + } - constexpr AliasIteratorRef(AliasInfoRef AS) noexcept - : AA(AS.AA), VT(AS.VT) { - assert(AS != nullptr); + constexpr explicit AliasIteratorRef(void *AA, const VTable *VT) noexcept + : AA(AA), VT(VT) { + assert(AA != nullptr); + assert(VT != nullptr); } constexpr AliasIteratorRef(const AliasIteratorRef &) noexcept = default; @@ -210,15 +217,55 @@ template class [[gsl::Pointer]] AliasIteratorRef { } template - constexpr static detail::AliasIteratorVTableBase VtableFor = { + constexpr static VTable VtableFor = { &aliasesOfThunk, &aliasThunk, }; void *AA{}; - const detail::AliasIteratorVTableBase *VT{}; + const VTable *VT{}; }; // namespace psr +template struct ReachableAllocationSitesIterator { + using n_t = typename UnderlyingAA::n_t; + using v_t = typename UnderlyingAA::v_t; + + void aliasesOf(v_t Of, n_t At, llvm::function_ref WithAlias) { + assert(AA != nullptr); + + auto AliasSetPtr = AA->getReachableAllocationSites(Of, true, At); + if (!AliasSetPtr || AliasSetPtr->empty()) { + // The alias-relation should be reflexive + WithAlias(Of); + return; + } + + for (auto &&Alias : *AliasSetPtr) { + WithAlias(PSR_FWD(Alias)); + } + } + + [[nodiscard]] AliasResult alias(v_t Ptr, v_t Alias, n_t At) { + assert(AA != nullptr); + + if (Ptr == Alias) { + return AliasResult::MustAlias; + } + + if (AA->isInReachableAllocationSites(Ptr, Alias, true, At)) { + return AliasResult::MayAlias; + } + + return AliasResult::NoAlias; + } + + UnderlyingAA *AA{}; +}; + +template +ReachableAllocationSitesIterator(UnderlyingAA *) + -> ReachableAllocationSitesIterator; + } // namespace psr #endif // PHASAR_POINTER_ALIASITERATOR_H diff --git a/unittests/PhasarLLVM/Pointer/LLVMAliasSetTest.cpp b/unittests/PhasarLLVM/Pointer/LLVMAliasSetTest.cpp index 74a242dec3..22a552af30 100644 --- a/unittests/PhasarLLVM/Pointer/LLVMAliasSetTest.cpp +++ b/unittests/PhasarLLVM/Pointer/LLVMAliasSetTest.cpp @@ -4,12 +4,15 @@ #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" #include "phasar/PhasarLLVM/Passes/ValueAnnotationPass.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include "phasar/PhasarLLVM/Pointer/LLVMPointsToUtils.h" #include "phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h" #include "TestConfig.h" #include "gtest/gtest.h" +#include + using namespace psr; TEST(LLVMAliasSet, Intra_01) { @@ -64,6 +67,13 @@ TEST(LLVMAliasSet, Global_01) { llvm::outs() << '\n'; } +static_assert(std::is_convertible_v); +static_assert(std::is_convertible_v); +static_assert(std::is_convertible_v); +static_assert( + std::is_convertible_v, + LLVMAliasIteratorRef>); + int main(int Argc, char **Argv) { ::testing::InitGoogleTest(&Argc, Argv); return RUN_ALL_TESTS(); From e1373dd16ab0fc6b8711dfd3df12c074692ea409 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Thu, 19 Jun 2025 12:56:22 +0200 Subject: [PATCH 05/11] Add PointsToIterator and integrate it with SVFBasedPointsToInfo --- .../PhasarLLVM/Pointer/LLVMPointsToInfo.h | 31 ++ .../PhasarLLVM/Pointer/SVF/SVFPointsToSet.h | 27 ++ include/phasar/Pointer/AliasInfo.h | 3 +- include/phasar/Pointer/AliasIterator.h | 30 +- include/phasar/Pointer/PointsToInfo.h | 81 +++-- include/phasar/Pointer/PointsToIterator.h | 281 ++++++++++++++++++ include/phasar/Utils/PointerUtils.h | 41 +++ include/phasar/Utils/TypeErasureUtils.h | 64 ++++ lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp | 113 ++++++- .../PhasarLLVM/Pointer/LLVMAliasSetTest.cpp | 2 +- .../PhasarLLVM/Pointer/SVFAliasSetTest.cpp | 21 ++ 11 files changed, 632 insertions(+), 62 deletions(-) create mode 100644 include/phasar/PhasarLLVM/Pointer/LLVMPointsToInfo.h create mode 100644 include/phasar/Pointer/PointsToIterator.h create mode 100644 include/phasar/Utils/TypeErasureUtils.h diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMPointsToInfo.h b/include/phasar/PhasarLLVM/Pointer/LLVMPointsToInfo.h new file mode 100644 index 0000000000..160d633580 --- /dev/null +++ b/include/phasar/PhasarLLVM/Pointer/LLVMPointsToInfo.h @@ -0,0 +1,31 @@ +/****************************************************************************** + * Copyright (c) 2025 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_POINTER_LLVMPOINTSTOINFO_H +#define PHASAR_PHASARLLVM_POINTER_LLVMPOINTSTOINFO_H + +#include "phasar/Pointer/PointsToIterator.h" + +namespace llvm { +class Instruction; +class Value; +} // namespace llvm + +namespace psr { + +using LLVMPointsToIteratorRef = + PointsToIteratorRef; + +using LLVMPointsToIterator = + PointsToIterator; +} // namespace psr + +#endif // PHASAR_PHASARLLVM_POINTER_LLVMPOINTSTOINFO_H diff --git a/include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h b/include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h index 55c753222b..6c037835b4 100644 --- a/include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h +++ b/include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h @@ -10,11 +10,16 @@ #define PHASAR_PHASARLLVM_POINTER_SVF_SVFPOINTSTOSET_H #include "phasar/Config/phasar-config.h" +#include "phasar/PhasarLLVM/Pointer/LLVMPointsToInfo.h" +#include "phasar/Pointer/AliasAnalysisType.h" #include "phasar/Pointer/PointsToInfo.h" +#include "phasar/Pointer/PointsToIterator.h" #include "llvm/ADT/DenseSet.h" #include "llvm/IR/Value.h" +#include + #ifndef PHASAR_USE_SVF #error \ "Don't include SVFPointsToSet.h when PhASAR is not configured to include SVF. Set the cmake variable PHASAR_USE_SVF and retry." @@ -37,8 +42,18 @@ struct SVFPointsToInfoTraits { using PointsToSetPtrTy = PointsToSetTy; }; +enum class SVFPointsToAnalysisType { + DDA = int(AliasAnalysisType::SVFDDA), + VFS = int(AliasAnalysisType::SVFVFS), +}; + using SVFBasedPointsToInfo = PointsToInfo; using SVFBasedPointsToInfoRef = PointsToInfoRef; +using SVFBasedPointsToIterator = + PointsToIterator; +using SVFBasedPointsToIteratorRef = + PointsToIteratorRef; /// Use SVF to perform a VersionedFlowSensitive pointer analysis and return the /// results compatible to psr::PointsToInfo and psr::PointsToInfoRef @@ -50,6 +65,18 @@ createSVFVFSPointsToInfo(LLVMProjectIRDB &IRDB); [[nodiscard]] SVFBasedPointsToInfo createSVFDDAPointsToInfo(LLVMProjectIRDB &IRDB); +/// Use SVF to perform the specified pointer analysis and return the results +/// compatible to psr::PointsToInfo and psr::PointsToInfoRef +[[nodiscard]] SVFBasedPointsToInfo +createSVFPointsToInfo(LLVMProjectIRDB &IRDB, SVFPointsToAnalysisType PTATy); + +/// Use SVF to perform the specified pointer analysis and return the results +/// compatoble to psr::LLVMPointsToIterator, converting the points-to sets to +/// LLVM allocation-sites +[[nodiscard]] LLVMPointsToIterator +createLLVMSVFPointsToIterator(LLVMProjectIRDB &IRDB, + SVFPointsToAnalysisType PTATy); + } // namespace psr #endif // PHASAR_PHASARLLVM_POINTER_SVF_SVFPOINTSTOSET_H diff --git a/include/phasar/Pointer/AliasInfo.h b/include/phasar/Pointer/AliasInfo.h index 2f268b07d5..6aef775658 100644 --- a/include/phasar/Pointer/AliasInfo.h +++ b/include/phasar/Pointer/AliasInfo.h @@ -93,9 +93,10 @@ class [[gsl::Pointer]] AliasInfoRef explicit operator bool() const noexcept { return VT != nullptr; } - constexpr operator AliasIteratorRef() const noexcept { + constexpr operator AliasIteratorRef() const & noexcept { return AliasIteratorRef(AA, VT); } + constexpr operator AliasIteratorRef() && noexcept = delete; // -- Impl for IsAliasInfo: diff --git a/include/phasar/Pointer/AliasIterator.h b/include/phasar/Pointer/AliasIterator.h index 0630ddc214..4398bd78f2 100644 --- a/include/phasar/Pointer/AliasIterator.h +++ b/include/phasar/Pointer/AliasIterator.h @@ -13,6 +13,7 @@ #include "phasar/Pointer/AliasResult.h" #include "phasar/Utils/ByRef.h" #include "phasar/Utils/Macros.h" +#include "phasar/Utils/TypeErasureUtils.h" #include "phasar/Utils/Utilities.h" #include "llvm/ADT/STLFunctionalExtras.h" @@ -67,31 +68,8 @@ PSR_CONCEPT IsAliasIterator = detail::IsAliasIterator::value; /// LLVMAliasSet ASet(...); /// LLVMAliasIteratorRef AA = &ASet; /// \endcode -template class [[gsl::Pointer]] AliasIteratorRef { - template - static constexpr bool CanSSO = std::is_trivially_copyable_v && - sizeof(ConcreteAA) <= sizeof(void *); - - template - constexpr static ConcreteAA *fromOpaquePtr(void *&EF) noexcept { - if constexpr (CanSSO) { - return static_cast(static_cast(&EF)); - } else { - return static_cast(EF); - } - } - - template - constexpr void *getOpaquePtr(ConcreteAA &AA) noexcept { - if constexpr (CanSSO) { - void *Ret{}; - ::new (&Ret) ConcreteAA(AA); - return Ret; - } else { - return &AA; - } - } - +template +class [[gsl::Pointer]] AliasIteratorRef : private TypeErasureUtils { public: using n_t = N; using v_t = V; @@ -181,7 +159,7 @@ template class [[gsl::Pointer]] AliasIteratorRef { llvm::function_ref WithAlias) { auto *CAA = fromOpaquePtr(AA); if constexpr (IsAliasIterator) { - return CAA->aliasesof(Of, At, WithAlias); + return (void)CAA->aliasesof(Of, At, WithAlias); } else { auto AliasSetPtr = CAA->getAliasSet(Of, At); for (auto &&Alias : *AliasSetPtr) { diff --git a/include/phasar/Pointer/PointsToInfo.h b/include/phasar/Pointer/PointsToInfo.h index af2f54d587..6b0f9610df 100644 --- a/include/phasar/Pointer/PointsToInfo.h +++ b/include/phasar/Pointer/PointsToInfo.h @@ -11,6 +11,7 @@ #define PHASAR_POINTER_POINTSTOINFO_H #include "phasar/Pointer/PointsToInfoBase.h" +#include "phasar/Pointer/PointsToIterator.h" #include "phasar/Utils/ByRef.h" #include @@ -52,15 +53,15 @@ class PointsToInfoRef && is_equivalent_PointsToTraits_v>>> - PointsToInfoRef(const ConcretePTA *PT) noexcept + constexpr PointsToInfoRef(const ConcretePTA *PT) noexcept : PT(PT), VT(&VTableFor) { if constexpr (!std::is_empty_v) { assert(PT != nullptr); @@ -75,19 +76,22 @@ class PointsToInfoRef() const & noexcept { + return PointsToIteratorRef(PT, VT); + } + + constexpr operator PointsToIteratorRef() && noexcept = delete; private: - struct VTableBase { - o_t (*AsAbstractObject)(const void *, ByConstRef) noexcept; + struct VTableBase : public PointsToIteratorRef::VTable { + std::optional (*AsPointerOrNull)(const void *, ByConstRef) noexcept; - bool (*MayPointsTo)(const void *, ByConstRef, ByConstRef, - ByConstRef); + PointsToSetPtrTy (*GetPointsToSet)(const void *, ByConstRef, ByConstRef); - - void (*Destroy)(const void *) noexcept; // Useful for the owning variant }; template struct VTable : VTableBase {}; @@ -102,26 +106,43 @@ class PointsToInfoRef constexpr static VTable<> makeVTableFor() noexcept { constexpr VTableBase Base = { - [](const void *PT, ByConstRef Pointer) noexcept { - return static_cast(PT)->asAbstractObject( - Pointer); + { + [](const void *PT, ByConstRef Pointer) noexcept { + return static_cast(PT)->asAbstractObject( + Pointer); + }, + [](const void *PT, ByConstRef Pointer, ByConstRef At, + llvm::function_ref WithPointee) { + const auto *CPT = static_cast(PT); + if constexpr (detail::IsPointsToIterator::value) { + return (void)CPT->foreachPointeesOf(Pointer, At, WithPointee); + } else { + auto PointsToSet = CPT->getPointsToSet(Pointer, At); + // The PointsToSet can be a set or a pointer to a set + auto PointsToSetPtr = getPointerFrom(PointsToSet); + for (auto &&Pointee : *PointsToSetPtr) { + WithPointee(PSR_FWD(Pointee)); + } + } + }, + [](const void *PT, ByConstRef Pointer, ByConstRef Obj, + ByConstRef AtInstruction) { + return static_cast(PT)->mayPointsTo( + Pointer, Obj, AtInstruction); + }, + [](const void *PT) noexcept { + delete static_cast(PT); + }, + }, [](const void *PT, ByConstRef Obj) noexcept { return static_cast(PT)->asPointerOrNull(Obj); }, - [](const void *PT, ByConstRef Pointer, ByConstRef Obj, - ByConstRef AtInstruction) { - return static_cast(PT)->mayPointsTo( - Pointer, Obj, AtInstruction); - }, [](const void *PT, ByConstRef Pointer, ByConstRef AtInstruction) { return static_cast(PT)->getPointsToSet( Pointer, AtInstruction); }, - [](const void *PT) noexcept { - delete static_cast(PT); - }, }; if constexpr (std::is_same_v) { return {Base}; @@ -214,34 +235,34 @@ class [[clang::trivial_abi]] PointsToInfo< using typename base_t::PointsToSetTy; using typename base_t::v_t; - PointsToInfo() noexcept = default; - PointsToInfo(std::nullptr_t) noexcept {}; + constexpr PointsToInfo() noexcept = default; + constexpr PointsToInfo(std::nullptr_t) noexcept {}; PointsToInfo(const PointsToInfo &) = delete; PointsToInfo &operator=(const PointsToInfo &) = delete; - PointsToInfo(PointsToInfo &&Other) noexcept { swap(Other); } - PointsToInfo &operator=(PointsToInfo &&Other) noexcept { + constexpr PointsToInfo(PointsToInfo &&Other) noexcept { swap(Other); } + constexpr PointsToInfo &operator=(PointsToInfo &&Other) noexcept { PointsToInfo(std::move(Other)).swap(*this); return *this; } - void swap(PointsToInfo &Other) noexcept { + constexpr void swap(PointsToInfo &Other) noexcept { std::swap(this->PT, Other.PT); std::swap(this->VT, Other.VT); } - friend void swap(PointsToInfo &LHS, PointsToInfo &RHS) noexcept { + constexpr friend void swap(PointsToInfo &LHS, PointsToInfo &RHS) noexcept { LHS.swap(RHS); } template - explicit PointsToInfo(std::in_place_type_t /*unused*/, - ArgTys &&...Args) + constexpr explicit PointsToInfo(std::in_place_type_t /*unused*/, + ArgTys &&...Args) : PointsToInfoRef( new ConcretePTA(std::forward(Args)...)) {} template - PointsToInfo(std::unique_ptr PTA) + constexpr PointsToInfo(std::unique_ptr PTA) : PointsToInfoRef(PTA.release()) {} ~PointsToInfo() noexcept { diff --git a/include/phasar/Pointer/PointsToIterator.h b/include/phasar/Pointer/PointsToIterator.h new file mode 100644 index 0000000000..77cdf99135 --- /dev/null +++ b/include/phasar/Pointer/PointsToIterator.h @@ -0,0 +1,281 @@ +/****************************************************************************** + * Copyright (c) 2025 Fabian Schíebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel and others + *****************************************************************************/ + +#ifndef PHASAR_POINTER_POINTSTOITERATOR_H +#define PHASAR_POINTER_POINTSTOITERATOR_H + +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/Macros.h" +#include "phasar/Utils/PointerUtils.h" +#include "phasar/Utils/TypeErasureUtils.h" +#include "phasar/Utils/Utilities.h" + +#include "llvm/ADT/STLFunctionalExtras.h" + +#include +#include + +namespace psr { +namespace detail { +template +struct IsPointsToIterator : std::false_type {}; + +template +struct IsPointsToIterator< + T, std::void_t().foreachPointeesOf( + std::declval(), std::declval(), + std::declval>()))>> + : std::true_type {}; + +template +struct HasMayPointsTo : std::false_type {}; +template +struct HasMayPointsTo().mayPointsTo( + std::declval(), + std::declval(), + std::declval()))> + : std::true_type {}; + +template +struct HasGetPointsToSet : std::false_type {}; +template +struct HasGetPointsToSet().getPointsToSet( + std::declval(), + std::declval()))> + : std::true_type {}; + +template +struct HasAsAbstractObject : std::false_type {}; +template +struct HasAsAbstractObject().asAbstractObject( + std::declval()))> + : std::true_type {}; +} // namespace detail + +template +class [[gsl::Pointer]] PointsToIteratorRef : protected TypeErasureUtils { +public: + using v_t = V; + using o_t = O; + using n_t = N; + + struct VTable { + o_t (*AsAbstractObject)(const void *, ByConstRef) noexcept; + void (*ForeachPointeesOf)(const void *, ByConstRef, ByConstRef, + llvm::function_ref); + bool (*MayPointsTo)(const void *, ByConstRef, ByConstRef, + ByConstRef); + void (*Destroy)(const void *) noexcept; // Useful for the owning variant + }; + + template && + std::is_same_v && + std::is_same_v && + std::is_same_v>> + constexpr PointsToIteratorRef(const ConcretePTA *PT) noexcept + : PT(getOpaquePtr(psr::assertNotNull(PT))), VT(&VTableFor) { + static_assert(detail::IsPointsToIterator::value); + } + + template && + std::is_same_v && + std::is_same_v && + std::is_same_v && + CanSSO>> + constexpr PointsToIteratorRef(ConcretePTA PT) noexcept + : PT(getOpaquePtr(PT)), VT(&VTableFor) { + static_assert(detail::IsPointsToIterator::value); + } + + constexpr explicit PointsToIteratorRef(const void *PT, + const VTable *VT) noexcept + : PT(PT), VT(VT) { + assert(PT != nullptr); + assert(VT != nullptr); + } + + constexpr PointsToIteratorRef(const PointsToIteratorRef &) noexcept = default; + constexpr PointsToIteratorRef & + operator=(const PointsToIteratorRef &) noexcept = default; + ~PointsToIteratorRef() = default; + + [[nodiscard]] o_t asAbstractObject(ByConstRef Pointer) const noexcept { + assert(VT != nullptr); + return VT->AsAbstractObject(PT, Pointer); + } + + void foreachPointeesOf(ByConstRef Pointer, ByConstRef At, + llvm::function_ref WithPointee) const { + assert(VT != nullptr); + VT->ForeachPointeesOf(PT, Pointer, At, WithPointee); + } + template > + [[nodiscard]] SetT asSet(ByConstRef Pointer, ByConstRef At) { + SetT Set; + foreachPointeesOf(Pointer, At, [&Set](o_t Obj) { Set.insert(Obj); }); + return Set; + } + + [[nodiscard]] bool mayPointsTo(ByConstRef Pointer, ByConstRef Obj, + ByConstRef At) const { + assert(VT != nullptr); + return VT->MayPointsTo(PT, Pointer, Obj, At); + } + +protected: + constexpr PointsToIteratorRef() noexcept = default; + +private: + template + static o_t asAbstractObjectThunk(const void *PT, + ByConstRef Pointer) noexcept { + if constexpr (detail::HasAsAbstractObject::value) { + return fromOpaquePtr(PT)->asAbstractObject(Pointer); + } else if constexpr (std::is_convertible_v) { + return Pointer; + } else { + static_assert(detail::HasAsAbstractObject::value); + } + } + + template + static void foreachPointeesOfThunk(const void *PT, ByConstRef Pointer, + ByConstRef At, + llvm::function_ref WithPointee) { + const auto *CPT = fromOpaquePtr(PT); + if constexpr (detail::IsPointsToIterator::value) { + return (void)CPT->foreachPointeesOf(Pointer, At, WithPointee); + } else { + auto PointsToSet = CPT->getPointsToSet(Pointer, At); + // The PointsToSet can be a set or a pointer to a set + auto PointsToSetPtr = getPointerFrom(PointsToSet); + for (auto &&Pointee : *PointsToSetPtr) { + WithPointee(PSR_FWD(Pointee)); + } + } + } + + template + static bool mayPointsToThunk(const void *PT, ByConstRef Pointer, + ByConstRef Obj, ByConstRef At) { + const auto *CPT = fromOpaquePtr(PT); + if constexpr (detail::HasMayPointsTo::value) { + return CPT->mayPointsTo(Pointer, Obj, At); + } else if constexpr (detail::HasGetPointsToSet::value) { + auto PointsToSet = CPT->getPointsToSet(Pointer, At); + // The PointsToSet can be a set or a pointer to a set + auto PointsToSetPtr = getPointerFrom(PointsToSet); + return PointsToSetPtr->count(Obj); + } else { + bool Ret = false; + CPT->foreachPointeesOf([&Ret, Obj](o_t Pointee) { + if (Pointee == Obj) { + Ret = true; + } + }); + return Ret; + } + } + + template + static void destroyThunk(const void *PT) noexcept { + if constexpr (!CanSSO) { + delete fromOpaquePtr(PT); + } + } + + template + static constexpr VTable VTableFor = { + &asAbstractObjectThunk, + &foreachPointeesOfThunk, + &mayPointsToThunk, + &destroyThunk, + }; + +protected: + const void *PT{}; + const VTable *VT{}; +}; + +template +class [[clang::trivial_abi, gsl::Owner]] PointsToIterator + : public PointsToIteratorRef { + using base_t = PointsToIteratorRef; + +public: + using v_t = typename base_t::v_t; + using o_t = typename base_t::o_t; + using n_t = typename base_t::n_t; + + constexpr PointsToIterator() noexcept = default; + constexpr PointsToIterator(std::nullptr_t) noexcept {}; + + PointsToIterator(const PointsToIterator &) = delete; + PointsToIterator &operator=(const PointsToIterator &) = delete; + + PointsToIterator(PointsToIterator &&Other) noexcept + : base_t(std::exchange((base_t &)Other, {})) {} + PointsToIterator &operator=(PointsToIterator &&Other) noexcept { + PointsToIterator(std::move(Other)).swap(*this); + return *this; + } + + constexpr void swap(PointsToIterator &Other) noexcept { + std::swap(this->PT, Other.PT); + std::swap(this->VT, Other.VT); + } + constexpr friend void swap(PointsToIterator &LHS, + PointsToIterator &RHS) noexcept { + LHS.swap(RHS); + } + + template + constexpr PointsToIterator(std::unique_ptr PTA) + : base_t(PTA.get()) { + if constexpr (!base_t::template CanSSO) { + PTA.release(); + } + } + + template >> + constexpr explicit PointsToIterator( + std::in_place_type_t /*unused*/, ArgTys &&...Args) + : PointsToIterator( + std::make_unique(std::forward(Args)...)) {} + + template >> + constexpr explicit PointsToIterator( + std::in_place_type_t /*unused*/, ArgTys &&...Args) + : base_t([&] { + if constexpr (std::is_aggregate_v) { + return ConcretePTA{std::forward(Args)...}; + } else { + return ConcretePTA(std::forward(Args)...); + } + }()) {} + + ~PointsToIterator() noexcept { + if (this->VT != nullptr) { + this->VT->Destroy(this->PT); + this->PT = nullptr; + this->VT = nullptr; + } + } +}; + +} // namespace psr + +#endif // PHASAR_POINTER_POINTSTOITERATOR_H diff --git a/include/phasar/Utils/PointerUtils.h b/include/phasar/Utils/PointerUtils.h index 2023d9d693..88da749b84 100644 --- a/include/phasar/Utils/PointerUtils.h +++ b/include/phasar/Utils/PointerUtils.h @@ -1,6 +1,9 @@ #ifndef PHASAR_UTILS_POINTERUTILS_H #define PHASAR_UTILS_POINTERUTILS_H +#include "phasar/Utils/BoxedPointer.h" +#include "phasar/Utils/MaybeUniquePtr.h" + #include "llvm/ADT/IntrusiveRefCntPtr.h" #include @@ -34,6 +37,22 @@ template constexpr T *getPointerFrom(std::unique_ptr &Ptr) noexcept { return Ptr.get(); } +template +constexpr T *getPointerFrom(std::unique_ptr &&Ptr) noexcept = delete; + +template +constexpr T * +getPointerFrom(const MaybeUniquePtr &Ptr) noexcept { + return Ptr.get(); +} +template +constexpr T *getPointerFrom(MaybeUniquePtr &Ptr) noexcept { + return Ptr.get(); +} +template +constexpr T * +getPointerFrom(MaybeUniquePtr &&Ptr) noexcept = delete; + template constexpr T *getPointerFrom(const std::shared_ptr &Ptr) noexcept { return Ptr.get(); @@ -42,6 +61,9 @@ template constexpr T *getPointerFrom(std::shared_ptr &Ptr) noexcept { return Ptr.get(); } +template +constexpr T *getPointerFrom(std::shared_ptr &&Ptr) noexcept = delete; + template constexpr T *getPointerFrom(const llvm::IntrusiveRefCntPtr &Ptr) noexcept { return Ptr.get(); @@ -50,6 +72,25 @@ template constexpr T *getPointerFrom(llvm::IntrusiveRefCntPtr &Ptr) noexcept { return Ptr.get(); } +template +constexpr T *getPointerFrom(llvm::IntrusiveRefCntPtr &&Ptr) noexcept; + +template +constexpr BoxedPtr getPointerFrom(BoxedPtr Ptr) noexcept { + return Ptr; +} +template +constexpr BoxedConstPtr getPointerFrom(BoxedConstPtr Ptr) noexcept { + return Ptr; +} + +static_assert( + std::is_same_v &>()))>); + +static_assert( + std::is_same_v, + decltype(getPointerFrom(std::declval>()))>); } // namespace psr diff --git a/include/phasar/Utils/TypeErasureUtils.h b/include/phasar/Utils/TypeErasureUtils.h new file mode 100644 index 0000000000..13ad2e27ae --- /dev/null +++ b/include/phasar/Utils/TypeErasureUtils.h @@ -0,0 +1,64 @@ +/****************************************************************************** + * Copyright (c) 2025 Fabian Schíebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel and others + *****************************************************************************/ + +#ifndef PHASAR_POINTER_TYPEERASUREUTILS_H +#define PHASAR_POINTER_TYPEERASUREUTILS_H + +#include + +namespace psr { +struct TypeErasureUtils { + template + static constexpr bool CanSSO = std::is_trivially_copyable_v && + sizeof(ConcreteAA) <= sizeof(void *); + + template + constexpr static ConcreteAA *fromOpaquePtr(void *&EF) noexcept { + if constexpr (CanSSO) { + return static_cast(static_cast(&EF)); + } else { + return static_cast(EF); + } + } + + template + constexpr static const ConcreteAA * + fromOpaquePtr(const void *const &EF) noexcept { + if constexpr (CanSSO) { + return static_cast(static_cast(&EF)); + } else { + return static_cast(EF); + } + } + + template + constexpr void *getOpaquePtr(ConcreteAA &AA) noexcept { + if constexpr (CanSSO) { + void *Ret{}; + ::new (&Ret) ConcreteAA(AA); + return Ret; + } else { + return &AA; + } + } + + template + constexpr const void *getOpaquePtr(const ConcreteAA &AA) noexcept { + if constexpr (CanSSO) { + void *Ret{}; + ::new (&Ret) ConcreteAA(AA); + return Ret; + } else { + return &AA; + } + } +}; +} // namespace psr + +#endif // PHASAR_POINTER_TYPEERASUREUTILS_H diff --git a/lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp b/lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp index 1aa5744403..c3904385f0 100644 --- a/lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp +++ b/lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp @@ -1,7 +1,11 @@ #include "phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h" +#include "phasar/PhasarLLVM/Pointer/LLVMPointsToInfo.h" #include "phasar/Pointer/PointsToInfoBase.h" +#include "llvm/IR/Instruction.h" +#include "llvm/Support/ErrorHandling.h" + #include "DDA/ContextDDA.h" #include "DDA/DDAClient.h" #include "InitSVF.h" @@ -10,6 +14,7 @@ #include "SVF-LLVM/SVFIRBuilder.h" #include "WPA/Andersen.h" +#include #include namespace { @@ -49,6 +54,8 @@ class SVFPointsToSet : public psr::PointsToInfoBase> { SVF::LLVMModuleSet::releaseLLVMModuleSet(); } + [[nodiscard]] constexpr SVF::SVFIR &getPAG() const noexcept { return *PAG; } + private: SVFPointsToSet(SVF::SVFModule *Mod) : IRBuilder(Mod), PAG(IRBuilder.build()) {} @@ -69,10 +76,12 @@ class SVFPointsToSet : public psr::PointsToInfoBase> { } [[nodiscard]] std::optional asPointerOrNullImpl(o_t Obj) const noexcept { - if (const auto *Val = PAG->getObject(Obj)->getValue()) { - auto *ModSet = SVF::LLVMModuleSet::getLLVMModuleSet(); - if (const auto *LLVMVal = ModSet->getLLVMValue(Val)) { - return LLVMVal; + if (const SVF::MemObj *Mem = PAG->getObject(Obj)) { + if (const auto *Val = Mem->getValue()) { + auto *ModSet = SVF::LLVMModuleSet::getLLVMModuleSet(); + if (const auto *LLVMVal = ModSet->getLLVMValue(Val)) { + return LLVMVal; + } } } @@ -151,3 +160,99 @@ auto psr::createSVFDDAPointsToInfo(LLVMProjectIRDB &IRDB) return SVFBasedPointsToInfo(std::in_place_type, psr::initSVFModule(IRDB)); } + +auto psr::createSVFPointsToInfo(LLVMProjectIRDB &IRDB, + SVFPointsToAnalysisType PTATy) + -> SVFBasedPointsToInfo { + switch (PTATy) { + case SVFPointsToAnalysisType::DDA: + return SVFBasedPointsToInfo(std::in_place_type, + psr::initSVFModule(IRDB)); + case SVFPointsToAnalysisType::VFS: + return SVFBasedPointsToInfo(std::in_place_type, + psr::initSVFModule(IRDB)); + } + llvm_unreachable("Should have handled all SVFPointsToAnalysisType variants " + "in the switch above!"); +} + +namespace { + +template struct SVFLLVMPointsToIterator { + using n_t = const llvm::Instruction *; + using v_t = const llvm::Value *; + using o_t = const llvm::Value *; + + SVFLLVMPointsToIterator(SVF::SVFModule *Mod) : PT(Mod) {} + + [[nodiscard]] constexpr o_t asAbstractObject(v_t Pointer) const noexcept { + return Pointer; + } + + [[nodiscard]] SVF::NodeID getNodeId(v_t Pointer) const noexcept { + auto *ModSet = SVF::LLVMModuleSet::getLLVMModuleSet(); + auto *Nod = ModSet->getSVFValue(Pointer); + + return PT.getPAG().getValueNode(Nod); + } + [[nodiscard]] SVF::NodeID getObjNodeId(o_t Obj) const noexcept { + auto *ModSet = SVF::LLVMModuleSet::getLLVMModuleSet(); + auto *Nod = ModSet->getSVFValue(Obj); + + return PT.getPAG().getObjectNode(Nod); + } + + void foreachPointeesOf(o_t Pointer, n_t /*At*/, + llvm::function_ref WithPointee) const { + SVF::PointerAnalysis &PTA = PT.getPTA(); + + auto Nod = getNodeId(Pointer); + + const auto &Pts = PTA.getPts(Nod); + + auto *ModSet = SVF::LLVMModuleSet::getLLVMModuleSet(); + SVF::SVFIR &PAG = PT.getPAG(); + for (auto PointeeNod : Pts) { + const SVF::MemObj *Mem = PAG.getObject(PointeeNod); + if (!Mem) { + continue; + } + + const auto *Val = Mem->getValue(); + if (!Val) { + continue; + } + + if (const auto *LLVMVal = ModSet->getLLVMValue(Val)) { + WithPointee(LLVMVal); + } + } + } + + [[nodiscard]] bool mayPointsTo(o_t Pointer, o_t Obj, n_t /*At*/) const { + SVF::PointerAnalysis &PTA = PT.getPTA(); + auto PointerNod = getNodeId(Pointer); + auto ObjNod = getObjNodeId(Obj); + + const auto &Pts = PTA.getPts(PointerNod); + return Pts.test(ObjNod); + } + + SVFPointsToSetT PT; +}; +} // namespace + +auto psr::createLLVMSVFPointsToIterator(LLVMProjectIRDB &IRDB, + SVFPointsToAnalysisType PTATy) + -> LLVMPointsToIterator { + auto *Mod = psr::initSVFModule(IRDB); + + switch (PTATy) { + case SVFPointsToAnalysisType::DDA: + return {std::make_unique>(Mod)}; + case SVFPointsToAnalysisType::VFS: + return {std::make_unique>(Mod)}; + } + llvm_unreachable("Should have handled all SVFPointsToAnalysisType variants " + "in the switch above!"); +} diff --git a/unittests/PhasarLLVM/Pointer/LLVMAliasSetTest.cpp b/unittests/PhasarLLVM/Pointer/LLVMAliasSetTest.cpp index 22a552af30..d645c310ca 100644 --- a/unittests/PhasarLLVM/Pointer/LLVMAliasSetTest.cpp +++ b/unittests/PhasarLLVM/Pointer/LLVMAliasSetTest.cpp @@ -69,7 +69,7 @@ TEST(LLVMAliasSet, Global_01) { static_assert(std::is_convertible_v); static_assert(std::is_convertible_v); -static_assert(std::is_convertible_v); +static_assert(std::is_convertible_v); static_assert( std::is_convertible_v, LLVMAliasIteratorRef>); diff --git a/unittests/PhasarLLVM/Pointer/SVFAliasSetTest.cpp b/unittests/PhasarLLVM/Pointer/SVFAliasSetTest.cpp index 4482d6071e..9ab3f30e04 100644 --- a/unittests/PhasarLLVM/Pointer/SVFAliasSetTest.cpp +++ b/unittests/PhasarLLVM/Pointer/SVFAliasSetTest.cpp @@ -86,6 +86,27 @@ TEST(SVFAliasSetTest, PointsTo_02) { EXPECT_TRUE(PT.mayPointsTo(V, AllocObj, V->getNextNode())); } +TEST(SVFAliasSetTest, PointsTo_03) { + LLVMProjectIRDB IRDB(unittest::PathToLLTestFiles + + "pointers/basic_01_cpp_dbg.ll"); + + auto PT = createLLVMSVFPointsToIterator(IRDB, SVFPointsToAnalysisType::VFS); + + const auto *V = IRDB.getInstruction(5); + ASSERT_TRUE(V && V->getType()->isPointerTy()); + + const auto *Alloc = IRDB.getInstruction(0); + + auto PSet = PT.asSet(V, V->getNextNode()); + ASSERT_EQ(1, PSet.size()); + EXPECT_EQ(Alloc, *PSet.begin()); + + auto AllocObjs = PT.asSet(Alloc, Alloc->getNextNode()); + ASSERT_EQ(1, AllocObjs.size()); + const auto *AllocObj = *AllocObjs.begin(); + EXPECT_TRUE(PT.mayPointsTo(V, AllocObj, V->getNextNode())); +} + int main(int Argc, char **Argv) { ::testing::InitGoogleTest(&Argc, Argv); return RUN_ALL_TESTS(); From 9bfe0b1e7923180e8b44430027bfe7a220c44ca8 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Thu, 19 Jun 2025 14:16:23 +0200 Subject: [PATCH 06/11] Integrate reachableAllocationSites with PointsToIterator --- include/phasar/Pointer/AliasIterator.h | 55 ++---------- include/phasar/Pointer/PointsToInfo.h | 3 +- include/phasar/Pointer/PointsToIterator.h | 87 +++++++++++++++---- .../DefaultAliasAwareIDEFlowFunctions.cpp | 2 +- lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp | 4 +- .../PhasarLLVM/Pointer/LLVMAliasSetTest.cpp | 8 +- 6 files changed, 85 insertions(+), 74 deletions(-) diff --git a/include/phasar/Pointer/AliasIterator.h b/include/phasar/Pointer/AliasIterator.h index 4398bd78f2..266a353663 100644 --- a/include/phasar/Pointer/AliasIterator.h +++ b/include/phasar/Pointer/AliasIterator.h @@ -30,7 +30,7 @@ struct IsAliasIterator : std::false_type {}; template struct IsAliasIterator< - T, std::void_t().aliasesOf( + T, std::void_t().forallAliasesOf( std::declval(), std::declval(), std::declval>()))>> : std::true_type {}; @@ -75,8 +75,8 @@ class [[gsl::Pointer]] AliasIteratorRef : private TypeErasureUtils { using v_t = V; struct VTable { - void (*AliasesOf)(void *, ByConstRef, ByConstRef, - llvm::function_ref); + void (*ForallAliasesOf)(void *, ByConstRef, ByConstRef, + llvm::function_ref); AliasResult (*Alias)(void *, ByConstRef, ByConstRef, ByConstRef); }; @@ -121,10 +121,10 @@ class [[gsl::Pointer]] AliasIteratorRef : private TypeErasureUtils { /// \param At The instruction, where the alias-query is raised. /// Implementations may ignore this parameter /// \param WithAlias Callback to invoke for each alias of Of - void aliasesOf(ByConstRef Of, ByConstRef At, - llvm::function_ref WithAlias) { + void forallAliasesOf(ByConstRef Of, ByConstRef At, + llvm::function_ref WithAlias) { assert(VT != nullptr); - VT->AliasesOf(AA, Of, At, WithAlias); + VT->ForallAliasesOf(AA, Of, At, WithAlias); } /// \brief Convenience function to aggregate all aliases of Of in a set. @@ -137,7 +137,8 @@ class [[gsl::Pointer]] AliasIteratorRef : private TypeErasureUtils { template > [[nodiscard]] SetT asSet(ByConstRef Of, ByConstRef At) { SetT Set; - aliasesOf(Of, At, [&Set](v_t Alias) { Set.insert(std::move(Alias)); }); + forallAliasesOf(Of, At, + [&Set](v_t Alias) { Set.insert(std::move(Alias)); }); return Set; } @@ -204,46 +205,6 @@ class [[gsl::Pointer]] AliasIteratorRef : private TypeErasureUtils { const VTable *VT{}; }; // namespace psr -template struct ReachableAllocationSitesIterator { - using n_t = typename UnderlyingAA::n_t; - using v_t = typename UnderlyingAA::v_t; - - void aliasesOf(v_t Of, n_t At, llvm::function_ref WithAlias) { - assert(AA != nullptr); - - auto AliasSetPtr = AA->getReachableAllocationSites(Of, true, At); - if (!AliasSetPtr || AliasSetPtr->empty()) { - // The alias-relation should be reflexive - WithAlias(Of); - return; - } - - for (auto &&Alias : *AliasSetPtr) { - WithAlias(PSR_FWD(Alias)); - } - } - - [[nodiscard]] AliasResult alias(v_t Ptr, v_t Alias, n_t At) { - assert(AA != nullptr); - - if (Ptr == Alias) { - return AliasResult::MustAlias; - } - - if (AA->isInReachableAllocationSites(Ptr, Alias, true, At)) { - return AliasResult::MayAlias; - } - - return AliasResult::NoAlias; - } - - UnderlyingAA *AA{}; -}; - -template -ReachableAllocationSitesIterator(UnderlyingAA *) - -> ReachableAllocationSitesIterator; - } // namespace psr #endif // PHASAR_POINTER_ALIASITERATOR_H diff --git a/include/phasar/Pointer/PointsToInfo.h b/include/phasar/Pointer/PointsToInfo.h index 6b0f9610df..fd29a8e96b 100644 --- a/include/phasar/Pointer/PointsToInfo.h +++ b/include/phasar/Pointer/PointsToInfo.h @@ -115,7 +115,7 @@ class PointsToInfoRef WithPointee) { const auto *CPT = static_cast(PT); if constexpr (detail::IsPointsToIterator::value) { - return (void)CPT->foreachPointeesOf(Pointer, At, WithPointee); + return (void)CPT->forallPointeesOf(Pointer, At, WithPointee); } else { auto PointsToSet = CPT->getPointsToSet(Pointer, At); // The PointsToSet can be a set or a pointer to a set @@ -133,7 +133,6 @@ class PointsToInfoRef(PT); }, - }, [](const void *PT, ByConstRef Obj) noexcept { return static_cast(PT)->asPointerOrNull(Obj); diff --git a/include/phasar/Pointer/PointsToIterator.h b/include/phasar/Pointer/PointsToIterator.h index 77cdf99135..93b7ace164 100644 --- a/include/phasar/Pointer/PointsToIterator.h +++ b/include/phasar/Pointer/PointsToIterator.h @@ -10,6 +10,7 @@ #ifndef PHASAR_POINTER_POINTSTOITERATOR_H #define PHASAR_POINTER_POINTSTOITERATOR_H +#include "phasar/Pointer/AliasInfoBase.h" #include "phasar/Utils/ByRef.h" #include "phasar/Utils/Macros.h" #include "phasar/Utils/PointerUtils.h" @@ -28,7 +29,7 @@ struct IsPointsToIterator : std::false_type {}; template struct IsPointsToIterator< - T, std::void_t().foreachPointeesOf( + T, std::void_t().forallPointeesOf( std::declval(), std::declval(), std::declval>()))>> : std::true_type {}; @@ -68,24 +69,38 @@ class [[gsl::Pointer]] PointsToIteratorRef : protected TypeErasureUtils { struct VTable { o_t (*AsAbstractObject)(const void *, ByConstRef) noexcept; - void (*ForeachPointeesOf)(const void *, ByConstRef, ByConstRef, - llvm::function_ref); + void (*ForallPointeesOf)(const void *, ByConstRef, ByConstRef, + llvm::function_ref); bool (*MayPointsTo)(const void *, ByConstRef, ByConstRef, ByConstRef); void (*Destroy)(const void *) noexcept; // Useful for the owning variant }; - template && - std::is_same_v && - std::is_same_v && - std::is_same_v>> + template < + typename ConcretePTA, + std::enable_if_t && + std::is_same_v && + std::is_same_v && + std::is_same_v && + !IsAliasInfo> * = nullptr> constexpr PointsToIteratorRef(const ConcretePTA *PT) noexcept : PT(getOpaquePtr(psr::assertNotNull(PT))), VT(&VTableFor) { static_assert(detail::IsPointsToIterator::value); } + template < + typename ConcretePTA, + std::enable_if_t && + std::is_same_v && + std::is_same_v && + std::is_same_v && + IsAliasInfo> * = nullptr> + constexpr PointsToIteratorRef(const ConcretePTA *PT) noexcept + : PT(&psr::assertNotNull(PT)), + VT(&ReachableAllocSitesVTFor) { + static_assert(detail::IsPointsToIterator::value); + } + template && @@ -115,15 +130,15 @@ class [[gsl::Pointer]] PointsToIteratorRef : protected TypeErasureUtils { return VT->AsAbstractObject(PT, Pointer); } - void foreachPointeesOf(ByConstRef Pointer, ByConstRef At, - llvm::function_ref WithPointee) const { + void forallPointeesOf(ByConstRef Pointer, ByConstRef At, + llvm::function_ref WithPointee) const { assert(VT != nullptr); - VT->ForeachPointeesOf(PT, Pointer, At, WithPointee); + VT->ForallPointeesOf(PT, Pointer, At, WithPointee); } template > [[nodiscard]] SetT asSet(ByConstRef Pointer, ByConstRef At) { SetT Set; - foreachPointeesOf(Pointer, At, [&Set](o_t Obj) { Set.insert(Obj); }); + forallPointeesOf(Pointer, At, [&Set](o_t Obj) { Set.insert(Obj); }); return Set; } @@ -150,12 +165,12 @@ class [[gsl::Pointer]] PointsToIteratorRef : protected TypeErasureUtils { } template - static void foreachPointeesOfThunk(const void *PT, ByConstRef Pointer, - ByConstRef At, - llvm::function_ref WithPointee) { + static void forallPointeesOfThunk(const void *PT, ByConstRef Pointer, + ByConstRef At, + llvm::function_ref WithPointee) { const auto *CPT = fromOpaquePtr(PT); if constexpr (detail::IsPointsToIterator::value) { - return (void)CPT->foreachPointeesOf(Pointer, At, WithPointee); + return (void)CPT->forallPointeesOf(Pointer, At, WithPointee); } else { auto PointsToSet = CPT->getPointsToSet(Pointer, At); // The PointsToSet can be a set or a pointer to a set @@ -166,6 +181,19 @@ class [[gsl::Pointer]] PointsToIteratorRef : protected TypeErasureUtils { } } + template + static void + forallReachableAllocationSitesThunk(const void *AS, ByConstRef Pointer, + ByConstRef At, + llvm::function_ref WithPointee) { + auto AliasSetPtr = + ((ConcretePTA *)AS)->getReachableAllocationSites(Pointer, true, At); + + for (auto &&Alias : *AliasSetPtr) { + WithPointee(PSR_FWD(Alias)); + } + } + template static bool mayPointsToThunk(const void *PT, ByConstRef Pointer, ByConstRef Obj, ByConstRef At) { @@ -179,7 +207,7 @@ class [[gsl::Pointer]] PointsToIteratorRef : protected TypeErasureUtils { return PointsToSetPtr->count(Obj); } else { bool Ret = false; - CPT->foreachPointeesOf([&Ret, Obj](o_t Pointee) { + CPT->forallPointeesOf([&Ret, Obj](o_t Pointee) { if (Pointee == Obj) { Ret = true; } @@ -188,6 +216,19 @@ class [[gsl::Pointer]] PointsToIteratorRef : protected TypeErasureUtils { } } + template + static bool maybeInReachableALlocationSitesThunk(const void *AS, + ByConstRef Pointer, + ByConstRef Obj, + ByConstRef At) { + if (Pointer == Obj) { + return true; + } + + return ((ConcretePTA *)AS) + ->isInReachableAllocationSites(Pointer, Obj, true, At); + } + template static void destroyThunk(const void *PT) noexcept { if constexpr (!CanSSO) { @@ -198,11 +239,19 @@ class [[gsl::Pointer]] PointsToIteratorRef : protected TypeErasureUtils { template static constexpr VTable VTableFor = { &asAbstractObjectThunk, - &foreachPointeesOfThunk, + &forallPointeesOfThunk, &mayPointsToThunk, &destroyThunk, }; + template + static constexpr VTable ReachableAllocSitesVTFor = { + &asAbstractObjectThunk, + &forallReachableAllocationSitesThunk, + &maybeInReachableALlocationSitesThunk, + &destroyThunk, + }; + protected: const void *PT{}; const VTable *VT{}; diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEFlowFunctions.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEFlowFunctions.cpp index f3d0cb4d20..80c2fd95fa 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEFlowFunctions.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEFlowFunctions.cpp @@ -47,7 +47,7 @@ static void populateWithMayAliases(LLVMAliasIteratorRef AS, const llvm::Instruction *Context) { container_type Tmp = Facts; for (const auto *Fact : Tmp) { - AS.aliasesOf(Fact, Context, [&Facts, Context](const auto *Alias) { + AS.forallAliasesOf(Fact, Context, [&Facts, Context](const auto *Alias) { if (const auto *Inst = llvm::dyn_cast(Alias)) { if (Inst->getParent() == Context->getParent() && Context->comesBefore(Inst)) { diff --git a/lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp b/lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp index c3904385f0..75a59a209f 100644 --- a/lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp +++ b/lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp @@ -202,8 +202,8 @@ template struct SVFLLVMPointsToIterator { return PT.getPAG().getObjectNode(Nod); } - void foreachPointeesOf(o_t Pointer, n_t /*At*/, - llvm::function_ref WithPointee) const { + void forallPointeesOf(o_t Pointer, n_t /*At*/, + llvm::function_ref WithPointee) const { SVF::PointerAnalysis &PTA = PT.getPTA(); auto Nod = getNodeId(Pointer); diff --git a/unittests/PhasarLLVM/Pointer/LLVMAliasSetTest.cpp b/unittests/PhasarLLVM/Pointer/LLVMAliasSetTest.cpp index d645c310ca..15208cccc5 100644 --- a/unittests/PhasarLLVM/Pointer/LLVMAliasSetTest.cpp +++ b/unittests/PhasarLLVM/Pointer/LLVMAliasSetTest.cpp @@ -5,6 +5,7 @@ #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" #include "phasar/PhasarLLVM/Passes/ValueAnnotationPass.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" +#include "phasar/PhasarLLVM/Pointer/LLVMPointsToInfo.h" #include "phasar/PhasarLLVM/Pointer/LLVMPointsToUtils.h" #include "phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h" @@ -65,14 +66,15 @@ TEST(LLVMAliasSet, Global_01) { } PTS.print(llvm::outs()); llvm::outs() << '\n'; + + [[maybe_unused]] LLVMPointsToIteratorRef PTIt = &PTS; + [[maybe_unused]] LLVMAliasIteratorRef ASIt = &PTS; } static_assert(std::is_convertible_v); static_assert(std::is_convertible_v); static_assert(std::is_convertible_v); -static_assert( - std::is_convertible_v, - LLVMAliasIteratorRef>); +static_assert(std::is_convertible_v); int main(int Argc, char **Argv) { ::testing::InitGoogleTest(&Argc, Argv); From 5a59e29b7942b799005d6cbb2699a48752901905 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Wed, 25 Jun 2025 18:53:45 +0200 Subject: [PATCH 07/11] Add boilerplate + some comments --- include/phasar/PhasarLLVM/Pointer.h | 5 +++++ .../PhasarLLVM/Pointer/SVF/SVFPointsToSet.h | 2 +- include/phasar/Pointer.h | 2 ++ include/phasar/Pointer/AliasAnalysisType.def | 3 +++ include/phasar/Pointer/AliasAnalysisType.h | 3 +++ include/phasar/Pointer/AliasIterator.h | 4 ++-- include/phasar/Pointer/PointsToIterator.h | 21 +++++++++++++++---- lib/PhasarLLVM/Pointer/AliasAnalysisView.cpp | 13 +++--------- lib/PhasarLLVM/Pointer/Pointer.cppm | 5 +++++ lib/Pointer/PhasarPointer.cppm | 7 +++++++ 10 files changed, 48 insertions(+), 17 deletions(-) diff --git a/include/phasar/PhasarLLVM/Pointer.h b/include/phasar/PhasarLLVM/Pointer.h index 8025df0f4d..be473838bc 100644 --- a/include/phasar/PhasarLLVM/Pointer.h +++ b/include/phasar/PhasarLLVM/Pointer.h @@ -10,10 +10,15 @@ #ifndef PHASAR_PHASARLLVM_POINTER_H #define PHASAR_PHASARLLVM_POINTER_H +#include "phasar/Config/phasar-config.h" // for PHASAR_USE_SVF #include "phasar/PhasarLLVM/Pointer/AliasAnalysisView.h" #include "phasar/PhasarLLVM/Pointer/FilteredLLVMAliasSet.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" #include "phasar/PhasarLLVM/Pointer/LLVMPointsToUtils.h" +#ifdef PHASAR_USE_SVF +#include "phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h" +#endif + #endif // PHASAR_PHASARLLVM_POINTER_H diff --git a/include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h b/include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h index 6c037835b4..85df7010be 100644 --- a/include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h +++ b/include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h @@ -71,7 +71,7 @@ createSVFDDAPointsToInfo(LLVMProjectIRDB &IRDB); createSVFPointsToInfo(LLVMProjectIRDB &IRDB, SVFPointsToAnalysisType PTATy); /// Use SVF to perform the specified pointer analysis and return the results -/// compatoble to psr::LLVMPointsToIterator, converting the points-to sets to +/// compatible to psr::LLVMPointsToIterator, converting the points-to sets to /// LLVM allocation-sites [[nodiscard]] LLVMPointsToIterator createLLVMSVFPointsToIterator(LLVMProjectIRDB &IRDB, diff --git a/include/phasar/Pointer.h b/include/phasar/Pointer.h index def25c239d..fcf9f7555a 100644 --- a/include/phasar/Pointer.h +++ b/include/phasar/Pointer.h @@ -14,9 +14,11 @@ #include "phasar/Pointer/AliasInfo.h" #include "phasar/Pointer/AliasInfoBase.h" #include "phasar/Pointer/AliasInfoTraits.h" +#include "phasar/Pointer/AliasIterator.h" #include "phasar/Pointer/AliasResult.h" #include "phasar/Pointer/AliasSetOwner.h" #include "phasar/Pointer/PointsToInfo.h" #include "phasar/Pointer/PointsToInfoBase.h" +#include "phasar/Pointer/PointsToIterator.h" #endif // PHASAR_POINTER_H diff --git a/include/phasar/Pointer/AliasAnalysisType.def b/include/phasar/Pointer/AliasAnalysisType.def index dda81808ca..c65c28b105 100644 --- a/include/phasar/Pointer/AliasAnalysisType.def +++ b/include/phasar/Pointer/AliasAnalysisType.def @@ -15,7 +15,10 @@ ALIAS_ANALYSIS_TYPE(Basic, "basic", "Basic LLVM alias resolving based on simple, ALIAS_ANALYSIS_TYPE(CFLSteens, "cflsteens", "Steensgaard-style alias analysis (equality-based)") ALIAS_ANALYSIS_TYPE(CFLAnders, "cflanders", "Andersen-style alias analysis (subset-based) (default)") ALIAS_ANALYSIS_TYPE(PointsTo, "points-to", "Alias-information based on (external) points-to information") + +#ifdef PHASAR_USE_SVF ALIAS_ANALYSIS_TYPE(SVFDDA, "svf-dda", "Alias-information based on SVF's ContextDDA analysis. Requires SVF.") ALIAS_ANALYSIS_TYPE(SVFVFS, "svf-vfs", "Alias-information based on SVF's VersionedFlowSensitive analysis. Requires SVF.") +#endif #undef ALIAS_ANALYSIS_TYPE diff --git a/include/phasar/Pointer/AliasAnalysisType.h b/include/phasar/Pointer/AliasAnalysisType.h index 7e8fb61b17..8a9fb35b15 100644 --- a/include/phasar/Pointer/AliasAnalysisType.h +++ b/include/phasar/Pointer/AliasAnalysisType.h @@ -1,12 +1,15 @@ #ifndef PHASAR_POINTER_ALIASANALYSISTYPE_H #define PHASAR_POINTER_ALIASANALYSISTYPE_H +#include "phasar/Config/phasar-config.h" // For PHASAR_USE_SVF in AliasAnalysisType.def + #include "llvm/ADT/StringRef.h" #include "llvm/Support/raw_ostream.h" #include namespace psr { + enum class AliasAnalysisType { #define ALIAS_ANALYSIS_TYPE(NAME, CMDFLAG, TYPE) NAME, #include "phasar/Pointer/AliasAnalysisType.def" diff --git a/include/phasar/Pointer/AliasIterator.h b/include/phasar/Pointer/AliasIterator.h index 266a353663..274d2eadba 100644 --- a/include/phasar/Pointer/AliasIterator.h +++ b/include/phasar/Pointer/AliasIterator.h @@ -102,7 +102,6 @@ class [[gsl::Pointer]] AliasIteratorRef : private TypeErasureUtils { constexpr explicit AliasIteratorRef(void *AA, const VTable *VT) noexcept : AA(AA), VT(VT) { - assert(AA != nullptr); assert(VT != nullptr); } @@ -181,7 +180,8 @@ class [[gsl::Pointer]] AliasIteratorRef : private TypeErasureUtils { return CAA->alias(Ptr, Alias, At); } else if constexpr (detail::HasGetAliasSet::value) { auto AliasSetPtr = CAA->getAliasSet(Ptr, At); - return AliasSetPtr->count(Alias); + return AliasSetPtr->count(Alias) ? AliasResult::MayAlias + : AliasResult::NoAlias; } else { AliasResult Ret = AliasResult::NoAlias; diff --git a/include/phasar/Pointer/PointsToIterator.h b/include/phasar/Pointer/PointsToIterator.h index 93b7ace164..36008834bb 100644 --- a/include/phasar/Pointer/PointsToIterator.h +++ b/include/phasar/Pointer/PointsToIterator.h @@ -60,6 +60,19 @@ struct HasAsAbstractObject +PSR_CONCEPT IsPointsToIterator = detail::IsPointsToIterator::value; + +/// A type-erased reference to any object implementing the IsPointsToIterator +/// interface. Use this, if your alias-aware analysis just needs +/// a minimal interface to work with points-to relations and does not require +/// the versatility of PointsToInfoRef. +/// +/// This is a *non-owning* reference similar to std::string_view and +/// llvm::ArrayRef. Pass values of this type by value. +/// +/// \note You can also convert an AliasInfoRef to PointsToIteratorRef. In this +/// case, it will use the getReachableAllocationSites() API. template class [[gsl::Pointer]] PointsToIteratorRef : protected TypeErasureUtils { public: @@ -85,7 +98,7 @@ class [[gsl::Pointer]] PointsToIteratorRef : protected TypeErasureUtils { !IsAliasInfo> * = nullptr> constexpr PointsToIteratorRef(const ConcretePTA *PT) noexcept : PT(getOpaquePtr(psr::assertNotNull(PT))), VT(&VTableFor) { - static_assert(detail::IsPointsToIterator::value); + static_assert(IsPointsToIterator); } template < @@ -98,7 +111,7 @@ class [[gsl::Pointer]] PointsToIteratorRef : protected TypeErasureUtils { constexpr PointsToIteratorRef(const ConcretePTA *PT) noexcept : PT(&psr::assertNotNull(PT)), VT(&ReachableAllocSitesVTFor) { - static_assert(detail::IsPointsToIterator::value); + static_assert(IsPointsToIterator); } template At, llvm::function_ref WithPointee) { const auto *CPT = fromOpaquePtr(PT); - if constexpr (detail::IsPointsToIterator::value) { + if constexpr (IsPointsToIterator) { return (void)CPT->forallPointeesOf(Pointer, At, WithPointee); } else { auto PointsToSet = CPT->getPointsToSet(Pointer, At); @@ -257,6 +269,7 @@ class [[gsl::Pointer]] PointsToIteratorRef : protected TypeErasureUtils { const VTable *VT{}; }; +/// Owning version of PointsToIteratorRef template class [[clang::trivial_abi, gsl::Owner]] PointsToIterator : public PointsToIteratorRef { diff --git a/lib/PhasarLLVM/Pointer/AliasAnalysisView.cpp b/lib/PhasarLLVM/Pointer/AliasAnalysisView.cpp index d4833b8c18..0ecf705e40 100644 --- a/lib/PhasarLLVM/Pointer/AliasAnalysisView.cpp +++ b/lib/PhasarLLVM/Pointer/AliasAnalysisView.cpp @@ -1,6 +1,6 @@ #include "phasar/PhasarLLVM/Pointer/AliasAnalysisView.h" -#include "phasar/Config/phasar-config.h" +#include "phasar/Config/phasar-config.h" // for PHASAR_USE_SVF #ifdef PHASAR_USE_SVF #include "SVF/SVFBasedAliasAnalysis.h" @@ -14,18 +14,11 @@ std::unique_ptr AliasAnalysisView::create(LLVMProjectIRDB &IRDB, bool UseLazyEvaluation, AliasAnalysisType PATy) { switch (PATy) { +#ifdef PHASAR_USE_SVF case AliasAnalysisType::SVFDDA: -#ifndef PHASAR_USE_SVF - throw std::runtime_error("AliasAnalysisType::SVFVFS requires SVF, which is " - "not included in your PhASAR build!"); -#else return createSVFDDAAnalysis(IRDB); -#endif + case AliasAnalysisType::SVFVFS: -#ifndef PHASAR_USE_SVF - throw std::runtime_error("AliasAnalysisType::SVFDDA requires SVF, which is " - "not included in your PhASAR build!"); -#else return createSVFVFSAnalysis(IRDB); #endif default: diff --git a/lib/PhasarLLVM/Pointer/Pointer.cppm b/lib/PhasarLLVM/Pointer/Pointer.cppm index d7c812df74..aec8789139 100644 --- a/lib/PhasarLLVM/Pointer/Pointer.cppm +++ b/lib/PhasarLLVM/Pointer/Pointer.cppm @@ -1,3 +1,4 @@ +#include "phasar/PhasarLLVM/Pointer/LLVMPointsToInfo.h" module; #include "phasar/Config/phasar-config.h" @@ -22,10 +23,14 @@ using psr::FunctionAliasView; using psr::isInterestingPointer; using psr::LLVMAliasInfo; using psr::LLVMAliasInfoRef; +using psr::LLVMAliasIteratorRef; using psr::LLVMAliasSet; using psr::LLVMAliasSetData; +using psr::LLVMPointsToIterator; +using psr::LLVMPointsToIteratorRef; #ifdef PHASAR_USE_SVF +using psr::createLLVMSVFPointsToIterator; using psr::createSVFDDAPointsToInfo; using psr::createSVFVFSPointsToInfo; using psr::SVFBasedPointsToInfo; diff --git a/lib/Pointer/PhasarPointer.cppm b/lib/Pointer/PhasarPointer.cppm index 21e84a075a..bdea5ab2e1 100644 --- a/lib/Pointer/PhasarPointer.cppm +++ b/lib/Pointer/PhasarPointer.cppm @@ -4,9 +4,11 @@ module; #include "phasar/Pointer/AliasInfo.h" #include "phasar/Pointer/AliasInfoBase.h" #include "phasar/Pointer/AliasInfoTraits.h" +#include "phasar/Pointer/AliasIterator.h" #include "phasar/Pointer/AliasResult.h" #include "phasar/Pointer/AliasSetOwner.h" #include "phasar/Pointer/PointsToInfo.h" +#include "phasar/Pointer/PointsToIterator.h" export module phasar.pointer; @@ -20,10 +22,13 @@ using psr::AliasInfo; using psr::AliasInfoBaseUtils; using psr::AliasInfoRef; using psr::AliasInfoTraits; +using psr::AliasIteratorRef; using psr::AliasResult; using psr::AnalysisProperties; using psr::DefaultAATraits; using psr::IsAliasInfo; +using psr::IsAliasIterator; +using psr::IsPointsToIterator; using psr::toAliasResult; using psr::toString; using psr::operator<<; @@ -34,5 +39,7 @@ using psr::is_PointsToTraits_v; using psr::PointsToInfo; using psr::PointsToInfoBase; using psr::PointsToInfoRef; +using psr::PointsToIterator; +using psr::PointsToIteratorRef; using psr::PointsToTraits; } // namespace psr From e2e8ab2760020370540ed46e5984d95fd18686ce Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Wed, 25 Jun 2025 20:13:24 +0200 Subject: [PATCH 08/11] Start adding SVFAliasInfo for SVF-DDA(WIP) --- ...efaultReachableAllocationSitesIDEProblem.h | 16 +- .../PhasarLLVM/Pointer/SVF/SVFPointsToSet.h | 7 + include/phasar/Pointer/PointsToIterator.h | 22 ++- ...aultReachableAllocationSitesIDEProblem.cpp | 46 +++-- .../Pointer/SVF/SVFBasedAliasAnalysis.cpp | 159 +++++++++++++++++- .../IfdsIde/DefaultFlowFunctionTest.cpp | 1 + .../PhasarLLVM/Pointer/SVFAliasSetTest.cpp | 17 ++ 7 files changed, 226 insertions(+), 42 deletions(-) diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.h index eac661ccd4..0dfff7b677 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.h @@ -12,6 +12,7 @@ #include "phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" +#include "phasar/PhasarLLVM/Pointer/LLVMPointsToInfo.h" // Forward declaration of types for which we only use its pointer or ref type namespace llvm { @@ -34,15 +35,14 @@ class IDEReachableAllocationSitesDefaultFlowFunctionsImpl using IDENoAliasDefaultFlowFunctionsImpl::isFunctionModeled; - [[nodiscard]] constexpr LLVMAliasInfoRef getAliasInfo() const noexcept { + [[nodiscard]] constexpr LLVMPointsToIteratorRef + getPointsToInfo() const noexcept { return AS; } constexpr IDEReachableAllocationSitesDefaultFlowFunctionsImpl( - LLVMAliasInfoRef AS) noexcept - : AS(AS) { - assert(AS && "You must provide an alias information handle!"); - } + LLVMPointsToIteratorRef AS) noexcept + : AS(AS) {} [[nodiscard]] FlowFunctionPtrType getNormalFlowFunctionImpl(n_t Curr, n_t /*Succ*/); @@ -56,7 +56,7 @@ class IDEReachableAllocationSitesDefaultFlowFunctionsImpl using IDENoAliasDefaultFlowFunctionsImpl::getCallToRetFlowFunctionImpl; protected: - LLVMAliasInfoRef AS; + LLVMPointsToIteratorRef AS; }; } // namespace detail @@ -73,7 +73,7 @@ class DefaultReachableAllocationSitesIDEProblem /// \note It is useful to use an instance of FilteredAliasSet for the alias /// information to lower suprious aliases explicit DefaultReachableAllocationSitesIDEProblem( - const ProjectIRDBBase *IRDB, LLVMAliasInfoRef AS, + const ProjectIRDBBase *IRDB, LLVMPointsToIteratorRef AS, std::vector EntryPoints, std::optional ZeroValue) noexcept(std::is_nothrow_move_constructible_v) @@ -115,7 +115,7 @@ class DefaultReachableAllocationSitesIFDSProblem /// \note It is useful to use an instance of FilteredAliasSet for the alias /// information to lower suprious aliases explicit DefaultReachableAllocationSitesIFDSProblem( - const ProjectIRDBBase *IRDB, LLVMAliasInfoRef AS, + const ProjectIRDBBase *IRDB, LLVMPointsToIteratorRef AS, std::vector EntryPoints, d_t ZeroValue) noexcept(std::is_nothrow_move_constructible_v) : IFDSTabulationProblem(IRDB, std::move(EntryPoints), ZeroValue), diff --git a/include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h b/include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h index 85df7010be..e97b22f2a3 100644 --- a/include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h +++ b/include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h @@ -10,6 +10,7 @@ #define PHASAR_PHASARLLVM_POINTER_SVF_SVFPOINTSTOSET_H #include "phasar/Config/phasar-config.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include "phasar/PhasarLLVM/Pointer/LLVMPointsToInfo.h" #include "phasar/Pointer/AliasAnalysisType.h" #include "phasar/Pointer/PointsToInfo.h" @@ -77,6 +78,12 @@ createSVFPointsToInfo(LLVMProjectIRDB &IRDB, SVFPointsToAnalysisType PTATy); createLLVMSVFPointsToIterator(LLVMProjectIRDB &IRDB, SVFPointsToAnalysisType PTATy); +/// Use SVF to perform the specified pointer analysis and return the results +/// compatible to psr::LLVMAliasInfo and psr::LLVMAliasInfoRef +/// +/// \note Only support DDA for now, as VFS seems to not support getRevPts(). +[[nodiscard]] LLVMAliasInfo createLLVMSVFDDAAliasInfo(LLVMProjectIRDB &IRDB); + } // namespace psr #endif // PHASAR_PHASARLLVM_POINTER_SVF_SVFPOINTSTOSET_H diff --git a/include/phasar/Pointer/PointsToIterator.h b/include/phasar/Pointer/PointsToIterator.h index 36008834bb..b951cdf780 100644 --- a/include/phasar/Pointer/PointsToIterator.h +++ b/include/phasar/Pointer/PointsToIterator.h @@ -58,6 +58,18 @@ struct HasAsAbstractObject().asAbstractObject( std::declval()))> : std::true_type {}; + +template +struct HasReachableAllocationSites : std::false_type {}; +template +struct HasReachableAllocationSites< + T, std::void_t().getReachableAllocationSites( + std::declval(), true, + std::declval())), + decltype(std::declval().isInReachableAllocationSites( + std::declval(), + std::declval(), true, + std::declval()))>> : std::true_type {}; } // namespace detail template @@ -95,7 +107,8 @@ class [[gsl::Pointer]] PointsToIteratorRef : protected TypeErasureUtils { std::is_same_v && std::is_same_v && std::is_same_v && - !IsAliasInfo> * = nullptr> + !detail::HasReachableAllocationSites::value> + * = nullptr> constexpr PointsToIteratorRef(const ConcretePTA *PT) noexcept : PT(getOpaquePtr(psr::assertNotNull(PT))), VT(&VTableFor) { static_assert(IsPointsToIterator); @@ -107,7 +120,8 @@ class [[gsl::Pointer]] PointsToIteratorRef : protected TypeErasureUtils { std::is_same_v && std::is_same_v && std::is_same_v && - IsAliasInfo> * = nullptr> + detail::HasReachableAllocationSites::value> + * = nullptr> constexpr PointsToIteratorRef(const ConcretePTA *PT) noexcept : PT(&psr::assertNotNull(PT)), VT(&ReachableAllocSitesVTFor) { @@ -229,7 +243,7 @@ class [[gsl::Pointer]] PointsToIteratorRef : protected TypeErasureUtils { } template - static bool maybeInReachableALlocationSitesThunk(const void *AS, + static bool maybeInReachableAllocationSitesThunk(const void *AS, ByConstRef Pointer, ByConstRef Obj, ByConstRef At) { @@ -260,7 +274,7 @@ class [[gsl::Pointer]] PointsToIteratorRef : protected TypeErasureUtils { static constexpr VTable ReachableAllocSitesVTFor = { &asAbstractObjectThunk, &forallReachableAllocationSitesThunk, - &maybeInReachableALlocationSitesThunk, + &maybeInReachableAllocationSitesThunk, &destroyThunk, }; diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.cpp index f76f660ca8..522c7a53bc 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.cpp @@ -2,6 +2,7 @@ #include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" +#include "phasar/PhasarLLVM/Pointer/LLVMPointsToInfo.h" #include "llvm/IR/Instructions.h" #include "llvm/Support/Casting.h" @@ -21,10 +22,7 @@ auto detail::IDEReachableAllocationSitesDefaultFlowFunctionsImpl:: if (const auto *Store = llvm::dyn_cast(Curr)) { - auto AliasSet = - AS.getReachableAllocationSites(Store->getPointerOperand(), true, Store); - - container_type Gen(AliasSet->begin(), AliasSet->end()); + auto Gen = AS.asSet(Store->getPointerOperand(), Store); return FFTemplates::lambdaFlow( [Store, Gen{std::move(Gen)}, AS = AS](d_t Source) -> container_type { @@ -32,8 +30,7 @@ auto detail::IDEReachableAllocationSitesDefaultFlowFunctionsImpl:: return {}; } if (Store->getValueOperand() == Source || - AS.isInReachableAllocationSites(Store->getValueOperand(), Source, - true, Store)) { + AS.mayPointsTo(Store->getValueOperand(), Source, Store)) { auto Ret = Gen; Ret.insert(Source); return Ret; @@ -47,8 +44,7 @@ auto detail::IDEReachableAllocationSitesDefaultFlowFunctionsImpl:: return FFTemplates::lambdaFlow( [Load, AS = AS](d_t Source) -> container_type { if (Load->getPointerOperand() == Source || - AS.isInReachableAllocationSites(Load->getPointerOperand(), Source, - true, Load)) { + AS.mayPointsTo(Load->getPointerOperand(), Source, Load)) { return {Source, Load}; } @@ -65,24 +61,26 @@ auto detail::IDEReachableAllocationSitesDefaultFlowFunctionsImpl:: -> FlowFunctionPtrType { const auto *Call = llvm::cast(CallInst); - return mapFactsToCallee( - Call, CalleeFun, [Call, AS = AS](d_t Arg, d_t Source) -> bool { - if (Arg == Source) { - return true; - } + return mapFactsToCallee(Call, CalleeFun, + [Call, AS = AS](d_t Arg, d_t Source) -> bool { + if (Arg == Source) { + return true; + } - return Arg->getType()->isPointerTy() && - Source->getType()->isPointerTy() && - AS.isInReachableAllocationSites(Arg, Source, true, Call); - }); + return Arg->getType()->isPointerTy() && + Source->getType()->isPointerTy() && + AS.mayPointsTo(Arg, Source, Call); + }); } -static void populateWithMayAliases(LLVMAliasInfoRef AS, container_type &Facts, - const llvm::Instruction *Context) { +static void populateWithMayPointees(LLVMPointsToIteratorRef AS, + container_type &Facts, + const llvm::Instruction *Context) { container_type Tmp = Facts; for (const auto *Fact : Tmp) { - auto Aliases = AS.getReachableAllocationSites(Fact, true, Context); - Facts.insert(Aliases->begin(), Aliases->end()); + AS.forallPointeesOf(Fact, Context, [&Facts](const auto *Pointee) { + Facts.insert(Pointee); + }); } } @@ -91,7 +89,7 @@ auto detail::IDEReachableAllocationSitesDefaultFlowFunctionsImpl:: n_t /*RetSite*/) -> FlowFunctionPtrType { const auto *Call = llvm::cast(CallSite); const auto PostProcessFacts = [AS = AS, Call](container_type &Facts) { - populateWithMayAliases(AS, Facts, Call); + populateWithMayPointees(AS, Facts, Call); }; return mapFactsToCaller( @@ -108,7 +106,7 @@ auto detail::IDEReachableAllocationSitesDefaultFlowFunctionsImpl:: // Arguments are counted as allocation-sites, so we have generated them // as aliases return !llvm::isa(Source) && - AS.isInReachableAllocationSites(Param, Source, true, ExitInst); + AS.mayPointsTo(Param, Source, ExitInst); }, [AS = AS, ExitInst](d_t Ret, d_t Source) { if (Ret == Source) { @@ -117,7 +115,7 @@ auto detail::IDEReachableAllocationSitesDefaultFlowFunctionsImpl:: return Ret->getType()->isPointerTy() && Source->getType()->isPointerTy() && - AS.isInReachableAllocationSites(Ret, Source, true, ExitInst); + AS.mayPointsTo(Ret, Source, ExitInst); }, {}, true, true, PostProcessFacts); } diff --git a/lib/PhasarLLVM/Pointer/SVF/SVFBasedAliasAnalysis.cpp b/lib/PhasarLLVM/Pointer/SVF/SVFBasedAliasAnalysis.cpp index 0416a8415b..4e28217bbe 100644 --- a/lib/PhasarLLVM/Pointer/SVF/SVFBasedAliasAnalysis.cpp +++ b/lib/PhasarLLVM/Pointer/SVF/SVFBasedAliasAnalysis.cpp @@ -2,10 +2,18 @@ #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" #include "phasar/PhasarLLVM/Pointer/AliasAnalysisView.h" +#include "phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h" #include "phasar/Pointer/AliasAnalysisType.h" +#include "phasar/Pointer/AliasInfoTraits.h" #include "phasar/Pointer/AliasResult.h" +#include "phasar/Pointer/AliasSetOwner.h" +#include "phasar/Utils/AnalysisProperties.h" #include "phasar/Utils/Fn.h" +#include "llvm/IR/Value.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" + #include "DDA/ContextDDA.h" #include "DDA/DDAClient.h" #include "InitSVF.h" @@ -19,6 +27,8 @@ #include #include +#include + namespace psr { static constexpr psr::AliasResult translateSVFAliasResult(SVF::AliasResult AR) noexcept { @@ -34,9 +44,9 @@ translateSVFAliasResult(SVF::AliasResult AR) noexcept { } } -static psr::AliasResult aliasImpl(SVF::PointerAnalysis *AA, - const llvm::Value *V, const llvm::Value *Rep, - const llvm::DataLayout & /*DL*/) { +static psr::AliasResult doAliasImpl(SVF::PointerAnalysis *AA, + const llvm::Value *V, + const llvm::Value *Rep) { auto *ModSet = SVF::LLVMModuleSet::getLLVMModuleSet(); auto *Nod1 = ModSet->getSVFValue(V); auto *Nod2 = ModSet->getSVFValue(Rep); @@ -48,6 +58,12 @@ static psr::AliasResult aliasImpl(SVF::PointerAnalysis *AA, return translateSVFAliasResult(AA->alias(Nod1, Nod2)); } +static psr::AliasResult aliasImpl(SVF::PointerAnalysis *AA, + const llvm::Value *V, const llvm::Value *Rep, + const llvm::DataLayout & /*DL*/) { + return doAliasImpl(AA, V, Rep); +} + // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions) class SVFAliasAnalysisBase : public AliasAnalysisView { public: @@ -80,6 +96,14 @@ class SVFVFSAnalysis : public SVFAliasAnalysisBase { ~SVFVFSAnalysis() override { SVF::VersionedFlowSensitive::releaseVFSWPA(); } + [[nodiscard]] SVF::PointerAnalysis &getPTA() const { return *VFS; } + [[nodiscard]] AliasAnalysisType getAliasAnalysisType() const noexcept { + return AliasAnalysisType::SVFVFS; + } + [[nodiscard]] AnalysisProperties getAnalysisProperties() const noexcept { + return AnalysisProperties::FlowSensitive; + } + private: FunctionAliasView doGetAAResults(const llvm::Function * /*F*/) override { return {VFS, fn}; @@ -99,25 +123,148 @@ class SVFDDAAnalysis : public SVFAliasAnalysisBase { DDA->finalize(); } + [[nodiscard]] SVF::PointerAnalysis &getPTA() const { return *DDA; } + [[nodiscard]] AliasAnalysisType getAliasAnalysisType() const noexcept { + return AliasAnalysisType::SVFDDA; + } + [[nodiscard]] AnalysisProperties getAnalysisProperties() const noexcept { + return AnalysisProperties::ContextSensitive; + } + private: FunctionAliasView doGetAAResults(const llvm::Function * /*F*/) override { return {&*DDA, fn}; } SVF::DDAClient Client; - std::optional DDA; + // Note: SVF is not thread-safe anyway, so this 'mutable' should not be a + // problem + mutable std::optional DDA; }; } // namespace psr -[[nodiscard]] auto psr::createSVFVFSAnalysis(LLVMProjectIRDB &IRDB) +auto psr::createSVFVFSAnalysis(LLVMProjectIRDB &IRDB) -> std::unique_ptr { return std::make_unique(psr::initSVFModule(IRDB)); } -[[nodiscard]] auto psr::createSVFDDAAnalysis(LLVMProjectIRDB &IRDB) +auto psr::createSVFDDAAnalysis(LLVMProjectIRDB &IRDB) -> std::unique_ptr { return std::make_unique(psr::initSVFModule(IRDB)); } + +namespace psr { + +class SVFAliasInfoImpl; + +template <> +struct AliasInfoTraits + : DefaultAATraits {}; + +class SVFAliasInfoImpl + : public SVFDDAAnalysis, + public AnalysisPropertiesMixin, + public DefaultAATraits { +public: + using SVFDDAAnalysis::SVFDDAAnalysis; + + [[nodiscard]] bool isInterProcedural() const noexcept { return true; } + + [[nodiscard]] psr::AliasResult alias(const llvm::Value *V, + const llvm::Value *Rep, + const llvm::Instruction * /*At*/) const { + return doAliasImpl(&getPTA(), V, Rep); + } + + [[nodiscard]] AliasSetPtrTy getAliasSet(const llvm::Value *Ptr, + const llvm::Instruction * /*At*/) { + auto &Ret = Cache[Ptr]; + if (Ret) { + return Ret; + } + + auto Set = Owner.acquire(); + Ret = Set; + + auto *ModSet = SVF::LLVMModuleSet::getLLVMModuleSet(); + auto *Nod = ModSet->getSVFValue(Ptr); + + auto PointerNod = PAG->getValueNode(Nod); + + const auto &Pts = getPTA().getPts(PointerNod); + for (auto PointeeNod : Pts) { + + if (const SVF::MemObj *Mem = PAG->getObject(PointeeNod)) { + if (const auto *Val = Mem->getValue()) { + if (const auto *LLVMVal = ModSet->getLLVMValue(Val)) { + Set->insert(LLVMVal); + } + } + } + + const auto &RevPts = getPTA().getRevPts(PointeeNod); + for (auto AliasNod : RevPts) { + const auto *AliasGNod = PAG->getGNode(AliasNod); + if (!AliasGNod) { + continue; + } + const auto *AliasVal = AliasGNod->getValue(); + if (!AliasVal) { + continue; + } + if (const auto *LLVMAliasVal = ModSet->getLLVMValue(AliasVal)) { + Set->insert(LLVMAliasVal); + } + } + } + + return Set; + } + + // TODO: reachable allocation sites without too much code duplication + + AllocationSiteSetPtrTy + getReachableAllocationSites(const llvm::Value *Ptr, bool IntraProcOnly, + const llvm::Instruction *At) { + llvm::report_fatal_error( + "[getReachableAllocationSites]: Not implemented yet!"); + } + + bool isInReachableAllocationSites(const llvm::Value *Ptr, + const llvm::Value *AllocSite, + bool IntraProcOnly, + const llvm::Instruction *At) { + llvm::report_fatal_error( + "[getReachableAllocationSites]: Not implemented yet!"); + } + + void print(llvm::raw_ostream &OS) const { + // TODO + } + + void printAsJson(llvm::raw_ostream &OS) const { + // TODO + } + + void mergeWith(SVFAliasInfoImpl & /*Other*/) { + llvm::report_fatal_error("[mergeWith]: not supported"); + } + + void introduceAlias(const llvm::Value * /*V1*/, const llvm::Value * /*V2*/, + const llvm::Instruction * /*At*/, AliasResult /*Kind*/) { + llvm::report_fatal_error("[introduceAlias]: not supported"); + } + +private: + llvm::DenseMap Cache; + AliasSetOwner::memory_resource_type MRes; + AliasSetOwner Owner{&MRes}; +}; +} // namespace psr + +auto psr::createLLVMSVFDDAAliasInfo(LLVMProjectIRDB &IRDB) -> LLVMAliasInfo { + return std::make_unique(psr::initSVFModule(IRDB)); +} diff --git a/unittests/PhasarLLVM/DataFlow/IfdsIde/DefaultFlowFunctionTest.cpp b/unittests/PhasarLLVM/DataFlow/IfdsIde/DefaultFlowFunctionTest.cpp index 619617ccd0..5a5e6d32ce 100644 --- a/unittests/PhasarLLVM/DataFlow/IfdsIde/DefaultFlowFunctionTest.cpp +++ b/unittests/PhasarLLVM/DataFlow/IfdsIde/DefaultFlowFunctionTest.cpp @@ -4,6 +4,7 @@ #include "phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.h" #include "phasar/PhasarLLVM/Pointer/FilteredLLVMAliasSet.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" +#include "phasar/PhasarLLVM/Pointer/LLVMPointsToInfo.h" #include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" #include "llvm/ADT/STLExtras.h" diff --git a/unittests/PhasarLLVM/Pointer/SVFAliasSetTest.cpp b/unittests/PhasarLLVM/Pointer/SVFAliasSetTest.cpp index 9ab3f30e04..2a6ea1ef04 100644 --- a/unittests/PhasarLLVM/Pointer/SVFAliasSetTest.cpp +++ b/unittests/PhasarLLVM/Pointer/SVFAliasSetTest.cpp @@ -2,6 +2,7 @@ #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" #include "phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h" +#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" #include "phasar/Pointer/AliasAnalysisType.h" #include "phasar/Pointer/AliasResult.h" @@ -107,6 +108,22 @@ TEST(SVFAliasSetTest, PointsTo_03) { EXPECT_TRUE(PT.mayPointsTo(V, AllocObj, V->getNextNode())); } +TEST(SVFAliasSetTest, PointsTo_04) { + LLVMProjectIRDB IRDB(unittest::PathToLLTestFiles + + "pointers/call_01_cpp_dbg.ll"); + + auto PT = createLLVMSVFPointsToIterator(IRDB, SVFPointsToAnalysisType::DDA); + + const auto *V = IRDB.getInstruction(3); + ASSERT_TRUE(V && V->getType()->isPointerTy()); + + auto PSet = PT.asSet(V, V->getNextNode()); + llvm::errs() << "PointsToSet of " << llvmIRToString(V) << ":\n"; + for (const auto *Pt : PSet) { + llvm::errs() << " --> " << llvmIRToString(Pt) << '\n'; + } +} + int main(int Argc, char **Argv) { ::testing::InitGoogleTest(&Argc, Argv); return RUN_ALL_TESTS(); From baa577756203490876083798ac6501b40c196620 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Thu, 26 Jun 2025 20:11:58 +0200 Subject: [PATCH 09/11] Fill missing functions for SVFAliasInfoImpl --- .../phasar/PhasarLLVM/Pointer/LLVMAliasSet.h | 21 ++- lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp | 27 ++- lib/PhasarLLVM/Pointer/SVF/PhasarSVFUtils.h | 51 ++++++ .../Pointer/SVF/SVFBasedAliasAnalysis.cpp | 165 ++++++++++++++---- lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp | 38 ++-- lib/PhasarLLVM/Utils/LLVMShorthands.cpp | 3 + test/llvm_test_code/pointers/call_01.cpp | 4 +- .../PhasarLLVM/Pointer/SVFAliasSetTest.cpp | 20 ++- 8 files changed, 244 insertions(+), 85 deletions(-) create mode 100644 lib/PhasarLLVM/Pointer/SVF/PhasarSVFUtils.h diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h b/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h index eeffe7412a..4024e92a16 100644 --- a/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h +++ b/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h @@ -39,6 +39,13 @@ template <> struct AliasInfoTraits : DefaultAATraits {}; +/// Utility function to check whether PotentialValue may be in the reachable +/// allocation-sites of V, if V and PotentialValue alias. +[[nodiscard]] bool isInReachableAllocationSitesTy( + const llvm::Value *V, const llvm::Value *PotentialValue, bool IntraProcOnly, + const llvm::Function *VFun = nullptr, + const llvm::GlobalObject *VG = nullptr); + class LLVMAliasSet : public AnalysisPropertiesMixin, public AliasInfoBaseUtils { // For int*IsReachableAllocationSiteTy: @@ -120,6 +127,12 @@ class LLVMAliasSet : public AnalysisPropertiesMixin, [[nodiscard]] inline bool empty() const { return AnalyzedFunctions.empty(); } + friend bool isInReachableAllocationSitesTy(const llvm::Value *V, + const llvm::Value *PotentialValue, + bool IntraProcOnly, + const llvm::Function *VFun, + const llvm::GlobalObject *VG); + private: void computeValuesAliasSet(const llvm::Value *V); @@ -131,14 +144,6 @@ class LLVMAliasSet : public AnalysisPropertiesMixin, void mergeAliasSets(BoxedPtr PTS1, BoxedPtr PTS2); - bool interIsReachableAllocationSiteTy(const llvm::Value *V, - const llvm::Value *P) const; - - bool intraIsReachableAllocationSiteTy(const llvm::Value *V, - const llvm::Value *P, - const llvm::Function *VFun, - const llvm::GlobalObject *VG) const; - /// Utility function used by computeFunctionsAliasSet(...) void addPointer(FunctionAliasView AA, const llvm::DataLayout &DL, const llvm::Value *V, std::vector &Reps); diff --git a/lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp b/lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp index 7b216842c2..b7d4e22967 100644 --- a/lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp +++ b/lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp @@ -262,8 +262,9 @@ void LLVMAliasSet::mergeAliasSets(BoxedPtr PTS1, Owner.release(ToDelete); } -bool LLVMAliasSet::interIsReachableAllocationSiteTy( - [[maybe_unused]] const llvm::Value *V, const llvm::Value *P) const { +[[nodiscard]] static bool +interIsReachableAllocationSiteTy([[maybe_unused]] const llvm::Value *V, + const llvm::Value *P) { // consider the full inter-procedural points-to/alias information if (llvm::isa(P)) { @@ -279,9 +280,9 @@ bool LLVMAliasSet::interIsReachableAllocationSiteTy( return false; } -bool LLVMAliasSet::intraIsReachableAllocationSiteTy( +[[nodiscard]] static bool intraIsReachableAllocationSiteTy( [[maybe_unused]] const llvm::Value *V, const llvm::Value *P, - const llvm::Function *VFun, const llvm::GlobalObject *VG) const { + const llvm::Function *VFun, const llvm::GlobalObject *VG) { // consider the function-local, i.e. intra-procedural, points-to/alias // information only @@ -785,3 +786,21 @@ void LLVMAliasSet::drawAliasSetsDistribution(int Peak) const { } } // namespace psr + +bool psr::isInReachableAllocationSitesTy(const llvm::Value *V, + const llvm::Value *PotentialValue, + bool IntraProcOnly, + const llvm::Function *VFun, + const llvm::GlobalObject *VG) { + if (IntraProcOnly) { + if (!VFun) { + VFun = AliasInfoBaseUtils::retrieveFunction(V); + } + if (!VG) { + VG = llvm::dyn_cast(V); + } + return intraIsReachableAllocationSiteTy(V, PotentialValue, VFun, VG); + } + + return interIsReachableAllocationSiteTy(V, PotentialValue); +} diff --git a/lib/PhasarLLVM/Pointer/SVF/PhasarSVFUtils.h b/lib/PhasarLLVM/Pointer/SVF/PhasarSVFUtils.h new file mode 100644 index 0000000000..85dfd53042 --- /dev/null +++ b/lib/PhasarLLVM/Pointer/SVF/PhasarSVFUtils.h @@ -0,0 +1,51 @@ +#ifndef PHASAR_PHASARLLVM_POINTER_SVFUTILS_H +#define PHASAR_PHASARLLVM_POINTER_SVFUTILS_H + +#include "SVF-LLVM/LLVMModule.h" +#include "SVFIR/SVFIR.h" +#include "Util/GeneralType.h" + +namespace psr { +[[nodiscard]] inline const llvm::Value * +pointerNodeToLLVMOrNull(SVF::NodeID Nod, SVF::LLVMModuleSet &ModSet, + SVF::SVFIR &PAG) { + + if (const SVF::SVFVar *Var = PAG.getGNode(Nod)) { + if (const auto *Val = Var->getValue()) { + if (const auto *LLVMVal = ModSet.getLLVMValue(Val)) { + return LLVMVal; + } + } + } + return nullptr; +} + +[[nodiscard]] inline const llvm::Value * +objectNodeToLLVMOrNull(SVF::NodeID Nod, SVF::LLVMModuleSet &ModSet, + SVF::SVFIR &PAG) { + if (const SVF::MemObj *Mem = PAG.getObject(Nod)) { + if (const auto *Val = Mem->getValue()) { + if (const auto *LLVMVal = ModSet.getLLVMValue(Val)) { + return LLVMVal; + } + } + } + return nullptr; +} + +[[nodiscard]] inline SVF::NodeID getNodeId(const llvm::Value *Pointer, + SVF::LLVMModuleSet &ModSet, + SVF::SVFIR &PAG) { + auto *Nod = ModSet.getSVFValue(Pointer); + return PAG.getValueNode(Nod); +} +[[nodiscard]] inline SVF::NodeID getObjNodeId(const llvm::Value *Obj, + SVF::LLVMModuleSet &ModSet, + SVF::SVFIR &PAG) { + auto *Nod = ModSet.getSVFValue(Obj); + return PAG.getObjectNode(Nod); +} + +} // namespace psr + +#endif // PHASAR_PHASARLLVM_POINTER_SVFUTILS_H diff --git a/lib/PhasarLLVM/Pointer/SVF/SVFBasedAliasAnalysis.cpp b/lib/PhasarLLVM/Pointer/SVF/SVFBasedAliasAnalysis.cpp index 4e28217bbe..a87901706c 100644 --- a/lib/PhasarLLVM/Pointer/SVF/SVFBasedAliasAnalysis.cpp +++ b/lib/PhasarLLVM/Pointer/SVF/SVFBasedAliasAnalysis.cpp @@ -2,7 +2,10 @@ #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" #include "phasar/PhasarLLVM/Pointer/AliasAnalysisView.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" +#include "phasar/PhasarLLVM/Pointer/LLVMPointsToUtils.h" #include "phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h" +#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" #include "phasar/Pointer/AliasAnalysisType.h" #include "phasar/Pointer/AliasInfoTraits.h" #include "phasar/Pointer/AliasResult.h" @@ -10,6 +13,8 @@ #include "phasar/Utils/AnalysisProperties.h" #include "phasar/Utils/Fn.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/IR/Argument.h" #include "llvm/IR/Value.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" @@ -17,18 +22,20 @@ #include "DDA/ContextDDA.h" #include "DDA/DDAClient.h" #include "InitSVF.h" +#include "MemoryModel/PointerAnalysis.h" +#include "PhasarSVFUtils.h" +#include "SVF-LLVM/LLVMModule.h" #include "SVF-LLVM/SVFIRBuilder.h" #include "SVFIR/SVFIR.h" #include "SVFIR/SVFModule.h" #include "SVFIR/SVFType.h" +#include "Util/GeneralType.h" #include "WPA/Andersen.h" #include "WPA/VersionedFlowSensitive.h" #include #include -#include - namespace psr { static constexpr psr::AliasResult translateSVFAliasResult(SVF::AliasResult AR) noexcept { @@ -179,6 +186,26 @@ class SVFAliasInfoImpl return doAliasImpl(&getPTA(), V, Rep); } + void createAliasSet(SVF::NodeID PointerNod, AliasSetTy &Into, + SVF::LLVMModuleSet &ModSet) const { + const auto &Pts = getPTA().getPts(PointerNod); + for (auto PointeeNod : Pts) { + + if (const auto *PointeeVal = + objectNodeToLLVMOrNull(PointeeNod, ModSet, *PAG)) { + Into.insert(PointeeVal); + } + + const auto &RevPts = getPTA().getRevPts(PointeeNod); + for (auto AliasNod : RevPts) { + if (const auto *AliasVal = + pointerNodeToLLVMOrNull(AliasNod, ModSet, *PAG)) { + Into.insert(AliasVal); + } + } + } + } + [[nodiscard]] AliasSetPtrTy getAliasSet(const llvm::Value *Ptr, const llvm::Instruction * /*At*/) { auto &Ret = Cache[Ptr]; @@ -194,59 +221,121 @@ class SVFAliasInfoImpl auto PointerNod = PAG->getValueNode(Nod); - const auto &Pts = getPTA().getPts(PointerNod); - for (auto PointeeNod : Pts) { - - if (const SVF::MemObj *Mem = PAG->getObject(PointeeNod)) { - if (const auto *Val = Mem->getValue()) { - if (const auto *LLVMVal = ModSet->getLLVMValue(Val)) { - Set->insert(LLVMVal); - } - } - } - - const auto &RevPts = getPTA().getRevPts(PointeeNod); - for (auto AliasNod : RevPts) { - const auto *AliasGNod = PAG->getGNode(AliasNod); - if (!AliasGNod) { - continue; - } - const auto *AliasVal = AliasGNod->getValue(); - if (!AliasVal) { - continue; - } - if (const auto *LLVMAliasVal = ModSet->getLLVMValue(AliasVal)) { - Set->insert(LLVMAliasVal); - } - } - } + createAliasSet(PointerNod, *Set, *ModSet); return Set; } - // TODO: reachable allocation sites without too much code duplication - AllocationSiteSetPtrTy getReachableAllocationSites(const llvm::Value *Ptr, bool IntraProcOnly, - const llvm::Instruction *At) { - llvm::report_fatal_error( - "[getReachableAllocationSites]: Not implemented yet!"); + const llvm::Instruction * /*At*/) { + auto Ret = std::make_unique(); + if (!psr::isInterestingPointer(Ptr)) { + return Ret; + } + + auto &ModSet = *SVF::LLVMModuleSet::getLLVMModuleSet(); + auto Nod = getNodeId(Ptr, ModSet, *PAG); + const auto &Pts = getPTA().getPts(Nod); + + const auto *VFun = AliasInfoBaseUtils::retrieveFunction(Ptr); + const auto *VG = llvm::dyn_cast(Ptr); + + for (auto PointeeNod : Pts) { + const auto *PointeeVal = objectNodeToLLVMOrNull(PointeeNod, ModSet, *PAG); + if (!PointeeVal) { + continue; + } + + if (!IntraProcOnly || psr::isInReachableAllocationSitesTy( + Ptr, PointeeVal, true, VFun, VG)) { + Ret->insert(PointeeVal); + } + } + + return Ret; } bool isInReachableAllocationSites(const llvm::Value *Ptr, const llvm::Value *AllocSite, bool IntraProcOnly, - const llvm::Instruction *At) { - llvm::report_fatal_error( - "[getReachableAllocationSites]: Not implemented yet!"); + const llvm::Instruction * /*At*/) { + + if (IntraProcOnly && + !psr::isInReachableAllocationSitesTy(Ptr, AllocSite, true)) { + return false; + } + + auto &ModSet = *SVF::LLVMModuleSet::getLLVMModuleSet(); + auto Nod = getNodeId(Ptr, ModSet, *PAG); + + if (IntraProcOnly && llvm::isa(AllocSite)) { + auto AllocSiteNod = getNodeId(AllocSite, ModSet, *PAG); + return getPTA().alias(Nod, AllocSiteNod) != SVF::NoAlias; + } + + auto AllocSiteNod = getObjNodeId(AllocSite, ModSet, *PAG); + const auto &Pts = getPTA().getPts(Nod); + + return Pts.test(AllocSiteNod); } void print(llvm::raw_ostream &OS) const { - // TODO + OS << "========== SVFDDAAliasSet ==========\n"; + + auto &ModSet = *SVF::LLVMModuleSet::getLLVMModuleSet(); + for (const auto &[Nod, Var] : *PAG) { + if (!Var->hasValue()) { + continue; + } + + const auto *PointerVal = ModSet.getLLVMValue(Var->getValue()); + if (!PointerVal) { + continue; + } + + AliasSetTy Buf; + const auto &Aliases = [&, Nod = Nod]() -> const AliasSetTy & { + auto It = Cache.find(PointerVal); + if (It != Cache.end()) { + return *It->second; + } + + createAliasSet(Nod, Buf, ModSet); + return Buf; + }(); + + if (Aliases.empty()) { + continue; + } + + OS << "V: " << llvmIRToString(PointerVal) << '\n'; + for (const auto *Alias : Aliases) { + OS << "\taliases " << llvmIRToString(Alias) << '\n'; + } + } + + OS << "=====\n"; } void printAsJson(llvm::raw_ostream &OS) const { - // TODO + OS << "{\n"; + + bool First = true; + for (const auto &[Nod, Var] : *PAG) { + if (First) { + First = false; + } else { + OS << ",\n"; + } + + OS << " \"" << Nod << "\": ["; + const auto &Pts = getPTA().getPts(Nod); + llvm::interleaveComma(Pts, OS); + OS << "]"; + } + + OS << "\n}\n"; } void mergeWith(SVFAliasInfoImpl & /*Other*/) { diff --git a/lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp b/lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp index 75a59a209f..2de171a186 100644 --- a/lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp +++ b/lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp @@ -10,6 +10,7 @@ #include "DDA/DDAClient.h" #include "InitSVF.h" #include "MemoryModel/PointerAnalysis.h" +#include "PhasarSVFUtils.h" #include "SVF-LLVM/LLVMModule.h" #include "SVF-LLVM/SVFIRBuilder.h" #include "WPA/Andersen.h" @@ -70,19 +71,13 @@ class SVFPointsToSet : public psr::PointsToInfoBase> { [[nodiscard]] o_t asAbstractObjectImpl(psr::ByConstRef Pointer) const noexcept { auto *ModSet = SVF::LLVMModuleSet::getLLVMModuleSet(); - auto *Nod = ModSet->getSVFValue(Pointer); - - return PAG->getValueNode(Nod); + return psr::getNodeId(Pointer, *ModSet, *PAG); } [[nodiscard]] std::optional asPointerOrNullImpl(o_t Obj) const noexcept { - if (const SVF::MemObj *Mem = PAG->getObject(Obj)) { - if (const auto *Val = Mem->getValue()) { - auto *ModSet = SVF::LLVMModuleSet::getLLVMModuleSet(); - if (const auto *LLVMVal = ModSet->getLLVMValue(Val)) { - return LLVMVal; - } - } + if (const auto *LLVMVal = psr::objectNodeToLLVMOrNull( + Obj, *SVF::LLVMModuleSet::getLLVMModuleSet(), *PAG)) { + return LLVMVal; } return std::nullopt; @@ -191,15 +186,11 @@ template struct SVFLLVMPointsToIterator { [[nodiscard]] SVF::NodeID getNodeId(v_t Pointer) const noexcept { auto *ModSet = SVF::LLVMModuleSet::getLLVMModuleSet(); - auto *Nod = ModSet->getSVFValue(Pointer); - - return PT.getPAG().getValueNode(Nod); + return psr::getNodeId(Pointer, *ModSet, PT.getPAG()); } [[nodiscard]] SVF::NodeID getObjNodeId(o_t Obj) const noexcept { auto *ModSet = SVF::LLVMModuleSet::getLLVMModuleSet(); - auto *Nod = ModSet->getSVFValue(Obj); - - return PT.getPAG().getObjectNode(Nod); + return psr::getObjNodeId(Obj, *ModSet, PT.getPAG()); } void forallPointeesOf(o_t Pointer, n_t /*At*/, @@ -213,18 +204,9 @@ template struct SVFLLVMPointsToIterator { auto *ModSet = SVF::LLVMModuleSet::getLLVMModuleSet(); SVF::SVFIR &PAG = PT.getPAG(); for (auto PointeeNod : Pts) { - const SVF::MemObj *Mem = PAG.getObject(PointeeNod); - if (!Mem) { - continue; - } - - const auto *Val = Mem->getValue(); - if (!Val) { - continue; - } - - if (const auto *LLVMVal = ModSet->getLLVMValue(Val)) { - WithPointee(LLVMVal); + if (const auto *PointeeVal = + psr::objectNodeToLLVMOrNull(PointeeNod, *ModSet, PAG)) { + WithPointee(PointeeVal); } } } diff --git a/lib/PhasarLLVM/Utils/LLVMShorthands.cpp b/lib/PhasarLLVM/Utils/LLVMShorthands.cpp index 2b8fac6aa3..74b36f7bab 100644 --- a/lib/PhasarLLVM/Utils/LLVMShorthands.cpp +++ b/lib/PhasarLLVM/Utils/LLVMShorthands.cpp @@ -160,6 +160,9 @@ std::string psr::llvmIRToString(const llvm::Value *V) { if (!V) { return ""; } + if (const auto *F = llvm::dyn_cast(V)) { + return "fun @" + F->getName().str(); + } std::string IRBuffer; llvm::raw_string_ostream RSO(IRBuffer); diff --git a/test/llvm_test_code/pointers/call_01.cpp b/test/llvm_test_code/pointers/call_01.cpp index 45480454b9..6301dcb262 100644 --- a/test/llvm_test_code/pointers/call_01.cpp +++ b/test/llvm_test_code/pointers/call_01.cpp @@ -1,5 +1,7 @@ -void setInteger(int *x) { *x = 42; } +void setInteger(int *x) { // + *x = 42; +} int main() { int i; diff --git a/unittests/PhasarLLVM/Pointer/SVFAliasSetTest.cpp b/unittests/PhasarLLVM/Pointer/SVFAliasSetTest.cpp index 2a6ea1ef04..5121421e1b 100644 --- a/unittests/PhasarLLVM/Pointer/SVFAliasSetTest.cpp +++ b/unittests/PhasarLLVM/Pointer/SVFAliasSetTest.cpp @@ -112,16 +112,24 @@ TEST(SVFAliasSetTest, PointsTo_04) { LLVMProjectIRDB IRDB(unittest::PathToLLTestFiles + "pointers/call_01_cpp_dbg.ll"); - auto PT = createLLVMSVFPointsToIterator(IRDB, SVFPointsToAnalysisType::DDA); + auto PT = createLLVMSVFDDAAliasInfo(IRDB); const auto *V = IRDB.getInstruction(3); ASSERT_TRUE(V && V->getType()->isPointerTy()); - auto PSet = PT.asSet(V, V->getNextNode()); - llvm::errs() << "PointsToSet of " << llvmIRToString(V) << ":\n"; - for (const auto *Pt : PSet) { - llvm::errs() << " --> " << llvmIRToString(Pt) << '\n'; - } + decltype(PT)::AliasSetTy GroundTruth = { + fromMetaDataId(IRDB, "_Z10setIntegerPi.0"), // x + IRDB.getValueFromId(3), // itself + IRDB.getValueFromId(7), // i = alloca + IRDB.getValueFromId(13), // load p before call + IRDB.getValueFromId(15), // load p after call + }; + + auto PSet = PT.getAliasSet(V, V->getNextNode()); + PT.print(); + PT.printAsJson(llvm::outs()); + + EXPECT_EQ(*PSet, GroundTruth); } int main(int Argc, char **Argv) { From 43674b0fc0e7afed8488de913177b7977e9cb64f Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Sat, 28 Jun 2025 11:14:54 +0200 Subject: [PATCH 10/11] minor --- include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h b/include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h index e97b22f2a3..451d37b595 100644 --- a/include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h +++ b/include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h @@ -78,7 +78,7 @@ createSVFPointsToInfo(LLVMProjectIRDB &IRDB, SVFPointsToAnalysisType PTATy); createLLVMSVFPointsToIterator(LLVMProjectIRDB &IRDB, SVFPointsToAnalysisType PTATy); -/// Use SVF to perform the specified pointer analysis and return the results +/// Use SVF to perform a ContextDDA pointer analysis and return the results /// compatible to psr::LLVMAliasInfo and psr::LLVMAliasInfoRef /// /// \note Only support DDA for now, as VFS seems to not support getRevPts(). From 20ebcfb9d18c9983fa4672477e7aab9073a4d2ec Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Sat, 28 Jun 2025 12:14:14 +0200 Subject: [PATCH 11/11] Fix FilteredLLVMAliasSet --- .../Pointer/FilteredLLVMAliasSet.cpp | 50 ++++++------------- 1 file changed, 14 insertions(+), 36 deletions(-) diff --git a/lib/PhasarLLVM/Pointer/FilteredLLVMAliasSet.cpp b/lib/PhasarLLVM/Pointer/FilteredLLVMAliasSet.cpp index 2766f8e5a9..621e79a7f0 100644 --- a/lib/PhasarLLVM/Pointer/FilteredLLVMAliasSet.cpp +++ b/lib/PhasarLLVM/Pointer/FilteredLLVMAliasSet.cpp @@ -189,32 +189,18 @@ auto FilteredLLVMAliasSet::getReachableAllocationSites( AllocSites = std::make_unique(); - // consider the full inter-procedural points-to/alias information - if (!IntraProcOnly) { - foreachValidAliasIn(AS->getAliasSet(V), V, Fun, - [Set = AllocSites.get(), AS = AS.get(), V](v_t Alias) { - if (AS->interIsReachableAllocationSiteTy(V, Alias)) { - Set->insert(Alias); - } - }); - - } else { - // consider the function-local, i.e. intra-procedural, points-to/alias - // information only - - // We may not be able to retrieve a function for the given value since some - // pointer values can exist outside functions, for instance, in case of - // vtables, etc. - const auto *VFun = getFunction(V); - const auto *VG = llvm::dyn_cast(V); - foreachValidAliasIn( - AS->getAliasSet(V), V, Fun, - [Set = AllocSites.get(), AS = AS.get(), V, VFun, VG](v_t Alias) { - if (AS->intraIsReachableAllocationSiteTy(V, Alias, VFun, VG)) { - Set->insert(Alias); - } - }); - } + const auto *VFun = getFunction(V); + const auto *VG = llvm::dyn_cast(V); + + foreachValidAliasIn(AS->getAliasSet(V), V, Fun, + [Set = AllocSites.get(), V, AS = AS.get(), IntraProcOnly, + VFun, VG](v_t Alias) { + if (psr::isInReachableAllocationSitesTy( + V, Alias, IntraProcOnly, VFun, VG)) { + Set->insert(Alias); + } + }); + return AllocSites.get(); } @@ -227,16 +213,8 @@ bool FilteredLLVMAliasSet::isInReachableAllocationSites( return false; } - bool PVIsReachableAllocationSiteType = false; - if (IntraProcOnly) { - const auto *VFun = getFunction(V); - const auto *VG = llvm::dyn_cast(V); - PVIsReachableAllocationSiteType = - AS->intraIsReachableAllocationSiteTy(V, PotentialValue, VFun, VG); - } else { - PVIsReachableAllocationSiteType = - AS->interIsReachableAllocationSiteTy(V, PotentialValue); - } + bool PVIsReachableAllocationSiteType = + psr::isInReachableAllocationSitesTy(V, PotentialValue, IntraProcOnly); if (PVIsReachableAllocationSiteType) { const auto PTS = getAliasSet(V, I);