diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h index 98933d2caf..dad26e24bc 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h @@ -36,14 +36,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*/); @@ -54,8 +53,8 @@ class IDEAliasAwareDefaultFlowFunctionsImpl using IDENoAliasDefaultFlowFunctionsImpl::getCallFlowFunctionImpl; using IDENoAliasDefaultFlowFunctionsImpl::getCallToRetFlowFunctionImpl; -protected: - LLVMAliasInfoRef AS; +private: + LLVMAliasIteratorRef AS; }; } // namespace detail @@ -72,7 +71,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) @@ -116,7 +115,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/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.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/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/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/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..451d37b595 100644 --- a/include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h +++ b/include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h @@ -10,11 +10,17 @@ #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" +#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 +43,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 +66,24 @@ 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 +/// compatible to psr::LLVMPointsToIterator, converting the points-to sets to +/// LLVM allocation-sites +[[nodiscard]] LLVMPointsToIterator +createLLVMSVFPointsToIterator(LLVMProjectIRDB &IRDB, + SVFPointsToAnalysisType PTATy); + +/// 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(). +[[nodiscard]] LLVMAliasInfo createLLVMSVFDDAAliasInfo(LLVMProjectIRDB &IRDB); + } // namespace psr #endif // PHASAR_PHASARLLVM_POINTER_SVF_SVFPOINTSTOSET_H 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/AliasInfo.h b/include/phasar/Pointer/AliasInfo.h index a5f2f5f577..6aef775658 100644 --- a/include/phasar/Pointer/AliasInfo.h +++ b/include/phasar/Pointer/AliasInfo.h @@ -10,11 +10,12 @@ #ifndef PHASAR_POINTER_ALIASINFO_H #define 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/Utils/AnalysisProperties.h" #include "phasar/Utils/ByRef.h" -#include "phasar/Utils/TypeTraits.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" @@ -51,14 +52,16 @@ 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; + using traits_t = AliasInfoTraits>; public: @@ -68,14 +71,14 @@ class AliasInfoRef : public AnalysisPropertiesMixin> { 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) {} @@ -84,12 +87,17 @@ class AliasInfoRef : public AnalysisPropertiesMixin> { 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); + } + constexpr operator AliasIteratorRef() && noexcept = delete; + // -- Impl for IsAliasInfo: [[nodiscard]] bool isInterProcedural() const noexcept { @@ -181,11 +189,9 @@ class AliasInfoRef : public AnalysisPropertiesMixin> { } private: - struct VTable { + struct VTable : AliasIteratorRef::VTable { 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, @@ -205,17 +211,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); @@ -252,7 +273,7 @@ class AliasInfoRef : public AnalysisPropertiesMixin> { [](const void *AA) noexcept { delete static_cast(AA); }, - }; + }; // namespace psr // -- @@ -272,7 +293,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 0775346d79..b0fff91fc8 100644 --- a/include/phasar/Pointer/AliasInfoBase.h +++ b/include/phasar/Pointer/AliasInfoBase.h @@ -72,6 +72,7 @@ struct IsAliasInfo< decltype(testAliasInfo(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 new file mode 100644 index 0000000000..274d2eadba --- /dev/null +++ b/include/phasar/Pointer/AliasIterator.h @@ -0,0 +1,210 @@ +/****************************************************************************** + * 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/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" + +#include +#include + +namespace psr { + +namespace detail { + +template +struct IsAliasIterator : std::false_type {}; + +template +struct IsAliasIterator< + T, std::void_t().forallAliasesOf( + 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 +/// 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 : private TypeErasureUtils { +public: + using n_t = N; + using v_t = V; + + struct VTable { + void (*ForallAliasesOf)(void *, ByConstRef, ByConstRef, + llvm::function_ref); + AliasResult (*Alias)(void *, ByConstRef, ByConstRef, ByConstRef); + }; + + template && + std::is_same_v && + std::is_same_v>> + constexpr AliasIteratorRef(ConcreteAA *AA) noexcept + : 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 explicit AliasIteratorRef(void *AA, const VTable *VT) noexcept + : AA(AA), VT(VT) { + assert(VT != 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 forallAliasesOf(ByConstRef Of, ByConstRef At, + llvm::function_ref WithAlias) { + assert(VT != nullptr); + VT->ForallAliasesOf(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; + forallAliasesOf(Of, At, + [&Set](v_t Alias) { Set.insert(std::move(Alias)); }); + 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); + return VT->Alias(AA, Ptr, Alias, At); + } + +private: + template + static void aliasesOfThunk(void *AA, ByConstRef Of, ByConstRef At, + llvm::function_ref WithAlias) { + auto *CAA = fromOpaquePtr(AA); + if constexpr (IsAliasIterator) { + return (void)CAA->aliasesof(Of, At, WithAlias); + } else { + auto AliasSetPtr = CAA->getAliasSet(Of, At); + for (auto &&Alias : *AliasSetPtr) { + WithAlias(PSR_FWD(Alias)); + } + } + } + + template + static AliasResult aliasThunk(void *AA, ByConstRef Ptr, + ByConstRef Alias, ByConstRef At) { + if (Ptr == Alias) { + return AliasResult::MustAlias; + } + + 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) ? AliasResult::MayAlias + : AliasResult::NoAlias; + } 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{}; + const VTable *VT{}; +}; // namespace psr + +} // namespace psr + +#endif // PHASAR_POINTER_ALIASITERATOR_H diff --git a/include/phasar/Pointer/PointsToInfo.h b/include/phasar/Pointer/PointsToInfo.h index af2f54d587..fd29a8e96b 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,42 @@ 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->forallPointeesOf(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 +234,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..b951cdf780 --- /dev/null +++ b/include/phasar/Pointer/PointsToIterator.h @@ -0,0 +1,357 @@ +/****************************************************************************** + * 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/Pointer/AliasInfoBase.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().forallPointeesOf( + 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 {}; + +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 +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: + using v_t = V; + using o_t = O; + using n_t = N; + + struct VTable { + o_t (*AsAbstractObject)(const void *, ByConstRef) noexcept; + 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 < + typename ConcretePTA, + std::enable_if_t && + std::is_same_v && + std::is_same_v && + std::is_same_v && + !detail::HasReachableAllocationSites::value> + * = nullptr> + constexpr PointsToIteratorRef(const ConcretePTA *PT) noexcept + : PT(getOpaquePtr(psr::assertNotNull(PT))), VT(&VTableFor) { + static_assert(IsPointsToIterator); + } + + template < + typename ConcretePTA, + std::enable_if_t && + std::is_same_v && + std::is_same_v && + std::is_same_v && + detail::HasReachableAllocationSites::value> + * = nullptr> + constexpr PointsToIteratorRef(const ConcretePTA *PT) noexcept + : PT(&psr::assertNotNull(PT)), + VT(&ReachableAllocSitesVTFor) { + static_assert(IsPointsToIterator); + } + + 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(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 forallPointeesOf(ByConstRef Pointer, ByConstRef At, + llvm::function_ref WithPointee) const { + assert(VT != nullptr); + VT->ForallPointeesOf(PT, Pointer, At, WithPointee); + } + template > + [[nodiscard]] SetT asSet(ByConstRef Pointer, ByConstRef At) { + SetT Set; + forallPointeesOf(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 forallPointeesOfThunk(const void *PT, ByConstRef Pointer, + ByConstRef At, + llvm::function_ref WithPointee) { + const auto *CPT = fromOpaquePtr(PT); + if constexpr (IsPointsToIterator) { + 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 + auto PointsToSetPtr = getPointerFrom(PointsToSet); + for (auto &&Pointee : *PointsToSetPtr) { + WithPointee(PSR_FWD(Pointee)); + } + } + } + + 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) { + 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->forallPointeesOf([&Ret, Obj](o_t Pointee) { + if (Pointee == Obj) { + Ret = true; + } + }); + return Ret; + } + } + + 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) { + delete fromOpaquePtr(PT); + } + } + + template + static constexpr VTable VTableFor = { + &asAbstractObjectThunk, + &forallPointeesOfThunk, + &mayPointsToThunk, + &destroyThunk, + }; + + template + static constexpr VTable ReachableAllocSitesVTFor = { + &asAbstractObjectThunk, + &forallReachableAllocationSitesThunk, + &maybeInReachableAllocationSitesThunk, + &destroyThunk, + }; + +protected: + const void *PT{}; + const VTable *VT{}; +}; + +/// Owning version of PointsToIteratorRef +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/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 82725b65b1..3fce397f4e 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( @@ -39,31 +36,29 @@ auto detail::IDEAliasAwareDefaultFlowFunctionsImpl::getNormalFlowFunctionImpl( Curr, Succ); } -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.forallAliasesOf(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( 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/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/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); 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/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/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 0416a8415b..a87901706c 100644 --- a/lib/PhasarLLVM/Pointer/SVF/SVFBasedAliasAnalysis.cpp +++ b/lib/PhasarLLVM/Pointer/SVF/SVFBasedAliasAnalysis.cpp @@ -2,17 +2,34 @@ #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" +#include "phasar/Pointer/AliasSetOwner.h" +#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" + #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" @@ -34,9 +51,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 +65,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 +103,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 +130,230 @@ 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); + } + + 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]; + 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); + + createAliasSet(PointerNod, *Set, *ModSet); + + return Set; + } + + AllocationSiteSetPtrTy + getReachableAllocationSites(const llvm::Value *Ptr, bool IntraProcOnly, + 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*/) { + + 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 { + 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 { + 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*/) { + 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/lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp b/lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp index 1aa5744403..2de171a186 100644 --- a/lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp +++ b/lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp @@ -1,15 +1,21 @@ #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" #include "MemoryModel/PointerAnalysis.h" +#include "PhasarSVFUtils.h" #include "SVF-LLVM/LLVMModule.h" #include "SVF-LLVM/SVFIRBuilder.h" #include "WPA/Andersen.h" +#include #include namespace { @@ -49,6 +55,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()) {} @@ -63,17 +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 auto *Val = PAG->getObject(Obj)->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; @@ -151,3 +155,86 @@ 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(); + return psr::getNodeId(Pointer, *ModSet, PT.getPAG()); + } + [[nodiscard]] SVF::NodeID getObjNodeId(o_t Obj) const noexcept { + auto *ModSet = SVF::LLVMModuleSet::getLLVMModuleSet(); + return psr::getObjNodeId(Obj, *ModSet, PT.getPAG()); + } + + void forallPointeesOf(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) { + if (const auto *PointeeVal = + psr::objectNodeToLLVMOrNull(PointeeNod, *ModSet, PAG)) { + WithPointee(PointeeVal); + } + } + } + + [[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/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/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 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/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/LLVMAliasSetTest.cpp b/unittests/PhasarLLVM/Pointer/LLVMAliasSetTest.cpp index 74a242dec3..15208cccc5 100644 --- a/unittests/PhasarLLVM/Pointer/LLVMAliasSetTest.cpp +++ b/unittests/PhasarLLVM/Pointer/LLVMAliasSetTest.cpp @@ -4,12 +4,16 @@ #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/LLVMPointsToInfo.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) { @@ -62,8 +66,16 @@ 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); + int main(int Argc, char **Argv) { ::testing::InitGoogleTest(&Argc, Argv); return RUN_ALL_TESTS(); diff --git a/unittests/PhasarLLVM/Pointer/SVFAliasSetTest.cpp b/unittests/PhasarLLVM/Pointer/SVFAliasSetTest.cpp index 4482d6071e..5121421e1b 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" @@ -86,6 +87,51 @@ 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())); +} + +TEST(SVFAliasSetTest, PointsTo_04) { + LLVMProjectIRDB IRDB(unittest::PathToLLTestFiles + + "pointers/call_01_cpp_dbg.ll"); + + auto PT = createLLVMSVFDDAAliasInfo(IRDB); + + const auto *V = IRDB.getInstruction(3); + ASSERT_TRUE(V && V->getType()->isPointerTy()); + + 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) { ::testing::InitGoogleTest(&Argc, Argv); return RUN_ALL_TESTS();