diff --git a/.clang-tidy b/.clang-tidy index 94ed69701..3e95f2eae 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -29,10 +29,12 @@ Checks: '-*, -cppcoreguidelines-init-variables, -cppcoreguidelines-macro-usage, -cppcoreguidelines-avoid-do-while, + -cppcoreguidelines-avoid-c-arrays, bugprone-*, -bugprone-easily-swappable-parameters, modernize-*, -modernize-use-trailing-return-type, + -modernize-avoid-c-arrays, performance-*, clang-analyzer-* ' diff --git a/.gitignore b/.gitignore index f86934e8b..2aaf8ce5f 100644 --- a/.gitignore +++ b/.gitignore @@ -30,11 +30,8 @@ doc/* log/* **/*/logs/ -# CMake build dir -build/* - # MS VS Code -.vscode/* +.vscode/ # Eclipse .cproject diff --git a/CMakeLists.txt b/CMakeLists.txt index 72e0a73b9..5370c88ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -308,6 +308,18 @@ endif() include(add_llvm) add_llvm() +# SVF +option(PHASAR_USE_SVF "Use SVF for more options in alias analysis (default is OFF)" OFF) +if(PHASAR_USE_SVF) + find_package(SVF REQUIRED CONFIG) + message(STATUS "Found SVF ${SVF_VERSION}") + + if (NOT PHASAR_USE_Z3) + message(WARNING "SVF requires Z3. Set PHASAR_USE_Z3=ON") + set(PHASAR_USE_Z3 ON) + endif() +endif() + # Z3 Solver if(PHASAR_IN_TREE) set (PHASAR_USE_Z3 OFF) diff --git a/config.h.in b/config.h.in index 97b0e8a9f..926d69b0b 100644 --- a/config.h.in +++ b/config.h.in @@ -13,4 +13,6 @@ #cmakedefine PHASAR_HAS_SQLITE +#cmakedefine PHASAR_USE_SVF + #endif /* PHASAR_CONFIG_CONFIG_H */ diff --git a/include/phasar/ControlFlow/CFGBase.h b/include/phasar/ControlFlow/CFGBase.h index 6f0f1d610..5b932f2b5 100644 --- a/include/phasar/ControlFlow/CFGBase.h +++ b/include/phasar/ControlFlow/CFGBase.h @@ -11,6 +11,7 @@ #define PHASAR_CONTROLFLOW_CFGBASE_H #include "phasar/Utils/ByRef.h" +#include "phasar/Utils/CRTPUtils.h" #include "phasar/Utils/TypeTraits.h" namespace psr { @@ -22,7 +23,12 @@ template struct CFGTraits { // using f_t }; -template class CFGBase { +template class CFGBase : public CRTPBase { + friend Derived; + +protected: + using CRTPBase::self; + public: using n_t = typename CFGTraits::n_t; using f_t = typename CFGTraits::f_t; @@ -129,19 +135,14 @@ template class CFGBase { void print(ByConstRef Fun, llvm::raw_ostream &OS) const { self().printImpl(Fun, OS); } - -protected: - Derived &self() noexcept { return static_cast(*this); } - const Derived &self() const noexcept { - return static_cast(*this); - } }; template // NOLINTNEXTLINE(readability-identifier-naming) -PSR_CONCEPT is_cfg_v = is_crtp_base_of_v - &&std::is_same_v - &&std::is_same_v; +PSR_CONCEPT is_cfg_v = + is_crtp_base_of_v && + std::is_same_v && + std::is_same_v; } // namespace psr diff --git a/include/phasar/ControlFlow/CallGraphBase.h b/include/phasar/ControlFlow/CallGraphBase.h index 2f2371618..67bfb0cc9 100644 --- a/include/phasar/ControlFlow/CallGraphBase.h +++ b/include/phasar/ControlFlow/CallGraphBase.h @@ -11,6 +11,7 @@ #define PHASAR_CONTROLFLOW_CALLGRAPHBASE_H #include "phasar/Utils/ByRef.h" +#include "phasar/Utils/CRTPUtils.h" #include "phasar/Utils/TypeTraits.h" namespace psr { @@ -22,7 +23,10 @@ template struct CGTraits { /// Base class of all CallGraph implementations within phasar (currently only /// CallGraph). /// Only represents the data, not how to create it. -template class CallGraphBase { +template class CallGraphBase : public CRTPBase { + friend Derived; + using CRTPBase::self; + public: using n_t = typename CGTraits::n_t; using f_t = typename CGTraits::f_t; @@ -46,11 +50,6 @@ template class CallGraphBase { is_iterable_over_v); return self().getCallersOfImpl(Fun); } - -private: - const Derived &self() const noexcept { - return static_cast(*this); - } }; } // namespace psr diff --git a/include/phasar/ControlFlow/ICFGBase.h b/include/phasar/ControlFlow/ICFGBase.h index 4ef63126d..71f340462 100644 --- a/include/phasar/ControlFlow/ICFGBase.h +++ b/include/phasar/ControlFlow/ICFGBase.h @@ -12,6 +12,7 @@ #include "phasar/ControlFlow/CFGBase.h" #include "phasar/ControlFlow/CallGraphBase.h" +#include "phasar/Utils/CRTPUtils.h" #include "phasar/Utils/TypeTraits.h" #include "llvm/ADT/StringRef.h" @@ -20,16 +21,14 @@ #include namespace psr { -template class ICFGBase { +template class ICFGBase : public CRTPBase { + friend Derived; + using CRTPBase::self; + public: using n_t = typename CFGTraits::n_t; using f_t = typename CFGTraits::f_t; - ICFGBase() noexcept { - static_assert(is_crtp_base_of_v, - "An ICFG must also be a CFG"); - } - /// Returns an iterable range of all function definitions or declarations in /// the ICFG [[nodiscard]] decltype(auto) getAllFunctions() const { @@ -113,20 +112,16 @@ template class ICFGBase { [[nodiscard]] size_t getNumCallSites() const noexcept { return self().getNumCallSitesImpl(); } - -private: - const Derived &self() const noexcept { - return static_cast(*this); - } }; /// True, iff ICF is a proper instantiation of ICFGBase with n_t and f_t taken /// from the given analysis-Domain template // NOLINTNEXTLINE(readability-identifier-naming) -PSR_CONCEPT is_icfg_v = is_crtp_base_of_v - &&std::is_same_v - &&std::is_same_v; +PSR_CONCEPT is_icfg_v = + is_crtp_base_of_v && + std::is_same_v && + std::is_same_v; } // namespace psr diff --git a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h index a3134c713..9eee1395d 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h +++ b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h @@ -38,7 +38,6 @@ template <> struct CFGTraits : CFGTraits {}; namespace detail { template class LLVMBasedCFGImpl : public CFGBase { - friend CFGBase; friend class LLVMBasedBackwardCFG; public: diff --git a/include/phasar/PhasarLLVM/Pointer.h b/include/phasar/PhasarLLVM/Pointer.h index a2a67fd20..8025df0f4 100644 --- a/include/phasar/PhasarLLVM/Pointer.h +++ b/include/phasar/PhasarLLVM/Pointer.h @@ -10,10 +10,10 @@ #ifndef PHASAR_PHASARLLVM_POINTER_H #define PHASAR_PHASARLLVM_POINTER_H +#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/LLVMBasedAliasAnalysis.h" #include "phasar/PhasarLLVM/Pointer/LLVMPointsToUtils.h" #endif // PHASAR_PHASARLLVM_POINTER_H diff --git a/include/phasar/PhasarLLVM/Pointer/AliasAnalysisView.h b/include/phasar/PhasarLLVM/Pointer/AliasAnalysisView.h new file mode 100644 index 000000000..0ae69e983 --- /dev/null +++ b/include/phasar/PhasarLLVM/Pointer/AliasAnalysisView.h @@ -0,0 +1,112 @@ +/****************************************************************************** + * 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_ALIASANALYSISVIEW_H +#define PHASAR_PHASARLLVM_POINTER_ALIASANALYSISVIEW_H + +#include "phasar/Pointer/AliasAnalysisType.h" +#include "phasar/Pointer/AliasResult.h" + +#include +#include + +namespace llvm { +class Value; +class DataLayout; +class Function; +} // namespace llvm + +namespace psr { +class LLVMProjectIRDB; + +class FunctionAliasView { +public: + template + using AliasCallbackTy = AliasResult (*)(T *, const llvm::Value *, + const llvm::Value *, + const llvm::DataLayout &); + + [[nodiscard]] AliasResult alias(const llvm::Value *V, const llvm::Value *Rep, + const llvm::DataLayout &DL) { + return Alias(Context, V, Rep, DL); + } + + template < + typename T, typename AliasFn, + typename = std::enable_if_t && + std::is_default_constructible_v>> + constexpr FunctionAliasView(T *Context, AliasFn /*Alias*/) noexcept + : Context(Context), Alias(&callAlias) {} + +private: + template + static AliasResult callAlias(void *Context, const llvm::Value *V1, + const llvm::Value *V2, + const llvm::DataLayout &DL) { + return AliasFn{}(static_cast(Context), V1, V2, DL); + } + + void *Context{}; + AliasCallbackTy Alias{}; +}; + +#define PSR_BIND_ALIASVIEW(Ctx, ...) \ + ::psr::FunctionAliasView { \ + (Ctx), [] { \ + struct DefaultConstructibleCallable { \ + auto operator()(decltype(Ctx) Context, const llvm::Value *V1, \ + const llvm::Value *V2, const llvm::DataLayout &DL) { \ + return __VA_ARGS__(Context, V1, V2, DL); \ + } \ + }; \ + return DefaultConstructibleCallable{}; \ + }() \ + } + +class AliasAnalysisView { +public: + constexpr AliasAnalysisView(AliasAnalysisType PATy) noexcept : PATy(PATy) {} + + virtual ~AliasAnalysisView() = default; + + [[nodiscard]] FunctionAliasView getAAResults(const llvm::Function *F) { + assert(F != nullptr); + return doGetAAResults(F); + } + + void erase(llvm::Function *F) noexcept { + assert(F != nullptr); + doErase(F); + } + + void clear() noexcept { doClear(); } + + [[nodiscard]] constexpr AliasAnalysisType + getPointerAnalysisType() const noexcept { + return PATy; + }; + + [[nodiscard]] static std::unique_ptr + create(LLVMProjectIRDB &IRDB, bool UseLazyEvaluation, AliasAnalysisType PATy); + +private: + static std::unique_ptr + createLLVMBasedAnalysis(LLVMProjectIRDB &IRDB, bool UseLazyEvaluation, + AliasAnalysisType PATy); + + virtual FunctionAliasView doGetAAResults(const llvm::Function *F) = 0; + virtual void doErase(llvm::Function *F) noexcept = 0; + virtual void doClear() noexcept = 0; + + AliasAnalysisType PATy{}; +}; + +} // namespace psr + +#endif // PHASAR_PHASARLLVM_POINTER_ALIASANALYSISVIEW_H diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h b/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h index ff4ccbe5d..1c77b6f0c 100644 --- a/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h +++ b/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h @@ -10,8 +10,8 @@ #ifndef PHASAR_PHASARLLVM_POINTER_LLVMALIASSET_H #define PHASAR_PHASARLLVM_POINTER_LLVMALIASSET_H +#include "phasar/PhasarLLVM/Pointer/AliasAnalysisView.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasSetData.h" -#include "phasar/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h" #include "phasar/Pointer/AliasInfoBase.h" #include "phasar/Pointer/AliasInfoTraits.h" #include "phasar/Pointer/AliasResult.h" @@ -69,7 +69,7 @@ class LLVMAliasSet : public AnalysisPropertiesMixin, }; [[nodiscard]] inline AliasAnalysisType getAliasAnalysisType() const noexcept { - return PTA.getPointerAnalysisType(); + return PTA->getPointerAnalysisType(); }; [[nodiscard]] AliasResult alias(const llvm::Value *V1, const llvm::Value *V2, @@ -143,12 +143,12 @@ class LLVMAliasSet : public AnalysisPropertiesMixin, const llvm::GlobalObject *VG) const; /// Utility function used by computeFunctionsAliasSet(...) - void addPointer(llvm::AAResults &AA, const llvm::DataLayout &DL, + void addPointer(FunctionAliasView AA, const llvm::DataLayout &DL, const llvm::Value *V, std::vector &Reps); [[nodiscard]] static BoxedPtr getEmptyAliasSet(); - LLVMBasedAliasAnalysis PTA; + std::unique_ptr PTA; llvm::DenseSet AnalyzedFunctions; AliasSetOwner::memory_resource_type MRes; diff --git a/include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h b/include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h new file mode 100644 index 000000000..55c753222 --- /dev/null +++ b/include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h @@ -0,0 +1,55 @@ +/****************************************************************************** + * 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_SVF_SVFPOINTSTOSET_H +#define PHASAR_PHASARLLVM_POINTER_SVF_SVFPOINTSTOSET_H + +#include "phasar/Config/phasar-config.h" +#include "phasar/Pointer/PointsToInfo.h" + +#include "llvm/ADT/DenseSet.h" +#include "llvm/IR/Value.h" + +#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." +#endif + +namespace psr { +class LLVMProjectIRDB; +class SVFPointsToInfo; + +struct SVFPointsToInfoTraits { + using v_t = const llvm::Value *; + using n_t = const llvm::Instruction *; + using o_t = uint32_t; + + // TODO: Use a more efficient representation; maybe even one that does not + // require an expensive transformation from SVF::PointsTo + using PointsToSetTy = llvm::SmallDenseSet; + + // No special pointer type + using PointsToSetPtrTy = PointsToSetTy; +}; + +using SVFBasedPointsToInfo = PointsToInfo; +using SVFBasedPointsToInfoRef = PointsToInfoRef; + +/// Use SVF to perform a VersionedFlowSensitive pointer analysis and return the +/// results compatible to psr::PointsToInfo and psr::PointsToInfoRef +[[nodiscard]] SVFBasedPointsToInfo +createSVFVFSPointsToInfo(LLVMProjectIRDB &IRDB); + +/// Use SVF to perform a ContextDDA pointer analysis and return the +/// results compatible to psr::PointsToInfo and psr::PointsToInfoRef +[[nodiscard]] SVFBasedPointsToInfo +createSVFDDAPointsToInfo(LLVMProjectIRDB &IRDB); + +} // namespace psr + +#endif // PHASAR_PHASARLLVM_POINTER_SVF_SVFPOINTSTOSET_H diff --git a/include/phasar/Pointer/AliasAnalysisType.def b/include/phasar/Pointer/AliasAnalysisType.def index 93c3599d7..dda81808c 100644 --- a/include/phasar/Pointer/AliasAnalysisType.def +++ b/include/phasar/Pointer/AliasAnalysisType.def @@ -15,5 +15,7 @@ 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") +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.") #undef ALIAS_ANALYSIS_TYPE diff --git a/include/phasar/Pointer/PointsToInfo.h b/include/phasar/Pointer/PointsToInfo.h index 2eaf0f438..af2f54d58 100644 --- a/include/phasar/Pointer/PointsToInfo.h +++ b/include/phasar/Pointer/PointsToInfo.h @@ -14,10 +14,10 @@ #include "phasar/Utils/ByRef.h" #include +#include #include #include #include -#include namespace psr { @@ -29,7 +29,7 @@ struct PointsToTraits> : PTATraits {}; template struct PointsToTraits> : PTATraits {}; -/// A type-erased reference to any object implementing th PointsToInfoBase +/// A type-erased reference to any object implementing the PointsToInfoBase /// interface. Use this, if your analysis is not tied to a specific points-to /// info implementation. /// @@ -87,7 +87,6 @@ class PointsToInfoRef, ByConstRef); - std::vector (*GetInterestingPointersAt)(const void *, ByConstRef); void (*Destroy)(const void *) noexcept; // Useful for the owning variant }; @@ -120,15 +119,6 @@ class PointsToInfoRef(PT)->getPointsToSet( Pointer, AtInstruction); }, - [](const void *PT, ByConstRef AtInstruction) { - std::vector Ret; - for (ByConstRef Ptr : - static_cast(PT)->getInterestingPointersAt( - AtInstruction)) { - Ret.push_back(Ptr); - } - return Ret; - }, [](const void *PT) noexcept { delete static_cast(PT); }, @@ -164,7 +154,7 @@ class PointsToInfoRef - asPointerOrNull(ByConstRef Obj) const noexcept { + asPointerOrNullImpl(ByConstRef Obj) const noexcept { assert(VT); return VT->AsPointerOrNull(PT, Obj); } @@ -201,18 +191,12 @@ class PointsToInfoRefGetPointsToSetV(PT, Pointer, AtInstruction); } - std::vector - getInterestingPointersAtImpl(ByConstRef AtInstruction) const { - assert(VT); - return VT->GetInterestingPointersAt(PT, AtInstruction); - } - // --- const void *PT{}; const VTable<> *VT{}; }; -/// Similar to PointsToInfoRef, but owns the held reference. Us this, if you +/// Similar to PointsToInfoRef, but owns the held reference. Use this, if you /// need to decide dynamically, which points-to info implementation to use. /// /// Implicitly convertible to PointsToInfoRef. @@ -232,12 +216,13 @@ class [[clang::trivial_abi]] PointsToInfo< PointsToInfo() noexcept = default; 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 { - auto Cpy{std::move(Other)}; - swap(Cpy); + PointsToInfo(std::move(Other)).swap(*this); return *this; } @@ -255,6 +240,10 @@ class [[clang::trivial_abi]] PointsToInfo< : PointsToInfoRef( new ConcretePTA(std::forward(Args)...)) {} + template + PointsToInfo(std::unique_ptr PTA) + : PointsToInfoRef(PTA.release()) {} + ~PointsToInfo() noexcept { if (*this) { this->VT->Destroy(this->PT); diff --git a/include/phasar/Pointer/PointsToInfoBase.h b/include/phasar/Pointer/PointsToInfoBase.h index 9eef7cf3a..0bb22091f 100644 --- a/include/phasar/Pointer/PointsToInfoBase.h +++ b/include/phasar/Pointer/PointsToInfoBase.h @@ -11,6 +11,8 @@ #define PHASAR_POINTER_POINTSTOINFOBASE_H #include "phasar/Utils/ByRef.h" +#include "phasar/Utils/CRTPUtils.h" +#include "phasar/Utils/PointerUtils.h" #include "phasar/Utils/TypeTraits.h" #include @@ -52,7 +54,10 @@ PSR_CONCEPT is_equivalent_PointsToTraits_v = // NOLINT /// Base class of all points-to analysis implementations. Don't use this class /// directly. For a type-erased variant, use PointsToInfoRef or PointsToInfo. -template class PointsToInfoBase { +template class PointsToInfoBase : public CRTPBase { + friend Derived; + using CRTPBase::self; + public: using v_t = typename PointsToTraits::v_t; using n_t = typename PointsToTraits::n_t; @@ -60,12 +65,6 @@ template class PointsToInfoBase { using PointsToSetTy = typename PointsToTraits::PointsToSetTy; using PointsToSetPtrTy = typename PointsToTraits::PointsToSetPtrTy; - explicit PointsToInfoBase() noexcept { - static_assert(std::is_base_of_v, - "Invalid CRTP instantiation: Derived must inherit from " - "PointsToInfoBase!"); - } - /// Creates an abstract object corresponding to the given pointer [[nodiscard]] o_t asAbstractObject(ByConstRef Pointer) const noexcept { return self().asAbstractObjectImpl(Pointer); @@ -102,29 +101,21 @@ template class PointsToInfoBase { return self().getPointsToSetImpl(Pointer, AtInstruction); } - /// Gets all pointers v_t where we have non-empty points-to information at - /// this instruction - [[nodiscard]] decltype(auto) - getInterestingPointersAt(ByConstRef AtInstruction) const { - static_assert( - is_iterable_over_v< - decltype(self().getInterestingPointersAtImpl(AtInstruction)), v_t>); - return self().getInterestingPointersAtImpl(AtInstruction); - } - private: - template >> - [[nodiscard]] bool mayPointsToImpl(ByConstRef Pointer, + [[nodiscard]] bool mayPointsToImpl(ByConstRef Pointer, ByConstRef Obj, ByConstRef AtInstruction) const { - return getPointsToSet(asAbstractObject(Pointer), AtInstruction)->count(Obj); + auto &&Pts = getPointsToSet(Pointer, AtInstruction); + return getPointerFrom(Pts)->count(Obj); } - [[nodiscard]] bool mayPointsToImpl(ByConstRef Pointer, + template >> + [[nodiscard]] bool mayPointsToImpl(ByConstRef Pointer, ByConstRef Obj, ByConstRef AtInstruction) const { - return getPointsToSet(Pointer, AtInstruction)->count(Obj); + return self().mayPointsTo(self().asAbstractObject(Pointer), Obj, + AtInstruction); } template class PointsToInfoBase { ByConstRef AtInstruction) const { return self().getPointsToSetImpl(asAbstractObject(Pointer), AtInstruction); } - - [[nodiscard]] Derived &self() noexcept { - return static_cast(*this); - } - [[nodiscard]] const Derived &self() const noexcept { - return static_cast(*this); - } }; } // namespace psr diff --git a/include/phasar/Utils/CRTPUtils.h b/include/phasar/Utils/CRTPUtils.h new file mode 100644 index 000000000..e66e394e0 --- /dev/null +++ b/include/phasar/Utils/CRTPUtils.h @@ -0,0 +1,32 @@ +/****************************************************************************** + * 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_UTILS_CRTPUTILS_H +#define PHASAR_UTILS_CRTPUTILS_H + +namespace psr { +template class CRTPBase { + friend Derived; + +protected: + constexpr CRTPBase() noexcept = default; + + [[nodiscard]] constexpr Derived &self() & noexcept { + return static_cast(*this); + } + [[nodiscard]] constexpr Derived &&self() && noexcept { + return static_cast(*this); + } + [[nodiscard]] constexpr const Derived &self() const & noexcept { + return static_cast(*this); + } +}; +} // namespace psr + +#endif // PHASAR_UTILS_CRTPUTILS_H diff --git a/include/phasar/Utils/PointerUtils.h b/include/phasar/Utils/PointerUtils.h index 117a5bea9..2023d9d69 100644 --- a/include/phasar/Utils/PointerUtils.h +++ b/include/phasar/Utils/PointerUtils.h @@ -4,25 +4,52 @@ #include "llvm/ADT/IntrusiveRefCntPtr.h" #include +#include namespace psr { /// A simple helper function to get a raw pointer from an arbitrary pointer type /// in generic code. This overload set is extendable. +template +constexpr std::enable_if_t, T *> +getPointerFrom(T &Ref) noexcept { + return std::addressof(Ref); +} +template +constexpr std::enable_if_t, const T *> +getPointerFrom(const T &Ref) noexcept { + return std::addressof(Ref); +} +template +constexpr std::enable_if_t, T *> +getPointerFrom(T &&Ref) noexcept = delete; + template T *getPointerFrom(T *Ptr) noexcept { return Ptr; } template constexpr T *getPointerFrom(const std::unique_ptr &Ptr) noexcept { return Ptr.get(); } template +constexpr T *getPointerFrom(std::unique_ptr &Ptr) noexcept { + return Ptr.get(); +} +template constexpr T *getPointerFrom(const std::shared_ptr &Ptr) noexcept { return Ptr.get(); } template +constexpr T *getPointerFrom(std::shared_ptr &Ptr) noexcept { + return Ptr.get(); +} +template constexpr T *getPointerFrom(const llvm::IntrusiveRefCntPtr &Ptr) noexcept { return Ptr.get(); } +template +constexpr T *getPointerFrom(llvm::IntrusiveRefCntPtr &Ptr) noexcept { + return Ptr.get(); +} } // namespace psr diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 5cb0b04e8..ffe1291f3 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -42,6 +42,9 @@ endif() if(SQLite3_FOUND) list(APPEND PHASAR_LINK_LIBS phasar_db) endif() +if(PHASAR_USE_SVF) + list(APPEND PHASAR_LINK_LIBS phasar_llvm_pointer_svf) +endif() add_phasar_library(phasar ${PHASAR_DYNLIB_KIND} FILES diff --git a/lib/PhasarLLVM/Pointer/AliasAnalysisView.cpp b/lib/PhasarLLVM/Pointer/AliasAnalysisView.cpp new file mode 100644 index 000000000..d4833b8c1 --- /dev/null +++ b/lib/PhasarLLVM/Pointer/AliasAnalysisView.cpp @@ -0,0 +1,34 @@ +#include "phasar/PhasarLLVM/Pointer/AliasAnalysisView.h" + +#include "phasar/Config/phasar-config.h" + +#ifdef PHASAR_USE_SVF +#include "SVF/SVFBasedAliasAnalysis.h" +#endif + +#include + +using namespace psr; + +std::unique_ptr +AliasAnalysisView::create(LLVMProjectIRDB &IRDB, bool UseLazyEvaluation, + AliasAnalysisType PATy) { + switch (PATy) { + 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: + return createLLVMBasedAnalysis(IRDB, UseLazyEvaluation, PATy); + } +} diff --git a/lib/PhasarLLVM/Pointer/CMakeLists.txt b/lib/PhasarLLVM/Pointer/CMakeLists.txt index 35cabf8af..522fd55e5 100644 --- a/lib/PhasarLLVM/Pointer/CMakeLists.txt +++ b/lib/PhasarLLVM/Pointer/CMakeLists.txt @@ -1,4 +1,4 @@ -file(GLOB_RECURSE POINTER_SRC *.h *.cpp) +file(GLOB POINTER_SRC *.cpp) add_phasar_library(phasar_llvm_pointer ${POINTER_SRC} @@ -19,3 +19,10 @@ add_phasar_library(phasar_llvm_pointer LINK_PRIVATE ${Boost_LIBRARIES} ) + +add_subdirectory(external) + +if(PHASAR_USE_SVF) + add_subdirectory(SVF) + target_link_libraries(phasar_llvm_pointer PRIVATE phasar_llvm_pointer_svf) +endif() diff --git a/lib/PhasarLLVM/Pointer/FilteredLLVMAliasSet.cpp b/lib/PhasarLLVM/Pointer/FilteredLLVMAliasSet.cpp index 1eac91310..2766f8e5a 100644 --- a/lib/PhasarLLVM/Pointer/FilteredLLVMAliasSet.cpp +++ b/lib/PhasarLLVM/Pointer/FilteredLLVMAliasSet.cpp @@ -9,6 +9,8 @@ #include "phasar/Utils/NlohmannLogging.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/IR/GlobalAlias.h" +#include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Instructions.h" #include "nlohmann/json_fwd.hpp" diff --git a/lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp b/lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp index 6beca389f..7b216842c 100644 --- a/lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp +++ b/lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp @@ -10,10 +10,12 @@ #include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" +#include "phasar/PhasarLLVM/Pointer/AliasAnalysisView.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include "phasar/PhasarLLVM/Pointer/LLVMPointsToUtils.h" #include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" #include "phasar/Pointer/AliasAnalysisType.h" +#include "phasar/Pointer/AliasResult.h" #include "phasar/Utils/BoxedPointer.h" #include "phasar/Utils/Logger.h" #include "phasar/Utils/NlohmannLogging.h" @@ -45,10 +47,7 @@ #include #include #include -#include -#include #include -#include #include namespace psr { @@ -59,7 +58,7 @@ template class AliasSetOwner; LLVMAliasSet::LLVMAliasSet(LLVMProjectIRDB *IRDB, bool UseLazyEvaluation, AliasAnalysisType PATy) - : PTA(*IRDB, UseLazyEvaluation, PATy) { + : PTA(AliasAnalysisView::create(*IRDB, UseLazyEvaluation, PATy)) { assert(IRDB != nullptr); auto NumGlobals = IRDB->getNumGlobals(); @@ -99,7 +98,7 @@ LLVMAliasSet::LLVMAliasSet(LLVMProjectIRDB *IRDB, bool UseLazyEvaluation, LLVMAliasSet::LLVMAliasSet(LLVMProjectIRDB *IRDB, const nlohmann::json &SerializedPTS) - : PTA(*IRDB, true) { + : PTA(AliasAnalysisView::create(*IRDB, true, AliasAnalysisType::Basic)) { assert(IRDB != nullptr); // Assume, we already have validated the json schema @@ -317,51 +316,13 @@ bool LLVMAliasSet::intraIsReachableAllocationSiteTy( return false; } -static llvm::Type *getPointeeTypeOrNull(const llvm::Value *Ptr) { - assert(Ptr->getType()->isPointerTy()); - - if (!Ptr->getType()->isOpaquePointerTy()) { - return Ptr->getType()->getNonOpaquePointerElementType(); - } - - if (const auto *Arg = llvm::dyn_cast(Ptr)) { - if (auto *Ty = Arg->getParamByValType()) { - return Ty; - } - if (auto *Ty = Arg->getParamStructRetType()) { - return Ty; - } - } - if (const auto *Alloca = llvm::dyn_cast(Ptr)) { - return Alloca->getAllocatedType(); - } - return nullptr; -} - -static bool mayAlias(llvm::AAResults &AA, const llvm::DataLayout &DL, +static bool mayAlias(FunctionAliasView AA, const llvm::DataLayout &DL, const llvm::Value *V, const llvm::Value *Rep) { - assert(V->getType()->isPointerTy()); - assert(Rep->getType()->isPointerTy()); - - auto *ElTy = getPointeeTypeOrNull(V); - auto *RepElTy = getPointeeTypeOrNull(Rep); - - auto VSize = ElTy && ElTy->isSized() - ? llvm::LocationSize::precise(DL.getTypeStoreSize(ElTy)) - : llvm::LocationSize::precise(1); - - auto RepSize = RepElTy && RepElTy->isSized() - ? llvm::LocationSize::precise(DL.getTypeStoreSize(RepElTy)) - : llvm::LocationSize::precise(1); - if (AA.alias(V, VSize, Rep, RepSize) != llvm::AliasResult::NoAlias) { - return true; - } - - return false; + return AA.alias(V, Rep, DL) != AliasResult::NoAlias; } -void LLVMAliasSet::addPointer(llvm::AAResults &AA, const llvm::DataLayout &DL, +void LLVMAliasSet::addPointer(FunctionAliasView AA, const llvm::DataLayout &DL, const llvm::Value *V, std::vector &Reps) { llvm::SmallVector ToMerge; @@ -474,13 +435,13 @@ void LLVMAliasSet::computeFunctionsAliasSet(llvm::Function *F) { PHASAR_LOG_LEVEL_CAT(DEBUG, "LLVMAliasSet", "Analyzing function: " << F->getName()); - llvm::AAResults &AA = *PTA.getAAResults(F); + auto AA = PTA->getAAResults(F); bool EvalAAMD = true; const llvm::DataLayout &DL = F->getParent()->getDataLayout(); - auto addPointer = [this, &AA, &DL](const llvm::Value *V, // NOLINT - std::vector &Reps) { + auto addPointer = [this, AA, &DL](const llvm::Value *V, // NOLINT + std::vector &Reps) { return this->addPointer(AA, DL, V, Reps); }; @@ -567,7 +528,7 @@ void LLVMAliasSet::computeFunctionsAliasSet(llvm::Function *F) { } // we no longer need the LLVM representation - PTA.erase(F); + PTA->erase(F); } AliasResult LLVMAliasSet::alias(const llvm::Value *V1, const llvm::Value *V2, diff --git a/lib/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.cpp b/lib/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.cpp index 4473b19fe..38a79105b 100644 --- a/lib/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.cpp +++ b/lib/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.cpp @@ -7,11 +7,13 @@ * Philipp Schubert and others *****************************************************************************/ -#include "phasar/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h" +#include "LLVMBasedAliasAnalysis.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" +#include "phasar/PhasarLLVM/Pointer/AliasAnalysisView.h" #include "phasar/PhasarLLVM/Pointer/LLVMPointsToUtils.h" #include "phasar/Pointer/AliasAnalysisType.h" +#include "phasar/Pointer/AliasResult.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallVector.h" @@ -38,41 +40,34 @@ using namespace psr; namespace psr { -struct LLVMBasedAliasAnalysis::Impl { - llvm::PassBuilder PB{}; - llvm::FunctionAnalysisManager FAM{}; - llvm::FunctionPassManager FPM{}; -}; - bool LLVMBasedAliasAnalysis::hasAliasInfo(const llvm::Function &Fun) const { return AAInfos.find(&Fun) != AAInfos.end(); } void LLVMBasedAliasAnalysis::computeAliasInfo(llvm::Function &Fun) { - assert(PImpl != nullptr); - llvm::PreservedAnalyses PA = PImpl->FPM.run(Fun, PImpl->FAM); - llvm::AAResults &AAR = PImpl->FAM.getResult(Fun); + llvm::PreservedAnalyses PA = FPM.run(Fun, FAM); + llvm::AAResults &AAR = FAM.getResult(Fun); AAInfos.insert(std::make_pair(&Fun, &AAR)); } -void LLVMBasedAliasAnalysis::erase(llvm::Function *F) noexcept { +void LLVMBasedAliasAnalysis::doErase(llvm::Function *F) noexcept { // after we clear all stuff, we need to set it up for the next function-wise // analysis AAInfos.erase(F); - PImpl->FAM.clear(*F, F->getName()); + FAM.clear(*F, F->getName()); } -void LLVMBasedAliasAnalysis::clear() noexcept { +void LLVMBasedAliasAnalysis::doClear() noexcept { AAInfos.clear(); - PImpl->FAM.clear(); + FAM.clear(); } LLVMBasedAliasAnalysis::LLVMBasedAliasAnalysis(LLVMProjectIRDB &IRDB, bool UseLazyEvaluation, AliasAnalysisType PATy) - : PImpl(new Impl{}), PATy(PATy) { + : AliasAnalysisView(PATy) { - PImpl->FAM.registerPass([&] { + FAM.registerPass([&] { llvm::AAManager AA; switch (PATy) { case AliasAnalysisType::CFLAnders: @@ -95,7 +90,7 @@ LLVMBasedAliasAnalysis::LLVMBasedAliasAnalysis(LLVMProjectIRDB &IRDB, AA.registerFunctionAnalysis(); return AA; }); - PImpl->PB.registerFunctionAnalyses(PImpl->FAM); + PB.registerFunctionAnalyses(FAM); if (!UseLazyEvaluation) { for (auto &F : *IRDB.getModule()) { @@ -108,4 +103,66 @@ LLVMBasedAliasAnalysis::LLVMBasedAliasAnalysis(LLVMProjectIRDB &IRDB, LLVMBasedAliasAnalysis::~LLVMBasedAliasAnalysis() = default; +static AliasResult translateAAResult(llvm::AliasResult Res) noexcept { + switch (Res) { + case llvm::AliasResult::NoAlias: + return AliasResult::NoAlias; + case llvm::AliasResult::MayAlias: + return AliasResult::MayAlias; + case llvm::AliasResult::PartialAlias: + return AliasResult::PartialAlias; + case llvm::AliasResult::MustAlias: + return AliasResult::MustAlias; + } +} + +static llvm::Type *getPointeeTypeOrNull(const llvm::Value *Ptr) { + assert(Ptr->getType()->isPointerTy()); + + if (!Ptr->getType()->isOpaquePointerTy()) { + return Ptr->getType()->getNonOpaquePointerElementType(); + } + + if (const auto *Arg = llvm::dyn_cast(Ptr)) { + if (auto *Ty = Arg->getParamByValType()) { + return Ty; + } + if (auto *Ty = Arg->getParamStructRetType()) { + return Ty; + } + } + if (const auto *Alloca = llvm::dyn_cast(Ptr)) { + return Alloca->getAllocatedType(); + } + return nullptr; +} + +AliasResult LLVMBasedAliasAnalysis::aliasImpl(llvm::AAResults *AA, + const llvm::Value *V, + const llvm::Value *Rep, + const llvm::DataLayout &DL) { + + assert(V->getType()->isPointerTy()); + assert(Rep->getType()->isPointerTy()); + + auto *ElTy = getPointeeTypeOrNull(V); + auto *RepElTy = getPointeeTypeOrNull(Rep); + + auto VSize = ElTy && ElTy->isSized() + ? llvm::LocationSize::precise(DL.getTypeStoreSize(ElTy)) + : llvm::LocationSize::precise(1); + + auto RepSize = RepElTy && RepElTy->isSized() + ? llvm::LocationSize::precise(DL.getTypeStoreSize(RepElTy)) + : llvm::LocationSize::precise(1); + + return translateAAResult(AA->alias(V, VSize, Rep, RepSize)); +} + +std::unique_ptr AliasAnalysisView::createLLVMBasedAnalysis( + LLVMProjectIRDB &IRDB, bool UseLazyEvaluation, AliasAnalysisType PATy) { + return std::make_unique(IRDB, UseLazyEvaluation, + PATy); +} + } // namespace psr diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h b/lib/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h similarity index 55% rename from include/phasar/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h rename to lib/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h index 1195da6c8..071def013 100644 --- a/include/phasar/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h +++ b/lib/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h @@ -10,9 +10,12 @@ #ifndef PHASAR_PHASARLLVM_POINTER_LLVMBASEDALIASANALYSIS_H_ #define PHASAR_PHASARLLVM_POINTER_LLVMBASEDALIASANALYSIS_H_ +#include "phasar/PhasarLLVM/Pointer/AliasAnalysisView.h" #include "phasar/Pointer/AliasAnalysisType.h" +#include "phasar/Pointer/AliasResult.h" #include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/Passes/PassBuilder.h" namespace llvm { class Value; @@ -25,47 +28,42 @@ namespace psr { class LLVMProjectIRDB; -class LLVMBasedAliasAnalysis { - +class LLVMBasedAliasAnalysis : public AliasAnalysisView { public: explicit LLVMBasedAliasAnalysis( LLVMProjectIRDB &IRDB, bool UseLazyEvaluation, AliasAnalysisType PATy = AliasAnalysisType::Basic); - LLVMBasedAliasAnalysis(LLVMBasedAliasAnalysis &&) noexcept = default; - LLVMBasedAliasAnalysis & - operator=(LLVMBasedAliasAnalysis &&) noexcept = default; - - LLVMBasedAliasAnalysis(const LLVMBasedAliasAnalysis &) = delete; - LLVMBasedAliasAnalysis &operator=(const LLVMBasedAliasAnalysis &) = delete; - ~LLVMBasedAliasAnalysis(); + ~LLVMBasedAliasAnalysis() override; - [[nodiscard]] inline llvm::AAResults *getAAResults(llvm::Function *F) { +private: + FunctionAliasView doGetAAResults(const llvm::Function *F) override { if (!hasAliasInfo(*F)) { - computeAliasInfo(*F); + // NOLINTNEXTLINE - FIXME when it is fixed in LLVM + computeAliasInfo(const_cast(*F)); } - return AAInfos.lookup(F); + return createFAView(AAInfos.lookup(F)); }; - void erase(llvm::Function *F) noexcept; + void doErase(llvm::Function *F) noexcept override; - void clear() noexcept; + void doClear() noexcept override; - [[nodiscard]] inline AliasAnalysisType - getPointerAnalysisType() const noexcept { - return PATy; - }; + static AliasResult aliasImpl(llvm::AAResults *, const llvm::Value *, + const llvm::Value *, const llvm::DataLayout &); + [[nodiscard]] constexpr FunctionAliasView + createFAView(llvm::AAResults *AAR) noexcept { + return PSR_BIND_ALIASVIEW(AAR, aliasImpl); + } -private: [[nodiscard]] bool hasAliasInfo(const llvm::Function &Fun) const; void computeAliasInfo(llvm::Function &Fun); // -- data members - - struct Impl; - std::unique_ptr PImpl; - AliasAnalysisType PATy; + llvm::PassBuilder PB; + llvm::FunctionAnalysisManager FAM; + llvm::FunctionPassManager FPM; llvm::DenseMap AAInfos; }; diff --git a/lib/PhasarLLVM/Pointer/SVF/CMakeLists.txt b/lib/PhasarLLVM/Pointer/SVF/CMakeLists.txt new file mode 100644 index 000000000..ec59ba7f2 --- /dev/null +++ b/lib/PhasarLLVM/Pointer/SVF/CMakeLists.txt @@ -0,0 +1,24 @@ +file(GLOB_RECURSE SVF_AA_SRC *.h *.cpp) + +add_phasar_library(phasar_llvm_pointer_svf + ${SVF_AA_SRC} + + LINKS + phasar_utils + phasar_pointer + phasar_llvm_utils + phasar_llvm_db + + LLVM_LINK_COMPONENTS + Core + Support + Analysis + Passes + Demangle + + LINK_PRIVATE + SvfLLVM SvfCore ${Z3_LIBRARIES} +) + +target_include_directories(phasar_llvm_pointer_svf SYSTEM PRIVATE ${SVF_INSTALL_INCLUDE_DIR}) +target_link_directories(phasar_llvm_pointer_svf PUBLIC ${SVF_INSTALL_LIB_DIR}) diff --git a/lib/PhasarLLVM/Pointer/SVF/InitSVF.cpp b/lib/PhasarLLVM/Pointer/SVF/InitSVF.cpp new file mode 100644 index 000000000..32376f4ae --- /dev/null +++ b/lib/PhasarLLVM/Pointer/SVF/InitSVF.cpp @@ -0,0 +1,46 @@ +#include "InitSVF.h" + +#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" +#include "phasar/Utils/EmptyBaseOptimizationUtils.h" + +#include "SVF-LLVM/LLVMModule.h" +#include "Util/CommandLine.h" +#include "Util/DPItem.h" +#include "Util/Options.h" + +static psr::EmptyType initializeSVFImpl() { + char EmptyStr[] = ""; + char NoAliasCheck[] = "-alias-check=false"; + char NoStat[] = "-stat=false"; + char *MockArgv[] = { + EmptyStr, + NoAliasCheck, + NoStat, + }; + OptionBase::parseOptions(std::size(MockArgv), MockArgv, "", ""); + + // Initialize these parameters to their default values. + // See https://github.com/SVF-tools/SVF/blob/master/svf/lib/DDA/DDAPass.cpp + // for reference + SVF::ContextCond::setMaxCxtLen(SVF::Options::MaxContextLen()); + SVF::ContextCond::setMaxPathLen(SVF::Options::MaxPathLen()); + + return psr::EmptyType{}; +} + +void psr::initializeSVF() { + static const auto SVFInitialized = initializeSVFImpl(); + (void)SVFInitialized; +} + +SVF::SVFModule *psr::initSVFModule(psr::LLVMProjectIRDB &IRDB) { + psr::initializeSVF(); + + auto *Mod = SVF::LLVMModuleSet::buildSVFModule(*IRDB.getModule()); + if (!Mod) { + throw std::runtime_error( + "SVF failed to create an SVFModule from an llvm::Module!"); + } + + return Mod; +} diff --git a/lib/PhasarLLVM/Pointer/SVF/InitSVF.h b/lib/PhasarLLVM/Pointer/SVF/InitSVF.h new file mode 100644 index 000000000..2599a3626 --- /dev/null +++ b/lib/PhasarLLVM/Pointer/SVF/InitSVF.h @@ -0,0 +1,15 @@ +#pragma once + +#include "llvm/Support/Compiler.h" + +namespace SVF { +class SVFModule; +} // namespace SVF + +namespace psr { +class LLVMProjectIRDB; + +LLVM_LIBRARY_VISIBILITY void initializeSVF(); +LLVM_LIBRARY_VISIBILITY SVF::SVFModule * +initSVFModule(psr::LLVMProjectIRDB &IRDB); +} // namespace psr diff --git a/lib/PhasarLLVM/Pointer/SVF/SVFBasedAliasAnalysis.cpp b/lib/PhasarLLVM/Pointer/SVF/SVFBasedAliasAnalysis.cpp new file mode 100644 index 000000000..aae43a439 --- /dev/null +++ b/lib/PhasarLLVM/Pointer/SVF/SVFBasedAliasAnalysis.cpp @@ -0,0 +1,122 @@ +#include "SVFBasedAliasAnalysis.h" + +#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" +#include "phasar/PhasarLLVM/Pointer/AliasAnalysisView.h" +#include "phasar/Pointer/AliasAnalysisType.h" +#include "phasar/Pointer/AliasResult.h" + +#include "DDA/ContextDDA.h" +#include "DDA/DDAClient.h" +#include "InitSVF.h" +#include "SVF-LLVM/SVFIRBuilder.h" +#include "SVFIR/SVFIR.h" +#include "SVFIR/SVFModule.h" +#include "SVFIR/SVFType.h" +#include "WPA/Andersen.h" +#include "WPA/VersionedFlowSensitive.h" + +#include +#include + +namespace psr { +static constexpr psr::AliasResult +translateSVFAliasResult(SVF::AliasResult AR) noexcept { + switch (AR) { + case SVF::NoAlias: + return AliasResult::NoAlias; + case SVF::MayAlias: + return AliasResult::MayAlias; + case SVF::MustAlias: + return AliasResult::MustAlias; + case SVF::PartialAlias: + return AliasResult::PartialAlias; + } +} + +static psr::AliasResult aliasImpl(SVF::PointerAnalysis *AA, + const llvm::Value *V, const llvm::Value *Rep, + const llvm::DataLayout & /*DL*/) { + auto *ModSet = SVF::LLVMModuleSet::getLLVMModuleSet(); + auto *Nod1 = ModSet->getSVFValue(V); + auto *Nod2 = ModSet->getSVFValue(Rep); + + if (!Nod1 || !Nod2) { + return AliasResult::MayAlias; + } + + return translateSVFAliasResult(AA->alias(Nod1, Nod2)); +} + +// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions) +class SVFAliasAnalysisBase : public AliasAnalysisView { +public: + SVFAliasAnalysisBase(SVF::SVFModule *Mod, AliasAnalysisType PATy) + : AliasAnalysisView(PATy), IRBuilder(Mod), PAG(IRBuilder.build()) {} + + ~SVFAliasAnalysisBase() override { + SVF::SVFIR::releaseSVFIR(); + SVF::AndersenWaveDiff::releaseAndersenWaveDiff(); + SVF::SymbolTableInfo::releaseSymbolInfo(); + SVF::LLVMModuleSet::releaseLLVMModuleSet(); + } + +private: + void doErase(llvm::Function *F) noexcept override {} + void doClear() noexcept override {} + +protected: + SVF::SVFIRBuilder IRBuilder; + SVF::SVFIR *PAG; +}; + +class SVFVFSAnalysis : public SVFAliasAnalysisBase { +public: + SVFVFSAnalysis(SVF::SVFModule *Mod) + : SVFAliasAnalysisBase(Mod, AliasAnalysisType::SVFVFS), + // Note: We must use the static createVFSWPA() function, otherwise SVF + // will leak memory + VFS(SVF::VersionedFlowSensitive::createVFSWPA(PAG)) {} + + ~SVFVFSAnalysis() override { SVF::VersionedFlowSensitive::releaseVFSWPA(); } + +private: + FunctionAliasView doGetAAResults(const llvm::Function * /*F*/) override { + return PSR_BIND_ALIASVIEW(VFS, aliasImpl); + } + + SVF::VersionedFlowSensitive *VFS; +}; + +class SVFDDAAnalysis : public SVFAliasAnalysisBase { +public: + SVFDDAAnalysis(SVF::SVFModule *Mod) + : SVFAliasAnalysisBase(Mod, AliasAnalysisType::SVFVFS), Client(Mod) { + Client.initialise(Mod); + DDA.emplace(PAG, &Client); + DDA->initialize(); + Client.answerQueries(&*DDA); + DDA->finalize(); + } + +private: + FunctionAliasView doGetAAResults(const llvm::Function * /*F*/) override { + return PSR_BIND_ALIASVIEW(&*DDA, aliasImpl); + } + + SVF::DDAClient Client; + std::optional DDA; +}; + +} // namespace psr + +[[nodiscard]] auto psr::createSVFVFSAnalysis(LLVMProjectIRDB &IRDB) + -> std::unique_ptr { + + return std::make_unique(psr::initSVFModule(IRDB)); +} + +[[nodiscard]] auto psr::createSVFDDAAnalysis(LLVMProjectIRDB &IRDB) + -> std::unique_ptr { + + return std::make_unique(psr::initSVFModule(IRDB)); +} diff --git a/lib/PhasarLLVM/Pointer/SVF/SVFBasedAliasAnalysis.h b/lib/PhasarLLVM/Pointer/SVF/SVFBasedAliasAnalysis.h new file mode 100644 index 000000000..ec5955a0d --- /dev/null +++ b/lib/PhasarLLVM/Pointer/SVF/SVFBasedAliasAnalysis.h @@ -0,0 +1,25 @@ +/****************************************************************************** + * 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_SVFBASEDALIASANALYSIS_H +#define PHASAR_PHASARLLVM_POINTER_SVFBASEDALIASANALYSIS_H + +#include "phasar/PhasarLLVM/Pointer/AliasAnalysisView.h" + +#include + +namespace psr { +[[nodiscard]] std::unique_ptr +createSVFVFSAnalysis(LLVMProjectIRDB &IRDB); + +[[nodiscard]] std::unique_ptr +createSVFDDAAnalysis(LLVMProjectIRDB &IRDB); +} // namespace psr + +#endif // PHASAR_PHASARLLVM_POINTER_SVFBASEDALIASANALYSIS_H diff --git a/lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp b/lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp new file mode 100644 index 000000000..1aa574440 --- /dev/null +++ b/lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp @@ -0,0 +1,153 @@ +#include "phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h" + +#include "phasar/Pointer/PointsToInfoBase.h" + +#include "DDA/ContextDDA.h" +#include "DDA/DDAClient.h" +#include "InitSVF.h" +#include "MemoryModel/PointerAnalysis.h" +#include "SVF-LLVM/LLVMModule.h" +#include "SVF-LLVM/SVFIRBuilder.h" +#include "WPA/Andersen.h" + +#include + +namespace { +template class SVFPointsToSet; +struct DDAPointsToSetImpl; +struct VFSPointsToSetImpl; +} // namespace + +namespace psr { +template +struct PointsToTraits> : SVFPointsToInfoTraits {}; +template <> +struct PointsToTraits : SVFPointsToInfoTraits {}; +template <> +struct PointsToTraits : SVFPointsToInfoTraits {}; +} // namespace psr + +namespace { + +template +// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions) +class SVFPointsToSet : public psr::PointsToInfoBase> { + using base_t = psr::PointsToInfoBase>; + friend base_t; + +public: + using typename base_t::n_t; + using typename base_t::o_t; + using typename base_t::PointsToSetPtrTy; + using typename base_t::PointsToSetTy; + using typename base_t::v_t; + + ~SVFPointsToSet() { + SVF::SVFIR::releaseSVFIR(); + SVF::AndersenWaveDiff::releaseAndersenWaveDiff(); + SVF::SymbolTableInfo::releaseSymbolInfo(); + SVF::LLVMModuleSet::releaseLLVMModuleSet(); + } + +private: + SVFPointsToSet(SVF::SVFModule *Mod) + : IRBuilder(Mod), PAG(IRBuilder.build()) {} + + [[nodiscard]] constexpr Derived &self() noexcept { + return static_cast(*this); + } + [[nodiscard]] constexpr const Derived &self() const noexcept { + return static_cast(*this); + } + + [[nodiscard]] o_t + asAbstractObjectImpl(psr::ByConstRef Pointer) const noexcept { + auto *ModSet = SVF::LLVMModuleSet::getLLVMModuleSet(); + auto *Nod = ModSet->getSVFValue(Pointer); + + return PAG->getValueNode(Nod); + } + + [[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; + } + } + + return std::nullopt; + } + + bool mayPointsToImpl(o_t Pointer, o_t Obj, n_t /*AtInstruction*/) const { + auto &PTA = self().getPTA(); + const auto &Pts = PTA.getPts(Pointer); + return Pts.test(Obj); + } + + using base_t::mayPointsToImpl; + + PointsToSetPtrTy getPointsToSetImpl(o_t Pointer, + n_t /*AtInstruction*/) const { + auto &PTA = self().getPTA(); + const auto &Pts = PTA.getPts(Pointer); + + // TODO: Should we cache this? + PointsToSetPtrTy Ret; + Ret.reserve(Pts.count()); + Ret.insert(Pts.begin(), Pts.end()); + return Ret; + } + + using base_t::getPointsToSetImpl; + +protected: + SVF::SVFIRBuilder IRBuilder; + SVF::SVFIR *PAG; + friend Derived; +}; + +struct VFSPointsToSetImpl : SVFPointsToSet { + VFSPointsToSetImpl(SVF::SVFModule *Mod) + : SVFPointsToSet(Mod), + // Note: We must use the static createVFSWPA() function, otherwise SVF + // will leak memory + VFS(SVF::VersionedFlowSensitive::createVFSWPA(PAG)) {} + + ~VFSPointsToSetImpl() { SVF::VersionedFlowSensitive::releaseVFSWPA(); } + + [[nodiscard]] SVF::PointerAnalysis &getPTA() const noexcept { return *VFS; } + + SVF::VersionedFlowSensitive *VFS; +}; + +struct DDAPointsToSetImpl : SVFPointsToSet { + DDAPointsToSetImpl(SVF::SVFModule *Mod) : SVFPointsToSet(Mod), Client(Mod) { + Client.initialise(Mod); + DDA.emplace(PAG, &Client); + DDA->initialize(); + Client.answerQueries(&*DDA); + DDA->finalize(); + } + + [[nodiscard]] SVF::PointerAnalysis &getPTA() const noexcept { return *DDA; } + + SVF::DDAClient Client; + + // Note: SVF is not thread-safe anyway, so this 'mutable' should not be a + // problem + mutable std::optional DDA; +}; +} // namespace + +auto psr::createSVFVFSPointsToInfo(LLVMProjectIRDB &IRDB) + -> SVFBasedPointsToInfo { + return SVFBasedPointsToInfo(std::in_place_type, + psr::initSVFModule(IRDB)); +} + +auto psr::createSVFDDAPointsToInfo(LLVMProjectIRDB &IRDB) + -> SVFBasedPointsToInfo { + return SVFBasedPointsToInfo(std::in_place_type, + psr::initSVFModule(IRDB)); +} diff --git a/lib/PhasarLLVM/Pointer/external/CMakeLists.txt b/lib/PhasarLLVM/Pointer/external/CMakeLists.txt new file mode 100644 index 000000000..185b5103e --- /dev/null +++ b/lib/PhasarLLVM/Pointer/external/CMakeLists.txt @@ -0,0 +1,2 @@ +file(GLOB_RECURSE LLVM_AA_SRC *.h *.cpp) +target_sources(phasar_llvm_pointer PRIVATE ${LLVM_AA_SRC}) diff --git a/lib/Pointer/PointsToInfo.cpp b/lib/Pointer/PointsToInfo.cpp index df3079fce..ed3ca3b48 100644 --- a/lib/Pointer/PointsToInfo.cpp +++ b/lib/Pointer/PointsToInfo.cpp @@ -11,6 +11,7 @@ #include "phasar/Pointer/PointsToInfoBase.h" #include "phasar/Utils/ByRef.h" +#include "phasar/Utils/PointerUtils.h" #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/DenseSet.h" @@ -60,11 +61,6 @@ class DummyFieldInsensitivePointsToAnalysis static PointsToSetTy Empty{}; return &Empty; } - - [[nodiscard]] std::vector - getInterestingPointersAtImpl(ByConstRef /*AtInstruction*/) const { - return {}; - } }; template class PointsToInfoBase; @@ -129,23 +125,16 @@ class DummyFieldSensitivePointsToAnalysis static PointsToSetTy Empty{}; return &Empty; } - - [[nodiscard]] std::vector - getInterestingPointersAtImpl(ByConstRef /*AtInstruction*/) const { - return {}; - } }; [[maybe_unused]] void testTypeErasure() { DummyFieldInsensitivePointsToAnalysis PTA1; [[maybe_unused]] PointsToInfoRef< - PointsToTraits> - TEPTA1 = &PTA1; + PointsToTraits> TEPTA1 = &PTA1; DummyFieldSensitivePointsToAnalysis PTA2; [[maybe_unused]] PointsToInfoRef< - PointsToTraits> - TEPTA2 = &PTA2; + PointsToTraits> TEPTA2 = &PTA2; PointsToInfo> TEPTA3( std::in_place_type); @@ -154,7 +143,7 @@ class DummyFieldSensitivePointsToAnalysis std::in_place_type); // Make sure, the template gets instantiated: - std::ignore = TEPTA1.getInterestingPointersAt(nullptr); + std::ignore = TEPTA1.getPointsToSet({}, nullptr); } template class PointsToInfoBase; diff --git a/unittests/PhasarLLVM/Pointer/CMakeLists.txt b/unittests/PhasarLLVM/Pointer/CMakeLists.txt index 8cf7152ad..290062d69 100644 --- a/unittests/PhasarLLVM/Pointer/CMakeLists.txt +++ b/unittests/PhasarLLVM/Pointer/CMakeLists.txt @@ -1,9 +1,17 @@ -set(ControlFlowSources +set(PointerFlowSources LLVMAliasSetTest.cpp LLVMAliasSetSerializationTest.cpp FilteredLLVMAliasSetTest.cpp ) -foreach(TEST_SRC ${ControlFlowSources}) +if (PHASAR_USE_SVF) + list(APPEND PointerFlowSources SVFAliasSetTest.cpp) +endif() + +foreach(TEST_SRC ${PointerFlowSources}) add_phasar_unittest(${TEST_SRC}) endforeach(TEST_SRC) + +if(PHASAR_USE_SVF) + target_link_libraries(SVFAliasSetTest PRIVATE phasar_llvm_pointer_svf) +endif() diff --git a/unittests/PhasarLLVM/Pointer/SVFAliasSetTest.cpp b/unittests/PhasarLLVM/Pointer/SVFAliasSetTest.cpp new file mode 100644 index 000000000..4482d6071 --- /dev/null +++ b/unittests/PhasarLLVM/Pointer/SVFAliasSetTest.cpp @@ -0,0 +1,92 @@ + +#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" +#include "phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h" +#include "phasar/Pointer/AliasAnalysisType.h" +#include "phasar/Pointer/AliasResult.h" + +#include "TestConfig.h" +#include "gtest/gtest.h" + +using namespace psr; + +TEST(SVFAliasSetTest, Alias_01) { + LLVMProjectIRDB IRDB(unittest::PathToLLTestFiles + + "pointers/basic_01_cpp_dbg.ll"); + + LLVMAliasSet AS(&IRDB, false, AliasAnalysisType::SVFVFS); + + const auto *V = IRDB.getInstruction(5); + ASSERT_TRUE(V && V->getType()->isPointerTy()); + + const auto *Alias = IRDB.getInstruction(0); + + auto ASet = AS.getAliasSet(V); + LLVMAliasSet::AliasSetTy GroundTruth = {V, Alias}; + EXPECT_EQ(GroundTruth, *ASet); + EXPECT_NE(AliasResult::NoAlias, AS.alias(V, Alias)); +} + +TEST(SVFAliasSetTest, Alias_02) { + LLVMProjectIRDB IRDB(unittest::PathToLLTestFiles + + "pointers/basic_01_cpp_dbg.ll"); + + LLVMAliasSet AS(&IRDB, false, AliasAnalysisType::SVFDDA); + + const auto *V = IRDB.getInstruction(5); + ASSERT_TRUE(V && V->getType()->isPointerTy()); + + const auto *Alias = IRDB.getInstruction(0); + + auto ASet = AS.getAliasSet(V); + LLVMAliasSet::AliasSetTy GroundTruth = {V, Alias}; + EXPECT_EQ(GroundTruth, *ASet); + EXPECT_NE(AliasResult::NoAlias, AS.alias(V, Alias)); +} + +TEST(SVFAliasSetTest, PointsTo_01) { + LLVMProjectIRDB IRDB(unittest::PathToLLTestFiles + + "pointers/basic_01_cpp_dbg.ll"); + + auto PT = createSVFVFSPointsToInfo(IRDB); + + const auto *V = IRDB.getInstruction(5); + ASSERT_TRUE(V && V->getType()->isPointerTy()); + + const auto *Alloc = IRDB.getInstruction(0); + + auto PSet = PT.getPointsToSet(V, V->getNextNode()); + ASSERT_EQ(1, PSet.size()); + EXPECT_EQ(Alloc, PT.asPointerOrNull(*PSet.begin())); + + auto AllocObjs = PT.getPointsToSet(Alloc, Alloc->getNextNode()); + ASSERT_EQ(1, AllocObjs.size()); + auto AllocObj = *AllocObjs.begin(); + EXPECT_TRUE(PT.mayPointsTo(V, AllocObj, V->getNextNode())); +} + +TEST(SVFAliasSetTest, PointsTo_02) { + LLVMProjectIRDB IRDB(unittest::PathToLLTestFiles + + "pointers/basic_01_cpp_dbg.ll"); + + auto PT = createSVFDDAPointsToInfo(IRDB); + + const auto *V = IRDB.getInstruction(5); + ASSERT_TRUE(V && V->getType()->isPointerTy()); + + const auto *Alloc = IRDB.getInstruction(0); + + auto PSet = PT.getPointsToSet(V, V->getNextNode()); + ASSERT_EQ(1, PSet.size()); + EXPECT_EQ(Alloc, PT.asPointerOrNull(*PSet.begin())); + + auto AllocObjs = PT.getPointsToSet(Alloc, Alloc->getNextNode()); + ASSERT_EQ(1, AllocObjs.size()); + 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(); +}