diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb040a7d53..7cf419c0e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,7 +51,7 @@ jobs: - name: ccache # This should always come after the actions/checkout step. uses: hendrikmuhs/ccache-action@v1.2 with: - key: ${{ github.job }}-${{ matrix.os }}-${{ matrix.build }} + key: ${{ github.job }}-${{ matrix.os }}-llvm${{ matrix.llvm-version }}-${{ matrix.build }} - name: Install Phasar Dependencies shell: bash diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index f17f92e0e4..600be9314a 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -8,6 +8,7 @@ permissions: contents: write jobs: build-and-deploy: + if: github.repository == 'secure-software-engineering/phasar' runs-on: ubuntu-24.04 strategy: fail-fast: true diff --git a/BreakingChanges.md b/BreakingChanges.md index 31cccf68cc..08d3388003 100644 --- a/BreakingChanges.md +++ b/BreakingChanges.md @@ -2,6 +2,8 @@ ## development HEAD +- The function `HelperAnalyses::getAliasInfo()` no longer returns a `LLVMAliasSet &`, but a `LLVMAliasInfoRef`. +- The location of the library summary `FunctionDataFlowFacts` and `LLVMFunctionDataFlowFacts` has moved to `phasar/Utils/` and `phasar/PhasarLLVM/Utils`, respectively. - `IDESolver::initialize()` does no longer return a `bool`. Now, you are always allowed to call `next()` at least once. - `IntraMonoProblem` and `InterMonoProblem`, and all reference-implementations of these problems do not receive a TypeHierarchy-pointer anymore in the ctor. - Requiring C++20 instead of C++17 diff --git a/CMakeLists.txt b/CMakeLists.txt index c6a7b1c9f4..1610e9ad57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -410,7 +410,7 @@ if (PHASAR_ENABLE_WARNINGS) if (MSVC) string(APPEND CMAKE_CXX_FLAGS " /W4") else() - string(APPEND CMAKE_CXX_FLAGS " -Wall -Wextra -Wno-unused-parameter") + string(APPEND CMAKE_CXX_FLAGS " -Wall -Wextra -Wno-unused-parameter -Werror=return-type") endif() endif (PHASAR_ENABLE_WARNINGS) diff --git a/include/phasar/ControlFlow/CallGraphBase.h b/include/phasar/ControlFlow/CallGraphBase.h index 796a31c286..2d39581e3c 100644 --- a/include/phasar/ControlFlow/CallGraphBase.h +++ b/include/phasar/ControlFlow/CallGraphBase.h @@ -12,8 +12,14 @@ #include "phasar/Utils/ByRef.h" #include "phasar/Utils/CRTPUtils.h" +#include "phasar/Utils/Compressor.h" +#include "phasar/Utils/GraphTraits.h" +#include "phasar/Utils/IotaIterator.h" +#include "phasar/Utils/NonNullPtr.h" #include "phasar/Utils/TypeTraits.h" +#include "llvm/ADT/STLExtras.h" + #include #include @@ -127,6 +133,92 @@ template class CallGraphBase : public CRTPBase { [[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; } }; + +/// A view over a call-graph that implements psr::GraphTraits on the +/// getCallersOf() relation. +/// Useful to compute CG-SCCs. +template class ReverseCGGraph { + static constexpr bool NeedsMapping = !IdType; + + enum class FunctionIdImpl : uint32_t {}; + +public: + using FunctionId = std::conditional_t; + + constexpr ReverseCGGraph( + NonNullPtr CGView, NonNullPtr IRDB, + Compressor FC) noexcept + requires(NeedsMapping) + : CGView(CGView), IRDB(IRDB), FC(std::move(FC)) {} + + constexpr ReverseCGGraph(NonNullPtr CGView, + NonNullPtr IRDB) noexcept + : CGView(CGView), IRDB(IRDB) { + FC.reserve(CGView->getNumVertexFunctions()); + for (const auto &Fun : CGView->getAllVertexFunctions()) { + FC.insert(Fun); + } + } + + NonNullPtr CGView; + NonNullPtr IRDB; + [[no_unique_address]] std::conditional_t< + NeedsMapping, Compressor, + NoneCompressor> FC{}; +}; + +template +ReverseCGGraph(NonNullPtr, NonNullPtr) + -> ReverseCGGraph; +template +ReverseCGGraph(const CallGraphTy *, const DB *) + -> ReverseCGGraph; + +template +struct GraphTraits> { + using graph_type = ReverseCGGraph; + using value_type = typename CallGraphTy::f_t; + using vertex_t = typename graph_type::FunctionId; + using edge_t = vertex_t; + + static constexpr vertex_t Invalid = vertex_t(UINT32_MAX); + + static constexpr auto mapToFunction(const graph_type &G) { + return [&G](ByConstRef Inst) { + const auto &Fun = G.IRDB->getFunctionOf(Inst); + return G.FC.getOrNull(Fun).value(); + }; + } + + static constexpr auto outEdges(const graph_type &G, vertex_t Vtx) { + return llvm::map_range(G.CGView->getCallersOf(G.FC[Vtx]), mapToFunction(G)); + } + + static constexpr decltype(auto) nodes(const graph_type &G) { + return G.CGView->getAllVertexFunctions(); + } + + // TODO: Roots + + static constexpr auto vertices(const graph_type &G) { + return iota(G.CGView->getNumVertexFunctions()); + } + + static constexpr decltype(auto) node(const graph_type &G, vertex_t Vtx) { + return G.FC[Vtx]; + } + + static constexpr size_t size(const graph_type &G) { + return G.CGView->getNumVertexFunctions(); + } + + static constexpr vertex_t target(edge_t Edge) { return Edge; } + static constexpr vertex_t withEdgeTarget(edge_t /*Edge*/, vertex_t Vtx) { + return Vtx; + } +}; + } // namespace psr #endif // PHASAR_CONTROLFLOW_CALLGRAPHBASE_H diff --git a/include/phasar/DB/ProjectIRDBBase.h b/include/phasar/DB/ProjectIRDBBase.h index c30fffd342..0fccf84f0d 100644 --- a/include/phasar/DB/ProjectIRDBBase.h +++ b/include/phasar/DB/ProjectIRDBBase.h @@ -10,6 +10,7 @@ #ifndef PHASAR_DB_PROJECTIRDBBASE_H #define PHASAR_DB_PROJECTIRDBBASE_H +#include "phasar/Utils/ByRef.h" #include "phasar/Utils/Nullable.h" #include "phasar/Utils/TypeTraits.h" @@ -84,6 +85,13 @@ class LLVM_DEPRECATED( return self().hasFunctionImpl(FunctionName); } + /// Returns the function that contains the given instruction Inst. + /// Each instruction must be part of a function. + [[nodiscard]] f_t getFunctionOf(ByConstRef Inst) const { + assert(isValid()); + return self().getFunctionOfImpl(Inst); + } + /// Returns the global variable if available, nullptr/nullopt /// otherwise. [[nodiscard]] Nullable diff --git a/include/phasar/DataFlow/IfdsIde/Solver/Compressor.h b/include/phasar/DataFlow/IfdsIde/Solver/Compressor.h index 7479bbd0c9..98272a3b1e 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/Compressor.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/Compressor.h @@ -9,27 +9,6 @@ namespace psr { -struct NoneCompressor final { - constexpr NoneCompressor() noexcept = default; - - template - requires(!std::is_same_v) - constexpr NoneCompressor(const T & /*unused*/) noexcept {} - - template - [[nodiscard]] decltype(auto) getOrInsert(T &&Val) const noexcept { - return std::forward(Val); - } - template - [[nodiscard]] decltype(auto) operator[](T &&Val) const noexcept { - return std::forward(Val); - } - void reserve(size_t /*unused*/) const noexcept {} - - [[nodiscard]] size_t size() const noexcept { return 0; } - [[nodiscard]] size_t capacity() const noexcept { return 0; } -}; - class LLVMProjectIRDB; /// Once we have fast instruction IDs (as we already have in IntelliSecPhasar), diff --git a/include/phasar/PhasarLLVM/ControlFlow/VTA/TypePropagator.h b/include/phasar/PhasarLLVM/ControlFlow/VTA/TypePropagator.h index 2e6d6b874d..b6b12091b0 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/VTA/TypePropagator.h +++ b/include/phasar/PhasarLLVM/ControlFlow/VTA/TypePropagator.h @@ -11,6 +11,7 @@ #define PHASAR_PHASARLLVM_CONTROLFLOW_TYPEPROPAGATOR_H #include "phasar/PhasarLLVM/ControlFlow/VTA/TypeAssignmentGraph.h" +#include "phasar/Utils/SCCId.h" #include "phasar/Utils/TypedVector.h" #include "llvm/ADT/DenseSet.h" @@ -22,7 +23,6 @@ class Value; } // namespace llvm namespace psr { -template struct SCCId; template struct SCCHolder; template struct SCCDependencyGraph; template struct SCCOrder; diff --git a/include/phasar/PhasarLLVM/DB/LLVMProjectIRDB.h b/include/phasar/PhasarLLVM/DB/LLVMProjectIRDB.h index 13c3f1f5d5..337ac8ce64 100644 --- a/include/phasar/PhasarLLVM/DB/LLVMProjectIRDB.h +++ b/include/phasar/PhasarLLVM/DB/LLVMProjectIRDB.h @@ -189,6 +189,11 @@ class LLVMProjectIRDB : public ProjectIRDBBase { hasFunctionImpl(llvm::StringRef FunctionName) const noexcept { return Mod->getFunction(FunctionName) != nullptr; } + [[nodiscard]] f_t getFunctionOfImpl(n_t Inst) const { + assert(Inst != nullptr); + return Inst->getFunction(); + } + [[nodiscard]] g_t getGlobalVariableImpl(llvm::StringRef GlobalVariableName) const; [[nodiscard]] g_t diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFunctionDataFlowFacts.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFunctionDataFlowFacts.h deleted file mode 100644 index ec19a1b002..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFunctionDataFlowFacts.h +++ /dev/null @@ -1,89 +0,0 @@ -/****************************************************************************** - * 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, bulletSpace and others - *****************************************************************************/ - -#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_LLVMFUNCTIONDATAFLOWFACTS_H -#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_LLVMFUNCTIONDATAFLOWFACTS_H - -#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" -#include "phasar/PhasarLLVM/DataFlow/IfdsIde/FunctionDataFlowFacts.h" -#include "phasar/Utils/DefaultValue.h" - -#include "llvm/IR/Argument.h" -#include "llvm/IR/Function.h" - -#include -#include - -namespace psr::library_summary { - -class LLVMFunctionDataFlowFacts; -[[nodiscard]] LLVMFunctionDataFlowFacts -readFromFDFF(const FunctionDataFlowFacts &Fdff, const LLVMProjectIRDB &Irdb); - -/// @brief A LLVM-specific mapping of FunctionDataFlowFacts -class LLVMFunctionDataFlowFacts { -public: - LLVMFunctionDataFlowFacts() noexcept = default; - using ParameterMappingTy = FunctionDataFlowFacts::ParameterMappingTy; - - /// insert a set of data flow facts - void insertSet(const llvm::Function *Fun, uint32_t Index, - std::vector OutSet) { - - LLVMFdff[Fun].try_emplace(Index, std::move(OutSet)); - } - void insertSet(const llvm::Function *Fun, const llvm::Argument *Arg, - std::vector OutSet) { - - insertSet(Fun, Arg->getArgNo(), std::move(OutSet)); - } - - void addElement(const llvm::Function *Fun, uint32_t Index, DataFlowFact Out) { - LLVMFdff[Fun][Index].emplace_back(Out); - } - void addElement(const llvm::Function *Fun, const llvm::Argument *Arg, - DataFlowFact Out) { - addElement(Fun, Arg->getArgNo(), Out); - } - - [[nodiscard]] bool contains(const llvm::Function *Fn) { - return LLVMFdff.count(Fn); - } - - [[nodiscard]] const std::vector & - getFacts(const llvm::Function *Fun, uint32_t Index) { - auto Iter = LLVMFdff.find(Fun); - if (Iter != LLVMFdff.end()) { - return Iter->second[Index]; - } - return getDefaultValue>(); - } - [[nodiscard]] const std::vector & - getFacts(const llvm::Function *Fun, const llvm::Argument *Arg) { - return getFacts(Fun, Arg->getArgNo()); - } - - [[nodiscard]] const ParameterMappingTy & - getFactsForFunction(const llvm::Function *Fun) { - auto Iter = LLVMFdff.find(Fun); - if (Iter != LLVMFdff.end()) { - return Iter->second; - } - return getDefaultValue(); - } - - friend LLVMFunctionDataFlowFacts - readFromFDFF(const FunctionDataFlowFacts &Fdff, const LLVMProjectIRDB &Irdb); - -private: - std::unordered_map LLVMFdff; -}; -} // namespace psr::library_summary - -#endif // PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_LLVMFUNCTIONDATAFLOWFACTS_H diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.h index 4199dc4ee4..a6025c6254 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.h @@ -13,9 +13,9 @@ #include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" -#include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFunctionDataFlowFacts.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" +#include "phasar/PhasarLLVM/Utils/LLVMFunctionDataFlowFacts.h" #include #include diff --git a/include/phasar/PhasarLLVM/HelperAnalyses.h b/include/phasar/PhasarLLVM/HelperAnalyses.h index 63b9044444..156e1622bf 100644 --- a/include/phasar/PhasarLLVM/HelperAnalyses.h +++ b/include/phasar/PhasarLLVM/HelperAnalyses.h @@ -13,7 +13,9 @@ #include "phasar/ControlFlow/CallGraphAnalysisType.h" #include "phasar/ControlFlow/CallGraphData.h" #include "phasar/PhasarLLVM/HelperAnalysisConfig.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasSetData.h" +#include "phasar/Pointer/UnionFindAliasAnalysisType.h" #include "llvm/ADT/Twine.h" @@ -60,14 +62,14 @@ class HelperAnalyses { // NOLINT(cppcoreguidelines-special-member-functions) ~HelperAnalyses() noexcept; [[nodiscard]] LLVMProjectIRDB &getProjectIRDB(); - [[nodiscard]] LLVMAliasSet &getAliasInfo(); + [[nodiscard]] LLVMAliasInfoRef getAliasInfo(); [[nodiscard]] DIBasedTypeHierarchy &getTypeHierarchy(); [[nodiscard]] LLVMBasedICFG &getICFG(); [[nodiscard]] LLVMBasedCFG &getCFG(); private: std::unique_ptr IRDB; - std::unique_ptr PT; + LLVMAliasInfo PT{}; std::unique_ptr TH; std::unique_ptr ICF; std::unique_ptr CFG; @@ -78,6 +80,7 @@ class HelperAnalyses { // NOLINT(cppcoreguidelines-special-member-functions) // PTS std::optional PrecomputedPTS; AliasAnalysisType PTATy{}; + UnionFindAliasAnalysisType UFAATy{}; bool AllowLazyPTS{}; // ICF diff --git a/include/phasar/PhasarLLVM/HelperAnalysisConfig.h b/include/phasar/PhasarLLVM/HelperAnalysisConfig.h index be3427667b..d29c12c68d 100644 --- a/include/phasar/PhasarLLVM/HelperAnalysisConfig.h +++ b/include/phasar/PhasarLLVM/HelperAnalysisConfig.h @@ -14,6 +14,7 @@ #include "phasar/ControlFlow/CallGraphData.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasSetData.h" #include "phasar/Pointer/AliasAnalysisType.h" +#include "phasar/Pointer/UnionFindAliasAnalysisType.h" #include "phasar/Utils/Soundness.h" #include @@ -23,6 +24,7 @@ struct HelperAnalysisConfig { std::optional PrecomputedPTS = std::nullopt; std::optional PrecomputedCG = std::nullopt; AliasAnalysisType PTATy = AliasAnalysisType::CFLAnders; + UnionFindAliasAnalysisType UFAATy = UnionFindAliasAnalysisType::CtxIndSens; CallGraphAnalysisType CGTy = CallGraphAnalysisType::OTF; Soundness SoundnessLevel = Soundness::Soundy; bool AutoGlobalSupport = true; diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h b/include/phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h index bbe69a0f74..4bb0bc21f6 100644 --- a/include/phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h +++ b/include/phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h @@ -24,6 +24,11 @@ namespace psr { using LLVMAliasIteratorRef = AliasIteratorRef; +template +concept IsLLVMAliasIterator = + detail::IsAliasIteratorFor; + using LLVMAliasInfoRef = AliasInfoRef; diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMPointerAssignmentGraph.h b/include/phasar/PhasarLLVM/Pointer/LLVMPointerAssignmentGraph.h new file mode 100644 index 0000000000..7f118a7d4b --- /dev/null +++ b/include/phasar/PhasarLLVM/Pointer/LLVMPointerAssignmentGraph.h @@ -0,0 +1,170 @@ +#pragma once + +/****************************************************************************** + * Copyright (c) 2026 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 + *****************************************************************************/ + +#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" +#include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" +#include "phasar/Pointer/PointerAssignmentGraph.h" + +namespace llvm { +class Instruction; +class Value; +class Function; +} // namespace llvm + +namespace psr { + +namespace library_summary { +class LLVMFunctionDataFlowFacts; +} // namespace library_summary + +/// The PAG-node used for LLVM-based pointer-assignment graphs. It consists of +/// regular LLVM values + special return-slots per non-void function. In that +/// case, the stored pointer is the corresponding function. +/// +/// This class is compatible with llvm::TinyPtrVector and other APIs relying on +/// llvm::PointerLikeTypeTraits. +struct PAGVariable : public llvm::PointerIntPair { + using Base = llvm::PointerIntPair; + + struct Return { + const llvm::Function *Fun; + }; + + PAGVariable() noexcept = default; // To enable move-assign in TinyPtrVector + PAGVariable(const llvm::Value *Var) noexcept : Base(Var, false) {} + PAGVariable(Return RetVar) noexcept : Base(RetVar.Fun, true) { + assert(RetVar.Fun != nullptr); + } + + [[nodiscard]] bool isReturnVariable() const noexcept { + return this->getInt(); + } + + [[nodiscard]] bool isInFunction() const noexcept { + return isReturnVariable() || + llvm::isa(this->getPointer()); + } + + [[nodiscard]] const llvm::Function *getFunction() const noexcept { + const auto *Ptr = this->getPointer(); + if (isReturnVariable()) { + return llvm::cast(Ptr); + } + if (const auto *Arg = llvm::dyn_cast(Ptr)) { + return Arg->getParent(); + } + if (const auto *Inst = llvm::dyn_cast(Ptr)) { + return Inst->getFunction(); + } + + return nullptr; + } + + [[nodiscard]] const llvm::Value *get() const noexcept { + return this->getPointer(); + } + explicit operator const llvm::Value *() const noexcept { return get(); } + + [[nodiscard]] const llvm::Value *valueOrNull() const noexcept { + return isReturnVariable() ? nullptr : get(); + } + + friend bool operator==(PAGVariable V1, PAGVariable V2) noexcept { + return V1.getOpaqueValue() == V2.getOpaqueValue(); + } + friend bool operator!=(PAGVariable V1, PAGVariable V2) noexcept { + return !(V1 == V2); + } + + friend auto hash_value(PAGVariable V) noexcept { + return llvm::hash_value(V.getOpaqueValue()); + } +}; + +[[nodiscard]] std::string to_string(PAGVariable Var); + +/// Analysis domain for LLVM-IR pointer-assignment graphs. +/// Uses \c PAGVariable as the variable type and the standard LLVM instruction / +/// function / project-IRDB types from \c LLVMAnalysisDomainDefault. +struct LLVMPAGDomain : LLVMAnalysisDomainDefault { + using v_t = PAGVariable; +}; + +/// Concrete \c PAGBuilder for LLVM IR. +/// +/// Traverses all functions in the given \c LLVMProjectIRDB and emits PAG +/// nodes and edges to the provided \c PBStrategyRef. +/// +/// Achieves basic field sensitivity by handlking constant GEPs in an ad-hoc +/// manner. +/// +/// An optional \c LLVMFunctionDataFlowFacts object can be supplied to apply +/// pre-computed library summaries at potentially declaration-only calls. +class LLVMPAGBuilder : public PAGBuilder { +public: + constexpr LLVMPAGBuilder() noexcept = default; + /// \param MLSum Pre-computed library summary facts. If non-null, callee + /// analysis for matched library functions is replaced by these summaries. + constexpr LLVMPAGBuilder( + const library_summary::LLVMFunctionDataFlowFacts *MLSum) noexcept + : MLSum(MLSum) {} + + void buildPAG(const LLVMProjectIRDB &IRDB, ValueCompressor &VC, + pag::PBStrategyRef Strategy) override; + +private: + struct PAGBuildData; + + const library_summary::LLVMFunctionDataFlowFacts *MLSum{}; +}; + +} // namespace psr + +namespace llvm { +template <> struct PointerLikeTypeTraits { + static void *getAsVoidPointer(psr::PAGVariable P) { + return P.getOpaqueValue(); + } + + static psr::PAGVariable getFromVoidPointer(void *P) { + psr::PAGVariable V{nullptr}; + static_cast(V) = + psr::PAGVariable::Base::getFromOpaqueValue(P); + return V; + } + + static psr::PAGVariable getFromVoidPointer(const void *P) { + psr::PAGVariable V{nullptr}; + static_cast(V) = + psr::PAGVariable::Base::getFromOpaqueValue(P); + return V; + } + + static constexpr int NumLowBitsAvailable = + PointerLikeTypeTraits::NumLowBitsAvailable; +}; + +template <> struct DenseMapInfo { + static psr::PAGVariable getEmptyKey() noexcept { + return DenseMapInfo::getEmptyKey(); + } + static psr::PAGVariable getTombstoneKey() noexcept { + return DenseMapInfo::getTombstoneKey(); + } + static hash_code getHashValue(psr::PAGVariable V) noexcept { + return hash_value(V); + } + + static bool isEqual(psr::PAGVariable V1, psr::PAGVariable V2) noexcept { + return V1 == V2; + } +}; +} // namespace llvm diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMUnionFindAA.h b/include/phasar/PhasarLLVM/Pointer/LLVMUnionFindAA.h new file mode 100644 index 0000000000..30972bc9c7 --- /dev/null +++ b/include/phasar/PhasarLLVM/Pointer/LLVMUnionFindAA.h @@ -0,0 +1,465 @@ +#pragma once + +/****************************************************************************** + * Copyright (c) 2026 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 + *****************************************************************************/ + +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCallGraph.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" +#include "phasar/PhasarLLVM/Pointer/LLVMPointerAssignmentGraph.h" +#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" +#include "phasar/Pointer/AliasResult.h" +#include "phasar/Pointer/BottomupUnionFindAA.h" +#include "phasar/Pointer/RawAliasSet.h" +#include "phasar/Pointer/UnionFindAA.h" +#include "phasar/Utils/Macros.h" +#include "phasar/Utils/MapUtils.h" +#include "phasar/Utils/MaybeUniquePtr.h" +#include "phasar/Utils/NonNullPtr.h" +#include "phasar/Utils/Utilities.h" +#include "phasar/Utils/ValueCompressor.h" + +#include "llvm/IR/Instructions.h" +#include "llvm/Support/Casting.h" + +#include +#include + +namespace psr { +extern template class CallingContextSensUnionFindAA; +extern template class IndirectionSensUnionFindAA; +extern template class BottomupUnionFindAA; + +/// Returns a \c ValueId handler suitable for \c RawAliasSet::foreach() that +/// maps each alias \c ValueId back to all of its underlying \c llvm::Value* +/// (via \p VC), forwarding non-null values to \p Callback. +constexpr std::invocable auto +llvmUnionFindAliasHandler(const ValueCompressor &VC, + std::invocable auto Callback) { + return [&VC, Callback{copyOrRef(Callback)}](ValueId Alias) { + for (auto V : VC.id2vars(Alias)) { + if (const auto *LLVMVar = V.valueOrNull()) [[likely]] { + std::invoke(Callback, LLVMVar); + } + } + }; +} + +namespace pag { +/// Utility class to make pag::PBMixin implement PBStrategy. +class LLVMCGProvider : public LLVMPAGDomain { +public: + constexpr LLVMCGProvider(NonNullPtr CG) noexcept + : CG(CG) {} + + void withCalleesOfCallAt(n_t Inst, + std::invocable auto WithCallee) const { + for (const auto *Callee : CG->getCalleesOfCallAt(Inst)) { + std::invoke(WithCallee, Callee); + } + } + +private: + NonNullPtr CG; +}; +} // namespace pag + +/// CRTP mixin that adds the LLVM alias-iterator interface to a class that +/// holds a \c UnionFindAAResult and a \c ValueCompressor. +/// +/// Provides \c forallAliasesOf(), \c mayAlias(), and \c alias() overloads +/// accepting both \c llvm::Value* and \c ValueId arguments. Results are +/// reported as \c llvm::Value* via the stored \c ValueCompressor. +/// +/// The derived class must expose a \c VC member (pointer to a +/// \c ValueCompressor). +/// +/// \tparam Derived The CRTP derived class. +/// \tparam AAResT A type satisfying \c UnionFindAAResult. +template + requires UnionFindAAResult> +// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) +struct LLVMUnionFindAliasIteratorMixin { + [[no_unique_address]] AAResT AARes; + + using v_t = const llvm::Value *; + using n_t = const llvm::Instruction *; + + [[nodiscard]] decltype(auto) getRawAliasSet(ValueId ValId) const { + return AARes.getRawAliasSet(ValId); + } + + [[nodiscard]] const auto &base() const noexcept { return AARes; } + + void + forallAliasesOf(ValueId VId, const auto & /*Inst*/, + std::invocable auto Callback) const { + const auto &RawAliases = AARes.getRawAliasSet(VId); + RawAliases.foreach ( + llvmUnionFindAliasHandler(*self().VC, copyOrRef(Callback))); + } + + void + forallAliasesOf(const llvm::Value *Ptr, const auto &Inst, + std::invocable auto Callback) const { + if (auto ValId = self().VC->getOrNull(Ptr)) { + forallAliasesOf(*ValId, Inst, copyOrRef(Callback)); + } + } + + [[nodiscard]] bool mayAlias(ValueId Ptr1, ValueId Ptr2) const { + return AARes.mayAlias(Ptr1, Ptr2); + } + + [[nodiscard]] bool mayAlias(ValueId Ptr1, ValueId Ptr2, + const auto & /*AtInstruction*/) const { + return AARes.mayAlias(Ptr1, Ptr2); + } + + [[nodiscard]] bool mayAlias(const llvm::Value *Ptr1, + const llvm::Value *Ptr2) const { + auto ValId1 = self().VC->getOrNull(Ptr1); + auto ValId2 = self().VC->getOrNull(Ptr2); + + return ValId1 && ValId2 && mayAlias(*ValId1, *ValId2); + } + + [[nodiscard]] bool mayAlias(const llvm::Value *Ptr1, const llvm::Value *Ptr2, + const auto & /*AtInstruction*/) const { + return mayAlias(Ptr1, Ptr2); + } + + [[nodiscard]] AliasResult alias(const llvm::Value *Ptr1, + const llvm::Value *Ptr2, + const auto &AtInstruction) const { + auto ValId1 = self().VC->getOrNull(Ptr1); + auto ValId2 = self().VC->getOrNull(Ptr2); + if (!ValId1 || !ValId2) { + return AliasResult::NoAlias; + } + if (*ValId1 == *ValId2) { + // XXX: Check exactly the conditions for must alias! + return !llvm::isa(Ptr1) && + !llvm::isa(Ptr2) + ? AliasResult::MustAlias + : AliasResult::MayAlias; + } + return mayAlias(*ValId1, *ValId2, AtInstruction) ? AliasResult::MayAlias + : AliasResult::NoAlias; + } + + [[nodiscard]] constexpr const Derived &self() const noexcept { + return *static_cast(this); + } +}; + +template + requires UnionFindAAResult> +// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) +struct LLVMUnionFindAliasIterator + : public LLVMUnionFindAliasIteratorMixin, + AAResT> { + MaybeUniquePtr> VC; + + constexpr LLVMUnionFindAliasIterator( + AAResT &&AARes, MaybeUniquePtr> VC) + : psr::LLVMUnionFindAliasIteratorMixin, + AAResT>{PSR_FWD(AARes)}, + VC(std::move(VC)) {} +}; + +template +LLVMUnionFindAliasIterator(AAResT, + MaybeUniquePtr>) + -> LLVMUnionFindAliasIterator; +template +LLVMUnionFindAliasIterator(AAResT, const ValueCompressor *) + -> LLVMUnionFindAliasIterator; + +namespace detail { +class LLVMLocalUnionFindAliasIteratorBase { +public: + explicit LLVMLocalUnionFindAliasIteratorBase( + const ValueCompressor &VC); + +protected: + llvm::DenseMap> GlobalsOrInFun; +}; +} // namespace detail + +/// CRTP mixin adding a function-local view on top of a global +/// \c UnionFindAAResult. +/// +/// Extends \c LLVMUnionFindAliasIteratorMixin with \c forallAliasesOf() +/// overloads that accept an \c llvm::Function* or \c llvm::Instruction* +/// context. When a non-null context is provided, alias sets are intersected +/// with the set of variables that are visible in that function (globals plus +/// locals defined in that function), giving a function-local result even +/// though the underlying analysis is interprocedural. +template +class LLVMLocalUnionFindAliasIteratorMixin + : public detail::LLVMLocalUnionFindAliasIteratorBase { +public: + LLVMLocalUnionFindAliasIteratorMixin(AAResT &&AARes, + const ValueCompressor &VC) + : detail::LLVMLocalUnionFindAliasIteratorBase(VC), AARes(PSR_FWD(AARes)) { + } + + [[nodiscard]] decltype(auto) getRawAliasSet(ValueId ValId) const { + return AARes.getRawAliasSet(ValId); + } + + [[nodiscard]] auto getRawAliasSet(ValueId ValId, + const llvm::Function *Context) const { + auto Vars = AARes.getRawAliasSet(ValId); + if (Context) { + Vars &= getOrDefault(GlobalsOrInFun, Context); + } + return Vars; + } + + [[nodiscard]] const auto &base() const noexcept { return AARes; } + + [[nodiscard]] auto getRawAliasSet(ValueId ValId, + const llvm::Instruction *Context) const { + return getRawAliasSet(ValId, getFunction(Context)); + } + + void forallAliasesOf(ValueId VId, const llvm::Function *Context, + std::invocable auto WithAlias) { + const auto AliasHandler = + llvmUnionFindAliasHandler(*self().VC, copyOrRef(WithAlias)); + + auto &&RawVars = AARes.getRawAliasSet(VId); + if (Context) { + auto Vars = PSR_FWD(RawVars); + Vars &= getOrDefault(GlobalsOrInFun, Context); + Vars.foreach (AliasHandler); + } else { + RawVars.foreach (AliasHandler); + } + } + + void forallAliasesOf(const llvm::Value *Val, const llvm::Function *Context, + std::invocable auto WithAlias) { + if (auto ValId = self().VC->getOrNull(Val)) { + forallAliasesOf(*ValId, Context, copyOrRef(WithAlias)); + } + } + + void forallAliasesOf(ValueId ValId, const llvm::Instruction *AtInstruction, + std::invocable auto WithAlias) { + forallAliasesOf(ValId, psr::getFunction(AtInstruction), + copyOrRef(WithAlias)); + } + + void forallAliasesOf(const llvm::Value *Val, + const llvm::Instruction *AtInstruction, + std::invocable auto WithAlias) { + forallAliasesOf(Val, psr::getFunction(AtInstruction), copyOrRef(WithAlias)); + } + + void forallAliasesOf(const llvm::Value *Val, + std::invocable auto WithAlias) { + forallAliasesOf(Val, psr::getFunction(Val), copyOrRef(WithAlias)); + } + + [[nodiscard]] bool + mayAlias(ValueId ValId1, ValueId ValId2, + const llvm::Instruction * /*AtInstruction*/ = nullptr) const { + // XXX: Should we filter by AtInstruction-context here as well? + return AARes.mayAlias(ValId1, ValId2); + } + + [[nodiscard]] bool + mayAlias(const llvm::Value *Ptr1, const llvm::Value *Ptr2, + const llvm::Instruction * /*AtInstruction*/ = nullptr) const { + auto ValId1 = self().VC->getOrNull(Ptr1); + auto ValId2 = self().VC->getOrNull(Ptr2); + + // XXX: Should we filter by AtInstruction-context here as well? + return ValId1 && ValId2 && AARes.mayAlias(*ValId1, *ValId2); + } + + [[nodiscard]] AliasResult alias(const llvm::Value *Ptr1, + const llvm::Value *Ptr2, + const auto &AtInstruction) const { + auto ValId1 = self().VC->getOrNull(Ptr1); + auto ValId2 = self().VC->getOrNull(Ptr2); + if (!ValId1 || !ValId2) { + return AliasResult::NoAlias; + } + if (*ValId1 == *ValId2) { + // XXX: Check exactly the conditions for must alias! + return !llvm::isa(Ptr1) && + !llvm::isa(Ptr2) + ? AliasResult::MustAlias + : AliasResult::MayAlias; + } + return mayAlias(*ValId1, *ValId2, AtInstruction) ? AliasResult::MayAlias + : AliasResult::NoAlias; + } + + [[nodiscard]] constexpr const Derived &self() const noexcept { + return *static_cast(this); + } + +private: + AAResT AARes; +}; + +template +class LLVMLocalUnionFindAliasIterator + : public LLVMLocalUnionFindAliasIteratorMixin< + LLVMLocalUnionFindAliasIterator, AAResT> { + friend LLVMLocalUnionFindAliasIteratorMixin< + LLVMLocalUnionFindAliasIterator, AAResT>; + +public: + LLVMLocalUnionFindAliasIterator( + AAResT &&AARes, NonNullPtr> VC) + : LLVMLocalUnionFindAliasIteratorMixin< + LLVMLocalUnionFindAliasIterator, AAResT>(PSR_FWD(AARes), + *VC), + VC(VC) {} + +private: + NonNullPtr> VC; +}; + +/// Low-level entry point: builds the PAG for \p IRDB using the given +/// \p AnalysisT strategy, and returns the raw alias-analysis result (not +/// wrapped in an \c LLVMUnionFindAliasIterator). +/// +/// \param IRDB The project to analyze. +/// \param Ana A \c PBStrategy instance (e.g., \c BasicUnionFindAA, +/// \c BottomupUnionFindAA). Taken by forwarding reference and +/// consumed when results are extracted. +/// \param VC Optional pre-populated \c ValueCompressor. A new one is +/// allocated if null. +/// \param Impl PAG builder implementation (default: \c LLVMPAGBuilder). +template > PAGBuilderImpl = + LLVMPAGBuilder> +[[nodiscard]] inline UnionFindAAResult auto +computeUnionFindAARaw(const LLVMProjectIRDB &IRDB, AnalysisT &&Ana, + MaybeUniquePtr> VC = nullptr, + PAGBuilderImpl Impl = {}) { + if (!VC) { + VC = std::make_unique>(); + } + + Impl.buildPAG(IRDB, *VC, &Ana); + + const auto NumVars = VC->size(); + return PSR_FWD(Ana).consumeAAResults(NumVars); +} + +template > PAGBuilderImpl = + LLVMPAGBuilder> +[[nodiscard]] inline UnionFindAAResult auto +computeUnionFindAARaw(const LLVMProjectIRDB &IRDB, AnalysisT &&Ana, + const LLVMBasedCallGraph &CG, + MaybeUniquePtr> VC = nullptr, + PAGBuilderImpl Impl = {}) { + auto Strategy = pag::PBMixin{ + PSR_FWD(Ana), + pag::LLVMCGProvider{&CG}, + }; + return computeUnionFindAARaw(IRDB, Strategy, std::move(VC), std::move(Impl)); +} + +[[nodiscard]] CallingContextSensUnionFindAAResult computeCtxSensUnionFindAARaw( + const LLVMProjectIRDB &IRDB, const LLVMBasedCallGraph &CG, + MaybeUniquePtr> VC = nullptr); +[[nodiscard]] BasicUnionFindAAResult computeBotCtxSensUnionFindAARaw( + const LLVMProjectIRDB &IRDB, const LLVMBasedCallGraph &CG, + MaybeUniquePtr> VC = nullptr); +[[nodiscard]] BasicUnionFindAAResult computeIndSensUnionFindAARaw( + const LLVMProjectIRDB &IRDB, const LLVMBasedCallGraph &CG, + MaybeUniquePtr> VC = nullptr); +[[nodiscard]] UnionFindAAResultIntersection +computeCtxIndSensUnionFindAARaw( + const LLVMProjectIRDB &IRDB, const LLVMBasedCallGraph &CG, + MaybeUniquePtr> VC = nullptr); +[[nodiscard]] UnionFindAAResultIntersection +computeBotCtxIndSensUnionFindAARaw( + const LLVMProjectIRDB &IRDB, const LLVMBasedCallGraph &CG, + MaybeUniquePtr> VC = nullptr); + +/// Builds the PAG and returns a \c LLVMUnionFindAliasIterator (owns the \c +/// ValueCompressor and the raw result) that implements the \c +/// IsLLVMAliasIterator concept. +/// +/// \param IRDB The project to analyze. +/// \param Ana A \c PBStrategy instance. Consumed when results are extracted. +/// \param VC Optional pre-populated \c ValueCompressor. A new one is +/// allocated if null; the iterator takes ownership. +/// \param Impl PAG builder implementation (default: \c LLVMPAGBuilder). +template > PAGBuilderImpl = + LLVMPAGBuilder> +[[nodiscard]] inline IsLLVMAliasIterator auto +computeUnionFindAA(const LLVMProjectIRDB &IRDB, AnalysisT &&Ana, + MaybeUniquePtr> VC = nullptr, + PAGBuilderImpl Impl = {}) { + if (!VC) { + VC = std::make_unique>(); + } + + auto AARes = + computeUnionFindAARaw(IRDB, PSR_FWD(Ana), VC.get(), std::move(Impl)); + return LLVMUnionFindAliasIterator{ + std::move(AARes), + std::move(VC), + }; +} + +template > PAGBuilderImpl = + LLVMPAGBuilder> +[[nodiscard]] inline IsLLVMAliasIterator auto +computeUnionFindAA(const LLVMProjectIRDB &IRDB, AnalysisT &&Ana, + const LLVMBasedCallGraph &CG, + MaybeUniquePtr> VC = nullptr, + PAGBuilderImpl Impl = {}) { + auto Strategy = pag::PBMixin{ + PSR_FWD(Ana), + pag::LLVMCGProvider{&CG}, + }; + return computeUnionFindAA(IRDB, Strategy, std::move(VC), std::move(Impl)); +} + +[[nodiscard]] LLVMUnionFindAliasIterator +computeCtxSensUnionFindAA( + const LLVMProjectIRDB &IRDB, const LLVMBasedCallGraph &CG, + MaybeUniquePtr> VC = nullptr); +[[nodiscard]] LLVMUnionFindAliasIterator +computeBotCtxSensUnionFindAA( + const LLVMProjectIRDB &IRDB, const LLVMBasedCallGraph &CG, + MaybeUniquePtr> VC = nullptr); +[[nodiscard]] LLVMUnionFindAliasIterator +computeIndSensUnionFindAA( + const LLVMProjectIRDB &IRDB, const LLVMBasedCallGraph &CG, + MaybeUniquePtr> VC = nullptr); +[[nodiscard]] LLVMUnionFindAliasIterator> +computeCtxIndSensUnionFindAA( + const LLVMProjectIRDB &IRDB, const LLVMBasedCallGraph &CG, + MaybeUniquePtr> VC = nullptr); +[[nodiscard]] LLVMUnionFindAliasIterator> +computeBotCtxIndSensUnionFindAA( + const LLVMProjectIRDB &IRDB, const LLVMBasedCallGraph &CG, + MaybeUniquePtr> VC = nullptr); + +} // namespace psr diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMUnionFindAliasSet.h b/include/phasar/PhasarLLVM/Pointer/LLVMUnionFindAliasSet.h new file mode 100644 index 0000000000..06675f30da --- /dev/null +++ b/include/phasar/PhasarLLVM/Pointer/LLVMUnionFindAliasSet.h @@ -0,0 +1,216 @@ +#pragma once + +/****************************************************************************** + * Copyright (c) 2026 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 + *****************************************************************************/ + +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCallGraph.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" +#include "phasar/PhasarLLVM/Pointer/LLVMPointerAssignmentGraph.h" +#include "phasar/PhasarLLVM/Pointer/LLVMPointsToUtils.h" +#include "phasar/Pointer/AliasAnalysisType.h" +#include "phasar/Pointer/AliasInfoTraits.h" +#include "phasar/Pointer/AliasResult.h" +#include "phasar/Pointer/AliasSetOwner.h" +#include "phasar/Pointer/RawAliasSet.h" +#include "phasar/Pointer/UnionFindAliasAnalysisType.h" +#include "phasar/Utils/AnalysisProperties.h" +#include "phasar/Utils/TypedVector.h" +#include "phasar/Utils/ValueCompressor.h" + +#include +#include + +namespace llvm { +class Value; +class Instruction; +class Function; +} // namespace llvm + +namespace psr { + +class LLVMUnionFindAliasSet; +class LLVMProjectIRDB; + +template <> +struct AliasInfoTraits + : DefaultAATraits {}; + +/// Concrete \c IsAliasInfo implementation backed by a union-find alias +/// analysis. +/// +/// Constructed once from an \c LLVMProjectIRDB and a call graph; the chosen +/// analysis variant is determined by \c Config. Alias sets are materialized +/// lazily on first query and cached per \c ValueId in \c AliasSets. +/// +/// \note When \c AnalysisLocality::FunctionLocal is selected, alias sets are +/// filtered to variables visible in the function that contains the query +/// instruction. The per-\c ValueId cache does **not** account for the +/// instruction context, so the first caller's function wins — do not mix +/// queries to local pointers from different functions for the same value in +/// local mode. +class LLVMUnionFindAliasSet + : public AnalysisPropertiesMixin { +public: + using traits_t = AliasInfoTraits; + using n_t = traits_t::n_t; + using v_t = traits_t::v_t; + using AliasSetTy = traits_t::AliasSetTy; + using AliasSetPtrTy = traits_t::AliasSetPtrTy; + using AllocationSiteSetPtrTy = traits_t::AllocationSiteSetPtrTy; + + /// Whether alias sets are reported globally or filtered to the function + /// containing the query instruction. + enum class AnalysisLocality : uint8_t { + /// All aliases across all functions are reported. + Global, + /// Aliases are intersected with variables visible in the querying function + /// (globals + function-local values). + FunctionLocal, + }; + + struct Config { + /// The specific union-find analysis variant to run (default: + /// \c BotCtxIndSens — bottom-up, context- and indirection-sensitive). + UnionFindAliasAnalysisType AType = + UnionFindAliasAnalysisType::BotCtxIndSens; + /// Controls whether alias sets are scoped to the querying function. + AnalysisLocality ALocality = AnalysisLocality::Global; + }; + + explicit LLVMUnionFindAliasSet(const LLVMProjectIRDB *IRDB, + const LLVMBasedCallGraph &BaseCG, Config Cfg, + ValueCompressor *VC); + explicit LLVMUnionFindAliasSet(const LLVMProjectIRDB *IRDB, + const LLVMBasedCallGraph &BaseCG, Config Cfg) + : LLVMUnionFindAliasSet(IRDB, BaseCG, Cfg, nullptr) {} + explicit LLVMUnionFindAliasSet(const LLVMProjectIRDB *IRDB, + const LLVMBasedCallGraph &BaseCG) + : LLVMUnionFindAliasSet(IRDB, BaseCG, Config{}, nullptr) {} + + [[nodiscard]] constexpr std::true_type isInterProcedural() const noexcept { + return {}; + }; + + [[nodiscard]] constexpr std::integral_constant + getAliasAnalysisType() const noexcept { + return {}; + }; + + [[nodiscard]] constexpr AnalysisProperties + getAnalysisProperties() const noexcept { + return Props; + } + + [[nodiscard]] constexpr AliasResult alias(v_t V1, v_t V2, n_t I) const { + assert(isValid()); + return AARes->alias(V1, V2, I); + } + + void foreachAliasOf(v_t V, n_t I, + llvm::function_ref WithAlias) const { + assert(isValid()); + AARes->forallAliasesOf(V, I, WithAlias); + } + + [[nodiscard]] AliasSetPtrTy getAliasSet(v_t V, n_t I) { + assert(isValid()); + auto ValId = AARes->VC->getOrNull(V); + if (!ValId) { + return getEmptyAliasSet(); + } + + assert(AliasSets.inbounds(*ValId)); + if (!AliasSets[*ValId]) [[unlikely]] { + AliasSets[*ValId] = AARes->constructAliasSet(*ValId, I, Owner); + } + + return AliasSets[*ValId]; + } + + [[nodiscard]] AllocationSiteSetPtrTy + getReachableAllocationSites(v_t V, bool IntraProcOnly, n_t I) const { + assert(isValid()); + auto ValId = AARes->VC->getOrNull(V); + if (!ValId) { + return std::make_unique(); + } + + return AARes->constructReachableAllocSites(V, *ValId, IntraProcOnly, I); + } + + [[nodiscard]] bool isInReachableAllocationSites( + const llvm::Value *V, const llvm::Value *PotentialValue, + bool IntraProcOnly, const llvm::Instruction *I) const { + assert(isValid()); + if (!psr::isInterestingPointer(V)) { + return false; + } + + if (!psr::isInReachableAllocationSitesTy(V, PotentialValue, + IntraProcOnly)) { + return false; + } + + return alias(V, PotentialValue, I) != AliasResult::NoAlias; + } + + void print(llvm::raw_ostream &OS) const; + void printAsJson(llvm::raw_ostream &OS) const; + + [[nodiscard]] bool isValid() const noexcept { + return AARes != nullptr && Props != AnalysisProperties::None && + AARes->VC != nullptr && AARes->VC->size() == AliasSets.size(); + } + +private: + struct UnionFindAAResultConcept { + MaybeUniquePtr> VC; + std::optional> AllocationSites{}; + + UnionFindAAResultConcept( + MaybeUniquePtr> VC) noexcept + : VC(std::move(VC)) {} + virtual ~UnionFindAAResultConcept() = default; + + virtual void forallAliasesOf(v_t Ptr, n_t Inst, + llvm::function_ref Callback) = 0; + + virtual AliasResult alias(v_t Ptr1, v_t Ptr2, n_t AtInstruction) = 0; + + virtual AliasSetPtrTy + constructAliasSet(ValueId ValId, n_t Inst, + AliasSetOwner &Owner) = 0; + + virtual AllocationSiteSetPtrTy + constructReachableAllocSites(v_t V, ValueId ValId, bool IntraProcOnly, + n_t Inst) = 0; + + virtual void print(llvm::raw_ostream &OS, Config Cfg) const = 0; + }; + + template